-
Notifications
You must be signed in to change notification settings - Fork 199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lift []
and .
operators into AppFutures
#2904
Conversation
Thank you so much for the quick work on this initial attempt! The syntax you've proposed here is perfect in my opinion. It is exactly what I was hoping for as a user. Happy to test things out as well. |
@Andrew-S-Rosen it would be great if you could try this against a real application and report what unexpected problems you find |
Happy to do so later this afternoon and will report back. In reality, this logic would eventually become a core part of my quacc package, so I have lots of opportunities for thorough testing here. (I'm hoping to transition to quacc having Parsl as the recommended engine but needed to figure out how to address the "issue" I shared over Slack first). |
@benclifford: I just tried this branch out on several tests I had in mind, and it worked flawlessly. It may seem small, but this is a game-changing idea for me in terms of how I use Parsl, and I'm excited to see that it wasn't too painful to implement in this current form! For my own notes, I tried several things, including but not limited to: from parsl import python_app
import time
@python_app
def app1(val: float) -> dict:
"""
This app does some expensive calculation and produces a dictionary as the result.
"""
time.sleep(2)
return {"input": val, "result": val*100}
@python_app
def app2(val: float) -> dict:
"""
This app does a different expensive calculation.
"""
time.sleep(4)
return {"input": val, "result": val*200}
def workflow(val):
future1 = app1(val)
return app2(future1["result"])
# Run two separate workflows, ideally concurrently
print("Launching workflow 1...")
future1 = workflow(1)
print("Launching workflow 2...")
future2 = workflow(2)
print(future1.result(), future2.result()) from parsl import python_app
import time
@python_app
def app1(val: float) -> dict:
"""
This app does some expensive calculation and produces a dictionary as the result.
"""
time.sleep(2)
return [val, val*100]
@python_app
def app2(val: float) -> dict:
"""
This app does a different expensive calculation.
"""
time.sleep(4)
return [val, val*200]
def workflow(val):
future1 = app1(val)
return app2(future1[1])
# Run two separate workflows, ideally concurrently
print("Launching workflow 1...")
future1 = workflow(1)
print("Launching workflow 2...")
future2 = workflow(2)
print(future1.result(), future2.result()) from ase.build import molecule
from quacc.recipes.tblite.core import relax_job, freq_job
atoms = molecule("H2O")
output = relax_job(atoms)
output2 = freq_job(atoms, energy=output["energy"]) |
… bound to the same DFK
* Add support for `__getattr__` * Formatting patch * E302 lint fix * Add a test for a set * Rename test * Fix code comment
Ah, looks like the class attribute test failed with the |
…nto benc-lifted-dict
@Andrew-S-Rosen I can recreate that same test failure on my laptop - I'll dig into it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!! Btw, here are a few small typos to fix. Looks great so far.
looks like whats happening is that WorkQueueExecutor uses It's possible that this is very easy to change and probably makes sense. |
not quite as easy as I expected, but it's an interesting issue that would affect return of any sufficiently complex objects with Work Queue |
@Andrew-S-Rosen see #2908 for more general wq serialization issue you have uncovered |
… class definition returning test
[]
into apps[]
and .
operators onto AppFutures
[]
and .
operators onto AppFutures[]
and .
operators into AppFutures
@Andrew-S-Rosen I think this is unlikely to get merged this week due to ParslFest, but I'll make it as ready for review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@benclifford: Thanks so much for your timely work on this! Everything looks great to me. I look forward to seeing it eventually merged. 😄
- [X] I have read the [Developer Guide](https://quantum-accelerators.github.io/quacc/dev/contributing.html). Don't lie! 😉 - [X] My PR is on a custom branch and is _not_ named `main`. - [X] I have added relevant unit tests. Note: Your PR will likely not be merged without proper tests. Waiting on: - Parsl/parsl#2904 Closes #1059 (and #737).
@benclifford: Let me know if you need anything else from me on this! I've done a bunch of tests and haven't found anything problematic, at least based on how I plan to use this feature. |
Conflicts: parsl/dataflow/dflow.py
Description
This comes from discussion with @Andrew-S-Rosen on #parsl-help
The aim is to allow more python syntax to "work" within parsl's DAG framework.
This same style of implementation could be used for many other dunder methods defined on objects but not defined on Future, I think - for example to allow
some_app()+1
orsome_app() + some_other_app()
- if the approach in this PR works for[]
, then I think it would be mostly mechanical work to implement the same approach for some other dunder methods.The dunder methods where this makes sense are the methods where any type can be returned: for example, af[x] returns a future that will itself contain the value of af.result()[x] when the result is available.
The dunder methods where this does not make sense are roughly the methods where there is a stronger meaning to the return value: for example len(af), af.len() must always return an int, and that does not make sense in the world of deferring to the future: the length is not known at the time of invoking len(af) and so no meaningful integer can be returned.
This PR has an awkward hack to work around a circular import problem - ideally for going into- this doesn't matter, it has to be redecorated at least sometimes to deal with multiple DFKsmaster
, that would be reworked in some less ugly way - especially because the current implementation in this PR redecorates the lifted[]
operation every time, resulting in many app instances, which can in some circumstances be less efficient (when an app definition is cached by an executor somewhere, hypothetically, or in now-removed code which cached app definitions for monitoring)Type of change