rams 0.1.1 → 0.1.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 892ffe4b05f309fee78d179745713f1880362428
4
- data.tar.gz: a659e1f65de80be3dae780ea30cb3b3ed504e5f9
3
+ metadata.gz: 8340e74253257a2e32d343f0c1c9a17e50ddeb47
4
+ data.tar.gz: 62eb080153e76cfa116a34c0afcb326e023484f6
5
5
  SHA512:
6
- metadata.gz: dcd53a0bd69bf259195e74ad33162b4176a89d55388ca400516da6b43916d819d2a613ade587906d3b26bf07897b105d20f7098487e839bfbbb0f99dcc1e723b
7
- data.tar.gz: 45cd3f47128cb9c4ee68ece0e9e309495b0b2049e53522aa3914aa404147164183ce67b0b0e4ceb49dbc4118f31174d192215ee7407f328a403edec651063fc3
6
+ metadata.gz: c4cf8db5cc61366fcf80cdabde0ba1f87d93a837f2cff11ca62de37088f4afb02d12df01d4fa2899d3cf35fbe18f9747da7b1ba0bdfea44e116ec319abc494b9
7
+ data.tar.gz: d7ea6f0faf629e241cb205fc6e28057b6639d51b1ce2bff4fc654594e0d84bce5fd87c7901c2ac57a6eb1dcc0c8275f239f1df61c03bec0a9d9c009db4d4c68d
data/lib/rams/model.rb CHANGED
@@ -2,6 +2,7 @@ require 'tempfile'
2
2
  require_relative 'variable'
3
3
  require_relative 'solvers/cbc'
4
4
  require_relative 'solvers/clp'
5
+ require_relative 'solvers/cplex'
5
6
  require_relative 'solvers/glpk'
6
7
 
7
8
  module RAMS
@@ -33,6 +34,7 @@ module RAMS
33
34
  SOLVERS = {
34
35
  cbc: RAMS::Solvers::CBC.new,
35
36
  clp: RAMS::Solvers::CLP.new,
37
+ cplex: RAMS::Solvers::CPLEX.new,
36
38
  glpk: RAMS::Solvers::GLPK.new
37
39
  }.freeze
38
40
 
@@ -4,13 +4,13 @@ module RAMS
4
4
  module Solvers
5
5
  # Interface to COIN-OR Branch-and-Cut
6
6
  class CBC < Solver
7
- def solver_command(model_file, solution_file, args)
8
- ['cbc', model_file.path] + args + ['printingOptions', 'all', 'solve', 'solution', solution_file.path]
7
+ def solver_command(model_path, solution_path, args)
8
+ ['cbc', model_path] + args + ['printingOptions', 'all', 'solve', 'solution', solution_path]
9
9
  end
10
10
 
11
11
  private
12
12
 
13
- def parse_status(model, lines)
13
+ def parse_status(_model, lines)
14
14
  return :undefined if lines.count < 1
15
15
  status = lines.first
16
16
  return :optimal if status =~ /Optimal/
@@ -4,13 +4,13 @@ module RAMS
4
4
  module Solvers
5
5
  # Interface to COIN-OR Linear Programming
6
6
  class CLP < Solver
7
- def solver_command(model_file, solution_file, args)
8
- ['clp', model_file.path] + args + ['printingOptions', 'all', 'solve', 'solution', solution_file.path]
7
+ def solver_command(model_path, solution_path, args)
8
+ ['clp', model_path] + args + ['printingOptions', 'all', 'solve', 'solution', solution_path]
9
9
  end
10
10
 
11
11
  private
12
12
 
13
- def parse_status(model, lines)
13
+ def parse_status(_model, lines)
14
14
  return :undefined if lines.count < 1
15
15
  status = lines.first
16
16
  return :optimal if status =~ /optimal/
@@ -0,0 +1,55 @@
1
+ require 'nokogiri'
2
+ require_relative 'solver'
3
+
4
+ module RAMS
5
+ module Solvers
6
+ # Interface to CPLEX
7
+ class CPLEX < Solver
8
+ def solve_and_parse(model, model_path, solution_path)
9
+ call_solver model, model_path, solution_path
10
+ return RAMS::Solution.new(:infeasible, nil, {}, {}) unless File.exist? solution_path
11
+ parse_solution model, File.read(solution_path)
12
+ end
13
+
14
+ def solver_command(model_path, solution_path, args)
15
+ ['cplex', '-c', "read #{model_path}"] + args + ['optimize', "write #{solution_path}"]
16
+ end
17
+
18
+ private
19
+
20
+ def parse_solution(model, solution_text)
21
+ xml_doc = Nokogiri::XML solution_text
22
+ RAMS::Solution.new(
23
+ parse_status(model, xml_doc),
24
+ parse_objective(model, xml_doc),
25
+ parse_primal(model, xml_doc),
26
+ parse_dual(model, xml_doc)
27
+ )
28
+ end
29
+
30
+ def parse_status(_model, xml_doc)
31
+ status = xml_doc.css('CPLEXSolution').css('header').first['solutionStatusString']
32
+ return :optimal if status =~ /optimal/
33
+ return :feasible if status =~ /feasible/
34
+ return :unbounded if status =~ /unbounded/
35
+ :unknown
36
+ end
37
+
38
+ def parse_objective(_model, xml_doc)
39
+ xml_doc.css('CPLEXSolution').css('header').first['objectiveValue'].to_f
40
+ end
41
+
42
+ def parse_primal(model, xml_doc)
43
+ xml_doc.css('CPLEXSolution').css('variables').css('variable').map do |v|
44
+ [model.variables[v['name']], v['value'].to_f]
45
+ end.to_h
46
+ end
47
+
48
+ def parse_dual(model, xml_doc)
49
+ xml_doc.css('CPLEXSolution').css('linearConstraints').css('constraint').map do |c|
50
+ [model.constraints[c['name']], c['dual'].to_f]
51
+ end.to_h
52
+ end
53
+ end
54
+ end
55
+ end
@@ -4,8 +4,8 @@ module RAMS
4
4
  module Solvers
5
5
  # Interface to the GNU Linear Programming Kit
6
6
  class GLPK < Solver
7
- def solver_command(model_file, solution_file, args)
8
- ['glpsol', '--lp', model_file.path, '--output', solution_file.path] + args
7
+ def solver_command(model_path, solution_path, args)
8
+ ['glpsol', '--lp', model_path, '--output', solution_path] + args
9
9
  end
10
10
 
11
11
  private
@@ -8,7 +8,7 @@ module RAMS
8
8
  def solve(model)
9
9
  model_file = write_model_file model
10
10
  begin
11
- get_solution model, model_file
11
+ get_solution model, model_file.path
12
12
  ensure
13
13
  model_file.unlink
14
14
  end
@@ -23,23 +23,24 @@ module RAMS
23
23
  model_file
24
24
  end
25
25
 
26
- def get_solution(model, model_file)
27
- solution_file = Tempfile.new ['', '.sol']
26
+ def get_solution(model, model_path)
27
+ solution_path = model_path + '.sol'
28
28
  begin
29
- solve_and_parse model, model_file, solution_file
29
+ solve_and_parse model, model_path, solution_path
30
30
  ensure
31
- solution_file.unlink
31
+ File.delete(solution_path) if File.exist?(solution_path)
32
32
  end
33
33
  end
34
34
 
35
- def solve_and_parse(model, model_file, solution_file)
36
- call_solver model, model_file, solution_file
37
- parse_solution model, File.read(solution_file)
35
+ def solve_and_parse(model, model_path, solution_path)
36
+ call_solver model, model_path, solution_path
37
+ return RAMS::Solution.new(:unknown, nil, {}, {}) unless File.exist? solution_path
38
+ parse_solution model, File.read(solution_path)
38
39
  end
39
40
 
40
41
  # rubocop:disable MethodLength
41
- def call_solver(model, model_file, solution_file)
42
- command = solver_command(model_file, solution_file, model.args)
42
+ def call_solver(model, model_path, solution_path)
43
+ command = solver_command(model_path, solution_path, model.args)
43
44
  _, stdout, stderr, exit_code = Open3.popen3(*command)
44
45
 
45
46
  begin
@@ -54,7 +55,7 @@ module RAMS
54
55
  end
55
56
  # rubocop:enable MethodLength
56
57
 
57
- def solver_command(_model_file, _solution_file, _args)
58
+ def solver_command(_model_file, _solution_path, _args)
58
59
  raise NotImplementedError
59
60
  end
60
61
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rams
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan J. O'Neil
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-08 00:00:00.000000000 Z
11
+ date: 2017-01-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A library for solving MILPs in Ruby.
14
14
  email:
@@ -25,6 +25,7 @@ files:
25
25
  - lib/rams/solution.rb
26
26
  - lib/rams/solvers/cbc.rb
27
27
  - lib/rams/solvers/clp.rb
28
+ - lib/rams/solvers/cplex.rb
28
29
  - lib/rams/solvers/glpk.rb
29
30
  - lib/rams/solvers/solver.rb
30
31
  - lib/rams/variable.rb