using System; using System.Collections.Generic; using System.Collections.ObjectModel; using MultiWheelC.TrajectoryPlanning.Utils; namespace MultiWheelC.TrajectoryPlanning.EMPlanner; public sealed class QuadraticProgram { public const double MaximumFiniteBound = 1e30d; public QuadraticProgram( SparseCscMatrix upperTriangularP, IReadOnlyList q, SparseCscMatrix a, IReadOnlyList lowerBounds, IReadOnlyList upperBounds) { if (upperTriangularP == null) throw new ArgumentNullException(nameof(upperTriangularP)); if (q == null) throw new ArgumentNullException(nameof(q)); if (a == null) throw new ArgumentNullException(nameof(a)); if (lowerBounds == null) throw new ArgumentNullException(nameof(lowerBounds)); if (upperBounds == null) throw new ArgumentNullException(nameof(upperBounds)); if (upperTriangularP.RowCount != upperTriangularP.ColumnCount) throw new ArgumentOutOfRangeException(nameof(upperTriangularP), "The quadratic Hessian must be square."); if (q.Count != upperTriangularP.ColumnCount) throw new ArgumentOutOfRangeException(nameof(q), "The linear cost length must match the variable count."); if (a.ColumnCount != upperTriangularP.ColumnCount) throw new ArgumentOutOfRangeException(nameof(a), "Constraint columns must match the variable count."); if (lowerBounds.Count != a.RowCount || upperBounds.Count != a.RowCount) throw new ArgumentOutOfRangeException(nameof(lowerBounds), "Constraint bounds must match the constraint count."); ValidateUpperTriangle(upperTriangularP); ValidateFinite(q, nameof(q)); ValidateBounds(lowerBounds, upperBounds); UpperTriangularP = CopyMatrix(upperTriangularP); LinearCost = Copy(q); ConstraintMatrix = CopyMatrix(a); LowerBounds = Copy(lowerBounds); UpperBounds = Copy(upperBounds); VariableCount = UpperTriangularP.ColumnCount; ConstraintCount = ConstraintMatrix.RowCount; } public SparseCscMatrix UpperTriangularP { get; } public IReadOnlyList LinearCost { get; } public SparseCscMatrix ConstraintMatrix { get; } public IReadOnlyList LowerBounds { get; } public IReadOnlyList UpperBounds { get; } public int VariableCount { get; } public int ConstraintCount { get; } private static void ValidateUpperTriangle(SparseCscMatrix matrix) { for (int column = 0; column < matrix.ColumnCount; column++) { for (int index = matrix.ColumnPointers[column]; index < matrix.ColumnPointers[column + 1]; index++) { if (matrix.RowIndices[index] > column) throw new ArgumentOutOfRangeException(nameof(matrix), "The quadratic Hessian must store only its upper triangle."); } } } private static void ValidateFinite(IReadOnlyList values, string parameterName) { for (int index = 0; index < values.Count; index++) { if (!NumericGuard.IsFinite(values[index])) throw new ArgumentOutOfRangeException(parameterName, "Values must be finite."); } } private static void ValidateBounds(IReadOnlyList lowerBounds, IReadOnlyList upperBounds) { for (int index = 0; index < lowerBounds.Count; index++) { double lower = lowerBounds[index]; double upper = upperBounds[index]; if (!NumericGuard.IsFinite(lower) || !NumericGuard.IsFinite(upper) || lower < -MaximumFiniteBound || upper > MaximumFiniteBound || lower > upper) { throw new ArgumentOutOfRangeException(nameof(lowerBounds), "Constraint bounds must be finite, within the supported range, and ordered."); } } } private static SparseCscMatrix CopyMatrix(SparseCscMatrix source) { return new SparseCscMatrix(source.RowCount, source.ColumnCount, source.Values, source.RowIndices, source.ColumnPointers); } private static IReadOnlyList Copy(IReadOnlyList source) { var copy = new List(source.Count); for (int index = 0; index < source.Count; index++) copy.Add(source[index]); return new ReadOnlyCollection(copy); } }