Skip to content

Commit 1735e37

Browse files
2 parents 09cfd91 + 53b2814 commit 1735e37

18 files changed

Lines changed: 254 additions & 81 deletions

File tree

4.8.1/Numerics/Data/Statistics/Statistics.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ public static double[] ProductMoments(IList<double> data)
471471
{
472472
if (data == null) throw new ArgumentNullException(nameof(data));
473473
double N = data.Count;
474-
if (N < 4) throw new ArgumentException("There must be more than 4 data points.", nameof(data));
474+
if (N < 4) return [double.NaN, double.NaN, double.NaN, double.NaN];
475475

476476
// sums of powers
477477
double X1 = 0, X2 = 0, X3 = 0, X4 = 0;
@@ -524,8 +524,8 @@ public static double[] LinearMoments(IList<double> data)
524524
{
525525
if (data == null) throw new ArgumentNullException(nameof(data));
526526
double N = data.Count;
527-
if (N < 4) throw new ArgumentException("There must be more than 4 data points.", nameof(data));
528-
527+
if (N < 4) return [double.NaN, double.NaN, double.NaN, double.NaN];
528+
529529
// Copy and sort data
530530
var sortedData = data.ToArray();
531531
Array.Sort(sortedData);

4.8.1/Numerics/Distributions/Univariate/CompetingRisks.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,8 @@ public override XElement ToXElement()
940940
result.SetAttributeValue(nameof(Type), Type.ToString());
941941
result.SetAttributeValue(nameof(XTransform), XTransform.ToString());
942942
result.SetAttributeValue(nameof(ProbabilityTransform), ProbabilityTransform.ToString());
943+
result.SetAttributeValue(nameof(MinimumOfRandomVariables), MinimumOfRandomVariables.ToString());
944+
result.SetAttributeValue(nameof(Dependency), Dependency.ToString());
943945
result.SetAttributeValue(nameof(Distributions), String.Join("|", Distributions.Select(x => x.Type)));
944946
// Parameters
945947
var parms = GetParameters;
@@ -949,6 +951,36 @@ public override XElement ToXElement()
949951
parmStrings[i] = parms[i].ToString("G17", CultureInfo.InvariantCulture);
950952
}
951953
result.SetAttributeValue("Parameters", String.Join("|", parmStrings));
954+
955+
// Correlation matrix
956+
var corrMatrixElement = new XElement(nameof(CorrelationMatrix));
957+
if (CorrelationMatrix != null
958+
&& CorrelationMatrix.GetLength(0) == Distributions.Count
959+
&& CorrelationMatrix.GetLength(1) == Distributions.Count)
960+
{
961+
int rows = Distributions.Count;
962+
int cols = Distributions.Count;
963+
var row = new double[cols];
964+
965+
for (int i = 0; i < rows; i++)
966+
{
967+
var corrRowElement = new XElement("Correlation_Row");
968+
969+
// collect one row of the 2D array
970+
for (int j = 0; j < cols; j++)
971+
{
972+
row[j] = _correlationMatrix[i, j];
973+
}
974+
975+
// format each double to "G17" with invariant culture
976+
var formatted = row.Select(v => v.ToString("G17", CultureInfo.InvariantCulture));
977+
// join with '|' and set as the element's text
978+
corrRowElement.Value = string.Join("|", formatted);
979+
corrMatrixElement.Add(corrRowElement);
980+
}
981+
}
982+
result.Add(corrMatrixElement);
983+
952984
return result;
953985
}
954986

@@ -989,6 +1021,18 @@ public static CompetingRisks FromXElement(XElement xElement)
9891021
Enum.TryParse(xElement.Attribute(nameof(ProbabilityTransform)).Value, out Transform probabilityTransform);
9901022
competingRisks.ProbabilityTransform = probabilityTransform;
9911023
}
1024+
if (xElement.Attribute(nameof(MinimumOfRandomVariables)) != null)
1025+
{
1026+
bool.TryParse(xElement.Attribute(nameof(MinimumOfRandomVariables)).Value, out bool minOfValues);
1027+
competingRisks.MinimumOfRandomVariables = minOfValues;
1028+
}
1029+
if (xElement.Attribute(nameof(Dependency)) != null)
1030+
{
1031+
Enum.TryParse(xElement.Attribute(nameof(Dependency)).Value, out Probability.DependencyType dependency);
1032+
competingRisks.Dependency = dependency;
1033+
}
1034+
1035+
// Parameters
9921036
if (xElement.Attribute("Parameters") != null)
9931037
{
9941038
var vals = xElement.Attribute("Parameters").Value.Split('|');
@@ -1001,12 +1045,46 @@ public static CompetingRisks FromXElement(XElement xElement)
10011045
competingRisks.SetParameters(parameters);
10021046
}
10031047

1048+
// Correlation matrix
1049+
var corrMatrixElement = xElement.Element(nameof(CorrelationMatrix));
1050+
if (corrMatrixElement != null)
1051+
{
1052+
var _corrMatrix = new double[competingRisks.Distributions.Count, competingRisks.Distributions.Count];
1053+
int counter = 0;
1054+
foreach (var rowEl in corrMatrixElement.Elements("Correlation_Row"))
1055+
{
1056+
if (counter >= competingRisks.Distributions.Count)
1057+
break;
1058+
1059+
// Split on '|' to get each stringified value
1060+
var parts = rowEl.Value.Split('|');
1061+
int maxCols = Math.Min(parts.Length, competingRisks.Distributions.Count);
1062+
1063+
for (int j = 0; j < maxCols; j++)
1064+
{
1065+
// Try to parse each part; if it fails, leave as 0 or assign NaN if you prefer
1066+
if (double.TryParse(parts[j],NumberStyles.Any,CultureInfo.InvariantCulture, out var p))
1067+
{
1068+
_corrMatrix[counter, j] = p;
1069+
}
1070+
else
1071+
{
1072+
_corrMatrix[counter, j] = double.NaN;
1073+
}
1074+
}
1075+
1076+
counter++;
1077+
}
1078+
competingRisks.CorrelationMatrix = _corrMatrix;
1079+
}
1080+
10041081
return competingRisks;
10051082
}
10061083
else
10071084
{
10081085
return null;
10091086
}
10101087
}
1088+
10111089
}
10121090
}

4.8.1/Numerics/Distributions/Univariate/GeneralizedBeta.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public static GeneralizedBeta PERT(double min, double mode, double max, double s
124124
// get alpha and beta
125125
double mean = (min + scale * mode + max) / (scale + 2d);
126126
double alpha = 1d + scale / 2d;
127-
if (mean != mode)
127+
if (!mean.AlmostEquals(mode))
128128
{
129129
alpha = (mean - min) * (2d * mode - min - max) / ((mode - mean) * (max - min));
130130
}

4.8.1/Numerics/Distributions/Univariate/Pert.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ public override double Mean
186186
{
187187
get
188188
{
189-
if (_min == _max && _min == _mode) return _min;
189+
if (_min.AlmostEquals(_max) && _min.AlmostEquals(_mode)) return _min;
190190
return (Min + 4 * MostLikely + Max) / 6;
191191
}
192192
}
@@ -196,7 +196,7 @@ public override double Median
196196
{
197197
get
198198
{
199-
if (_min == _max && _min == _mode) return _min;
199+
if (_min.AlmostEquals(_max) && _min.AlmostEquals(_mode)) return _min;
200200
return (Min + 6 * MostLikely + Max) / 8;
201201
}
202202
}
@@ -405,7 +405,7 @@ public override double PDF(double x)
405405
if (_parametersValid == false) ValidateParameters(Min, Mode, Max, true);
406406
// These checks are done specifically for an application where a
407407
// user inputs min = max = mode
408-
if (_min == _max && _min == _mode) return 0.0d;
408+
if (_min.AlmostEquals(_max) && _min.AlmostEquals(_mode)) return 0.0d;
409409
if (double.IsNaN(_mode)) return 0.0d;
410410
return _beta.PDF(x);
411411
}
@@ -417,7 +417,7 @@ public override double CDF(double x)
417417
if (_parametersValid == false) ValidateParameters(Min, Mode, Max, true);
418418
// These checks are done specifically for an application where a
419419
// user inputs min = max = mode
420-
if (_min == _max && _min == _mode) return 1d;
420+
if (_min.AlmostEquals(_max) && _min.AlmostEquals(_mode)) return 1d;
421421
if (double.IsNaN(_mode)) return 1;
422422
return _beta.CDF(x);
423423
}
@@ -429,7 +429,7 @@ public override double InverseCDF(double probability)
429429
if (_parametersValid == false) ValidateParameters(Min, Mode, Max, true);
430430
// These checks are done specifically for an application where a
431431
// user inputs min = max = mode
432-
if (_min == _max && _min == _mode) return Min;
432+
if (_min.AlmostEquals(_max) && _min.AlmostEquals(_mode)) return Min;
433433
if (double.IsNaN(_mode)) return Min;
434434
return _beta.InverseCDF(probability);
435435
}

4.8.1/Numerics/Distributions/Univariate/PertPercentile.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ public override double Mean
220220
{
221221
get
222222
{
223-
if (_beta.Min == _beta.Max) return _beta.Min;
223+
if (_beta.Min.AlmostEquals(_beta.Max)) return _beta.Min;
224224
return _beta.Mean < MinAllowableValue ? MinAllowableValue : _beta.Mean > MaxAllowableValue ? MaxAllowableValue : _beta.Mean;
225225
}
226226
}
@@ -230,7 +230,7 @@ public override double Median
230230
{
231231
get
232232
{
233-
if (_beta.Min == _beta.Max) return _beta.Min;
233+
if (_beta.Min.AlmostEquals(_beta.Max)) return _beta.Min;
234234
return _beta.Median < MinAllowableValue ? MinAllowableValue : _beta.Median > MaxAllowableValue ? MaxAllowableValue : _beta.Median;
235235
}
236236
}
@@ -240,7 +240,7 @@ public override double Mode
240240
{
241241
get
242242
{
243-
if (_beta.Min == _beta.Max) return _beta.Min;
243+
if (_beta.Min.AlmostEquals(_beta.Max)) return _beta.Min;
244244
return _beta.Mode < MinAllowableValue ? MinAllowableValue : _beta.Mode > MaxAllowableValue ? MaxAllowableValue : _beta.Mode;
245245
}
246246
}
@@ -404,7 +404,7 @@ public override double PDF(double x)
404404
if (_parametersSolved == false) SolveParameters();
405405
// These checks are done specifically for an application where a
406406
// user inputs min = max = mode
407-
if (_beta.Min == _beta.Max) return 0.0d;
407+
if (_beta.Min.AlmostEquals(_beta.Max) && _beta.Min.AlmostEquals(_beta.Mode)) return 0.0d;
408408
if (double.IsNaN(_beta.Mode)) return 0.0d;
409409
//
410410
if (x < MinAllowableValue) x = MinAllowableValue;
@@ -419,7 +419,7 @@ public override double CDF(double x)
419419
if (_parametersSolved == false) SolveParameters();
420420
// These checks are done specifically for an application where a
421421
// user inputs min = max = mode
422-
if (_beta.Min == _beta.Max) return 1d;
422+
if (_beta.Min.AlmostEquals(_beta.Max) && _beta.Min.AlmostEquals(_beta.Mode)) return 1d;
423423
if (double.IsNaN(_beta.Mode)) return 1d;
424424
//
425425
if (x < MinAllowableValue) x = MinAllowableValue;
@@ -439,7 +439,7 @@ public override double InverseCDF(double probability)
439439
if (x > MaxAllowableValue) x = MaxAllowableValue;
440440
// These checks are done specifically for an application where a
441441
// user inputs min = max = mode
442-
if (_beta.Min == _beta.Max) return x;
442+
if (_beta.Min.AlmostEquals(_beta.Max) && _beta.Min.AlmostEquals(_beta.Mode)) return x;
443443
if (double.IsNaN(_beta.Mode)) return x;
444444
//
445445
try

4.8.1/Numerics/Distributions/Univariate/PertPercentileZ.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ public override double Mean
209209
{
210210
get
211211
{
212-
if (_beta.Min == _beta.Max) return Normal.StandardCDF(_beta.Min);
212+
if (_beta.Min.AlmostEquals(_beta.Max)) return Normal.StandardCDF(_beta.Min);
213213
return Normal.StandardCDF(_beta.Mean);
214214
}
215215
}
@@ -219,7 +219,7 @@ public override double Median
219219
{
220220
get
221221
{
222-
if (_beta.Min == _beta.Max) return Normal.StandardCDF(_beta.Min);
222+
if (_beta.Min.AlmostEquals(_beta.Max)) return Normal.StandardCDF(_beta.Min);
223223
return Normal.StandardCDF(_beta.Median);
224224
}
225225
}
@@ -229,7 +229,7 @@ public override double Mode
229229
{
230230
get
231231
{
232-
if (_beta.Min == _beta.Max) return Normal.StandardCDF(_beta.Min);
232+
if (_beta.Min.AlmostEquals(_beta.Max)) return Normal.StandardCDF(_beta.Min);
233233
return Normal.StandardCDF(_beta.Mode);
234234
}
235235
}
@@ -413,7 +413,7 @@ public override double PDF(double x)
413413
if (_parametersSolved == false) SolveParameters();
414414
// These checks are done specifically for an application where a
415415
// user inputs min = max = mode
416-
if (_beta.Min == _beta.Max) return 0.0d;
416+
if (_beta.Min.AlmostEquals(_beta.Max) && _beta.Min.AlmostEquals(_beta.Mode)) return 0.0d;
417417
if (double.IsNaN(_beta.Mode)) return 0.0d;
418418
//
419419
return _beta.PDF(Normal.StandardZ(x));
@@ -428,7 +428,7 @@ public override double CDF(double x)
428428
if (_parametersSolved == false) SolveParameters();
429429
// These checks are done specifically for an application where a
430430
// user inputs min = max = mode
431-
if (_beta.Min == _beta.Max) return 1d;
431+
if (_beta.Min.AlmostEquals(_beta.Max) && _beta.Min.AlmostEquals(_beta.Mode)) return 1d;
432432
if (double.IsNaN(_beta.Mode)) return 1d;
433433
//
434434
return _beta.CDF(Normal.StandardZ(x));
@@ -443,7 +443,7 @@ public override double InverseCDF(double probability)
443443
var x = Normal.StandardCDF(_beta.Min);
444444
// These checks are done specifically for an application where a
445445
// user inputs min = max = mode
446-
if (_beta.Min == _beta.Max) return x;
446+
if (_beta.Min.AlmostEquals(_beta.Max) && _beta.Min.AlmostEquals(_beta.Mode)) return x;
447447
if (double.IsNaN(_beta.Mode)) return x;
448448
//
449449
try

4.8.1/Numerics/Distributions/Univariate/Poisson.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -259,10 +259,17 @@ public override double InverseCDF(double probability)
259259
if (_parametersValid == false)
260260
ValidateParameters([Lambda], true);
261261

262-
double lower = Minimum;
263-
double upper = Mean;
264-
Brent.Bracket(x => CDF(x) - probability, ref lower, ref upper, out var f1, out var f2);
265-
return Brent.Solve(x => CDF(x) - probability, lower, upper);
262+
int k = (int)Math.Max(0, Math.Floor(Lambda)); // start near the mean
263+
264+
// Move downward if needed
265+
while (k > 0 && CDF(k - 1) >= probability)
266+
k--;
267+
268+
// Move upward to find first k such that CDF(k) >= probability
269+
while (CDF(k) < probability)
270+
k++;
271+
272+
return k;
266273
}
267274

268275
/// <inheritdoc/>

4.8.1/Numerics/Distributions/Univariate/Triangular.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ public override double PDF(double x)
399399
if (_parametersValid == false)
400400
ValidateParameters(Min, MostLikely, Max, true);
401401
//
402-
if (Min == Max && Min == MostLikely) return 0.0d;
402+
if (Min.AlmostEquals(Max) && Min.AlmostEquals(MostLikely)) return 0.0d;
403403
if (x < Minimum || x > Maximum) return 0.0d;
404404
if (x >= Min && x < MostLikely)
405405
{
@@ -422,7 +422,7 @@ public override double CDF(double x)
422422
// Validate parameters
423423
if (_parametersValid == false)
424424
ValidateParameters(Min, MostLikely, Max, true);
425-
if (Min == Max && Min == MostLikely) return 1d;
425+
if (Min.AlmostEquals(Max) && Min.AlmostEquals(MostLikely)) return 1d;
426426
if (x <= Minimum) return 0d;
427427
if (x >= Maximum) return 1d;
428428
if (x > Min && x <= MostLikely)
@@ -442,7 +442,7 @@ public override double InverseCDF(double probability)
442442
// Validate probability
443443
if (probability < 0.0d || probability > 1.0d)
444444
throw new ArgumentOutOfRangeException("probability", "Probability must be between 0 and 1.");
445-
if (Min == Max && Min == MostLikely) return Min;
445+
if (Min.AlmostEquals(Max) && Min.AlmostEquals(MostLikely)) return Min;
446446
if (probability == 0.0d) return Minimum;
447447
if (probability == 1.0d) return Maximum;
448448
// Validate parameters

4.8.1/Numerics/Utilities/ExtensionMethods.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public static T GetAttributeOfType<T>(this Enum enumValue) where T : Attribute
7878
/// <param name="epsilon">The absolute tolerance level. Default = 1E-15.</param>
7979
public static bool AlmostEquals(this double a, double b, double epsilon = 1E-15)
8080
{
81-
return Math.Abs(a - b) < epsilon;
81+
return Math.Abs(a - b) <= epsilon;
8282
}
8383

8484
#endregion

8.0/Numerics/Data/Statistics/Statistics.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ public static double[] ProductMoments(IList<double> data)
466466
{
467467
if (data == null) throw new ArgumentNullException(nameof(data));
468468
double N = data.Count;
469-
if (N < 4) throw new ArgumentException("There must be more than 4 data points.", nameof(data));
469+
if (N < 4) return [double.NaN, double.NaN, double.NaN, double.NaN];
470470

471471
// sums of powers
472472
double X1 = 0, X2 = 0, X3 = 0, X4 = 0;
@@ -519,7 +519,7 @@ public static double[] LinearMoments(IList<double> data)
519519
{
520520
if (data == null) throw new ArgumentNullException(nameof(data));
521521
double N = data.Count;
522-
if (N < 4) throw new ArgumentException("There must be more than 4 data points.", nameof(data));
522+
if (N < 4) return [double.NaN, double.NaN, double.NaN, double.NaN];
523523

524524
// Copy and sort data
525525
var sortedData = data.ToArray();

0 commit comments

Comments
 (0)