1- using MathNet . Numerics . LinearAlgebra ;
1+ using MathNet . Numerics . Distributions ;
2+ using MathNet . Numerics . LinearAlgebra ;
3+ using System ;
4+ using System . Linq ;
25
36namespace MathNet . Numerics . Optimization
47{
8+ /// <summary>
9+ /// Represents the result of a nonlinear minimization operation, including
10+ /// the optimal parameters and various statistical measures of fitness.
11+ /// </summary>
512 public class NonlinearMinimizationResult
613 {
14+ /// <summary>
15+ /// The objective model evaluated at the minimum point.
16+ /// </summary>
717 public IObjectiveModel ModelInfoAtMinimum { get ; }
818
919 /// <summary>
@@ -16,6 +26,30 @@ public class NonlinearMinimizationResult
1626 /// </summary>
1727 public Vector < double > StandardErrors { get ; private set ; }
1828
29+ /// <summary>
30+ /// Returns the t-statistics for each parameter (parameter value / standard error).
31+ /// These measure how many standard deviations each parameter is from zero.
32+ /// </summary>
33+ public Vector < double > TStatistics { get ; private set ; }
34+
35+ /// <summary>
36+ /// Returns the p-values for each parameter based on t-distribution.
37+ /// Lower p-values indicate statistically significant parameters.
38+ /// </summary>
39+ public Vector < double > PValues { get ; private set ; }
40+
41+ /// <summary>
42+ /// Returns the dependency values for each parameter, measuring how linearly related
43+ /// each parameter is to the others. Values close to 1 indicate high dependency (multicollinearity).
44+ /// </summary>
45+ public Vector < double > Dependencies { get ; private set ; }
46+
47+ /// <summary>
48+ /// Returns the half-width of the confidence intervals for each parameter at the 95% confidence level.
49+ /// These represent the margin of error for each parameter estimate.
50+ /// </summary>
51+ public Vector < double > ConfidenceIntervalHalfWidths { get ; private set ; }
52+
1953 /// <summary>
2054 /// Returns the y-values of the fitted model that correspond to the independent values.
2155 /// </summary>
@@ -31,19 +65,69 @@ public class NonlinearMinimizationResult
3165 /// </summary>
3266 public Matrix < double > Correlation { get ; private set ; }
3367
68+ /// <summary>
69+ /// Returns the residual sum of squares at the minimum point,
70+ /// calculated as F(p) = 1/2 * ∑(residuals²).
71+ /// </summary>
72+ public double MinimizedValue => ModelInfoAtMinimum . Value ;
73+
74+ /// <summary>
75+ /// Root Mean Squared Error (RMSE) - measures the average magnitude of errors.
76+ /// </summary>
77+ public double RootMeanSquaredError { get ; private set ; }
78+
79+ /// <summary>
80+ /// Coefficient of determination (R-Square) - proportion of variance explained by the model.
81+ /// </summary>
82+ public double RSquared { get ; private set ; }
83+
84+ /// <summary>
85+ /// Adjusted R-Squre - accounts for the number of predictors in the model.
86+ /// </summary>
87+ public double AdjustedRSquared { get ; private set ; }
88+
89+ /// <summary>
90+ /// Residual standard error of the regression, calculated as sqrt(RSS/df) where RSS is the
91+ /// residual sum of squares and df is the degrees of freedom. This measures the average
92+ /// distance between the observed values and the fitted model.
93+ /// </summary>
94+ public double StandardError { get ; private set ; }
95+
96+ /// <summary>
97+ /// Pearson correlation coefficient between observed and predicted values.
98+ /// </summary>
99+ public double CorrelationCoefficient { get ; private set ; }
100+
101+ /// <summary>
102+ /// Number of iterations performed during optimization.
103+ /// </summary>
34104 public int Iterations { get ; }
35105
106+ /// <summary>
107+ /// Reason why the optimization algorithm terminated.
108+ /// </summary>
36109 public ExitCondition ReasonForExit { get ; }
37110
111+ /// <summary>
112+ /// Creates a new instance of the NonlinearMinimizationResult class.
113+ /// </summary>
114+ /// <param name="modelInfo">The objective model at the minimizing point.</param>
115+ /// <param name="iterations">The number of iterations performed.</param>
116+ /// <param name="reasonForExit">The reason why the algorithm terminated.</param>
38117 public NonlinearMinimizationResult ( IObjectiveModel modelInfo , int iterations , ExitCondition reasonForExit )
39118 {
40119 ModelInfoAtMinimum = modelInfo ;
41120 Iterations = iterations ;
42121 ReasonForExit = reasonForExit ;
43122
44123 EvaluateCovariance ( modelInfo ) ;
124+ EvaluateGoodnessOfFit ( modelInfo ) ;
45125 }
46126
127+ /// <summary>
128+ /// Evaluates the covariance matrix, correlation matrix, standard errors, t-statistics and p-values.
129+ /// </summary>
130+ /// <param name="objective">The objective model at the minimizing point.</param>
47131 void EvaluateCovariance ( IObjectiveModel objective )
48132 {
49133 objective . EvaluateAt ( objective . Point ) ; // Hessian may be not yet updated.
@@ -66,15 +150,208 @@ void EvaluateCovariance(IObjectiveModel objective)
66150 {
67151 StandardErrors = Covariance . Diagonal ( ) . PointwiseSqrt ( ) ;
68152
153+ // Calculate t-statistics and p-values
154+ TStatistics = Vector < double > . Build . Dense ( StandardErrors . Count ) ;
155+ for ( var i = 0 ; i < StandardErrors . Count ; i ++ )
156+ {
157+ TStatistics [ i ] = StandardErrors [ i ] > double . Epsilon
158+ ? objective . Point [ i ] / StandardErrors [ i ]
159+ : double . NaN ;
160+ }
161+
162+ // Calculate p-values based on t-distribution with DegreeOfFreedom
163+ PValues = Vector < double > . Build . Dense ( TStatistics . Count ) ;
164+ var tDist = new StudentT ( 0 , 1 , objective . DegreeOfFreedom ) ;
165+
166+ // Calculate confidence interval half-widths (for 95% confidence)
167+ ConfidenceIntervalHalfWidths = Vector < double > . Build . Dense ( StandardErrors . Count ) ;
168+ var tCritical = tDist . InverseCumulativeDistribution ( 0.975 ) ; // Two-tailed, 95% confidence
169+
170+ for ( var i = 0 ; i < TStatistics . Count ; i ++ )
171+ {
172+ // Two-tailed p-value from t-distribution
173+ var tStat = Math . Abs ( TStatistics [ i ] ) ;
174+ PValues [ i ] = 2 * ( 1 - tDist . CumulativeDistribution ( tStat ) ) ;
175+
176+ // Calculate confidence interval half-width
177+ ConfidenceIntervalHalfWidths [ i ] = tCritical * StandardErrors [ i ] ;
178+ }
179+
69180 var correlation = Covariance . Clone ( ) ;
70181 var d = correlation . Diagonal ( ) . PointwiseSqrt ( ) ;
71182 var dd = d . OuterProduct ( d ) ;
72183 Correlation = correlation . PointwiseDivide ( dd ) ;
184+
185+ // Calculate dependencies (measure of multicollinearity)
186+ Dependencies = Vector < double > . Build . Dense ( Correlation . RowCount ) ;
187+ for ( var i = 0 ; i < Correlation . RowCount ; i ++ )
188+ {
189+ // For each parameter, calculate 1 - 1/VIF where VIF is the variance inflation factor
190+ // VIF is calculated as 1/(1-R²) where R² is the coefficient of determination when
191+ // parameter i is regressed against all other parameters
192+
193+ // Extract the correlation coefficients related to parameter i (excluding self-correlation)
194+ var correlations = Vector < double > . Build . Dense ( Correlation . RowCount - 1 ) ;
195+ var index = 0 ;
196+ for ( var j = 0 ; j < Correlation . RowCount ; j ++ )
197+ {
198+ if ( j != i )
199+ {
200+ correlations [ index ++ ] = Correlation [ i , j ] ;
201+ }
202+ }
203+
204+ // Calculate the square of the multiple correlation coefficient
205+ // In the simple case, this is the maximum of the squared individual correlations
206+ var maxSquaredCorrelation = 0.0 ;
207+ for ( var j = 0 ; j < correlations . Count ; j ++ )
208+ {
209+ var squaredCorr = correlations [ j ] * correlations [ j ] ;
210+ if ( squaredCorr > maxSquaredCorrelation )
211+ {
212+ maxSquaredCorrelation = squaredCorr ;
213+ }
214+ }
215+
216+ // Calculate dependency = 1 - 1/VIF
217+ var maxSquaredCorrelationCapped = Math . Min ( maxSquaredCorrelation , 0.9999 ) ;
218+ var vif = 1.0 / ( 1.0 - maxSquaredCorrelationCapped ) ;
219+ Dependencies [ i ] = 1.0 - 1.0 / vif ;
220+ }
73221 }
74222 else
75223 {
76224 StandardErrors = null ;
225+ TStatistics = null ;
226+ PValues = null ;
227+ ConfidenceIntervalHalfWidths = null ;
77228 Correlation = null ;
229+ Dependencies = null ;
230+ }
231+ }
232+
233+ /// <summary>
234+ /// Evaluates goodness of fit statistics like R-squared, RMSE, etc.
235+ /// </summary>
236+ /// <param name="objective">The objective model at the minimizing point.</param>
237+ void EvaluateGoodnessOfFit ( IObjectiveModel objective )
238+ {
239+ // Note: GoodnessOfFit class methods do not support weights, so we apply weighting manually here.
240+
241+ // Check if we have the essentials for calculating statistics
242+ var hasResiduals = objective . Residuals != null ;
243+ var hasObservations = objective . ObservedY != null ;
244+ var hasModelValues = objective . ModelValues != null ;
245+ var hasSufficientDof = objective . DegreeOfFreedom >= 1 ;
246+
247+ // Set values to NaN if we can't calculate them
248+ RootMeanSquaredError = double . NaN ;
249+ RSquared = double . NaN ;
250+ AdjustedRSquared = double . NaN ;
251+ StandardError = double . NaN ;
252+ CorrelationCoefficient = double . NaN ;
253+
254+ // Need residuals and sufficient DOF for most calculations
255+ if ( ! hasResiduals || ! hasSufficientDof )
256+ {
257+ return ;
258+ }
259+
260+ var n = hasObservations ? objective . ObservedY . Count : objective . Residuals . Count ;
261+ var dof = objective . DegreeOfFreedom ;
262+
263+ // Calculate sum of squared residuals using vector operations
264+ var ssRes = objective . Residuals . DotProduct ( objective . Residuals ) ;
265+
266+ // Guard against zero or negative SSR
267+ if ( ssRes <= 0 )
268+ {
269+ RootMeanSquaredError = 0 ;
270+ StandardError = 0 ;
271+
272+ // Only calculate these if we have observations
273+ if ( hasObservations )
274+ {
275+ RSquared = 1.0 ;
276+ AdjustedRSquared = 1.0 ;
277+ }
278+
279+ // Only calculate if we have model values and observations
280+ if ( hasModelValues && hasObservations )
281+ {
282+ CorrelationCoefficient = 1.0 ;
283+ }
284+ return ;
285+ }
286+
287+ // Calculate standard error and RMSE, which only require residuals
288+ StandardError = Math . Sqrt ( ssRes / dof ) ;
289+ RootMeanSquaredError = Math . Sqrt ( ssRes / n ) ;
290+
291+ // The following statistics require observations
292+ if ( ! hasObservations )
293+ {
294+ return ;
295+ }
296+
297+ // Calculate total sum of squares
298+ double ssTot ;
299+
300+ // If weights are present, calculate weighted total sum of squares
301+ if ( objective . Weights != null )
302+ {
303+ var weightSum = 0.0 ;
304+ var weightedSum = 0.0 ;
305+
306+ for ( var i = 0 ; i < n ; i ++ )
307+ {
308+ var weight = objective . Weights [ i , i ] ;
309+ weightSum += weight ;
310+ weightedSum += weight * objective . ObservedY [ i ] ;
311+ }
312+
313+ // Avoid division by zero
314+ var weightedMean = weightSum > double . Epsilon ? weightedSum / weightSum : 0 ;
315+
316+ ssTot = 0.0 ;
317+ for ( var i = 0 ; i < n ; i ++ )
318+ {
319+ var weight = objective . Weights [ i , i ] ;
320+ var dev = objective . ObservedY [ i ] - weightedMean ;
321+ ssTot += weight * dev * dev ;
322+ }
323+ }
324+ else
325+ {
326+ // Unweighted case - use vector operations for total sum of squares
327+ var yMean = objective . ObservedY . Average ( ) ;
328+ var deviations = objective . ObservedY . Subtract ( yMean ) ;
329+ ssTot = deviations . DotProduct ( deviations ) ;
330+ }
331+
332+ // Guard against zero or negative total sum of squares
333+ if ( ssTot <= double . Epsilon )
334+ {
335+ RSquared = 0.0 ;
336+ AdjustedRSquared = 0.0 ;
337+ }
338+ else
339+ {
340+ // Calculate R-squared directly
341+ RSquared = 1 - ( ssRes / ssTot ) ;
342+
343+ // Calculate adjusted R-squared using the ratio of mean squares
344+ AdjustedRSquared = 1 - ( ssRes / dof ) / ( ssTot / ( n - 1 ) ) ;
345+
346+ // Ensure adjusted R-squared is not greater than 1 or less than 0
347+ AdjustedRSquared = Math . Min ( 1.0 , Math . Max ( 0.0 , AdjustedRSquared ) ) ;
348+ }
349+
350+ // Only calculate correlation coefficient if we have model values
351+ if ( hasModelValues )
352+ {
353+ // Calculate correlation coefficient between observed and predicted
354+ CorrelationCoefficient = GoodnessOfFit . R ( objective . ModelValues , objective . ObservedY ) ;
78355 }
79356 }
80357 }
0 commit comments