Class: LpSolver::Solution

Inherits:
Object
  • Object
show all
Defined in:
lib/lpsolver/solution.rb

Overview

Represents the solution returned by solving a model.

The Solution object contains the optimal (or best-found) values for all decision variables, the optimal objective value, and metadata about the solver’s execution.

Examples:

Accessing solution values

solution = model.solve
solution[:x]        # => 4.0 (value of variable x)
solution[:y]        # => 0.0 (value of variable y)
solution.objective_value  # => 12.0 (optimal objective)

Checking solution status

solution.feasible?      # => true
solution.infeasible?    # => false
solution.unbounded?     # => false

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(variables:, objective_value:, model_status:, iterations:) ⇒ Solution

Creates a new Solution object.

Parameters:

  • variables (Hash{String => Float})

    Maps variable names to values.

  • objective_value (Float)

    The optimal objective value.

  • model_status (String)

    The solver-reported model status.

  • iterations (Integer)

    The number of solver iterations.



66
67
68
69
70
71
# File 'lib/lpsolver/solution.rb', line 66

def initialize(variables:, objective_value:, model_status:, iterations:)
  @variables = variables
  @objective_value = objective_value
  @model_status = model_status
  @iterations = iterations
end

Instance Attribute Details

#iterationsInteger (readonly)

Returns The number of iterations the solver performed. This is a diagnostic metric; may be 0 for some solver types.

Returns:

  • (Integer)

    The number of iterations the solver performed. This is a diagnostic metric; may be 0 for some solver types.



45
46
47
# File 'lib/lpsolver/solution.rb', line 45

def iterations
  @iterations
end

#model_statusString (readonly)

Returns The status of the model as reported by HiGHS. Possible values include:

  • “optimal”: An optimal solution was found.

  • “infeasible”: No feasible solution exists.

  • “unbounded”: The objective can be improved without bound.

  • “unknown”: The solver could not determine the status.

Returns:

  • (String)

    The status of the model as reported by HiGHS. Possible values include:

    • “optimal”: An optimal solution was found.

    • “infeasible”: No feasible solution exists.

    • “unbounded”: The objective can be improved without bound.

    • “unknown”: The solver could not determine the status.



41
42
43
# File 'lib/lpsolver/solution.rb', line 41

def model_status
  @model_status
end

#objective_valueFloat (readonly)

Returns The optimal objective function value. For minimization problems, this is the minimum value. For maximization problems, this is the maximum value. When the solution is infeasible, this returns ‘0.0`. Always check `infeasible?` or `unbounded?` before reading this value.

Returns:

  • (Float)

    The optimal objective function value. For minimization problems, this is the minimum value. For maximization problems, this is the maximum value. When the solution is infeasible, this returns ‘0.0`. Always check `infeasible?` or `unbounded?` before reading this value.



33
34
35
# File 'lib/lpsolver/solution.rb', line 33

def objective_value
  @objective_value
end

#variablesHash{String => Float} (readonly)

Returns Maps variable names to their optimal values. The keys are the variable names as strings (as produced by HiGHS), and the values are the optimal decision variable values. When the solution is infeasible, this hash is empty. Always check ‘infeasible?` or `unbounded?` before reading variable values.

Returns:

  • (Hash{String => Float})

    Maps variable names to their optimal values. The keys are the variable names as strings (as produced by HiGHS), and the values are the optimal decision variable values. When the solution is infeasible, this hash is empty. Always check ‘infeasible?` or `unbounded?` before reading variable values.



26
27
28
# File 'lib/lpsolver/solution.rb', line 26

def variables
  @variables
end

Instance Method Details

#[](name) ⇒ Float

Note:

When the solution is infeasible, all variables are empty. Check ‘infeasible?` first before accessing variable values.

Retrieves the value of a variable by name.

Examples:

solution[:x]        # => 4.0 (by symbol)
solution['x']       # => 4.0 (by string)
solution[x]         # => 4.0 (by Variable object)

Parameters:

  • name (Symbol, String, Variable)

    The variable name (Symbol, String, or Variable object).

Returns:

  • (Float)

    The optimal value of the variable, or ‘nil` if the solution is infeasible or unbounded.

Raises:

  • (KeyError)

    If the variable name is not found in the solution.



86
87
88
89
90
91
92
93
# File 'lib/lpsolver/solution.rb', line 86

def [](name)
  key = if name.is_a?(Variable)
    name.name.to_s
  else
    name.to_s
  end
  variables[key]
end

#all_variablesHash{Symbol => Float}

Returns all variables as a hash with Symbol keys.

This is a convenience method that converts the internal String-keyed variables hash to a Symbol-keyed hash for easier Ruby-style access.

Examples:

solution.all_variables
# => { :x => 4.0, :y => 0.0 }

Returns:

  • (Hash{Symbol => Float})

    Maps variable names (as symbols) to their optimal values.



117
118
119
# File 'lib/lpsolver/solution.rb', line 117

def all_variables
  variables.transform_keys(&:to_sym)
end

#each_variable {|name, value| ... } ⇒ self

Iterates over all variables and their optimal values.

Yields each variable name (as a Symbol) and its value.

Examples:

solution.each_variable { |name, value| puts "#{name} = #{value}" }

Yield Parameters:

  • name (Symbol)

    The variable name.

  • value (Float)

    The optimal value of the variable.

Returns:

  • (self)

    self for chaining.



142
143
144
145
# File 'lib/lpsolver/solution.rb', line 142

def each_variable
  all_variables.each { |name, value| yield name, value }
  self
end

#feasible?Boolean

Checks if the solution is feasible.

A solution is feasible if the solver found a solution that satisfies all constraints and bounds.

Returns:

  • (Boolean)

    True if the model status is “optimal”.



169
170
171
# File 'lib/lpsolver/solution.rb', line 169

def feasible?
  @model_status == 'optimal'
end

#has_variable?(name) ⇒ Boolean

Checks if a variable exists in the solution.

Examples:

solution.has_variable?(:x)  # => true
solution.has_variable?(:z)  # => false

Parameters:

  • name (Symbol, String, Variable)

    The variable name to check.

Returns:

  • (Boolean)

    True if the variable exists in the solution.



154
155
156
157
158
159
160
161
# File 'lib/lpsolver/solution.rb', line 154

def has_variable?(name)
  key = if name.is_a?(Variable)
    name.name.to_s
  else
    name.to_s
  end
  variables.key?(key)
end

#infeasible?Boolean

Checks if the model is infeasible.

A model is infeasible if no solution exists that satisfies all constraints simultaneously.

Returns:

  • (Boolean)

    True if the model status is “infeasible”.



179
180
181
# File 'lib/lpsolver/solution.rb', line 179

def infeasible?
  @model_status == 'infeasible'
end

#statusSymbol

Returns the model status as a Symbol.

Examples:

solution.status  # => :optimal

Returns:

  • (Symbol)

    The solver status as a Ruby symbol:

    • :optimal — An optimal solution was found.

    • :infeasible — No feasible solution exists.

    • :unbounded — The objective can be improved without bound.

    • :unknown — The solver could not determine the status.



56
57
58
# File 'lib/lpsolver/solution.rb', line 56

def status
  @model_status.to_sym
end

#to_hHash{Symbol => Float}

Returns the solution as a plain hash with Symbol keys.

This is equivalent to #all_variables and is provided for Ruby convention compatibility.

Examples:

solution.to_h  # => { :x => 4.0, :y => 0.0 }

Returns:

  • (Hash{Symbol => Float})

    Maps variable names to values.



129
130
131
# File 'lib/lpsolver/solution.rb', line 129

def to_h
  all_variables
end

#to_sString

Returns a string representation of the solution.

Examples:

puts solution
# x = 4.0
# y = 0.0
# Objective: 12.0

Returns:

  • (String)

    A formatted string showing variable values and the objective value.



202
203
204
205
206
# File 'lib/lpsolver/solution.rb', line 202

def to_s
  lines = variables.map { |name, value| "#{name} = #{value}" }
  lines << "Objective: #{objective_value}"
  lines.join("\n")
end

#unbounded?Boolean

Checks if the model is unbounded.

A model is unbounded if the objective can be improved indefinitely without violating any constraints.

Returns:

  • (Boolean)

    True if the model status is “unbounded”.



189
190
191
# File 'lib/lpsolver/solution.rb', line 189

def unbounded?
  @model_status == 'unbounded'
end

#values_at(*names) ⇒ Array<Float>

Retrieves values for multiple variables by name.

Examples:

solution.values_at(:x, :y)      # => [4.0, 0.0]
solution.values_at(x, y)        # => [4.0, 0.0] (by Variable objects)
solution.values_at(:x, y, 'z')  # => [4.0, 0.0, 3.0] (mixed types)

Parameters:

  • *names (Symbol, String, Variable)

    The variable names to retrieve.

Returns:

  • (Array<Float>)

    An array of variable values in the same order.



103
104
105
# File 'lib/lpsolver/solution.rb', line 103

def values_at(*names)
  names.map { |name| self[name] }
end