AbstractSourceSinkGraph(
base_graph: DiGraph,
additional_starts: (
list | None
) = None,
additional_ends: (
list | None
) = None,
)
Bases: DiGraph
Base class for s-t augmented graphs (internal).
This class (introduced when unifying :class:stDAG
and :class:stDiGraph
) factors out
logic previously duplicated in both classes.
Core responsibilities
- Store and expose the original
base_graph
plus user supplied additional_starts
/ additional_ends
.
- Validate that all nodes are strings and that any additional start/end nodes belong to
base_graph
.
- Create unique global source / sink node identifiers (
self.source
/ self.sink
).
- Attach the global source to every (in-degree 0) source or additional start; attach every (out-degree 0) sink
or additional end to the global sink.
- Expose convenience collections:
source_edges
, sink_edges
, source_sink_edges
.
- Provide shared flow helper utilities: :meth:
get_non_zero_flow_edges
and
:meth:get_max_flow_value_and_check_non_negative_flow
.
Extension hooks
Subclasses customise behaviour via two lightweight hooks:
_pre_build_validate
- extra validation before augmentation (e.g. acyclicity for stDAG
).
_post_build
- populate subclass specific derived structures (e.g. condensation for stDiGraph
).
Backwards compatibility
External code should keep instantiating stDAG
or stDiGraph
; their public APIs
are unchanged. AbstractSourceSinkGraph
is an internal implementation detail and may change without notice.
Source code in flowpaths/abstractsourcesinkgraph.py
| def __init__(
self,
base_graph: nx.DiGraph,
additional_starts: list | None = None,
additional_ends: list | None = None,
):
if not all(isinstance(node, str) for node in base_graph.nodes()):
utils.logger.error(f"{__name__}: Every node of the graph must be a string.")
raise ValueError("Every node of the graph must be a string.")
super().__init__()
self.base_graph = base_graph
if "id" in base_graph.graph:
self.id = str(base_graph.graph["id"])
else:
self.id = str(id(self))
self.additional_starts = set(additional_starts or [])
self.additional_ends = set(additional_ends or [])
# Ensure any declared additional start/end nodes are in the base graph
if not self.additional_starts.issubset(base_graph.nodes()):
utils.logger.error(f"{__name__}: Some nodes in additional_starts are not in the base graph.")
raise ValueError("Some nodes in additional_starts are not in the base graph.")
if not self.additional_ends.issubset(base_graph.nodes()):
utils.logger.error(f"{__name__}: Some nodes in additional_ends are not in the base graph.")
raise ValueError("Some nodes in additional_ends are not in the base graph.")
self.source = f"source_{id(self)}"
self.sink = f"sink_{id(self)}"
# Hooks
self._pre_build_validate()
self._augment_with_source_sink()
self._post_build()
nx.freeze(self)
|
get_max_flow_value_and_check_non_negative_flow
get_max_flow_value_and_check_non_negative_flow(
flow_attr: str,
edges_to_ignore: set,
) -> float
Return maximum value of flow_attr
over edges (ignoring some) verifying non-negativity.
Raises ValueError if any required attribute missing or negative.
Source code in flowpaths/abstractsourcesinkgraph.py
| def get_max_flow_value_and_check_non_negative_flow(
self, flow_attr: str, edges_to_ignore: set
) -> float:
"""Return maximum value of `flow_attr` over edges (ignoring some) verifying non-negativity.
Raises ValueError if any required attribute missing or negative.
"""
w_max = float("-inf")
if edges_to_ignore is None:
edges_to_ignore = set()
for u, v, data in self.edges(data=True):
if (u, v) in edges_to_ignore:
continue
if flow_attr not in data:
utils.logger.error(
f"Edge ({u},{v}) does not have the required flow attribute '{flow_attr}'."
)
raise ValueError(
f"Edge ({u},{v}) does not have the required flow attribute '{flow_attr}'."
)
if data[flow_attr] < 0:
utils.logger.error(
f"Edge ({u},{v}) has negative flow value {data[flow_attr]}. All flow values must be >=0."
)
raise ValueError(
f"Edge ({u},{v}) has negative flow value {data[flow_attr]}. All flow values must be >=0."
)
w_max = max(w_max, data[flow_attr])
return w_max
|
get_non_zero_flow_edges
get_non_zero_flow_edges(
flow_attr: str,
edges_to_ignore: set = set(),
) -> set
Return set of edges whose attribute flow_attr
is non-zero and not ignored.
Source code in flowpaths/abstractsourcesinkgraph.py
| def get_non_zero_flow_edges(
self, flow_attr: str, edges_to_ignore: set = set()
) -> set:
"""Return set of edges whose attribute `flow_attr` is non-zero and not ignored."""
non_zero_flow_edges = set()
for u, v, data in self.edges(data=True):
if (u, v) not in edges_to_ignore and data.get(flow_attr, 0) != 0:
non_zero_flow_edges.add((u, v))
return non_zero_flow_edges
|