ULSAlgorithms 1.1.0-g3e5595996d
High-performance exact and heuristic algorithms for uncapacitated lot sizing
Loading...
Searching...
No Matches
UlsSolution.cs
Go to the documentation of this file.
2
3/// <summary>
4/// Represents a feasible production plan for a ULS problem.
5/// </summary>
6public sealed class UlsSolution
7{
8 private readonly double[] _productionQuantities;
9 private readonly double[] _endingInventories;
10 private readonly bool[] _setupDecisions;
11
12 /// <summary>
13 /// Initializes a solution by defensively copying all supplied decision vectors.
14 /// </summary>
15 /// <param name="productionQuantities">Production quantity in each period.</param>
16 /// <param name="endingInventories">Inventory remaining at the end of each period.</param>
17 /// <param name="setupDecisions">Whether production is set up in each period.</param>
18 /// <param name="setupCost">Total fixed setup cost.</param>
19 /// <param name="productionCost">Total variable production cost.</param>
20 /// <param name="holdingCost">Total inventory holding cost.</param>
22 ReadOnlySpan<double> productionQuantities,
23 ReadOnlySpan<double> endingInventories,
24 ReadOnlySpan<bool> setupDecisions,
25 double setupCost,
26 double productionCost,
27 double holdingCost)
28 : this(
29 productionQuantities.ToArray(),
30 endingInventories.ToArray(),
31 setupDecisions.ToArray(),
32 setupCost,
33 productionCost,
34 holdingCost,
35 takeOwnership: true)
36 {
37 }
38
39 private UlsSolution(
40 double[] productionQuantities,
41 double[] endingInventories,
42 bool[] setupDecisions,
43 double setupCost,
44 double productionCost,
45 double holdingCost,
46 bool takeOwnership)
47 {
48 ArgumentNullException.ThrowIfNull(productionQuantities);
49 ArgumentNullException.ThrowIfNull(endingInventories);
50 ArgumentNullException.ThrowIfNull(setupDecisions);
51
52 if (productionQuantities.Length == 0)
53 {
54 throw new ArgumentException(
55 "A ULS solution must contain at least one period.",
56 nameof(productionQuantities));
57 }
58
59 if (endingInventories.Length != productionQuantities.Length)
60 {
61 throw new ArgumentException(
62 "Ending-inventory and production vectors must have the same length.",
63 nameof(endingInventories));
64 }
65
66 if (setupDecisions.Length != productionQuantities.Length)
67 {
68 throw new ArgumentException(
69 "Setup-decision and production vectors must have the same length.",
70 nameof(setupDecisions));
71 }
72
73 ValidateNonNegativeFiniteVector(productionQuantities, nameof(productionQuantities));
74 ValidateNonNegativeFiniteVector(endingInventories, nameof(endingInventories));
75 ValidateCost(setupCost, nameof(setupCost));
76 ValidateCost(productionCost, nameof(productionCost));
77 ValidateCost(holdingCost, nameof(holdingCost));
78
79 _productionQuantities = takeOwnership
80 ? productionQuantities
81 : (double[])productionQuantities.Clone();
82
83 _endingInventories = takeOwnership
84 ? endingInventories
85 : (double[])endingInventories.Clone();
86
87 _setupDecisions = takeOwnership
88 ? setupDecisions
89 : (bool[])setupDecisions.Clone();
90
91 SetupCost = setupCost;
92 ProductionCost = productionCost;
93 HoldingCost = holdingCost;
94 TotalCost = setupCost + productionCost + holdingCost;
95 }
96
97 /// <summary>
98 /// Gets the number of periods represented by the solution.
99 /// </summary>
100 public int Horizon => _productionQuantities.Length;
101
102 /// <summary>
103 /// Gets production quantities by period.
104 /// </summary>
105 public ReadOnlySpan<double> ProductionQuantities => _productionQuantities;
106
107 /// <summary>
108 /// Gets end-of-period inventories by period.
109 /// </summary>
110 public ReadOnlySpan<double> EndingInventories => _endingInventories;
111
112 /// <summary>
113 /// Gets setup decisions by period.
114 /// </summary>
115 public ReadOnlySpan<bool> SetupDecisions => _setupDecisions;
116
117 /// <summary>
118 /// Gets the total fixed setup cost.
119 /// </summary>
120 public double SetupCost { get; }
121
122 /// <summary>
123 /// Gets the total variable production cost.
124 /// </summary>
125 public double ProductionCost { get; }
126
127 /// <summary>
128 /// Gets the total holding cost.
129 /// </summary>
130 public double HoldingCost { get; }
131
132 /// <summary>
133 /// Gets the complete objective value.
134 /// </summary>
135 public double TotalCost { get; }
136
137 /// <summary>
138 /// Creates a solution while transferring ownership of already allocated solver buffers.
139 /// </summary>
140 /// <remarks>
141 /// This internal fast path prevents an unnecessary second copy when an algorithm has
142 /// already produced dedicated output arrays.
143 /// </remarks>
145 double[] productionQuantities,
146 double[] endingInventories,
147 bool[] setupDecisions,
148 double setupCost,
149 double productionCost,
150 double holdingCost)
151 {
152 return new UlsSolution(
153 productionQuantities,
154 endingInventories,
155 setupDecisions,
156 setupCost,
157 productionCost,
158 holdingCost,
159 takeOwnership: true);
160 }
161
162 private static void ValidateNonNegativeFiniteVector(
163 ReadOnlySpan<double> values,
164 string parameterName)
165 {
166 for (var period = 0; period < values.Length; period++)
167 {
168 var value = values[period];
169
170 if (!double.IsFinite(value) || value < 0.0)
171 {
172 throw new ArgumentException(
173 $"Vector '{parameterName}' contains an invalid value at period {period}: {value}.",
174 parameterName);
175 }
176 }
177 }
178
179 private static void ValidateCost(double value, string parameterName)
180 {
181 if (!double.IsFinite(value) || value < 0.0)
182 {
183 throw new ArgumentOutOfRangeException(
184 parameterName,
185 value,
186 "Solution cost components must be finite and non-negative.");
187 }
188 }
189}
double SetupCost
Gets the total fixed setup cost.
ReadOnlySpan< bool > SetupDecisions
Gets setup decisions by period.
double TotalCost
Gets the complete objective value.
ReadOnlySpan< double > EndingInventories
Gets end-of-period inventories by period.
int Horizon
Gets the number of periods represented by the solution.
ReadOnlySpan< double > ProductionQuantities
Gets production quantities by period.
double ProductionCost
Gets the total variable production cost.
UlsSolution(ReadOnlySpan< double > productionQuantities, ReadOnlySpan< double > endingInventories, ReadOnlySpan< bool > setupDecisions, double setupCost, double productionCost, double holdingCost)
Initializes a solution by defensively copying all supplied decision vectors.
double HoldingCost
Gets the total holding cost.
static UlsSolution FromOwnedBuffers(double[] productionQuantities, double[] endingInventories, bool[] setupDecisions, double setupCost, double productionCost, double holdingCost)
Creates a solution while transferring ownership of already allocated solver buffers.