class MIPPeR::LPSolveModel

A linear programming model using the lp_solve solver

Attributes

ptr[R]

Public Class Methods

new() click to toggle source
Calls superclass method
# File lib/mipper/lp_solve/model.rb, line 6
def initialize
  fail unless MIPPeR.const_defined?(:LPSolve)

  super

  @var_count = 0
  @constr_count = 0

  # Construct a new model
  @ptr = FFI::AutoPointer.new LPSolve.make_lp(0, 0),
                              LPSolve.method(:delete_lp)

  # Disable output
  ret = LPSolve.set_outputfile @ptr, ''
  fail if ret != 1
end

Public Instance Methods

optimize() click to toggle source

Optimize the model

# File lib/mipper/lp_solve/model.rb, line 31
def optimize
  # Ensure pending variables and constraints are added
  update

  # Run the solver and save the status for later
  status = LPSolve.solve(@ptr)
  save_solution status
end
sense=(sense) click to toggle source

Set the sense of the model

# File lib/mipper/lp_solve/model.rb, line 24
def sense=(sense)
  @sense = sense
  sense = sense == :min ? 0 : 1
  LPSolve.set_sense @ptr, sense
end
set_variable_bounds(var_index, lb, ub) click to toggle source
# File lib/mipper/lp_solve/model.rb, line 40
def set_variable_bounds(var_index, lb, ub)
  ret = LPSolve.set_bounds @ptr, var_index, lb, ub
  fail if ret != 1
end

Protected Instance Methods

add_constraints(constrs) click to toggle source

Add multiple constraints at once

# File lib/mipper/lp_solve/model.rb, line 59
def add_constraints(constrs)
  ret = LPSolve.resize_lp(@ptr, @constraints.length + constrs.length,
                          @variables.length)
  fail if ret != 1

  constrs.each { |constr| store_constraint constr }
end
add_variables(vars) click to toggle source

Add multiple variables to the model simultaneously

# File lib/mipper/lp_solve/model.rb, line 48
def add_variables(vars)
  # Allocate space for the new variables
  ret = LPSolve.resize_lp(@ptr, @constraints.length,
                          @variables.length + vars.length)
  fail if ret != 1

  # Store all the variables in the model
  vars.each { |var| store_variable var }
end

Private Instance Methods

save_solution(status) click to toggle source

Save the solution to the model for access later

# File lib/mipper/lp_solve/model.rb, line 89
def save_solution(status)
  status = case status
           when LPSolve::OPTIMAL
             :optimized
           when LPSolve::INFEASIBLE, LPSolve::UNBOUNDED,
                LPSolve::DEGENERATE
             :invalid
           else
             :unknown
           end

  if status == :optimized
    objective_value = LPSolve.get_objective @ptr
    rows = LPSolve.get_Nrows(@ptr)
    variable_values = [nil]
    @variables.each do |var|
      variable_values << LPSolve.get_var_primalresult(@ptr,
                                                      rows + var.index)
    end
  else
    objective_value = nil
    variable_values = []
  end

  @solution = Solution.new status, objective_value, variable_values
end
set_variable_type(index, type) click to toggle source

Set the type of a variable

# File lib/mipper/lp_solve/model.rb, line 171
def set_variable_type(index, type)
  case type
  when :integer
    ret = LPSolve.set_int @ptr, index, 1
  when :binary
    ret = LPSolve.set_binary @ptr, index, 1
  when :continuous
    ret = LPSolve.set_int @ptr, index, 0
  else
    fail type
  end

  fail if ret != 1
end
store_constraint(constr) click to toggle source

Save the constraint to the model and update the constraint pointers

# File lib/mipper/lp_solve/model.rb, line 117
def store_constraint(constr)
  # Update the constraint to track the index in the model
  index = LPSolve.get_Nrows(@ptr)
  constr.model = self
  constr.index = index
  constr.freeze
  @constr_count += 1

  # Set constraint properties
  if constr.name
    ret = LPSolve.set_row_name(@ptr, index, constr.name)
    fail if ret != 1
  end

  # Determine the correct constant for the type of constraint
  case constr.sense
  when :==
    type = LPSolve::EQ
  when :>=
    type = LPSolve::GE
  when :<=
    type = LPSolve::LE
  end

  store_constraint_matrix constr, type
  @constraints << constr
end
store_constraint_matrix(constr, type) click to toggle source

Build the constraint matrix and add it to the model

# File lib/mipper/lp_solve/model.rb, line 70
def store_constraint_matrix(constr, type)
  # Initialize arrays used to hold the coefficients for each variable
  row = []
  colno = []
  constr.expression.terms.each do |var, coeff|
    row << coeff * 1.0
    colno << var.index
  end

  row_buffer = build_pointer_array row, :double
  colno_buffer = build_pointer_array colno, :int

  ret = LPSolve.add_constraintex(@ptr, constr.expression.terms.length,
                                 row_buffer, colno_buffer,
                                 type, constr.rhs)
  fail if ret != 1
end
store_variable(var) click to toggle source

Save the variable to the model and update the variable pointers

# File lib/mipper/lp_solve/model.rb, line 146
def store_variable(var)
  # Update the variable to track the index in the model
  index = @var_count + 1
  var.model = self
  var.index = index
  @var_count += 1

  ret = LPSolve.add_columnex @ptr, 0, nil, nil
  fail if ret != 1

  # Set variable properties
  set_variable_type index, var.type

  ret = LPSolve.set_col_name(@ptr, index, var.name)
  fail if ret != 1

  ret = LPSolve.set_obj(@ptr, index, var.coefficient)
  fail if ret != 1

  set_variable_bounds index, var.lower_bound, var.upper_bound

  @variables << var
end