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.
- 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
|
+
|