Source code for cutcutcodec.core.opti.cache.hashes.graph
"""Allow to summarize the state of each node of the graph.
This allows a finer management of the cache by a fine tracking of the unchanged elements.
"""
import hashlib
import networkx
[docs]
def compute_graph_items_hash(graph: networkx.MultiDiGraph) -> dict[str | tuple[str, str, str], str]:
"""Compute a signature for each node and edge, which reflects its state in the graph.
This is mean to detecting a change of attributes in one of the upstream elements.
Parameters
----------
graph : networkx.MultiDiGraph
The assembly graph.
Returns
-------
hashes : dict[str | tuple[str, str, str], str]
To each node and edge name, associate its state as a string.
Notes
-----
The graph must not contain any cycles because the function would never returns.
Examples
--------
>>> from pprint import pprint
>>> from cutcutcodec.core.classes.container import ContainerOutput
>>> from cutcutcodec.core.compilation.tree_to_graph import tree_to_graph
>>> from cutcutcodec.core.filter.audio.subclip import FilterAudioSubclip
>>> from cutcutcodec.core.generation.audio.noise import GeneratorAudioNoise
>>> from cutcutcodec.core.opti.cache.hashes.graph import compute_graph_items_hash
>>> container_out = ContainerOutput(
... FilterAudioSubclip(GeneratorAudioNoise(0).out_streams, 0, 1).out_streams
... )
>>> graph = tree_to_graph(container_out)
>>> pprint(compute_graph_items_hash(graph)) # doctest: +ELLIPSIS
{'container_output_1': '099c022d457eb220c36ae22a75bf1998',
'filter_audio_subclip_1': '10355c7ad764f111b15798bed884a821',
'generator_audio_noise_1': '4dc3de85c734bfd23024c381599bfe3f',
('filter_audio_subclip_1', 'container_output_1', '0->0'): '10355c7ad764f111b15798bed884a821|0',
('generator_audio_noise_1', '...subclip_1', '0->0'): '4dc3de85c734bfd23024c381599bfe3f|0'}
>>>
"""
assert isinstance(graph, networkx.MultiDiGraph), graph.__class__.__name__
def complete(hashes, graph, node_name) -> str:
if node_name not in hashes:
node_attr = graph.nodes[node_name]
local_node_signature = (
f"{node_attr['class'].__name__}-"
f"{'-'.join(str(node_attr['state'][k]) for k in sorted(node_attr['state']))}"
)
in_edges = sorted( # the name of the edges in order of arrival on the node
graph.in_edges(node_name, data=False, keys=True),
key=lambda src_dst_key: int(src_dst_key[2].split("->")[1]),
)
local_edges_signature = "-".join(k.split("->")[0] for _, _, k in in_edges)
parents_signature = "-".join(complete(hashes, graph, n) for n, _, _ in in_edges)
signature = hashlib.md5( # md5 is the fastest
f"{parents_signature}|{local_edges_signature}|{local_node_signature}".encode(),
).hexdigest()
hashes[node_name] = signature
for _, dst, key in graph.out_edges(node_name, keys=True):
hashes[(node_name, dst, key)] = f"{signature}|{int(key.split('->')[0])}"
return hashes[node_name]
hashes = {}
for node_name in graph:
complete(hashes, graph, node_name)
return hashes