Skip to content

Commit 4831413

Browse files
committed
Add recipe path / weight.
1 parent 647d3b3 commit 4831413

File tree

3 files changed

+472
-0
lines changed

3 files changed

+472
-0
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
from __future__ import annotations
2+
3+
import logging
4+
from dataclasses import dataclass
5+
from typing import Optional
6+
7+
from typing_extensions import override
8+
9+
from metricflow_semantics.experimental.mf_graph.path_finding.graph_path import MutableGraphPath
10+
from metricflow_semantics.experimental.mf_graph.path_finding.pathfinder import MetricflowPathfinder
11+
from metricflow_semantics.experimental.semantic_graph.attribute_resolution.attribute_recipe import (
12+
AttributeRecipe,
13+
)
14+
from metricflow_semantics.experimental.semantic_graph.attribute_resolution.attribute_recipe_step import (
15+
AttributeRecipeStep,
16+
)
17+
from metricflow_semantics.experimental.semantic_graph.sg_interfaces import SemanticGraphEdge, SemanticGraphNode
18+
from metricflow_semantics.mf_logging.pretty_formattable import MetricFlowPrettyFormattable
19+
from metricflow_semantics.mf_logging.pretty_formatter import PrettyFormatContext
20+
21+
logger = logging.getLogger(__name__)
22+
23+
_EMPTY_RECIPE = AttributeRecipe()
24+
25+
26+
@dataclass
27+
class AttributeRecipeWriterPath(MutableGraphPath[SemanticGraphNode, SemanticGraphEdge], MetricFlowPrettyFormattable):
28+
"""An implementation of a path in the semantic graph that writes recipes.
29+
30+
The nodes and edges in the semantic graph are annotated with recipe steps that describe the computation of
31+
attributes. This path takes those steps and merges them into a single recipe as nodes are added to the path.
32+
33+
In DFS traversal, nodes are added and popped at the end. To support this operation, recipe versions are
34+
stored in a list so that consistent state can be maintained.
35+
"""
36+
37+
# Every time a node / edge is added, the updated recipe is added to this list.
38+
_recipe_versions: list[AttributeRecipe]
39+
40+
@staticmethod
41+
def create(start_node: Optional[SemanticGraphNode] = None) -> AttributeRecipeWriterPath: # noqa: D102
42+
path = AttributeRecipeWriterPath(
43+
_nodes=[],
44+
_edges=[],
45+
_weight_addition_order=[],
46+
_current_weight=0,
47+
_current_node_set=set(),
48+
_node_set_addition_order=[],
49+
_recipe_versions=[_EMPTY_RECIPE],
50+
)
51+
if start_node:
52+
path._append_node(start_node)
53+
path._append_step(start_node.recipe_step_to_append)
54+
return path
55+
56+
@staticmethod
57+
def create_from_edge(start_edge: SemanticGraphEdge, weight: int) -> AttributeRecipeWriterPath: # noqa: D102
58+
path = AttributeRecipeWriterPath(
59+
_nodes=[],
60+
_edges=[],
61+
_weight_addition_order=[],
62+
_current_weight=0,
63+
_current_node_set=set(),
64+
_node_set_addition_order=[],
65+
_recipe_versions=[_EMPTY_RECIPE],
66+
)
67+
path.append_edge(start_edge, weight)
68+
return path
69+
70+
def _append_step(self, recipe_step: AttributeRecipeStep) -> None:
71+
previous_recipe = self._recipe_versions[-1]
72+
self._recipe_versions.append(previous_recipe.append_step(recipe_step))
73+
74+
@property
75+
def latest_recipe(self) -> AttributeRecipe: # noqa: D102
76+
return self._recipe_versions[-1]
77+
78+
@override
79+
def append_edge(self, edge: SemanticGraphEdge, weight: int) -> None:
80+
"""Add an edge with the given weight to this path."""
81+
if self.is_empty:
82+
self._append_step(edge.tail_node.recipe_step_to_append)
83+
self._append_step(edge.recipe_step_to_append)
84+
self._append_step(edge.head_node.recipe_step_to_append)
85+
86+
super().append_edge(edge, weight)
87+
88+
@override
89+
def pretty_format(self, format_context: PrettyFormatContext) -> Optional[str]:
90+
return format_context.formatter.pretty_format(self._nodes)
91+
92+
@override
93+
def pop_end(self) -> None:
94+
if self._edges:
95+
self._recipe_versions.pop()
96+
self._recipe_versions.pop()
97+
else:
98+
self._recipe_versions.pop()
99+
super().pop_end()
100+
101+
@override
102+
def copy(self) -> AttributeRecipeWriterPath:
103+
return AttributeRecipeWriterPath(
104+
_nodes=self._nodes.copy(),
105+
_edges=self._edges.copy(),
106+
_current_weight=self._current_weight,
107+
_current_node_set=self._current_node_set.copy(),
108+
_weight_addition_order=self._weight_addition_order.copy(),
109+
_node_set_addition_order=self._node_set_addition_order.copy(),
110+
_recipe_versions=self._recipe_versions.copy(),
111+
)
112+
113+
114+
RecipeWriterPathfinder = MetricflowPathfinder[SemanticGraphNode, SemanticGraphEdge, AttributeRecipeWriterPath]

0 commit comments

Comments
 (0)