class MIPPeR::GLPKModel

Attributes

ptr[R]

Public Class Methods

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

  super

  @var_count = 0
  @constr_count = 0

  # Constraint matrix initialization (see #add_constraints)
  @ia = [0]
  @ja = [0]
  @ar = [0]

  # Disable terminal output
  GLPK.glp_term_out GLPK::GLP_OFF

  # Construct a new model
  @ptr = FFI::AutoPointer.new GLPK.glp_create_prob,
                              GLPK.method(:glp_delete_prob)
end

Public Instance Methods

optimize() click to toggle source

Optimize the model

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

  # Run the solver and save the status for later
  iocp = GLPK::IOCP.new
  GLPK.glp_init_iocp iocp
  iocp[:presolve] = GLPK::GLP_ON
  status = GLPK.glp_intopt(@ptr, iocp)

  save_solution status
end
sense=(sense) click to toggle source

Set the sense of the model

# File lib/mipper/glpk/model.rb, line 39
def sense=(sense)
  @sense = sense
  sense = sense == :min ? GLPK::GLP_MIN : GLPK::GLP_MAX
  GLPK.glp_set_obj_dir @ptr, sense
end
set_variable_bounds(var_index, lb, ub) click to toggle source
# File lib/mipper/glpk/model.rb, line 59
def set_variable_bounds(var_index, lb, ub)
  # Determine the type of the variable bounds
  if lb == ub
    type = GLPK::GLP_FX
  elsif lb.finite?
    type = ub.finite? ? GLPK::GLP_DB : GLPK::GLP_LO
  else
    type = ub.finite? ? GLPK::GLP_UP : GLPK::GLP_FR
  end

  # Set the bounds on the variable
  GLPK.glp_set_col_bnds(@ptr, var_index, type, lb, ub)
end
write_lp(filename) click to toggle source

Write the model to a file in CPLEX LP format

# File lib/mipper/glpk/model.rb, line 27
def write_lp(filename)
  ret = GLPK.glp_write_lp @ptr, 0, filename
  fail if ret != 0
end
write_mps(filename) click to toggle source

Write the model to a file in MPS format

# File lib/mipper/glpk/model.rb, line 33
def write_mps(filename)
  ret = GLPK.glp_write_mps @ptr, GLPK::GLP_MPS_FILE, nil, filename
  fail if ret != 0
end

Protected Instance Methods

add_constraints(constrs) click to toggle source

Add multiple constraints at once

# File lib/mipper/glpk/model.rb, line 83
def add_constraints(constrs)
  GLPK.glp_add_rows(@ptr, constrs.length)

  # Store each constraint and initialize a matrix used to hold
  # the coefficients of each value in the constraint matrix
  #
  # * ia - row (constraint) index
  # * ja - column (variable) index
  # * ar - constraint coefficient
  constrs.each do |constr|
    store_constraint constr
    constr.expression.terms.each do |var, coeff|
      @ia << constr.index
      @ja << var.index
      @ar << coeff
    end
  end

  ia_buffer = build_pointer_array @ia, :int
  ja_buffer = build_pointer_array @ja, :int
  ar_buffer = build_pointer_array @ar, :double

  GLPK.glp_load_matrix(@ptr, @ar.length - 1, ia_buffer, ja_buffer,
                       ar_buffer)

  @constraints.concat constrs
end
add_variables(vars) click to toggle source

Add multiple variables to the model simultaneously

# File lib/mipper/glpk/model.rb, line 76
def add_variables(vars)
  # Store all the variables in the model
  GLPK.glp_add_cols(@ptr, vars.length)
  vars.each { |var| store_variable var }
end

Private Instance Methods

glpk_type(type) click to toggle source

Get the constant GLPK uses to represent our variable types

# File lib/mipper/glpk/model.rb, line 184
def glpk_type(type)
  case type
  when :integer
    GLPK::GLP_IV
  when :binary
    GLPK::GLP_BV
  when :continuous
    GLPK::GLP_CV
  else
    fail type
  end
end
save_solution(status) click to toggle source

Save the solution to the model for access later

# File lib/mipper/glpk/model.rb, line 114
def save_solution(status)
  status = case status
           when 0, GLPK::GLP_EMIPGAP
             :optimized
           when GLPK::GLP_EBOUND, GLPK::GLP_EROOT, GLPK::GLP_ENOPFS
             :invalid
           else
             :unknown
           end

  if status == :optimized
    objective_value = GLPK.glp_mip_obj_val @ptr
    variable_values = [nil]
    @variables.each do |var|
      variable_values << GLPK.glp_mip_col_val(@ptr, var.index)
    end
  else
    objective_value = nil
    variable_values = []
  end

  @solution = Solution.new status, objective_value, variable_values
end
store_constraint(constr) click to toggle source

Save the constraint to the model and update the constraint pointers

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

  # Set constraint properties
  GLPK.glp_set_row_name(@ptr, index, constr.name)

  case constr.sense
  when :==
    lb = ub = constr.rhs
    type = GLPK::GLP_FX
  when :>=
    lb = constr.rhs
    ub = Float::INFINITY
    type = GLPK::GLP_LO
  when :<=
    lb = -Float::INFINITY
    ub = constr.rhs
    type = GLPK::GLP_UP
  end
  GLPK.glp_set_row_bnds(@ptr, index, type, lb, ub)
end
store_variable(var) click to toggle source

Save the variable to the model and update the variable pointers

# File lib/mipper/glpk/model.rb, line 167
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

  @variables << var

  # Set variable properties
  GLPK.glp_set_col_name(@ptr, index, var.name)
  GLPK.glp_set_col_kind(@ptr, index, glpk_type(var.type))
  GLPK.glp_set_obj_coef(@ptr, index, var.coefficient)
  set_variable_bounds index, var.lower_bound, var.upper_bound
end