simplex 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/simplex.rb +137 -0
  3. metadata +44 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 607708dc2b1019f3de26f43a8f714d7ba18a1d27
4
+ data.tar.gz: e05193d59e5237b419560bdb8aa65add6894697e
5
+ SHA512:
6
+ metadata.gz: 57ee4659de8ec4e8660bcc38b412fdd636f300903def27a9b13b1b5baed75a84ead979fb8f7bb84d297dbd17b0c2f8fd0ccfe664463460435a3039ed2b06fc95
7
+ data.tar.gz: 4915382d169cd3e9743ef3bba31f94ef2c3d89c6862d0849f361689435d1984b3ffeb9b118f8a983ee5d4af24668098452af29cd78a1dcfbbdcf957019963320
data/lib/simplex.rb ADDED
@@ -0,0 +1,137 @@
1
+ require 'matrix'
2
+
3
+ class Matrix
4
+ def []=(i, j, x)
5
+ @rows[i][j] = x
6
+ end
7
+ end
8
+
9
+ class Vector
10
+ public :[]=
11
+ end
12
+
13
+ class Simplex
14
+ DEFAULT_MAX_ITERATIONS = 1_000_000
15
+ attr_accessor :max_iterations
16
+
17
+ def initialize(c, a, b)
18
+ @max_iterations = DEFAULT_MAX_ITERATIONS
19
+ # Problem dimensions
20
+ @num_non_slack_vars = a.first.length
21
+ @num_constraints = b.length
22
+ @num_vars = @num_non_slack_vars + @num_constraints
23
+ @x = Array.new(@num_vars)
24
+
25
+ # Set up initial matrix A and vectors b, c
26
+ @c = Vector[*c.map {|c1| -1*c1 } + [0]*@num_constraints]
27
+ @a = Matrix[*a.map {|a1| a1.clone + [0]*@num_constraints}]
28
+ @b = Vector[*b.clone]
29
+ 0.upto(@num_constraints - 1) {|i| @a[i, @num_non_slack_vars + i] = 1 }
30
+
31
+ @basic_vars = ((@num_non_slack_vars)...(@num_vars)).to_a
32
+
33
+ # set initial solution: all non-slack variables = 0
34
+ update_solution
35
+ @solved = false
36
+ end
37
+
38
+ def solve
39
+ return if @solved
40
+ i = 0
41
+ while can_improve? and i < @max_iterations
42
+ i += 1
43
+ pivot_column = entering_variable_ix
44
+ pivot_row = minimum_coefficient_ratio_row_ix(pivot_column)
45
+ leaving_var = leaving_variable(pivot_row)
46
+ @basic_vars.delete(leaving_var)
47
+
48
+ # update objective
49
+ c_ratio = Rational(@c[pivot_column], @a[pivot_row, pivot_column])
50
+ @c = @c - (@a.row(pivot_row)*c_ratio)
51
+
52
+ # update pivot row
53
+ ratio = Rational(1, @a[pivot_row, pivot_column])
54
+ 0.upto(@a.column_count - 1) do |column_ix|
55
+ @a[pivot_row, column_ix] = ratio * @a[pivot_row, column_ix]
56
+ end
57
+ @b[pivot_row] = ratio * @b[pivot_row]
58
+
59
+ # update A and B
60
+ 0.upto(@a.row_count - 1) do |row_ix|
61
+ next if row_ix == pivot_row
62
+ ratio = @a[row_ix, pivot_column]
63
+ 0.upto(@a.column_count - 1) do |column_ix|
64
+ @a[row_ix, column_ix] = @a[row_ix, column_ix] - ratio*@a[pivot_row, column_ix]
65
+ end
66
+ @b[row_ix] = @b[row_ix] - ratio*@b[pivot_row]
67
+ end
68
+
69
+ @basic_vars << pivot_column
70
+ @basic_vars.sort!
71
+ update_solution
72
+ end
73
+ @solved = true
74
+ end
75
+
76
+ def update_solution
77
+ 0.upto(@num_vars - 1) {|i| @x[i] = 0 }
78
+ @basic_vars.each do |basic_var|
79
+ row_coeff_1 = nil
80
+ 0.upto(@a.row_count - 1) do |row_ix|
81
+ coeff = @a[row_ix, basic_var]
82
+ if coeff == 1
83
+ if row_coeff_1 == nil
84
+ row_coeff_1 = row_ix
85
+ end
86
+ end
87
+ end
88
+ @x[basic_var] = @b[row_coeff_1]
89
+ end
90
+ end
91
+
92
+ def solution
93
+ solve
94
+ @x.to_a[0...@num_non_slack_vars]
95
+ end
96
+
97
+ def can_improve?
98
+ !!entering_variable_ix
99
+ end
100
+
101
+ def entering_variable_ix
102
+ current_min_value = nil
103
+ current_min_index = nil
104
+ @c.each_with_index do |v, i|
105
+ if v < 0
106
+ if current_min_value == nil || v <= current_min_value
107
+ current_min_value = v
108
+ current_min_index = i
109
+ end
110
+ end
111
+ end
112
+ current_min_index
113
+ end
114
+
115
+ def leaving_variable(pivot_row)
116
+ 0.upto(@a.column_count - 1) do |column_ix|
117
+ if @a[pivot_row, column_ix] == 1 and @basic_vars.include?(column_ix)
118
+ return column_ix
119
+ end
120
+ end
121
+ end
122
+
123
+ def minimum_coefficient_ratio_row_ix(column_ix)
124
+ current_min_value = nil
125
+ current_min_index = nil
126
+ 0.upto(@a.row_count - 1) do |row_ix|
127
+ next if @a[row_ix, column_ix] == 0
128
+ ratio = Rational(@b[row_ix], @a[row_ix, column_ix])
129
+ if ratio > 0 && (!current_min_value || ratio < current_min_value)
130
+ current_min_value = ratio
131
+ current_min_index = row_ix
132
+ end
133
+ end
134
+ return current_min_index
135
+ end
136
+
137
+ end
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simplex
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Lucraft
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-15 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Naive implementation of the simplex linear programming algorithm in pure
14
+ Ruby.
15
+ email: dan@fluentradical.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/simplex.rb
21
+ homepage:
22
+ licenses: []
23
+ metadata: {}
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ requirements: []
39
+ rubyforge_project:
40
+ rubygems_version: 2.0.6
41
+ signing_key:
42
+ specification_version: 4
43
+ summary: Simplex linear programming solver
44
+ test_files: []