Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_multicommodity.H
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
105# ifndef TPL_MULTICOMMODITY_H
106# define TPL_MULTICOMMODITY_H
107
108# include <vector>
109# include <limits>
110# include <chrono>
111# include <tpl_agraph.H>
112# include <tpl_dynMapTree.H>
113# include <Simplex.H>
114# include <ah-errors.H>
115
116namespace Aleph
117{
118
129 template <typename Node, typename Flow_Type = double>
131 {
132 size_t id;
136 std::string name;
137
140
143 const std::string& _name = "")
145 };
146
147
157 template <typename Arc_Info = Empty_Class, typename FT = double>
158 struct MCF_Arc : public Graph_Aarc<Arc_Info>
159 {
161 using Flow_Type = FT;
162
165 std::vector<Flow_Type> commodity_flow;
166 std::vector<Flow_Type> commodity_cost;
167
169 MCF_Arc() = default;
170
172 MCF_Arc(const Arc_Info&) : MCF_Arc() {}
173
175 MCF_Arc(Arc_Info&&) : MCF_Arc() {}
176
180
183 {
184 Flow_Type sum = 0;
185 for (auto f : commodity_flow)
186 sum += f;
187 return sum;
188 }
189
191 Flow_Type residual() const { return capacity - total_flow(); }
192
194 Flow_Type flow(size_t k) const
195 {
196 return k < commodity_flow.size() ? commodity_flow[k] : Flow_Type{0};
197 }
198
200 Flow_Type cost(size_t k) const
201 {
202 if (k < commodity_cost.size() and commodity_cost[k] != 0)
203 return commodity_cost[k];
204 return base_cost;
205 }
206
208 void set_flow(size_t k, Flow_Type f)
209 {
210 if (k >= commodity_flow.size())
211 commodity_flow.resize(k + 1, Flow_Type{0});
212 commodity_flow[k] = f;
213 }
214
216 void set_cost(size_t k, Flow_Type c)
217 {
218 if (k >= commodity_cost.size())
219 commodity_cost.resize(k + 1, Flow_Type{0});
220 commodity_cost[k] = c;
221 }
222
224 void init_commodities(size_t K)
225 {
226 commodity_flow.assign(K, Flow_Type{0});
227 commodity_cost.assign(K, Flow_Type{0});
228 }
229 };
230
231
245 template <class NodeT = Graph_Anode<Empty_Class>,
246 class ArcT = MCF_Arc<Empty_Class, double>>
247 class MCF_Graph : public Array_Digraph<NodeT, ArcT>
248 {
249 public:
251 using Node = NodeT;
252 using Arc = ArcT;
253 using Flow_Type = typename Arc::Flow_Type;
255
256 private:
257 std::vector<CommodityType> commodities;
259
260 public:
262 MCF_Graph() = default;
263
265 size_t num_commodities() const { return commodities.size(); }
266
268 const CommodityType& get_commodity(size_t k) const
269 {
271 << "Commodity index " << k << " out of range";
272 return commodities[k];
273 }
274
276 const std::vector<CommodityType>& get_commodities() const
277 {
278 return commodities;
279 }
280
289 size_t add_commodity(Node *source, Node *sink, Flow_Type demand,
290 const std::string& name = "")
291 {
292 size_t id = commodities.size();
293 commodities.emplace_back(id, source, sink, demand, name);
294
295 // Initialize commodity flow in all arcs
296 for (typename Base::Arc_Iterator it(*this); it.has_curr(); it.next_ne())
297 it.get_curr()->init_commodities(id + 1);
298
299 return id;
300 }
301
312 Arc* insert_arc(Node *src, Node *tgt, Flow_Type capacity, Flow_Type cost = 0)
313 {
314 auto arc = Base::insert_arc(src, tgt);
315 arc->capacity = capacity;
316 arc->base_cost = cost;
317 arc->init_commodities(commodities.size());
318 return arc;
319 }
320
323 {
325 size_t idx = 0;
326 for (typename Base::Node_Iterator it(*this); it.has_curr(); it.next_ne())
327 node_to_idx.insert(it.get_curr(), idx++);
328 }
329
331 size_t get_node_index(Node *p) const
332 {
333 return node_to_idx.find(p);
334 }
335
338 {
339 Flow_Type sum = 0;
340 for (typename Base::Arc_Iterator it(*this); it.has_curr(); it.next_ne())
341 {
342 auto arc = it.get_curr();
343 for (size_t k = 0; k < commodities.size(); ++k)
344 sum += arc->flow(k) * arc->cost(k);
345 }
346 return sum;
347 }
348
350 bool demands_satisfied() const
351 {
352 for (const auto& comm : commodities)
353 {
354 Flow_Type out_flow = 0;
355 for (Out_Iterator<MCF_Graph> it(comm.source); it.has_curr(); it.next_ne())
356 out_flow += it.get_curr()->flow(comm.id);
357
358 Flow_Type in_flow = 0;
359 for (In_Iterator<MCF_Graph> it(comm.source); it.has_curr(); it.next_ne())
360 in_flow += it.get_curr()->flow(comm.id);
361
362 if (std::abs((out_flow - in_flow) - comm.demand) > 1e-6)
363 return false;
364 }
365 return true;
366 }
367
370 {
371 for (typename Base::Arc_Iterator it(*this); it.has_curr(); it.next_ne())
372 {
373 auto arc = it.get_curr();
374 if (arc->total_flow() > arc->capacity + 1e-6)
375 return false;
376 }
377 return true;
378 }
379
381 void print_summary() const
382 {
383 std::cout << "=== Multi-commodity Network ===\n"
384 << "Nodes: " << this->vsize() << "\n"
385 << "Arcs: " << this->esize() << "\n"
386 << "Commodities: " << commodities.size() << "\n";
387
388 for (const auto& c : commodities)
389 std::cout << " [" << c.id << "] " << c.name
390 << ": demand=" << c.demand << "\n";
391
392 std::cout << "Total cost: " << total_cost() << "\n";
393 }
394 };
395
396
405 template <typename Flow_Type = double>
407 {
409
412 std::vector<Flow_Type> commodity_costs;
413 double solve_time_ms = 0;
414 size_t iterations = 0;
415
416 bool is_optimal() const { return status == Optimal; }
417 };
418
419
441 template <class Net>
443 {
444 public:
445 using Node = typename Net::Node;
446 using Arc = typename Net::Arc;
447 using Flow_Type = typename Net::Flow_Type;
449
450 private:
453
454 // Get variable index for commodity k on arc a
455 size_t var_index(size_t k, size_t arc_idx) const
456 {
457 return k * net.esize() + arc_idx;
458 }
459
460 public:
466 {
467 // Build arc index
468 size_t idx = 0;
469 for (typename Net::Arc_Iterator it(net); it.has_curr(); it.next_ne())
470 arc_to_idx.insert(it.get_curr(), idx++);
471 }
472
482 {
483 Result result;
484 auto start_time = std::chrono::high_resolution_clock::now();
485
486 const size_t K = net.num_commodities();
487 const size_t E = net.esize();
488
489 if (K == 0 || E == 0)
490 {
491 result.status = Result::Optimal;
492 result.total_cost = 0;
493 return result;
494 }
495
496 // Build node index
497 net.build_node_index();
498
499 // LP dimensions
500 const size_t num_vars = K * E; // x_{ij}^k for each arc and commodity
501
502 // Create LP solver for minimization
504 lp.set_minimize();
505
506 // Set objective: minimize Σ c_ij^k * x_ij^k
507 size_t var_idx = 0;
508 for (size_t k = 0; k < K; ++k)
509 {
510 for (typename Net::Arc_Iterator it(net); it.has_curr(); it.next_ne(), ++var_idx)
511 {
512 auto arc = it.get_curr();
513 lp.put_objetive_function_coef(var_idx, arc->cost(k));
514 }
515 }
516
517 // Build incoming arcs map: for each node, list of arcs entering it
518 // (In Array_Digraph, nodes only store outgoing arcs, so we build this manually)
520 for (typename Net::Arc_Iterator ait(net); ait.has_curr(); ait.next_ne())
521 {
522 auto arc = ait.get_curr();
523 auto tgt = static_cast<Node*>(arc->tgt_node);
524 incoming_arcs[tgt].append(arc);
525 }
526
527 // Flow conservation constraints: Σ_j x_ij^k - Σ_j x_ji^k = b_i^k
528 // Use equality constraints
529 // Row format: n coefficients + RHS at the end
530 // NOTE: We skip the sink node because the constraints are linearly
531 // dependent (sum of all = 0). Only N-1 constraints are needed.
532 size_t eq_count = 0;
533 for (size_t k = 0; k < K; ++k)
534 {
535 const auto& comm = net.get_commodity(k);
536
537 for (typename Net::Node_Iterator nit(net); nit.has_curr(); nit.next_ne())
538 {
539 auto node = nit.get_curr();
540
541 // Skip sink node - constraint is redundant
542 if (node == comm.sink)
543 continue;
544
545 // Determine b_i^k
546 Flow_Type b = 0;
547 if (node == comm.source)
548 b = comm.demand;
549 // else: intermediate node, b = 0
550
551 // Build constraint row: [coefficients, RHS]
552 std::vector<Flow_Type> row(num_vars + 1, Flow_Type{0});
553
554 // Outgoing arcs (+1 coefficient) - use Node_Arc_Iterator
555 for (typename Net::Node_Arc_Iterator ait(node); ait.has_curr(); ait.next_ne())
556 {
557 auto arc = ait.get_curr();
558 size_t a_idx = arc_to_idx.find(arc);
559 row[var_index(k, a_idx)] = Flow_Type{1};
560 }
561
562 // Incoming arcs (-1 coefficient) - use our pre-built map
563 if (incoming_arcs.has(node))
564 {
565 auto& in_list = incoming_arcs[node];
566 for (auto it = in_list.get_it(); it.has_curr(); it.next_ne())
567 {
568 auto arc = it.get_curr();
569 size_t a_idx = arc_to_idx.find(arc);
570 row[var_index(k, a_idx)] = Flow_Type{-1};
571 }
572 }
573
574 // RHS at the end
575 row[num_vars] = b;
576
577 // Add equality constraint (now works correctly with mixed LE constraints)
578 lp.put_eq_restriction(row.data());
579 ++eq_count;
580 }
581 }
582
583 // Capacity constraints: Σ_k x_ij^k ≤ u_ij
584 for (typename Net::Arc_Iterator it(net); it.has_curr(); it.next_ne())
585 {
586 auto arc = it.get_curr();
587 size_t a_idx = arc_to_idx.find(arc);
588
589 // Row format: [coefficients, RHS]
590 std::vector<Flow_Type> row(num_vars + 1, Flow_Type{0});
591 for (size_t k = 0; k < K; ++k)
592 row[var_index(k, a_idx)] = Flow_Type{1};
593
594 row[num_vars] = arc->capacity; // RHS
595
596 lp.put_restriction(row.data());
597 }
598
599 // Prepare and solve LP
600 lp.prepare_linear_program();
601 auto lp_state = lp.solve();
602
604 {
607 return result;
608 }
609
610 // Load solution
611 lp.load_solution();
612
613 // Extract solution
614 result.status = Result::Optimal;
615 result.commodity_costs.resize(K, Flow_Type{0});
616
617 var_idx = 0;
618 for (size_t k = 0; k < K; ++k)
619 {
620 for (typename Net::Arc_Iterator it(net); it.has_curr(); it.next_ne(), ++var_idx)
621 {
622 auto arc = it.get_curr();
623 Flow_Type flow = lp.get_solution(var_idx);
624 if (flow < 0) flow = 0; // Numerical cleanup
625
626 arc->set_flow(k, flow);
627 result.commodity_costs[k] += flow * arc->cost(k);
628 }
629 }
630
631 result.total_cost = lp.objetive_value();
632
633 auto end_time = std::chrono::high_resolution_clock::now();
634 result.solve_time_ms = std::chrono::duration<double, std::milli>(
635 end_time - start_time).count();
636 result.iterations = lp.get_stats().iterations;
637
638 return result;
639 }
640
645 void print_formulation(size_t max_constraints = 10) const
646 {
647 const size_t K = net.num_commodities();
648 const size_t E = net.esize();
649 const size_t V = net.vsize();
650
651 std::cout << "=== MCF LP Formulation ===\n"
652 << "Variables: " << (K * E) << " (K=" << K << " × E=" << E << ")\n"
653 << "Constraints: " << (K * V + E)
654 << " (flow: " << (K * V) << ", capacity: " << E << ")\n\n";
655
656 std::cout << "Objective: minimize Σ c_ij^k * x_ij^k\n\n";
657
658 std::cout << "Commodities:\n";
659 for (size_t k = 0; k < K; ++k)
660 {
661 const auto& c = net.get_commodity(k);
662 std::cout << " k=" << k << ": demand=" << c.demand << "\n";
663 }
664 }
665 };
666
667
679 template <class Net>
685
686} // end namespace Aleph
687
688# endif // TPL_MULTICOMMODITY_H
Simplex algorithm for linear programming.
Exception handling system with formatted messages for Aleph-w.
#define ah_out_of_range_error_if(C)
Throws std::out_of_range if condition holds.
Definition ah-errors.H:579
WeightedDigraph::Node Node
bool has_curr() const noexcept
Return true the iterator has an current arc.
Definition tpl_graph.H:1645
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3848
T & append(const T &item)
Append a new item by copy.
Definition htlist.H:1562
Generic key-value map implemented on top of a binary search tree.
Pair * insert(const Key &key, const Data &data)
Insert a key-value pair.
Data & find(const Key &key)
Find the value associated with key.
void empty()
remove all elements from the set
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
Filtered iterator for incoming arcs of a node.
Definition tpl_graph.H:1795
Multi-commodity flow network (directed graph).
const std::vector< CommodityType > & get_commodities() const
Get all commodities.
MCF_Graph()=default
Default constructor.
void print_summary() const
Print network summary.
DynMapTree< Node *, size_t > node_to_idx
Flow_Type total_cost() const
Total flow cost across all commodities.
size_t add_commodity(Node *source, Node *sink, Flow_Type demand, const std::string &name="")
Add a commodity to the network.
void build_node_index()
Build node index mapping (call after all nodes inserted)
std::vector< CommodityType > commodities
Arc * insert_arc(Node *src, Node *tgt, Flow_Type capacity, Flow_Type cost=0)
Insert arc with capacity and cost.
const CommodityType & get_commodity(size_t k) const
Get commodity by index.
bool capacities_respected() const
Check capacity constraints.
size_t num_commodities() const
Get number of commodities.
size_t get_node_index(Node *p) const
Get node index.
bool demands_satisfied() const
Check if all demands are satisfied.
typename Arc::Flow_Type Flow_Type
Multi-commodity flow solver using LP formulation.
typename Net::Flow_Type Flow_Type
size_t var_index(size_t k, size_t arc_idx) const
typename Net::Node Node
Result solve()
Solve the multi-commodity flow problem.
void print_formulation(size_t max_constraints=10) const
Print LP formulation (for debugging).
MCF_LP_Solver(Net &network)
Construct solver for a network.
DynMapTree< Arc *, size_t > arc_to_idx
Filtered iterator for outcoming arcs of a node.
Definition tpl_graph.H:1750
Linear program solver using the Simplex method.
Definition Simplex.H:227
constexpr size_t vsize() const noexcept
Definition graph-dry.H:698
size_t esize() const noexcept
Return the total of arcs of graph.
Definition graph-dry.H:792
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:190
double Flow_Type
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
MCF_Result< typename Net::Flow_Type > solve_multicommodity_flow(Net &net)
Solve multi-commodity flow problem.
DynList< T > maps(const C &c, Op op)
Classic map operation.
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
Iterator over arcs of a graph.
Definition tpl_agraph.H:325
Commodity definition for multi-commodity flow.
Node * source
Source node for this commodity.
Commodity(size_t _id, Node *_src, Node *_sink, Flow_Type _demand, const std::string &_name="")
Full constructor.
size_t id
Unique commodity identifier.
Flow_Type demand
Required flow amount.
std::string name
Optional descriptive name.
Node * sink
Sink node for this commodity.
Commodity()
Default constructor.
Arc information for multi-commodity flow.
Flow_Type cost(size_t k) const
Get cost for commodity k.
Flow_Type capacity
Arc capacity (shared)
MCF_Arc(Flow_Type cap, Flow_Type cost=0)
Constructor with capacity and cost.
Flow_Type flow(size_t k) const
Get flow for commodity k.
Flow_Type base_cost
Default cost per unit flow.
Flow_Type residual() const
Residual capacity.
MCF_Arc()=default
Default constructor.
Flow_Type total_flow() const
Total flow on this arc (sum of all commodities)
void set_flow(size_t k, Flow_Type f)
Set flow for commodity k.
MCF_Arc(const Arc_Info &)
Constructor from Arc_Info (required by graph framework)
void set_cost(size_t k, Flow_Type c)
Set cost for commodity k.
FT Flow_Type
Type for flow values.
std::vector< Flow_Type > commodity_cost
Cost per commodity (optional)
MCF_Arc(Arc_Info &&)
Constructor from Arc_Info rvalue (required by graph framework)
void init_commodities(size_t K)
Initialize for K commodities.
std::vector< Flow_Type > commodity_flow
Flow per commodity.
Multi-commodity flow result.
std::vector< Flow_Type > commodity_costs
Cost per commodity.
Flow network implemented with adjacency lists.
Definition tpl_net.H:261
ArcT Arc
Arc type.
Definition tpl_net.H:272
typename Arc::Flow_Type Flow_Type
Capacity/flow numeric type.
Definition tpl_net.H:278
NodeT Node
Node type.
Definition tpl_net.H:275
Array-based graph implementation.
Dynamic key-value map based on balanced binary search trees.