relax4 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ module Relax4
2
+ VERSION = '1.0.0'
3
+ end
data/lib/relax4.rb ADDED
@@ -0,0 +1,223 @@
1
+ require 'relax4.so'
2
+
3
+ #
4
+ # Methods for calling the RELAX IV solver.
5
+ #
6
+ # There is a Ruby-friendly wrapper method called {Relax4.solve}.
7
+ #
8
+ # There are also lower-level methods. Unfortunately, swig and rdoc don't work
9
+ # very well together, so the best references for these calls are currently the
10
+ # source of {Relax4.solve} and the <tt>ext/relax4.h</tt> header file listed
11
+ # below:
12
+ # :include:../ext/relax4.h
13
+ #
14
+ module Relax4
15
+ #
16
+ # Solve a minimum cost network flow problem.
17
+ #
18
+ # @param [Hash] args named arguments
19
+ #
20
+ # @option args [Array<Integer>] :start_nodes index of node at the start of
21
+ # each arc, where the first node is numbered 1.
22
+ #
23
+ # @option args [Array<Integer>] :end_nodes index of node at the end of each
24
+ # arc, where the first node is numbered 1.
25
+ #
26
+ # @option args [Array<Integer>] :costs for each arc; negative costs are
27
+ # allowed; negative cost cycles are allowed (edges involved in the cycle are
28
+ # set to large); all costs must be less than +large+ and should be less than
29
+ # <tt>large/10</tt> to avoid overflow (see RELAX4_DEFAULT_MAX_COST in
30
+ # relax4.h, above).
31
+ #
32
+ # @option args [Array<Integer>] :capacities (nil) for each arc; if not
33
+ # specified (problem is uncapacitated), all arc capacities are set to +large+;
34
+ # capacities must be in [0, +large+].
35
+ #
36
+ # @option args [Array<Integer>] :demands for each node; a node with negative
37
+ # demand is a surplus node; demands should balance (sum to zero), or else the
38
+ # problem will be infeasible (but you can add a dummy node).
39
+ #
40
+ # @option args [Boolean] :auction_init (false) use the auction routine to find
41
+ # an initial feasible solution; this is recommended only for hard problems.
42
+ #
43
+ # @option args [Integer] :large (RELAX4_DEFAULT_LARGE) a very large integer to
44
+ # represent infinity; see the ext/relax4.h listing above for more info.
45
+ #
46
+ # @option args [Integer] :max_cost (RELAX4_DEFAULT_MAX_COST) largest allowed
47
+ # cost, in order to avoid integer overflow; see the ext/relax4.h listing above
48
+ # for more info.
49
+ #
50
+ # @return [Array<Integer>] optimal flows for each arc
51
+ #
52
+ # @raise [ArgumentError] if problem inputs are invalid
53
+ #
54
+ # @raise [InfeasibleError] if problem is infeasible
55
+ #
56
+ def self.solve args
57
+
58
+ start_nodes = get_arg(args, :start_nodes)
59
+ end_nodes = get_arg(args, :end_nodes)
60
+ costs = get_arg(args, :costs)
61
+ large = args[:large] || RELAX4_DEFAULT_LARGE
62
+ max_cost = args[:max_cost] || RELAX4_DEFAULT_MAX_COST
63
+ num_arcs = start_nodes.size
64
+ capacities = args[:capacities] || [large]*num_arcs
65
+
66
+ sizes = [start_nodes, end_nodes, costs, capacities].map{|a| a.size}
67
+ raise ArgumentError.new("bad sizes for start/end_nodes, costs, capacities:"\
68
+ " #{sizes.join(',')}") unless sizes.all? {|s| s == num_arcs}
69
+
70
+ demands = get_arg(args, :demands)
71
+ num_nodes = demands.size
72
+
73
+ flows_ia = IntegerArray.new(num_arcs)
74
+ case relax4_init(num_nodes, num_arcs,
75
+ IntegerArray.from_array(start_nodes),
76
+ IntegerArray.from_array(end_nodes),
77
+ IntegerArray.from_array(costs),
78
+ IntegerArray.from_array(capacities),
79
+ IntegerArray.from_array(demands),
80
+ flows_ia, large)
81
+ when RELAX4_OK then
82
+ # continue
83
+ when RELAX4_FAIL_OUT_OF_MEMORY then
84
+ raise "could not allocate enough memory in relax4_init"
85
+ else
86
+ raise "relax4_init_phase_1 failed with unrecognized code"
87
+ end
88
+
89
+ begin
90
+ case relax4_check_inputs(max_cost)
91
+ when RELAX4_OK then
92
+ # continue
93
+ when RELAX4_FAIL_BAD_SIZE then
94
+ raise ArgumentError.new('problem has zero nodes or zero arcs')
95
+ when RELAX4_FAIL_BAD_NODE then
96
+ raise ArgumentError.new('start_nodes or end_nodes has a bad index')
97
+ when RELAX4_FAIL_BAD_COST then
98
+ raise ArgumentError.new("a cost exceeds the maximum (#{max_cost})")
99
+ when RELAX4_FAIL_BAD_CAPACITY then
100
+ raise ArgumentError.new("a capacity exceeds the maximum (#{large})")
101
+ else
102
+ raise "relax4_check_inputs failed with unrecognized code"
103
+ end
104
+
105
+ case relax4_init_phase_1()
106
+ when RELAX4_OK then
107
+ # continue
108
+ when RELAX4_INFEASIBLE then
109
+ raise InfeasibleError.new("infeasibility detected in phase 1 init")
110
+ else
111
+ raise "relax4_init_phase_1 failed with unrecognized code"
112
+ end
113
+
114
+ if args[:auction_init]
115
+ case relax4_auction()
116
+ when RELAX4_OK then
117
+ # continue
118
+ when RELAX4_INFEASIBLE then
119
+ raise InfeasibleError.new("infeasibility detected in auction")
120
+ else
121
+ raise "relax4_auction failed with unrecognized code"
122
+ end
123
+ else
124
+ case relax4_init_phase_2()
125
+ when RELAX4_OK then
126
+ # continue
127
+ when RELAX4_INFEASIBLE then
128
+ raise InfeasibleError.new("infeasibility detected in phase 2 init")
129
+ else
130
+ raise "relax4_init_phase_2 failed with unrecognized code"
131
+ end
132
+ end
133
+
134
+ case relax4_run()
135
+ when RELAX4_OK then
136
+ # continue
137
+ when RELAX4_INFEASIBLE then
138
+ raise InfeasibleError.new("infeasibility detected in relax4_run")
139
+ else
140
+ raise "relax4_run failed with unrecognized code"
141
+ end
142
+
143
+ case relax4_check_output()
144
+ when RELAX4_OK then
145
+ # continue
146
+ when RELAX4_OUTPUT_FAIL_NONZERO_DEMAND then
147
+ raise "relax4 failed: output contains nonzero demands"
148
+ when RELAX4_OUTPUT_FAIL_COMPLEMENTARY_SLACKNESS then
149
+ raise "relax4 failed: output does not satisfy complementary slackness"
150
+ else
151
+ raise "relax4_check_output failed with unrecognized code"
152
+ end
153
+
154
+ flows_ia.to_a!(num_arcs)
155
+ ensure
156
+ relax4_free()
157
+ end
158
+ end
159
+
160
+ #
161
+ # Raised to indicate that a problem is infeasible.
162
+ #
163
+ # There are several points in the program at which infeasibility is detected.
164
+ # If it fails in "phase 1" initialization, this means that the exogenous
165
+ # flow into / out of a node exceeds the total capacity of arcs out of / into
166
+ # that node. Otherwise, the information in the solver isn't of much help in
167
+ # figuring out why an instance is infeasible.
168
+ #
169
+ class InfeasibleError < RuntimeError; end
170
+
171
+ #
172
+ # Provided by SWIG for exchanging data with the solver.
173
+ # Intended for internal use; see relax4_init in relax4.h, above.
174
+ # You do not have to use this if you call {Relax4.solve}.
175
+ #
176
+ class IntegerArray
177
+ #
178
+ # Create from Ruby Array of Integers; creates C array of appropriate length
179
+ # and copies the Ruby values in.
180
+ #
181
+ # @param [Array<Integer>] a elements to assign to new IntegerArray
182
+ #
183
+ # @return [IntegerArray] not nil
184
+ #
185
+ def self.from_array a
186
+ ia = self.new(a.size) or raise 'failed to create IntegerArray'
187
+ ia.assign! a
188
+ end
189
+
190
+ #
191
+ # Set elements from Ruby Array; array length is not checked.
192
+ #
193
+ # @param [Array<Integer>] a elements to assign; must be of correct size,
194
+ # because array bounds are not checked.
195
+ #
196
+ # @return [IntegerArray] self
197
+ #
198
+ def assign! a
199
+ for i in 0...a.size
200
+ self[i] = a[i]
201
+ end
202
+ self
203
+ end
204
+
205
+ #
206
+ # Convert to Ruby Array of given size; array length is not checked.
207
+ #
208
+ # @return [Array<Integer>] with given +size+
209
+ #
210
+ def to_a! size
211
+ a = Array.new(size)
212
+ for i in 0...a.size
213
+ a[i] = self[i]
214
+ end
215
+ a
216
+ end
217
+ end
218
+
219
+ private
220
+
221
+ def self.get_arg(args, key); args[key] or raise "#{key} is required" end
222
+ end
223
+
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: relax4
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - John Lees-Miller
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-31 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Ruby interface for the RELAX IV code by D.P. Bertsekas and P. Tseng.
23
+ email:
24
+ - jdleesmiller@gmail.com
25
+ executables: []
26
+
27
+ extensions:
28
+ - ext/extconf.rb
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - lib/relax4.rb
33
+ - lib/relax4/version.rb
34
+ - ext/extconf.rb
35
+ - ext/relax4.c
36
+ - ext/relax4.h
37
+ - ext/relax4_f2c.h
38
+ - ext/relax4_wrap.c
39
+ - README.rdoc
40
+ has_rdoc: true
41
+ homepage: http://relax4.rubyforge.org/
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ - lib
50
+ - ext
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ hash: 3
66
+ segments:
67
+ - 0
68
+ version: "0"
69
+ requirements: []
70
+
71
+ rubyforge_project: relax4
72
+ rubygems_version: 1.3.7
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: The RELAX IV code for the Minimum Cost Network Flow Problem
76
+ test_files: []
77
+