|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
| 3 | +from dataclasses import dataclass, field |
3 | 4 | from typing import Optional, Tuple |
4 | 5 |
|
5 | | -from ngraph.lib.graph import NodeID, StrictMultiDiGraph |
6 | 6 | from ngraph.lib.flow_policy import FlowPolicy |
| 7 | +from ngraph.lib.graph import NodeID, StrictMultiDiGraph |
7 | 8 |
|
8 | 9 |
|
| 10 | +@dataclass |
9 | 11 | class Demand: |
10 | 12 | """ |
11 | | - Represents a network demand between two nodes. |
12 | | -
|
13 | | - A Demand can be realized through one or more flows. |
| 13 | + Represents a network demand between two nodes. It is realized via one or more |
| 14 | + flows through a single FlowPolicy. |
14 | 15 | """ |
15 | 16 |
|
16 | | - def __init__( |
17 | | - self, |
18 | | - src_node: NodeID, |
19 | | - dst_node: NodeID, |
20 | | - volume: float, |
21 | | - demand_class: int = 0, |
22 | | - ) -> None: |
| 17 | + src_node: NodeID |
| 18 | + dst_node: NodeID |
| 19 | + volume: float |
| 20 | + demand_class: int = 0 |
| 21 | + flow_policy: Optional[FlowPolicy] = None |
| 22 | + placed_demand: float = field(default=0.0, init=False) |
| 23 | + |
| 24 | + def __lt__(self, other: Demand) -> bool: |
23 | 25 | """ |
24 | | - Initializes a Demand instance. |
| 26 | + Compare Demands by their demand_class (priority). A lower demand_class |
| 27 | + indicates higher priority, so it should come first in sorting. |
25 | 28 |
|
26 | 29 | Args: |
27 | | - src_node: The source node identifier. |
28 | | - dst_node: The destination node identifier. |
29 | | - volume: The total volume of the demand. |
30 | | - demand_class: An integer representing the demand's class or priority. |
31 | | - """ |
32 | | - self.src_node: NodeID = src_node |
33 | | - self.dst_node: NodeID = dst_node |
34 | | - self.volume: float = volume |
35 | | - self.demand_class: int = demand_class |
36 | | - self.placed_demand: float = 0.0 |
| 30 | + other (Demand): Demand to compare against. |
37 | 31 |
|
38 | | - def __lt__(self, other: Demand) -> bool: |
39 | | - """Compares Demands based on their demand class.""" |
| 32 | + Returns: |
| 33 | + bool: True if self has higher priority (lower class value). |
| 34 | + """ |
40 | 35 | return self.demand_class < other.demand_class |
41 | 36 |
|
42 | 37 | def __str__(self) -> str: |
43 | | - """Returns a string representation of the Demand.""" |
| 38 | + """ |
| 39 | + String representation showing src, dst, volume, priority, and placed_demand. |
| 40 | + """ |
44 | 41 | return ( |
45 | 42 | f"Demand(src_node={self.src_node}, dst_node={self.dst_node}, " |
46 | | - f"volume={self.volume}, demand_class={self.demand_class}, placed_demand={self.placed_demand})" |
| 43 | + f"volume={self.volume}, demand_class={self.demand_class}, " |
| 44 | + f"placed_demand={self.placed_demand})" |
47 | 45 | ) |
48 | 46 |
|
49 | 47 | def place( |
50 | 48 | self, |
51 | 49 | flow_graph: StrictMultiDiGraph, |
52 | | - flow_policy: FlowPolicy, |
53 | 50 | max_fraction: float = 1.0, |
54 | 51 | max_placement: Optional[float] = None, |
55 | 52 | ) -> Tuple[float, float]: |
56 | 53 | """ |
57 | | - Places demand volume onto the network graph using the specified flow policy. |
58 | | -
|
59 | | - The function computes the remaining volume to place, applies any maximum |
60 | | - placement or fraction constraints, and delegates the flow placement to the |
61 | | - provided flow policy. It then updates the placed demand. |
| 54 | + Places demand volume onto the network via self.flow_policy. |
62 | 55 |
|
63 | 56 | Args: |
64 | | - flow_graph: The network graph on which flows are placed. |
65 | | - flow_policy: The flow policy used to place the demand. |
66 | | - max_fraction: Maximum fraction of the total demand volume to place in this call. |
67 | | - max_placement: Optional absolute limit on the volume to place. |
| 57 | + flow_graph (StrictMultiDiGraph): The graph to place flows onto. |
| 58 | + max_fraction (float): The fraction of the remaining demand to place now. |
| 59 | + max_placement (Optional[float]): An absolute upper bound on volume. |
68 | 60 |
|
69 | 61 | Returns: |
70 | | - A tuple (placed, remaining) where 'placed' is the volume successfully placed, |
71 | | - and 'remaining' is the volume that could not be placed. |
| 62 | + Tuple[float, float]: |
| 63 | + placed_now: Volume placed in this call. |
| 64 | + remaining: Volume that could not be placed in this call. |
| 65 | +
|
| 66 | + Raises: |
| 67 | + RuntimeError: If no FlowPolicy is set on this Demand. |
| 68 | + ValueError: If max_fraction is outside [0, 1]. |
72 | 69 | """ |
73 | | - to_place = self.volume - self.placed_demand |
| 70 | + if self.flow_policy is None: |
| 71 | + raise RuntimeError("No FlowPolicy set on this Demand.") |
74 | 72 |
|
| 73 | + if not (0 <= max_fraction <= 1): |
| 74 | + raise ValueError("max_fraction must be in the range [0, 1].") |
| 75 | + |
| 76 | + to_place = self.volume - self.placed_demand |
75 | 77 | if max_placement is not None: |
76 | 78 | to_place = min(to_place, max_placement) |
77 | 79 |
|
78 | 80 | if max_fraction > 0: |
79 | 81 | to_place = min(to_place, self.volume * max_fraction) |
80 | 82 | else: |
81 | | - # When max_fraction is non-positive, place the entire volume only if infinite; |
82 | | - # otherwise, no placement is performed. |
83 | | - to_place = self.volume if self.volume == float("inf") else 0 |
| 83 | + # If max_fraction <= 0, do not place any new volume (unless volume is infinite). |
| 84 | + to_place = self.volume if self.volume == float("inf") else 0.0 |
84 | 85 |
|
85 | | - flow_policy.place_demand( |
| 86 | + # Delegate flow placement |
| 87 | + self.flow_policy.place_demand( |
86 | 88 | flow_graph, |
87 | 89 | self.src_node, |
88 | 90 | self.dst_node, |
89 | 91 | self.demand_class, |
90 | 92 | to_place, |
91 | 93 | ) |
92 | | - placed = flow_policy.placed_demand - self.placed_demand |
93 | | - self.placed_demand = flow_policy.placed_demand |
94 | | - remaining = to_place - placed |
95 | | - return placed, remaining |
| 94 | + |
| 95 | + # placed_now is the difference from the old placed_demand |
| 96 | + placed_now = self.flow_policy.placed_demand - self.placed_demand |
| 97 | + self.placed_demand = self.flow_policy.placed_demand |
| 98 | + remaining = to_place - placed_now |
| 99 | + |
| 100 | + return placed_now, remaining |
0 commit comments