relax4 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +145 -0
- data/ext/extconf.rb +11 -0
- data/ext/relax4.c +3935 -0
- data/ext/relax4.h +187 -0
- data/ext/relax4_f2c.h +247 -0
- data/ext/relax4_wrap.c +2332 -0
- data/lib/relax4/version.rb +3 -0
- data/lib/relax4.rb +223 -0
- metadata +77 -0
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
|
+
|