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.
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