opl 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/opl.rb +159 -16
  2. metadata +2 -2
data/lib/opl.rb CHANGED
@@ -1,31 +1,34 @@
1
1
  require "rglpk"
2
2
 
3
3
  #TODO
4
- #my next goal should be handling all basic constraints
5
- #forget foralls and sums for a second
6
- #just allow the user to write a linear
7
- #model in a pleasant syntax
8
- #make sure extreme cases of foralls and sums
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
- #all relationships (<, >, =, <=, >=)
17
- #constants in constraints and objectives
18
- #float coefficients and constants
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, nil, upper_bound)
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
- lp = LinearProgram.new(objective, rows_c.map{|row|row.constraint})
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
- z = p.obj.get
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.1
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-08-18 00:00:00.000000000 Z
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