AgnostiqHQ / covalent

Pythonic tool for orchestrating machine-learning/high performance/quantum-computing workflows in heterogeneous compute environments.
https://www.covalent.xyz
Apache License 2.0
752 stars 91 forks source link

Implicit CallDeps not working #1466

Open AlejandroEsquivel opened 1 year ago

AlejandroEsquivel commented 1 year ago

Environment

What is happening?

Using Implicit form of CallDeps in a workflow seems to throw an issue (i.e using raw python functions into call_before or call_after instead of using the call deps class

How can we reproduce the issue?

Running the following workflow using implicit call deps fails:


def execute_before_electron(a,b):
   pass
def execute_after_electron():
   pass

@ct.electron(
    call_before=[execute_before_electron, (1, 2)],
    call_after=[shutdown_after_electron],
)
def identity(x):
    return x

@ct.lattice
def workflow(x):
    return identity(x)

dispatch_id = ct.dispatch(workflow)(9)
r = ct.get_result(dispatch_id, wait=True)
print(r)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[6], line 1
----> 1 @ct.electron(
      2     call_before=[execute_before_electron, (1, 2)],
      3     call_after=[shutdown_after_electron],
      4 )
      5 def identity(x):
      6     return x
      8 @ct.lattice
      9 def workflow(x):

File ~/.pyenv/versions/3.8.16/envs/qa/lib/python3.8/site-packages/covalent/_workflow/electron.py:603, in electron(_func, backend, executor, files, deps_bash, deps_pip, call_before, call_after)
    594 call_after = internal_call_after_deps + call_after
    596 constraints = {
    597     "executor": executor,
    598     "deps": deps,
    599     "call_before": call_before,
    600     "call_after": call_after,
    601 }
--> 603 constraints = encode_metadata(constraints)
    605 def decorator_electron(func=None):
    606     @wraps(func)
    607     def wrapper(*args, **kwargs):

File ~/.pyenv/versions/3.8.16/envs/qa/lib/python3.8/site-packages/covalent/_workflow/transport.py:271, in encode_metadata(metadata)
    269         for i, dep in enumerate(metadata["call_before"]):
    270             if not isinstance(dep, dict):
--> 271                 encoded_metadata["call_before"][i] = dep.to_dict()
    273 if "call_after" in metadata:
    274     if metadata["call_after"] is not None:

AttributeError: 'function' object has no attribute 'to_dict'

What should happen?

One should be able to execute the above workflow successfully

wjcunningham7 commented 11 months ago

This issue is a part of Hacktoberfest. Closing this issue will earn you 1 point.

VividhPandey003 commented 11 months ago

Hey could you please assign this issue to me

Smartmind12 commented 10 months ago

I believe Covalent's serialization logic only supports the CallDeps class for function calls and expects a callable object however not raw python functions in the call_before and call_after sections of the electron API. Hence It's necessary to wrap the raw python functions in CallDeps objects for function calls. The following updations in the workflow should help resolve the issue: @ct.electron( call_before=[ct.DepsCall(execute_before_electron, args=(1, 2))], call_after=[ct.DepsCall(execute_after_electron)] ) OR call_deps_before = ct.DepsCall(execute_before_electron, 1, 2) call_deps_after = ct.DepsCall(execute_after_electron)

@ct.electron( call_before=[call_deps_before], call_after=[call_deps_after], )

santoshkumarradha commented 9 months ago

I believe Covalent's serialization logic only supports the CallDeps class for function calls and expects a callable object however not raw python functions in the call_before and call_after sections of the electron API. Hence It's necessary to wrap the raw python functions in CallDeps objects for function calls. The following updations in the workflow should help resolve the issue:

@ct.electron(

call_before=[ct.DepsCall(execute_before_electron, args=(1, 2))],

call_after=[ct.DepsCall(execute_after_electron)]

)

OR

call_deps_before = ct.DepsCall(execute_before_electron, 1, 2)

call_deps_after = ct.DepsCall(execute_after_electron)

@ct.electron(

call_before=[call_deps_before],

call_after=[call_deps_after],

)

Indeed true ! Adding a feature label to this.