class Guruby::Model

Attributes

constraints[R]
environment[R]
ptr[R]
variables[R]

Public Class Methods

new(env) click to toggle source
# File lib/guruby/model.rb, line 7
def initialize(env)
  @environment = env

  @ptr = FFI::MemoryPointer.new :pointer
  Gurobi.GRBnewmodel env.ptr, @ptr, 'model', 0, nil, nil, nil, nil, nil
  @ptr = @ptr.read_pointer

  # Ensure the model is freed
  ObjectSpace.define_finalizer self, self.class.finalize(@ptr)

  @var_count = 0
  @variables = []
  @constraints = []

  @pending_variables = []
  @pending_constraints = []
end

Private Class Methods

finalize(ptr) click to toggle source

Free the model

# File lib/guruby/model.rb, line 102
def self.finalize(ptr)
  proc { Gurobi.GRBfreemodel ptr }
end

Public Instance Methods

<<(obj) click to toggle source

Add new objects (variables and constraints) to the model

# File lib/guruby/model.rb, line 26
def <<(obj)
  case obj
  when Variable
    @pending_variables << obj
  when Constraint
    @pending_constraints << obj
  else
    fail TypeError
  end
end
compute_IIS() click to toggle source

Compute an irreducible inconsistent subsytem for the model

# File lib/guruby/model.rb, line 86
def compute_IIS
  ret = Gurobi.GRBcomputeIIS @ptr
  fail if ret != 0
end
objective_value() click to toggle source

The value of the objective function

# File lib/guruby/model.rb, line 92
def objective_value
  dblptr = FFI::MemoryPointer.new :pointer
  ret = Gurobi.GRBgetdblattr @ptr, GRB_DBL_ATTR_OBJVAL, dblptr
  fail if ret != 0
  dblptr.read_double
end
optimize() click to toggle source

Optimize the model

# File lib/guruby/model.rb, line 69
def optimize
  # Ensure pending variables and constraints are added
  update

  ret = Gurobi.GRBoptimize @ptr
  fail if ret != 0
end
set_sense(sense) click to toggle source

Set the sense of the model

# File lib/guruby/model.rb, line 63
def set_sense(sense)
  ret = Gurobi.GRBsetintattr @ptr, GRB_INT_ATTR_MODELSENSE, sense
  fail if ret != 0
end
status() click to toggle source

Get the status of the model

# File lib/guruby/model.rb, line 78
def status
  intptr = FFI::MemoryPointer.new :pointer
  ret = Gurobi.GRBgetintattr @ptr, GRB_INT_ATTR_STATUS, intptr
  fail if ret != 0
  intptr.read_int
end
update() click to toggle source

Update the model

# File lib/guruby/model.rb, line 38
def update
  if @pending_variables.length == 1
    add_variable @pending_variables.first
  elsif @pending_variables.length > 0
    add_variables @pending_variables
  end
  @pending_variables = []

  if @pending_constraints.length == 1
    add_constraint @pending_constraints.first
  elsif @pending_constraints.length > 0
    add_constraints @pending_constraints
  end
  @pending_constraints = []

  ret = Gurobi.GRBupdatemodel @ptr
  fail if ret != 0
end
write(filename) click to toggle source

Write the model to a file

# File lib/guruby/model.rb, line 58
def write(filename)
  Gurobi.GRBwrite @ptr, filename
end

Private Instance Methods

add_constraint(constr) click to toggle source

Add a new constraint to the model

# File lib/guruby/model.rb, line 193
def add_constraint(constr)
  terms = constr.expression.terms
  indexes_buffer = FFI::MemoryPointer.new :int, terms.length
  indexes_buffer.write_array_of_int(terms.each_key.map do |var|
    var.instance_variable_get(:@index)
  end)

  values_buffer = FFI::MemoryPointer.new :double, terms.length
  values_buffer.write_array_of_double terms.values

  ret = Gurobi.GRBaddconstr @ptr, terms.length,
                            indexes_buffer, values_buffer,
                            constr.sense.ord, constr.rhs, constr.name
  fail if ret != 0

  @constraints << constr
end
add_constraints(constrs) click to toggle source

Add multiple constraints at once

# File lib/guruby/model.rb, line 155
def add_constraints(constrs)
  cbeg = []
  cind = []
  cval = []
  constrs.each_with_index.map do |constr, i|
    cbeg << cind.length
    constr.expression.terms.each do |var, coeff|
      cind << var.instance_variable_get(:@index)
      cval << coeff
    end
  end

  cbeg_buffer = FFI::MemoryPointer.new :pointer, cbeg.length
  cbeg_buffer.write_array_of_int cbeg

  cind_buffer = FFI::MemoryPointer.new :pointer, cind.length
  cind_buffer.write_array_of_int cind

  cval_buffer = FFI::MemoryPointer.new :pointer, cval.length
  cval_buffer.write_array_of_double cval

  sense_buffer = FFI::MemoryPointer.new :pointer, constrs.length
  sense_buffer.write_array_of_char constrs.map { |c| c.sense.ord }

  rhs_buffer = FFI::MemoryPointer.new :pointer, constrs.length
  rhs_buffer.write_array_of_double constrs.map(&:rhs)

  names = array_to_pointers_to_names constrs
  names_buffer = FFI::MemoryPointer.new :pointer, constrs.length
  names_buffer.write_array_of_pointer names

  ret = Gurobi.GRBaddconstrs @ptr, constrs.length, cind.length,
                             cbeg_buffer, cind_buffer, cval_buffer,
                             sense_buffer, rhs_buffer, names_buffer
  fail if ret != 0
end
add_variable(var) click to toggle source

Add a new variable to the model

# File lib/guruby/model.rb, line 135
def add_variable(var)
  ret = Gurobi.GRBaddvar @ptr, 0, nil, nil, var.coefficient,
                         var.lower_bound, var.upper_bound,
                         var.type.ord, var.name
  fail if ret != 0

  store_variable var
end
add_variables(vars) click to toggle source

Add multiple variables to the model simultaneously

# File lib/guruby/model.rb, line 107
def add_variables(vars)
  objective_buffer = FFI::MemoryPointer.new :double, vars.length
  objective_buffer.write_array_of_double vars.map(&:coefficient)

  lb_buffer = FFI::MemoryPointer.new :double, vars.length
  lb_buffer.write_array_of_double vars.map(&:lower_bound)

  ub_buffer = FFI::MemoryPointer.new :double, vars.length
  ub_buffer.write_array_of_double vars.map(&:upper_bound)

  type_buffer = FFI::MemoryPointer.new :char, vars.length
  type_buffer.write_array_of_char vars.map { |var| var.type.ord }

  names = array_to_pointers_to_names vars
  names_buffer = FFI::MemoryPointer.new :pointer, vars.length
  names_buffer.write_array_of_pointer names

  ret = Gurobi.GRBaddvars @ptr, vars.length, 0, nil, nil, nil,
                          objective_buffer, lb_buffer, ub_buffer,
                          type_buffer, names_buffer

  fail if ret != 0

  # Store all the variables in the model
  vars.each { |var| store_variable var }
end
array_to_pointers_to_names(arr) click to toggle source

Convert an array of objects to an FFI array of memory pointers to the names of each object

# File lib/guruby/model.rb, line 213
def array_to_pointers_to_names(arr)
  arr.map do |obj|
    obj.name.nil? ? nil : FFI::MemoryPointer.from_string(obj.name)
  end
end
store_variable(var) click to toggle source

Save the variable to the model and update the variable pointers

# File lib/guruby/model.rb, line 145
def store_variable(var)
  # Update the variable to track the index in the model
  var.instance_variable_set :@model, self
  var.instance_variable_set :@index, @var_count
  @var_count += 1

  @variables << var
end