Skip to content

DAG[func_node_name] and DAG[func_node_out]? #42

@thorwhalen

Description

@thorwhalen

A DAGs getitem doesn't return a single FuncNode when I ask for it via its (unique) out or its (unique) name (i.e. func_id).

Is there any reason it shouldn't?

import qo
from meshed import code_to_dag

@code_to_dag
def audio_anomalies():
    wf = get_audio(audio_source)
    model = train(learner, wf)
    results = apply(model, wf)
    
audio_anomalies.dot_digraph()

image

audio_anomalies['wf']
# or (should give the same thing):
audio_anomalies['get_audio']
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
/var/folders/19/3fn2895s5hng3n7418fqs6w40000gn/T/ipykernel_21931/314640200.py in <module>
----> 1 audio_anomalies['get_audio']

~/Dropbox/py/proj/i/meshed/meshed/dag.py in __getitem__(self, item)
    724 
    725         """
--> 726         return self._getitem(item)
    727 
    728     def _getitem(self, item):

~/Dropbox/py/proj/i/meshed/meshed/dag.py in _getitem(self, item)
    728     def _getitem(self, item):
    729         return DAG(
--> 730             func_nodes=self._ordered_subgraph_nodes(item),
    731             cache_last_scope=self.cache_last_scope,
    732             parameter_merge=self.parameter_merge,

~/Dropbox/py/proj/i/meshed/meshed/dag.py in _ordered_subgraph_nodes(self, item)
    734 
    735     def _ordered_subgraph_nodes(self, item):
--> 736         subgraph_nodes = self._subgraph_nodes(item)
    737         # TODO: When clone ready, use to do `constructor = type(self)` instead of DAG
    738         # constructor = type(self)  # instead of DAG

~/Dropbox/py/proj/i/meshed/meshed/dag.py in _subgraph_nodes(self, item)
    742 
    743     def _subgraph_nodes(self, item):
--> 744         ins, outs = self.process_item(item)
    745         _descendants = set(
    746             filter(FuncNode.has_as_instance, set(ins) | descendants(self.graph, ins))

~/Dropbox/py/proj/i/meshed/meshed/dag.py in process_item(self, item)
    821 
    822     def process_item(self, item):
--> 823         assert isinstance(item, slice), f'must be a slice, was: {item}'
    824 
    825         input_names, outs = item.start, item.stop

AssertionError: must be a slice, was: get_audio

Possible Solution

k -> k:k

If in dag[k], k is not a slice, change to k?

Would be okay, but only works when k is a FuncNode name, not an out.

>>> audio_anomalies['get_audio':'get_audio']
DAG(func_nodes=[FuncNode(audio_source -> get_audio -> wf)], name=None)

But

>>> audio_anomalies['wf':'wf']
DAG(func_nodes=[], name=None)

On the other hand, name:out works:

>>> audio_anomalies['get_audio':'wf']
DAG(func_nodes=[FuncNode(audio_source -> get_audio -> wf)], name=None)

Is there any reason we want an out:out (e.g. 'wf':'wf') to be an empty DAG, but not name:name (e.g. 'get_audio':'get_audio'?

Interpreting start:end with end being non-inclusive is consistent with list slicing, so that's a good thing.
But then, name:name should perhaps be consistent with that too.

a bit more work

The k -> k:k is clean and maximizes reuse, but we don't have to do that much more work to get the behavior we want.
We can easily condition that rule based on if k is a FuncNode or VarNode. Simply like so, if k is the name of a FuncNode fn, change to name:fn.out and if it's an out of a FuncNode fn, change to fn.name:out.

The real question here is: Are we creating complexity because of a bad design -- the inconsistency mentioned above -- or is the design choices mentioned above a good one?

Or do we NOT want to have DAG[...] deal with anything than slices in the first place?

Metadata

Metadata

Labels

enhancementNew feature or requestquestionFurther information is requested

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions