LotSizingDataModel.Solver 2.0.1
Solver-independent modeling, execution, monitoring and adapter infrastructure.
Loading...
Searching...
No Matches
MathematicalVariableValueNormalizer.cs
Go to the documentation of this file.
1using System;
4
6
7/// <summary>
8/// Normalizes raw mathematical-variable values before they are
9/// exposed through domain decision objects or used for objective
10/// post-processing.
11/// </summary>
12/// <remarks>
13/// <para>
14/// Solver values are floating-point values and can contain small
15/// numerical residuals such as -6E-13 or 180.00000000000006.
16/// This component converts such residuals to stable business
17/// values without rounding materially fractional quantities.
18/// </para>
19/// <para>
20/// The same normalization service must be used by solution mapping
21/// and objective-value recomputation so that the serialized
22/// solution and the recomputed objective refer to exactly the same
23/// numerical representation.
24/// </para>
25/// </remarks>
27{
28 /// <summary>
29 /// Gets the default tolerance used to identify numerical zero.
30 /// </summary>
31 public const double DefaultZeroTolerance = 1.0e-8;
32
33 /// <summary>
34 /// Gets the default tolerance used for integer-domain values.
35 /// </summary>
36 public const double DefaultIntegralityTolerance = 1.0e-7;
37
38 /// <summary>
39 /// Gets the default tolerance used to clean continuous values
40 /// that are numerically indistinguishable from an integer.
41 /// </summary>
42 public const double DefaultNearIntegerTolerance = 1.0e-8;
43
44 /// <summary>
45 /// Initializes a value normalizer with default tolerances.
46 /// </summary>
54
55 /// <summary>
56 /// Initializes a value normalizer.
57 /// </summary>
58 /// <param name="zeroTolerance">
59 /// Absolute tolerance used to identify numerical zero.
60 /// </param>
61 /// <param name="integralityTolerance">
62 /// Absolute tolerance used for integer and semi-integer values.
63 /// </param>
64 /// <param name="nearIntegerTolerance">
65 /// Absolute tolerance used to clean continuous values that are
66 /// numerically indistinguishable from an integer.
67 /// </param>
69 double zeroTolerance,
70 double integralityTolerance,
71 double nearIntegerTolerance)
72 {
73 ValidateTolerance(
74 zeroTolerance,
75 nameof(zeroTolerance));
76
77 ValidateTolerance(
78 integralityTolerance,
79 nameof(integralityTolerance));
80
81 ValidateTolerance(
82 nearIntegerTolerance,
83 nameof(nearIntegerTolerance));
84
86 zeroTolerance;
87
89 integralityTolerance;
90
92 nearIntegerTolerance;
93 }
94
95 /// <summary>
96 /// Gets the zero tolerance.
97 /// </summary>
98 public double ZeroTolerance { get; }
99
100 /// <summary>
101 /// Gets the integrality tolerance.
102 /// </summary>
103 public double IntegralityTolerance { get; }
104
105 /// <summary>
106 /// Gets the near-integer cleanup tolerance for continuous
107 /// variables.
108 /// </summary>
109 public double NearIntegerTolerance { get; }
110
111 /// <summary>
112 /// Normalizes one raw variable value.
113 /// </summary>
114 /// <param name="variable">
115 /// Mathematical variable defining the value domain.
116 /// </param>
117 /// <param name="rawValue">
118 /// Raw floating-point value returned by the solver.
119 /// </param>
120 /// <returns>
121 /// Normalized value suitable for domain mapping and objective
122 /// post-processing.
123 /// </returns>
124 public double Normalize(
125 MathematicalVariable variable,
126 double rawValue)
127 {
128 ArgumentNullException.ThrowIfNull(
129 variable);
130
131 if (!double.IsFinite(rawValue))
132 {
133 throw new ArgumentOutOfRangeException(
134 nameof(rawValue),
135 rawValue,
136 "A mathematical-variable value must be finite.");
137 }
138
139 if (Math.Abs(rawValue) <= ZeroTolerance)
140 {
141 return 0.0;
142 }
143
144 return variable.VariableType switch
145 {
147 NormalizeBinary(
148 rawValue),
149
152 NormalizeIntegerDomainValue(
153 rawValue),
154
157 NormalizeContinuousValue(
158 rawValue),
159
160 _ =>
161 NormalizeContinuousValue(
162 rawValue)
163 };
164 }
165
166 /// <summary>
167 /// Returns a normalized copy of one solver value.
168 /// </summary>
170 MathematicalVariable variable,
172 {
173 ArgumentNullException.ThrowIfNull(
174 rawValue);
175
176 return new MathematicalVariableValue(
177 rawValue.VariableId,
178 Normalize(
179 variable,
180 rawValue.Value),
181 rawValue.VariableName,
182 rawValue.DomainKey);
183 }
184
185 private double NormalizeBinary(
186 double rawValue)
187 {
188 if (Math.Abs(rawValue) <= ZeroTolerance)
189 {
190 return 0.0;
191 }
192
193 if (Math.Abs(rawValue - 1.0) <= IntegralityTolerance)
194 {
195 return 1.0;
196 }
197
198 /*
199 * A binary decision exposed by the domain layer must be
200 * either 0 or 1. A materially fractional value indicates
201 * an inconsistent solver result and must not be silently
202 * rounded.
203 */
204 throw new InvalidOperationException(
205 $"Binary solver value '{rawValue:G17}' is not within " +
206 $"the configured integrality tolerance " +
207 $"'{IntegralityTolerance:G17}' of 0 or 1.");
208 }
209
210 private double NormalizeIntegerDomainValue(
211 double rawValue)
212 {
213 double nearestInteger =
214 Math.Round(
215 rawValue,
216 MidpointRounding.AwayFromZero);
217
218 if (Math.Abs(
219 rawValue - nearestInteger) <=
221 {
222 return nearestInteger;
223 }
224
225 /*
226 * Preserve the raw value instead of inventing an integer.
227 * The mapper or validation layer can then reject it if the
228 * corresponding domain decision requires integrality.
229 */
230 return rawValue;
231 }
232
233 private double NormalizeContinuousValue(
234 double rawValue)
235 {
236 double nearestInteger =
237 Math.Round(
238 rawValue,
239 MidpointRounding.AwayFromZero);
240
241 if (Math.Abs(
242 rawValue - nearestInteger) <=
244 {
245 return nearestInteger;
246 }
247
248 return rawValue;
249 }
250
251 private static void ValidateTolerance(
252 double tolerance,
253 string parameterName)
254 {
255 if (!double.IsFinite(tolerance) ||
256 tolerance < 0.0)
257 {
258 throw new ArgumentOutOfRangeException(
259 parameterName,
260 tolerance,
261 "A numerical tolerance must be finite and " +
262 "non-negative.");
263 }
264 }
265}
Stores the value returned by a solver for one mathematical decision variable.
int VariableId
Gets or sets the identifier of the mathematical variable.
string VariableName
Gets or sets the mathematical-variable name.
double Value
Gets or sets the value returned by the solver.
string DomainKey
Gets or sets the business-domain key associated with the mathematical variable.
const double DefaultNearIntegerTolerance
Gets the default tolerance used to clean continuous values that are numerically indistinguishable fro...
double Normalize(MathematicalVariable variable, double rawValue)
Normalizes one raw variable value.
MathematicalVariableValue Normalize(MathematicalVariable variable, MathematicalVariableValue rawValue)
Returns a normalized copy of one solver value.
const double DefaultZeroTolerance
Gets the default tolerance used to identify numerical zero.
const double DefaultIntegralityTolerance
Gets the default tolerance used for integer-domain values.
MathematicalVariableValueNormalizer(double zeroTolerance, double integralityTolerance, double nearIntegerTolerance)
Initializes a value normalizer.
double NearIntegerTolerance
Gets the near-integer cleanup tolerance for continuous variables.
MathematicalVariableValueNormalizer()
Initializes a value normalizer with default tolerances.
Represents one decision variable in a mathematical optimization model.
@ SemiContinuous
The variable is either zero or belongs to a continuous interval bounded away from zero.
@ Continuous
The variable may take any real value within its bounds.
@ SemiInteger
The variable is either zero or belongs to an integer interval bounded away from zero.
@ Binary
The variable may take only the values zero and one.
@ Integer
The variable may take only integer values within its bounds.