turbograph package#
TurboGraph is a simple Python library, for quick dependency-based computation.
TurboGraph automatically builds a dependency graph from function argument names or explicit specifications, ensuring computations run in the correct order.
Here’s a simple example showing how TurboGraph automatically infers dependencies:
from turbograph import compute
specifications = {
"a": 2,
"sum": lambda a, b: a + b, # Depends on "a" and "b"
}
result = compute(specifications, ["sum"], {"b": 3})
print(result) # {"sum": 5}
TurboGraph analyzes the function signatures and determines that "sum"
depends
on "a"
and "b"
, executing the computations in the correct order.
- turbograph.build_graph(raw_specifications, vertices=None, *, call_mode='args', backend=None)[source]#
Build a validated directed acyclic graph (DAG) from raw vertex specifications.
This function processes raw vertex specifications to construct a dependency graph. Each vertex represents a quantity, and directed edges indicate dependencies. The graph is validated to ensure it is acyclic.
- Return type:
GraphWrapper
[Any
,TypeVar
(V
, bound=Hashable
)]- Parameters:
raw_specifications (Mapping[V, RawVertexSpecification[V]]) –
A mapping of vertex names to their specifications, defining how each vertex is computed and its dependencies. Specifications can take one of four forms:
Function: The function’s signature determines dependencies:
If
call_mode='args'
, positional arguments are considered predecessors.If
call_mode='kwargs'
, keyword arguments are considered predecessors.
Value: A non-callable value for the vertex.
- Dictionary:
func
is an optional function computing the vertex (default:None
).predecessors
is an optional list of dependency names (default:()
).value
is an optional precomputed value (default:turbograph.NA
).
Sequence:: A tuple of up to three elements
(func, predecessors, value)
.
vertices (Iterable[V] | None) – An optional subset of vertices to include in the graph. If
None
, all vertices inraw_specifications
are included. Otherwise, only the specified vertices and their ancestors are retained.call_mode (CallMode) –
The mode used for passing input values to functions, stored as a graph attribute:
"args"
(default): Inputs are passed as positional arguments."kwargs"
: Inputs are passed as keyword arguments."arg"
: Inputs are passed as a single dictionary argument.
backend (Backend | None) – The graph backend to use. If
None
, the first available backend is used.
- Returns:
A
GraphWrapper
object representing the constructed dependency graph.The graph contains:
- Vertices with attributes:
"func"
: Function computing the vertex (orNone
if absent)."value"
: Precomputed value, or a default placeholderturbograph.NA
if absent."predecessors"
: Ordered list of predecessor vertices.
Directed edges representing dependencies.
The
call_mode
stored as a graph attribute.
- Raises:
ValueError – If the specified dependencies create a cyclic graph.
ValueError – If the graph contains isolated vertices.
Example
Construct a simple graph computing
a - b
:>>> graph = build_graph({"sub": lambda a, b: a - b}) >>> compute_from_graph(graph, values={"a": 5, "b": 3}, vertices=["sub"]) {'sub': 2}
Construct a graph with precomputed values:
>>> graph = build_graph( ... {"a": 10, "b": 5, "sub": lambda a, b: a - b}, vertices=["sub"] ... ) >>> compute_from_graph(graph) {'a': 10, 'b': 5, 'sub': 5}
- turbograph.compute(raw_specifications, vertices=None, values=None, funcs=None, *, call_mode='args', auto_prune=True, backend=None)[source]#
Compute values from vertex specifications.
This function constructs a directed acyclic graph (DAG) from the given
raw_specifications
and determines the correct execution order for computing values. It allows users to specify predefined values or override functions for specific vertices.- Return type:
dict
[TypeVar
(V
, bound=Hashable
),Any
]- Parameters:
raw_specifications (Mapping[V, RawVertexSpecification[V]]) –
A mapping of vertex names to their specifications, defining how each vertex is computed and its dependencies. Specifications can take one of four forms:
Function: The function’s signature determines dependencies:
If
call_mode='args'
, positional arguments are considered predecessors.If
call_mode='kwargs'
, keyword arguments are considered predecessors.
Value: A non-callable value for the vertex.
- Dictionary:
func
is an optional function computing the vertex (default:None
).predecessors
is an optional list of dependency names (default:()
).value
is an optional precomputed value (default:turbograph.NA
).
Sequence:: A tuple of up to three elements
(func, predecessors, value)
.
vertices (Iterable[V] | None) – The set of vertices to compute. If specified, only the listed vertices and their dependencies are computed.
values (Mapping[V, VertexValue] | None) – A mapping of vertex names to pre-defined values. If a vertex has an entry in this mapping, the given value is used instead of computing it from the graph.
funcs (Mapping[V, VertexFunc] | None) – A mapping of vertex names to functions. If a vertex has an entry in this mapping, the provided function is used instead of the one in the graph.
call_mode (CallMode) –
Determines how dependencies are passed to functions
"args"
(default): Dependencies are passed as positional arguments."kwargs"
: Dependencies are passed as keyword arguments."arg"
: All dependencies are passed as a single dictionary argument.
auto_prune (bool) – If
True
, automatically removes intermediate vertices that are no longer needed after computation to optimize memory usage.backend (Backend | None) – The graph backend to use. If
None
, the first available backend isused.
- Returns:
A mapping from vertex names to their computed values.
- Raises:
ValueError – If the specified dependencies introduce a cyclic dependency,
preventing execution. –
Example
Compute the sum of two numbers:
>>> compute({"x": lambda: 3, "y": lambda: 4, "sum": lambda x, y: x + y}) {'x': 3, 'y': 4, 'sum': 7}
Compute only the sum, supplying values for
a
andb
, and explicitly defining dependencies:>>> compute( ... {"sum": (lambda x, y: x + y, ["a", "b"])}, ["sum"], {"a": 1, "b": 2} ... ) {'sum': 3}
Use the
"arg"
call mode to pass dependencies as a single dictionary argument:>>> compute( ... { ... "sum": { ... "func": lambda v: v["x"] + v["y"], ... "predecessors": ["x", "y"], ... }, ... }, ... ["sum"], ... values={"x": 1, "y": 2}, ... call_mode="arg", ... ) {'sum': 7}
- turbograph.compute_from_graph(graph, vertices=None, values=None, funcs=None, *, call_mode=None, auto_prune=True)[source]#
Compute values in a dependency graph.
This function evaluates the specified vertices in a directed acyclic graph (DAG), ensuring that computations follow the correct dependency order. It allows overriding values, updating functions dynamically, and pruning unnecessary intermediate values.
- Return type:
dict
[TypeVar
(V
, bound=Hashable
),Any
]- Parameters:
graph (GraphWrapper[Any, V]) – The dependency graph.
vertices (Iterable[V] | None) –
The set of vertices to compute.
If
None
, all vertices in the graph are computed.If specified, only the listed vertices and their dependencies are computed.
values (Mapping[V, VertexValue] | None) – A mapping from vertex names to precomputed values. If provided, these values override the existing ones in the graph.
funcs (Mapping[V, VertexFunc] | None) – A mapping from vertex names to updated functions. If provided, these functions override the existing ones in the graph.
call_mode (CallMode | None) –
The mode used to pass dependencies to functions. If
None
, the mode is inferred from the graph."args"
: Dependencies are passed as positional arguments."kwargs"
: Dependencies are passed as keyword arguments."arg"
: Dependencies are passed as a single dictionary.
auto_prune (bool) – If
True
, automatically removes intermediate vertices that are no longer needed after computation to optimize memory usage.
- Returns:
A dictionary mapping vertex names to their computed values.
- Raises:
ValueError – If cycles are detected in the graph.
Example
Compute values from a dependency graph.
>>> from turbograph import compute_from_graph, build_graph >>> deps = {"a": [], "b": [], "c": ["a", "b"], "d": ["c"]} >>> funcs = { ... "a": lambda: 5, ... "b": lambda: 10, ... "c": lambda a, b: a + b, ... "d": lambda c: c * 2, ... } >>> graph = build_graph(deps, funcs=funcs) >>> compute_from_graph(graph) {'a': 5, 'b': 10, 'c': 15, 'd': 30}
Override a vertex value before computation:
>>> compute_from_graph(graph, values={"a": 7}) {'a': 7, 'b': 10, 'c': 17, 'd': 34}
Modify a function before computation:
>>> compute_from_graph(graph, funcs={"c": lambda a, b: a * b}) {'a': 5, 'b': 10, 'c': 50, 'd': 100}
Compute only a specific vertex:
>>> compute_from_graph(graph, vertices=["c"]) {'c': 50}
Notes
If both a function update and a precomputed value are provided for a vertex, the precomputed value takes precedence.
- turbograph.rebuild_graph(graph, vertices=None, values=None, funcs=None, *, reduce=False, call_mode=None)[source]#
Rebuild an existing dependency graph with new functions, values, or vertices.
This function allows modifying a computation graph by: :rtype:
TypeVar
(GW
, bound=GraphWrapper
[Any
,Any
])Retaining only a subset of vertices.
Updating functions assigned to vertices.
Assigning or overriding precomputed values.
Setting a new function call mode.
Reducing the graph by treating known values as constants.
- Parameters:
graph (GW) – The dependency graph to update.
vertices (Iterable[V] | None) – The set of vertices to retain. If specified, only these vertices and their ancestors are kept.
values (Mapping[V, Any | NACLS] | None) – A mapping from vertex names to precomputed values. If provided, these values override the existing ones in the graph.
funcs (Mapping[V, Callable[..., Any] | None] | None) – A mapping from vertex names to updated functions. If provided, these functions override the existing ones in the graph.
reduce (bool) – If
True
, removes inbound edges for vertices with known values. This treats these vertices as constants and prevents unnecessary recomputation.call_mode (CallMode | None) –
The mode used to pass inputs to functions. If
None
, the call mode remains unchanged."args"
(default): Dependencies are passed as positional arguments."kwargs"
: Dependencies are passed as keyword arguments."arg"
: Dependencies are passed as a single dictionary.
reduce – If
True
, removes inbound edges for vertices with known values, treating them as constants. This ensures that these vertices no longer depend on their predecessors, preventing unnecessary computations
- Returns:
The updated graph object with the modified functions, values, and vertices.
- Return type:
Example
Update a graph with new functions and values.
>>> from turbograph import rebuild_graph, compute_from_graph, build_graph >>> graph = build_graph( ... {"sum": ["a", "b"]}, ... funcs={"sum": lambda a, b: a + b}, ... values={"a": 1, "b": 2}, ... ) >>> compute_from_graph(graph) # 1 + 2 = 3 {'a': 1, 'b': 2, 'sum': 3} >>> updated_graph = rebuild_graph( ... graph, funcs={"sum": lambda a, b: a * b}, values={"a": 10} ... ) >>> compute_from_graph(updated_graph) # 10 * 2 = 20 {'a': 10, 'b': 2, 'sum': 20}
Reduce the graph to retain only “b” and its dependencies.
>>> graph = build_graph( ... {"b": ["a"], "c": ["b"]}, ... funcs={"b": lambda x: x + 1, "c": lambda x: x + 2}, ... values={"a": 1}, ... ) >>> smaller_graph = rebuild_graph(graph, vertices=["b"]) >>> compute_from_graph(smaller_graph) {'a': 1, 'b': 2}
Notes
If both a function update and a precomputed value are provided for a vertex, the precomputed value takes precedence.
If vertices is specified, computations are limited to those vertices and their ancestors.
If reduce is enabled, vertices with known values will not be recomputed.
- turbograph.NA = <NA>#
Sentinel value representing a missing vertex value.
This singleton is used to distinguish between explicitly set
None
values and missing values. It helps differentiate uninitialized vertex attributes from those intentionally assignedNone
.