LotSizingDataModel.Solver 2.0.1
Solver-independent modeling, execution, monitoring and adapter infrastructure.
Loading...
Searching...
No Matches
SolverProgressRecorder.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3
5
6/// <summary>
7/// Selects and retains solver progress snapshots according to
8/// configured recording rules.
9/// </summary>
10public sealed class SolverProgressRecorder
11{
12 private readonly SolverProgressRecordingOptions _options;
13
14 private readonly List<SolverProgressSnapshot> _history =
15 new();
16
17 private SolverProgressSnapshot? _lastRecordedSnapshot;
18
19 /// <summary>
20 /// Initializes a progress recorder.
21 /// </summary>
22 /// <param name="options">
23 /// Progress-recording options.
24 /// </param>
25 /// <exception cref="ArgumentNullException">
26 /// Thrown when <paramref name="options"/> is
27 /// <see langword="null"/>.
28 /// </exception>
31 {
32 ArgumentNullException.ThrowIfNull(
33 options);
34
35 options.EnsureValid();
36
37 _options =
38 options;
39 }
40
41 /// <summary>
42 /// Gets the retained progress history.
43 /// </summary>
44 public IReadOnlyList<SolverProgressSnapshot> History =>
45 _history;
46
47 /// <summary>
48 /// Gets the number of retained progress snapshots.
49 /// </summary>
50 public int Count =>
51 _history.Count;
52
53 /// <summary>
54 /// Attempts to retain a progress snapshot.
55 /// </summary>
56 /// <param name="snapshot">
57 /// Solver progress snapshot.
58 /// </param>
59 /// <returns>
60 /// <see langword="true"/> when the snapshot was retained;
61 /// otherwise, <see langword="false"/>.
62 /// </returns>
63 /// <exception cref="ArgumentNullException">
64 /// Thrown when <paramref name="snapshot"/> is
65 /// <see langword="null"/>.
66 /// </exception>
67 public bool TryRecord(
69 {
70 ArgumentNullException.ThrowIfNull(
71 snapshot);
72
73 if (_options.Mode ==
75 {
76 return false;
77 }
78
79 if (HasReachedMaximumPointCount())
80 {
81 return false;
82 }
83
84 if (!ShouldRecord(
85 snapshot))
86 {
87 return false;
88 }
89
90 SolverProgressSnapshot retainedSnapshot =
91 CloneSnapshot(
92 snapshot);
93
94 _history.Add(
95 retainedSnapshot);
96
97 _lastRecordedSnapshot =
98 retainedSnapshot;
99
100 return true;
101 }
102
103 /// <summary>
104 /// Removes all retained progress snapshots.
105 /// </summary>
106 public void Clear()
107 {
108 _history.Clear();
109
110 _lastRecordedSnapshot =
111 null;
112 }
113
114 private bool ShouldRecord(
115 SolverProgressSnapshot snapshot)
116 {
117 if (_lastRecordedSnapshot is null)
118 {
119 return true;
120 }
121
122 return _options.Mode switch
123 {
124 SolverProgressRecordingMode.IncumbentOnly =>
125 IsNewIncumbent(
126 snapshot,
127 _lastRecordedSnapshot),
128
129 SolverProgressRecordingMode.TimeInterval =>
130 HasReachedTimeInterval(
131 snapshot,
132 _lastRecordedSnapshot),
133
134 SolverProgressRecordingMode.NodeInterval =>
135 HasReachedNodeInterval(
136 snapshot,
137 _lastRecordedSnapshot),
138
139 SolverProgressRecordingMode.SignificantChanges =>
140 HasSignificantChange(
141 snapshot,
142 _lastRecordedSnapshot),
143
144 SolverProgressRecordingMode.Full =>
145 true,
146
147 _ =>
148 false
149 };
150 }
151
152 private bool HasReachedMaximumPointCount()
153 {
154 return
155 _options.MaximumRecordedPointCount > 0 &&
156 _history.Count >=
157 _options.MaximumRecordedPointCount;
158 }
159
160 private static bool IsNewIncumbent(
161 SolverProgressSnapshot current,
162 SolverProgressSnapshot previous)
163 {
164 if (!current.IncumbentObjective.HasValue)
165 {
166 return false;
167 }
168
169 if (!previous.IncumbentObjective.HasValue)
170 {
171 return true;
172 }
173
174 return
175 !current.IncumbentObjective.Value.Equals(
176 previous.IncumbentObjective.Value);
177 }
178
179 private bool HasReachedTimeInterval(
180 SolverProgressSnapshot current,
181 SolverProgressSnapshot previous)
182 {
183 return
184 current.ElapsedSeconds -
185 previous.ElapsedSeconds >=
186 _options.TimeIntervalSeconds;
187 }
188
189 private bool HasReachedNodeInterval(
190 SolverProgressSnapshot current,
191 SolverProgressSnapshot previous)
192 {
193 if (!current.ExploredNodeCount.HasValue ||
194 !previous.ExploredNodeCount.HasValue)
195 {
196 return false;
197 }
198
199 return
200 current.ExploredNodeCount.Value -
201 previous.ExploredNodeCount.Value >=
202 _options.NodeInterval;
203 }
204
205 private bool HasSignificantChange(
206 SolverProgressSnapshot current,
207 SolverProgressSnapshot previous)
208 {
209 return
210 HasChangedByAtLeast(
211 current.IncumbentObjective,
212 previous.IncumbentObjective,
213 _options.MinimumObjectiveChange) ||
214 HasChangedByAtLeast(
215 current.BestBound,
216 previous.BestBound,
217 _options.MinimumBoundChange) ||
218 HasChangedByAtLeast(
219 current.RelativeGap,
220 previous.RelativeGap,
221 _options.MinimumRelativeGapChange) ||
222 current.Stage !=
223 previous.Stage ||
224 current.SolutionCount !=
225 previous.SolutionCount;
226 }
227
228 private static bool HasChangedByAtLeast(
229 double? current,
230 double? previous,
231 double minimumChange)
232 {
233 if (current.HasValue !=
234 previous.HasValue)
235 {
236 return true;
237 }
238
239 if (!current.HasValue ||
240 !previous.HasValue)
241 {
242 return false;
243 }
244
245 return
246 Math.Abs(
247 current.Value -
248 previous.Value) >=
249 minimumChange;
250 }
251
252 private static SolverProgressSnapshot CloneSnapshot(
253 SolverProgressSnapshot source)
254 {
255 return new SolverProgressSnapshot
256 {
257 Stage =
258 source.Stage,
259
260 ElapsedSeconds =
261 source.ElapsedSeconds,
262
263 ProcessorSeconds =
264 source.ProcessorSeconds,
265
266 ExploredNodeCount =
267 source.ExploredNodeCount,
268
269 OpenNodeCount =
270 source.OpenNodeCount,
271
272 IterationCount =
273 source.IterationCount,
274
275 IncumbentObjective =
276 source.IncumbentObjective,
277
278 BestBound =
279 source.BestBound,
280
281 AbsoluteGap =
282 source.AbsoluteGap,
283
284 RelativeGap =
285 source.RelativeGap,
286
287 SolutionCount =
288 source.SolutionCount,
289
290 MemoryUsageMegabytes =
291 source.MemoryUsageMegabytes,
292
293 Message =
294 source.Message
295 };
296 }
297}
bool TryRecord(SolverProgressSnapshot snapshot)
Attempts to retain a progress snapshot.
void Clear()
Removes all retained progress snapshots.
int Count
Gets the number of retained progress snapshots.
IReadOnlyList< SolverProgressSnapshot > History
Gets the retained progress history.
SolverProgressRecorder(SolverProgressRecordingOptions options)
Initializes a progress recorder.
Configures how solver progress snapshots are retained during an optimization run.
Represents an immutable snapshot of the current progress of a mathematical optimization run.
SolverProgressRecordingMode
Defines how solver progress snapshots are retained in the solution convergence history.