Why is my (sub)lattice generating extra list indexing tasks? #1635
-
I have been trying to better understand dynamic workflows (and, by extension, sublattices) and am running into some points of confusion. Below, you can find a sample workflow that I've tried to make reasonably simple. I used the example here as inspiration. Functionally, it's similar but slightly different (in ways I'm having difficulty putting into words...). Overall, the workflow should do: 1) Add one to the input number; 2) Turn this single number into a list of length three; 3) Add one to each number in that list; 4) Return this list. @ct.electron
def add_one(val):
return val + 1
@ct.electron # it works if I delete the decorator
def make_more(val):
return [val] * 3
@ct.electron
@ct.lattice
def sub_lattice(val):
vals = make_more(val) # it works if I do vals = [val] * 3
return [add_one(val) for val in vals]
@ct.lattice
def workflow(val1):
val2 = add_one(val1)
return sub_lattice(val2)
# Dispatch the workflow
dispatch_id = ct.dispatch(workflow)(1)
result = ct.get_result(dispatch_id, wait=True)
print(result) The error, as alluded to in the image below, is due to an excessive amount of list indexing tasks that are made in the sublattice, which then causes an I feel like I'm super close to understanding this one... |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 4 replies
-
Hi @arosen93! You're doing a great job diving into dynamic workflows and sublattices. You're definitely on the right track, and I'm here to help clarify things for you. The issue you're encountering is related to the iterable import covalent as ct
@ct.electron
def f(x):
return [x]*3
@ct.lattice
def workflow(x):
return [i for i in f(x)]
ct.dispatch(workflow)(x=2) Now let's look at how to create dynamic workflows in your case. The key pattern is to understand that a sublattice object will wait until its inputs are available before building the sub-graph. Here's an example that should work for the workflow you described: Here is an example to let you do what you had asked import covalent as ct
@ct.electron
def add_one(val):
return val + 1
@ct.electron
def make_more(val):
return [val] * 3
@ct.electron
@ct.lattice
def add_one_distributed(val):
return [add_one(i) for i in range(len(val))]
@ct.lattice
def workflow(val1):
val2 = add_one(val1)
val3=make_more(val2)
return add_one_distributed(val3)
ct.dispatch(workflow)(2) In this example, the sublattice import covalent as ct
@ct.electron
def add_one(val):
return val + 1
@ct.electron
def make_more(val, num=3):
return [val] * num
@ct.electron
def random_number():
import random
return random.randint(0, 20)
@ct.electron
@ct.lattice
def add_one_distributed(val):
return [add_one(i) for i in range(len(val))]
@ct.lattice
def workflow(val1):
val2 = add_one(val1)
repeate=random_number()
val3=make_more(val2,repeate)
return add_one_distributed(val3)
ct.dispatch(workflow)(2) Each time you run this, the sublattice I hope this helps clarify things for you. Please feel free to reach out if you have any more questions or concerns! |
Beta Was this translation helpful? Give feedback.
Hi @arosen93! You're doing a great job diving into dynamic workflows and sublattices. You're definitely on the right track, and I'm here to help clarify things for you.
The issue you're encountering is related to the iterable
vals
inside thesub_lattice
function. When constructing lattices (including sublattices), for loops or while loops can produce random graphs if the iterable is not determined and is a future. In your case,vals
insub_lattice
will not be known even when sub_lattice is compiled for the subgraph. Onlyval
(the input of the sublattice) is known during its construction. To reproduce your error without using sublattice, you can try the following example: