Skip to content

Instantly share code, notes, and snippets.

@hejmsdz
Created July 1, 2017 11:48
Show Gist options
  • Save hejmsdz/d7e9654a9517766801cc00082542ef84 to your computer and use it in GitHub Desktop.
Save hejmsdz/d7e9654a9517766801cc00082542ef84 to your computer and use it in GitHub Desktop.
Linear programming problems representation in Ruby
require "matrix"
class Variable
attr_accessor :name
def initialize(name)
@name = name
end
def to_s
@name.to_s
end
def coerce(other)
[self, other]
end
def *(a)
return 0 if a == 0
Linear.new({self => a})
end
def +(other)
Linear.new({self => 1, other => 1})
end
def -@
self * -1
end
def -(other)
-self + other
end
def to_linear
self * 1
end
end
class Linear
attr_reader :variables
def initialize(variables)
@variables = variables.select { |key, val| val != 0 }
end
def to_s
@variables.map { |key, val| val.to_s + "*" + key.to_s }
.map { |term| term.sub(/^1\*/, '').sub(/^-1\*/, '-') }
.join(" + ").gsub("+ -", "- ")
end
def [](var)
@variables[var]
end
def *(a)
return 0 if a == 0
Linear.new(Hash[@variables.map { |key, val| [key, val * a] }])
end
def +(other)
Linear.new(@variables.merge(other.to_linear.variables) { |key, oldval, newval| oldval + newval })
end
def -@
self * -1
end
def -(other)
-self + other
end
def <=(a)
Constraint.new(self, a)
end
def >=(a)
Constraint.new(-self, -a)
end
def to_linear
self
end
def eval(subs)
@variables.map { |key, val| subs[key] * val }.sum
end
end
class Constraint
attr_reader :linear, :rhs, :slack
def initialize(linear, rhs)
@slack = Variable.new(:s)
linear += @slack
if rhs < 0
linear *= -1
rhs *= -1
end
@linear = linear
@rhs = rhs
end
def to_s
linear.to_s + " = " + rhs.to_s
end
end
class Simplex
def initialize(variables, goal)
@variables = variables
@goal = goal
end
def self.create(block, direction = :max)
vars = block.parameters.map { |req, name| Variable.new(name) }
goal = block.call(*vars) * (direction == :min ? -1 : 1)
Simplex.new(Hash[vars.map { |var| [var.name, var] }], goal)
end
def self.max(&block)
self.create(block, :max)
end
def self.min(&block)
self.create(block, :min)
end
def constraints(&block)
@constraints = block.call(block.parameters.map { |req, name| @variables[name] })
@constraints.each_with_index { |c, i|
c.slack.name = (c.slack.name.to_s + (i+1).to_s).to_sym
@variables[c.slack.name] = c.slack
}
self
end
def find_basis
end
def matrix
vars = @variables.values
n = vars.length
m = @constraints.length
Matrix.build(m, n + 1) { |row, col|
if col < n
@constraints[row].linear[vars[col]] || 0
else
@constraints[row].rhs
end
}
end
def to_s
constr = @constraints.map { |c| "\t" + c.to_s }.join("\n")
"Maximize:\n\t#{@goal}\n\nSubject to:\n#{constr}"
end
end
lp = Simplex.max { |v|
v
}.constraints { |p1, p2, p3, v|
[6*p1 + 4*p2 + 2*p3 >= v,
2*p1 + 8*p2 + 6*p3 >= v,
10*p1 + p2 - 10*p3 >= v,
p1 + p2 + p3 === 1]
}
puts lp
puts
puts lp.matrix.row_vectors.map { |row| row.map { |cell| cell.to_s.rjust(5) }.to_a.insert(-2, " |").join }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment