opl 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/opl.rb +159 -16
- metadata +2 -2
data/lib/opl.rb
CHANGED
@@ -1,31 +1,34 @@
|
|
1
1
|
require "rglpk"
|
2
2
|
|
3
3
|
#TODO
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
#
|
9
|
-
#are handled
|
4
|
+
#1.0
|
5
|
+
|
6
|
+
#2.0
|
7
|
+
#summing of variables
|
8
|
+
#e.g. x1 + x1 <= 3
|
10
9
|
#need to be able to handle arithmetic operations
|
11
10
|
#within a constraint or index
|
12
11
|
#e.g. sum(i in (1..3), x[i-1])
|
13
12
|
#a matrix representation of the solution if using
|
14
13
|
#sub notation
|
14
|
+
|
15
|
+
#3.0
|
16
|
+
#make sure extreme cases of foralls and sums
|
17
|
+
#are handled
|
15
18
|
#multiple level sub notation e.g. x[1][[3]]
|
16
|
-
#
|
17
|
-
#
|
18
|
-
|
19
|
+
#float coefficients and constants (does rglpk even handle this?)
|
20
|
+
#will have to figure out "<" and ">"
|
21
|
+
|
19
22
|
#write as module
|
20
23
|
|
21
24
|
def sides(equation)
|
22
|
-
if equation.include?("
|
25
|
+
if equation.include?("<=")
|
23
26
|
char = "<="
|
24
|
-
elsif equation.include?("
|
27
|
+
elsif equation.include?(">=")
|
25
28
|
char = ">="
|
26
|
-
elsif equation.include?("
|
29
|
+
elsif equation.include?("<")
|
27
30
|
char = "<"
|
28
|
-
elsif equation.include?("
|
31
|
+
elsif equation.include?(">")
|
29
32
|
char = ">"
|
30
33
|
elsif equation.include?("=")
|
31
34
|
char = "="
|
@@ -258,6 +261,7 @@ class Objective
|
|
258
261
|
attr_accessor :function
|
259
262
|
attr_accessor :optimization#minimize, maximize, equals
|
260
263
|
attr_accessor :variable_coefficient_pairs
|
264
|
+
attr_accessor :optimized_value
|
261
265
|
|
262
266
|
def initialize(function, optimization)
|
263
267
|
@function = function
|
@@ -300,14 +304,139 @@ def get_all_vars(constraints)
|
|
300
304
|
all_vars.flatten.uniq
|
301
305
|
end
|
302
306
|
|
307
|
+
def get_constants(text)
|
308
|
+
#in: "-8 + x + y + 3"
|
309
|
+
#out: "[-8, +3]"
|
310
|
+
text = text.gsub(" ","")
|
311
|
+
text = text+"#"
|
312
|
+
cs = []
|
313
|
+
potential_constants = text.scan(/\d+[^a-z^\[^\]^\d^\.^\)]/)
|
314
|
+
#potential_constants = text.scan(/\d+[^a-z^\[^\]^\d]/)
|
315
|
+
constants = potential_constants.find_all{|c|![*('a'..'z'),*('A'..'Z')].include?(text[text.index(c)-1])}
|
316
|
+
constants.each do |constant|
|
317
|
+
c = constant.scan(/\d+/)[0]
|
318
|
+
index = text.index(constant)
|
319
|
+
if index == 0
|
320
|
+
c = "+"+c
|
321
|
+
else
|
322
|
+
c = text.scan(/[\-\+]#{constant}/)[0]
|
323
|
+
end
|
324
|
+
cs << c.scan(/[\-\+]\d+/)[0]
|
325
|
+
end
|
326
|
+
return({:formatted => cs, :unformatted => constants})
|
327
|
+
end
|
328
|
+
|
329
|
+
def put_constants_on_rhs(text)
|
330
|
+
#in: "-8 + x + y + 3 <= 100"
|
331
|
+
#out: "x + y <= 100 + 5"
|
332
|
+
text = text.gsub(" ","")
|
333
|
+
s = sides(text)
|
334
|
+
constants_results = get_constants(s[:lhs])
|
335
|
+
constants = constants_results[:formatted]
|
336
|
+
unless constants.empty?
|
337
|
+
sum = constants.map{|cc|cc.to_i}.inject("+").to_s
|
338
|
+
if sum.include?("-")
|
339
|
+
sum = sum.gsub("-","+")
|
340
|
+
else
|
341
|
+
sum = "-"+sum
|
342
|
+
end
|
343
|
+
lhs = s[:lhs].gsub(" ","")+"#"
|
344
|
+
constants_results[:unformatted].each do |constant|
|
345
|
+
index = lhs.index(constant)
|
346
|
+
if index == 0
|
347
|
+
lhs = lhs[(constant.size-1)..(lhs.size-1)]
|
348
|
+
else
|
349
|
+
lhs = lhs[0..(index-2)]+lhs[(index+(constant.size-1))..(lhs.size-1)]
|
350
|
+
end
|
351
|
+
end
|
352
|
+
text = text.gsub(s[:lhs], lhs[0..-2])
|
353
|
+
text += sum
|
354
|
+
end
|
355
|
+
return(text)
|
356
|
+
end
|
357
|
+
|
358
|
+
def sum_constants(text)
|
359
|
+
#in: "100+ 10-3"
|
360
|
+
#out: "107"
|
361
|
+
get_constants(text)[:formatted].map{|c|c.to_i}.inject("+").to_s
|
362
|
+
end
|
363
|
+
|
364
|
+
def sub_rhs_with_summed_constants(constraint)
|
365
|
+
rhs = sides(constraint)[:rhs]
|
366
|
+
constraint.gsub(rhs, sum_constants(rhs))
|
367
|
+
end
|
368
|
+
|
369
|
+
def get_coefficient_variable_pairs(text)
|
370
|
+
text.scan(/\d*[\*]*[a-z]\[*\d*\]*/)
|
371
|
+
end
|
372
|
+
|
373
|
+
def put_variables_on_lhs(text)
|
374
|
+
#in: "x + y - x[3] <= 3z + 2x[2] - 10"
|
375
|
+
#out: "x + y - x[3] - 3z - 2x[2] <= -10"
|
376
|
+
text = text.gsub(" ", "")
|
377
|
+
s = sides(text.gsub(" ",""))
|
378
|
+
rhs = s[:rhs]
|
379
|
+
lhs = s[:lhs]
|
380
|
+
coefficient_variable_pairs = get_coefficient_variable_pairs(rhs)
|
381
|
+
add_to_left = []
|
382
|
+
remove_from_right = []
|
383
|
+
coefficient_variable_pairs.each do |cvp|
|
384
|
+
index = rhs.index(cvp)
|
385
|
+
if index == 0
|
386
|
+
add_to_left << "-"+cvp
|
387
|
+
remove_from_right << cvp
|
388
|
+
else
|
389
|
+
if rhs[index-1] == "+"
|
390
|
+
add_to_left << "-"+cvp
|
391
|
+
remove_from_right << "+"+cvp
|
392
|
+
else
|
393
|
+
add_to_left << "+"+cvp
|
394
|
+
remove_from_right << "-"+cvp
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
new_lhs = lhs+add_to_left.join("")
|
399
|
+
text = text.gsub(lhs, new_lhs)
|
400
|
+
new_rhs = rhs
|
401
|
+
remove_from_right.each do |rfr|
|
402
|
+
new_rhs = new_rhs.gsub(rfr, "")
|
403
|
+
end
|
404
|
+
text = text.gsub(rhs, new_rhs)
|
405
|
+
return(text)
|
406
|
+
end
|
407
|
+
|
408
|
+
def split_equals(constraint)
|
409
|
+
[constraint.gsub("=", "<="), constraint.gsub("=", ">=")]
|
410
|
+
end
|
411
|
+
|
412
|
+
def split_equals_a(constraints)
|
413
|
+
constraints.map do |constraint|
|
414
|
+
if (constraint.split("") & ["<=",">=","<",">"]).empty?
|
415
|
+
split_equals(constraint)
|
416
|
+
else
|
417
|
+
constraint
|
418
|
+
end
|
419
|
+
end.flatten
|
420
|
+
end
|
421
|
+
|
303
422
|
def subject_to(constraints)
|
304
423
|
constraints = constraints.flatten
|
424
|
+
constraints = split_equals_a(constraints)
|
305
425
|
constraints = constraints.map do |constraint|
|
306
426
|
sub_forall(constraint)
|
307
427
|
end.flatten
|
308
428
|
constraints = constraints.map do |constraint|
|
309
429
|
sub_sum(constraint)
|
310
430
|
end
|
431
|
+
constraints = constraints.map do |constraint|
|
432
|
+
put_constants_on_rhs(constraint)
|
433
|
+
end
|
434
|
+
constraints = constraints.map do |constraint|
|
435
|
+
put_variables_on_lhs(constraint)
|
436
|
+
end
|
437
|
+
constraints = constraints.map do |constraint|
|
438
|
+
sub_rhs_with_summed_constants(constraint)
|
439
|
+
end
|
311
440
|
all_vars = get_all_vars(constraints)
|
312
441
|
rows = []
|
313
442
|
constraints.each do |constraint|
|
@@ -315,12 +444,19 @@ def subject_to(constraints)
|
|
315
444
|
constraint = constraint.gsub(" ", "")
|
316
445
|
name = constraint.split(":")[0]
|
317
446
|
value = constraint.split(":")[1] || constraint
|
447
|
+
lower_bound = nil
|
318
448
|
if value.include?("<=")
|
319
449
|
upper_bound = value.split("<=")[1]
|
320
450
|
elsif value.include?(">=")
|
321
451
|
negate = true
|
322
452
|
bound = value.split(">=")[1].to_i
|
323
453
|
upper_bound = (bound*-1).to_s
|
454
|
+
elsif value.include?("<")
|
455
|
+
upper_bound = (value.split("<")[1]).to_i - 1
|
456
|
+
elsif value.include?(">")
|
457
|
+
negate = true
|
458
|
+
bound = (value.split(">")[1].to_i).to_i + 1
|
459
|
+
upper_bound = (bound*-1).to_s
|
324
460
|
end
|
325
461
|
coefs = coefficients(sides(value)[:lhs])
|
326
462
|
if negate
|
@@ -334,7 +470,7 @@ def subject_to(constraints)
|
|
334
470
|
end
|
335
471
|
vars = variables(sides(value)[:lhs])
|
336
472
|
zero_coef_vars = all_vars - vars
|
337
|
-
row = Row.new(name,
|
473
|
+
row = Row.new(name, lower_bound, upper_bound)
|
338
474
|
row.constraint = constraint
|
339
475
|
coefs = coefs + zero_coef_vars.map{|z|0}
|
340
476
|
vars = vars + zero_coef_vars
|
@@ -359,8 +495,15 @@ def minimize(objective, rows_c)#objective function has no = in it
|
|
359
495
|
end
|
360
496
|
|
361
497
|
def optimize(optimization, objective, rows_c)
|
362
|
-
|
498
|
+
o = Objective.new(objective, optimization)
|
499
|
+
lp = LinearProgram.new(o, rows_c.map{|row|row.constraint})
|
363
500
|
objective = sub_sum(objective)
|
501
|
+
objective_constants = get_constants(objective)
|
502
|
+
if objective_constants[:formatted].empty?
|
503
|
+
objective_addition = 0
|
504
|
+
else
|
505
|
+
objective_addition = sum_constants(objective_constants[:formatted].inject("+"))
|
506
|
+
end
|
364
507
|
lp.rows = rows_c
|
365
508
|
p = Rglpk::Problem.new
|
366
509
|
p.name = "sample"
|
@@ -395,7 +538,7 @@ def optimize(optimization, objective, rows_c)
|
|
395
538
|
p.obj.coefs = all_obj_coefficients
|
396
539
|
p.set_matrix(rows_c.map{|row|row.variable_coefficient_pairs.map{|vcp|vcp.coefficient.to_i}}.flatten)
|
397
540
|
p.simplex
|
398
|
-
|
541
|
+
lp.objective.optimized_value = p.obj.get + objective_addition.to_f
|
399
542
|
answer = Hash.new()
|
400
543
|
cols.each do |c|
|
401
544
|
answer[c.name] = c.get_prim.to_s
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-09-03 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Built on top of the glpk gem for linear programming. The syntax is copied
|
15
15
|
from OPL Studio, which remains my favorite linear programming software, but the
|