Module: Relax4
- Defined in:
- lib/relax4.rb,
lib/relax4/version.rb
Overview
Methods for calling the RELAX IV solver.
There is a Ruby-friendly wrapper method called Relax4.solve.
There are also lower-level methods. Unfortunately, swig and rdoc don’t work very well together, so the best references for these calls are currently the source of Relax4.solve and the relax4.h
header file listed below:
:include:../ext/relax4/relax4.h
Defined Under Namespace
Classes: InfeasibleError, IntegerArray
Constant Summary collapse
- VERSION_MAJOR =
1
- VERSION_MINOR =
2
- VERSION_PATCH =
0
- VERSION =
[VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH].join('.')
Class Method Summary collapse
-
.solve(args) ⇒ Array<Integer>
Solve a minimum cost network flow problem.
-
.solve_assignment_problem(args) ⇒ Array<Integer>
Solve a square assignment problem.
-
.solve_transportation_problem(args) ⇒ Array<Array<Integer>>
Solve a transportation problem.
Class Method Details
.solve(args) ⇒ Array<Integer>
Solve a minimum cost network flow problem.
each arc, where the first node is numbered 1.
arc, where the first node is numbered 1.
allowed; negative cost cycles are allowed (edges involved in the cycle are set to large); all costs must be less than large
and should be less than large/10
to avoid overflow (see RELAX4_DEFAULT_MAX_COST in relax4.h, above).
specified (problem is uncapacitated), all arc capacities are set to RELAX4_UNCAPACITATED
; capacities must be in [0, large
].
demand is a surplus node; demands should balance (sum to zero), or else the problem will be infeasible (but you can add a dummy node).
an initial feasible solution; this is recommended only for hard problems.
represent infinity; see the relax4.h listing above for more info.
cost, in order to avoid integer overflow; see the relax4.h listing above for more info.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/relax4.rb', line 56 def self.solve args start_nodes = get_arg(args, :start_nodes) end_nodes = get_arg(args, :end_nodes) costs = get_arg(args, :costs) large = args[:large] || RELAX4_DEFAULT_LARGE max_cost = args[:max_cost] || RELAX4_DEFAULT_MAX_COST num_arcs = start_nodes.size capacities = args[:capacities] || [[large, RELAX4_UNCAPACITATED].min]*num_arcs sizes = [start_nodes, end_nodes, costs, capacities].map{|a| a.size} raise ArgumentError.new("bad sizes for start/end_nodes, costs, capacities:"\ " #{sizes.join(',')}") unless sizes.all? {|s| s == num_arcs} demands = get_arg(args, :demands) num_nodes = demands.size flows_ia = IntegerArray.new(num_arcs) case relax4_init(num_nodes, num_arcs, IntegerArray.from_array(start_nodes), IntegerArray.from_array(end_nodes), IntegerArray.from_array(costs), IntegerArray.from_array(capacities), IntegerArray.from_array(demands), flows_ia, large) when RELAX4_OK then # continue when RELAX4_FAIL_OUT_OF_MEMORY then raise "could not allocate enough memory in relax4_init" else raise "relax4_init_phase_1 failed with unrecognized code" end begin case relax4_check_inputs(max_cost) when RELAX4_OK then # continue when RELAX4_FAIL_BAD_SIZE then raise ArgumentError.new('problem has zero nodes or zero arcs') when RELAX4_FAIL_BAD_NODE then raise ArgumentError.new('start_nodes or end_nodes has a bad index') when RELAX4_FAIL_BAD_COST then raise ArgumentError.new("a cost exceeds the maximum (#{max_cost})") when RELAX4_FAIL_BAD_CAPACITY then raise ArgumentError.new("a capacity exceeds the maximum (#{large})") else raise "relax4_check_inputs failed with unrecognized code" end case relax4_init_phase_1() when RELAX4_OK then # continue when RELAX4_INFEASIBLE then raise InfeasibleError.new("infeasibility detected in phase 1 init") else raise "relax4_init_phase_1 failed with unrecognized code" end if args[:auction_init] case relax4_auction() when RELAX4_OK then # continue when RELAX4_INFEASIBLE then raise InfeasibleError.new("infeasibility detected in auction") else raise "relax4_auction failed with unrecognized code" end else case relax4_init_phase_2() when RELAX4_OK then # continue when RELAX4_INFEASIBLE then raise InfeasibleError.new("infeasibility detected in phase 2 init") else raise "relax4_init_phase_2 failed with unrecognized code" end end case relax4_run() when RELAX4_OK then # continue when RELAX4_INFEASIBLE then raise InfeasibleError.new("infeasibility detected in relax4_run") else raise "relax4_run failed with unrecognized code" end case relax4_check_output() when RELAX4_OK then # continue when RELAX4_OUTPUT_FAIL_NONZERO_DEMAND then raise "relax4 failed: output contains nonzero demands" when RELAX4_OUTPUT_FAIL_COMPLEMENTARY_SLACKNESS then raise "relax4 failed: output does not satisfy complementary slackness" else raise "relax4_check_output failed with unrecognized code" end flows_ia.to_a!(num_arcs) ensure relax4_free() end end |
.solve_assignment_problem(args) ⇒ Array<Integer>
Solve a square assignment problem.
There are n
agents and n
tasks, and each agent must be assigned to exactly one task. The cost of each assignment is given, and the objective is to find the assignment with the lowest cost.
Some assignments can be made illegal by passing nil
in the relevant entry in the cost matrix. If you have more agents than tasks (or vice versa), you can add some columns (or rows) of zeros to the cost matrix.
for each agent) and n
columns (one for each task); a cost of nil
means that the corresponding assignment is not allowed; negative costs are allowed.
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/relax4.rb', line 255 def self.solve_assignment_problem args args = args.dup costs = get_arg(args, :costs) n = costs.size arcs = set_args_from_cost_matrix(args, costs, n, n) args[:demands] = [-1]*n + [1]*n arc_flows = Relax4.solve(args) # Find indexes. assignment = Array.new(n) arcs.zip(arc_flows).each do |(i,j),fij| assignment[i] = j if fij > 0 end assignment end |
.solve_transportation_problem(args) ⇒ Array<Array<Integer>>
Solve a transportation problem.
There are m
suppliers and n
consumers of some commodity. Each supplier has a given supply which must be used, and each consumer has a given demand which must be satisfied; it is assumed that the total supply equals the total demand. The cost of transporting one unit of the commodity from each supplier to each consumer is given, and the objective is to perform the transport with minimum cost. The output is a m
by n
matrix that gives the flow from each supplier to each consumer.
each supplier) and n
columns (one for each consumer); a cost of nil
means that there is no corresponding edge (transport is impossible); negative costs are allowed; may be non-square but must be rectangular (all rows must have the same size).
demand should equal total supply, or else the problem will be infeasible (but you can add a dummy node to fix this).
demand should equal total supply, or else the problem will be infeasible (but you can add a dummy node to fix this).
consumer; size m
by n
; nil
entries in the cost matrix will have flow 0 here.
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/relax4.rb', line 202 def self.solve_transportation_problem args args = args.dup costs = get_arg(args, :costs) supplies = get_arg(args, :supplies) demands = get_arg(args, :demands) m = supplies.size n = demands.size arcs = set_args_from_cost_matrix(args, costs, m, n) args[:demands] = supplies.map{|x| -x} + demands arc_flows = Relax4.solve(args) # Extract output into matrix with same dims as input cost matrix. flows = Array.new(m) { Array.new(n, 0) } arcs.zip(arc_flows).each do |(i,j),fij| flows[i][j] = fij end flows end |