opl 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|