meshed.scrap.cached_dag
- class meshed.scrap.cached_dag.CachedDag(dag, cache=True, name=None)[source]
Wraps a DAG, using it to compute any of it’s var nodes from it’s dependents, with the capability of caching intermediate var nodes for later reuse.
>>> def add(a, b=1): ... return a + b >>> def mult(x, y=2): ... return x * y >>> def subtract(a, b=4): ... return a - b >>> from meshed import code_to_dag >>> >>> @code_to_dag(func_src=locals()) ... def dag(w, ww, www): ... x = mult(w, ww) ... y = add(x, www) ... z = subtract(x, y) >>> print(dag.dot_digraph_ascii())
│ │ ▼ ┌──────────┐ ww= ──▶ │ mult │ └──────────┘ │ │ ▼ x ─┐ │ │ │ │ │ ▼ │ ┌──────────┐ │ www= ──▶ │ add │ │ └──────────┘ │ │ │ │ │ ▼ │ │ y= │ │ │ │ │ │ ▼ │ ┌──────────┐ │ │ subtract │ ◀┘ └──────────┘ │ │ ▼ z
>>> from inspect import signature >>> g = CachedDag(dag) >>> signature(g) <Signature (k, /, **input_kwargs)>
We can get
ww
because it has a default:(TODO: This (and further tests) stopped working since code_to_dag was enhanced with the ability to use the wrapped function’s signature to determine the signature of the output dag. Need to fix this.)
>>> g('ww') 2
But we can’t get
y
because we don’t have what it depends on:>>> g('y') Traceback (most recent call last): ... TypeError: The input_kwargs of a dag call is missing 1 required argument: 'w'
It needs a
w?
! No, it needs anx
! But to get anx
you need aw
, and…>>> g('x') Traceback (most recent call last): ... TypeError: The input_kwargs of a dag call is missing 1 required argument: 'w'
So let’s give it a w!
>>> g('x', w=3) # == 3 * 2 == 6
And now this works:
>>> g('x') 6
because
>>> g.cache {'x': 6}
and this will work too:
>>> g('y') 7 >>> g.cache {'x': 6, 'y': 7}
But this is something we need to handle better!
>>> g('x', w=10) 6
This is happending because there’s already a x in the cache, and it takes precedence. This would be okay if consider CachedDag as a low level object that is never actually used by a user. But we need to protect the user from such effects!
First, we probably should cache inputs too.
The we can: - Make computation take precedence over cache, overwriting the existing cache
with the new resulting values
Allow the user to declare the entire cache, or just some variables in it,
as write-once, to avoid creating bugs with the above proposal.
Cache multiple paths (lru_cache style) for different input combinations
- roots_for(node)[source]
The set of roots that lead to
node
.>>> from meshed.makers import code_to_dag >>> @code_to_dag ... def dag(): ... x = mult(w, ww) ... y = add(x, www) ... z = subtract(x, y) >>> print(dag.synopsis_string()) w,ww -> mult -> x x,www -> add -> y x,y -> subtract -> z >>> g = CachedDag(dag) >>> sorted(g.roots_for('x')) ['w', 'ww'] >>> sorted(g.roots_for('y')) ['w', 'ww', 'www']
- class meshed.scrap.cached_dag.NoOverwritesDict[source]
A dict where you’re not allowed to write to a key that already has a value in it.
>>> d = NoOverwritesDict(a=1, b=2) >>> d {'a': 1, 'b': 2}
Writing is allowed, in new keys
>>> d['c'] = 3 >>> d {'a': 1, 'b': 2, 'c': 3}
It’s also okay to write into an existing key if the value it holds is identical. In fact, the write doesn’t even happen.
>>> d['b'] = 2
But if we try to write a different value…
>>> d['b'] = 22 Traceback (most recent call last): ... cached_dag.OverWritesNotAllowedError: The b key already exists and you're not allowed to change its value
- exception meshed.scrap.cached_dag.NotAllowed[source]
To use to indicate that something is not allowed
- exception meshed.scrap.cached_dag.OverWritesNotAllowedError[source]
Error to raise when a writes to existing keys are not allowed
- meshed.scrap.cached_dag.cached_dag_test()[source]
Covering issue https://github.com/i2mint/meshed/issues/34 about “CachedDag.cache should be populated with inputs that it was called on”