Class: LpSolver::QuadraticExpression
- Inherits:
-
Object
- Object
- LpSolver::QuadraticExpression
- Defined in:
- lib/lpsolver/quadratic_expression.rb
Overview
The Hessian is stored in a normalized form where off-diagonal entries (i ≠ j) are stored once as [i, j, coefficient], and the LP format automatically applies the ½ factor.
Represents a quadratic expression: linear terms + quadratic terms.
Quadratic expressions extend linear expressions by including second-order terms (products of two variables). They are used to model quadratic objectives in Quadratic Programming (QP) problems, such as portfolio variance, least-squares residuals, or convex penalty functions.
A quadratic expression has the mathematical form:
c₀ + Σᵢ cᵢ·xᵢ + ½ Σᵢⱼ Qᵢⱼ·xᵢ·xⱼ
where c₀ is the constant, cᵢ are linear coefficients, and Q is the symmetric Hessian matrix of quadratic coefficients.
Instance Attribute Summary collapse
-
#linear_terms ⇒ Hash{Integer => Float}
readonly
Maps variable indices to linear coefficients.
-
#quadratic_terms ⇒ Array<[Integer, Integer, Float]>
readonly
Quadratic term pairs.
Instance Method Summary collapse
-
#*(other) ⇒ QuadraticExpression
Multiplies this expression by a scalar.
-
#+(other) ⇒ QuadraticExpression
Adds another expression, variable, constant, or quadratic expression.
-
#-(other) ⇒ QuadraticExpression
Subtracts another expression, variable, constant, or quadratic expression.
-
#-@ ⇒ QuadraticExpression
Returns a QuadraticExpression with all coefficients negated.
-
#hessian_entries ⇒ Array<[Integer, Integer, Float]>
Converts quadratic terms to HiGHS Hessian entries.
-
#initialize(linear_terms = {}, quadratic_terms = []) ⇒ QuadraticExpression
constructor
Creates a new QuadraticExpression.
Constructor Details
#initialize(linear_terms = {}, quadratic_terms = []) ⇒ QuadraticExpression
Creates a new QuadraticExpression.
51 52 53 54 |
# File 'lib/lpsolver/quadratic_expression.rb', line 51 def initialize(linear_terms = {}, quadratic_terms = []) @linear_terms = linear_terms @quadratic_terms = quadratic_terms end |
Instance Attribute Details
#linear_terms ⇒ Hash{Integer => Float} (readonly)
Returns Maps variable indices to linear coefficients. The keys are internal variable indices, and the values are the coefficients of the linear terms (e.g., 2x + 3y → => 2.0, y_idx => 3.0).
36 37 38 |
# File 'lib/lpsolver/quadratic_expression.rb', line 36 def linear_terms @linear_terms end |
#quadratic_terms ⇒ Array<[Integer, Integer, Float]> (readonly)
Returns Quadratic term pairs. Each element is [var1_index, var2_index, coefficient], representing coefficient * var1 * var2. For diagonal terms (var1 == var2), this represents coefficient * var1².
45 46 47 |
# File 'lib/lpsolver/quadratic_expression.rb', line 45 def quadratic_terms @quadratic_terms end |
Instance Method Details
#*(other) ⇒ QuadraticExpression
Multiplies this expression by a scalar.
Scales both linear coefficients and quadratic coefficients by the given factor.
122 123 124 125 126 127 128 |
# File 'lib/lpsolver/quadratic_expression.rb', line 122 def *(other) s = other.to_f new_linear = @linear_terms.transform_values { |c| c * s } new_linear.reject! { |_, v| v.zero? } new_quad = @quadratic_terms.map { |i1, i2, c| [i1, i2, c * s] } QuadraticExpression.new(new_linear, new_quad) end |
#+(other) ⇒ QuadraticExpression
Adds another expression, variable, constant, or quadratic expression.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/lpsolver/quadratic_expression.rb', line 64 def +(other) if other.is_a?(Variable) new_linear = @linear_terms.dup new_linear[other.index] = (new_linear[other.index] || 0) + 1 QuadraticExpression.new(new_linear, @quadratic_terms.dup) elsif other.is_a?(LinearExpression) new_linear = @linear_terms.dup other.terms.each { |idx, coeff| new_linear[idx] = (new_linear[idx] || 0) + coeff } QuadraticExpression.new(new_linear.reject { |_, v| v.zero? }, @quadratic_terms.dup) elsif other.is_a?(QuadraticExpression) new_linear = @linear_terms.dup other.linear_terms.each { |idx, coeff| new_linear[idx] = (new_linear[idx] || 0) + coeff } new_linear.reject! { |_, v| v.zero? } QuadraticExpression.new(new_linear, @quadratic_terms + other.quadratic_terms) else new_linear = @linear_terms.dup new_linear[0] = (new_linear[0] || 0) + other.to_f QuadraticExpression.new(new_linear, @quadratic_terms.dup) end end |
#-(other) ⇒ QuadraticExpression
Subtracts another expression, variable, constant, or quadratic expression.
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/lpsolver/quadratic_expression.rb', line 91 def -(other) if other.is_a?(Variable) new_linear = @linear_terms.dup new_linear[other.index] = (new_linear[other.index] || 0) - 1 QuadraticExpression.new(new_linear.reject { |_, v| v.zero? }, @quadratic_terms.dup) elsif other.is_a?(LinearExpression) new_linear = @linear_terms.dup other.terms.each { |idx, coeff| new_linear[idx] = (new_linear[idx] || 0) - coeff } QuadraticExpression.new(new_linear.reject { |_, v| v.zero? }, @quadratic_terms.dup) elsif other.is_a?(QuadraticExpression) new_linear = @linear_terms.dup other.linear_terms.each { |idx, coeff| new_linear[idx] = (new_linear[idx] || 0) - coeff } new_linear.reject! { |_, v| v.zero? } neg_quad = other.quadratic_terms.map { |i1, i2, c| [i1, i2, -c] } QuadraticExpression.new(new_linear, @quadratic_terms + neg_quad) else new_linear = @linear_terms.dup new_linear[0] = (new_linear[0] || 0) - other.to_f QuadraticExpression.new(new_linear, @quadratic_terms.dup) end end |
#-@ ⇒ QuadraticExpression
Returns a QuadraticExpression with all coefficients negated.
135 136 137 138 139 140 |
# File 'lib/lpsolver/quadratic_expression.rb', line 135 def -@ QuadraticExpression.new( @linear_terms.transform_values { |c| -c }.reject { |_, v| v.zero? }, @quadratic_terms.map { |i1, i2, c| [i1, i2, -c] } ) end |
#hessian_entries ⇒ Array<[Integer, Integer, Float]>
Converts quadratic terms to HiGHS Hessian entries.
Combines symmetric entries (e.g., x*y and y*x) and multiplies by 2 to account for the ½ factor in the HiGHS LP format:
[ 2·Qᵢⱼ·xᵢ·xⱼ ] / 2 = Qᵢⱼ·xᵢ·xⱼ
153 154 155 156 157 158 159 160 161 162 |
# File 'lib/lpsolver/quadratic_expression.rb', line 153 def hessian_entries # Normalize: combine symmetric entries pairs = {} @quadratic_terms.each do |i1, i2, c| key = [i1, i2].sort pairs[key] = (pairs[key] || 0) + c end # Multiply by 2 to account for the "/ 2" in the LP format pairs.map { |key, c| [key[0], key[1], c * 2.0] } end |