relax4 1.0.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.
@@ -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
+