znflow


Nameznflow JSON
Version 0.2.1 PyPI version JSON
download
home_pageNone
SummaryA general purpose framework for building and running computational graphs.
upload_time2024-08-29 19:30:03
maintainerNone
docs_urlNone
authorzincwarecode
requires_python<4.0,>=3.9
licenseApache-2.0
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            [![zincware](https://img.shields.io/badge/Powered%20by-zincware-darkcyan)](https://github.com/zincware)
[![Coverage Status](https://coveralls.io/repos/github/zincware/ZnFlow/badge.svg?branch=main)](https://coveralls.io/github/zincware/ZnFlow?branch=main)
[![PyPI version](https://badge.fury.io/py/znflow.svg)](https://badge.fury.io/py/znflow)
[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/zincware/ZnFlow/HEAD)

# ZnFlow

The `ZnFlow` package provides a basic structure for building computational
graphs based on functions or classes. It is designed as a lightweight
abstraction layer to

- learn graph computing.
- build your own packages on top of it.

## Installation

```shell
pip install znflow
```

## Usage

### Connecting Functions

With ZnFlow you can connect functions to each other by using the `@nodify`
decorator. Inside the `znflow.DiGraph` the decorator will return a
`FunctionFuture` object that can be used to connect the function to other nodes.
The `FunctionFuture` object will also be used to retrieve the result of the
function. Outside the `znflow.DiGraph` the function behaves as a normal
function.

```python
import znflow

@znflow.nodify
def compute_mean(x, y):
    return (x + y) / 2

print(compute_mean(2, 8))
# >>> 5

with znflow.DiGraph() as graph:
    mean = compute_mean(2, 8)

graph.run()
print(mean.result)
# >>> 5

with znflow.DiGraph() as graph:
    n1 = compute_mean(2, 8)
    n2 = compute_mean(13, 7)
    n3 = compute_mean(n1, n2)

graph.run()
print(n3.result)
# >>> 7.5
```

### Connecting Classes

It is also possible to connect classes. They can be connected either directly or
via class attributes. This is possible by returning `znflow.Connections` inside
the `znflow.DiGraph` context manager. Outside the `znflow.DiGraph` the class
behaves as a normal class.

In the following example we use a dataclass, but it works with all Python
classes that inherit from `znflow.Node`.

```python
import znflow
import dataclasses

@znflow.nodify
def compute_mean(x, y):
    return (x + y) / 2

@dataclasses.dataclass
class ComputeMean(znflow.Node):
    x: float
    y: float

    results: float = None

    def run(self):
        self.results = (self.x + self.y) / 2

with znflow.DiGraph() as graph:
    n1 = ComputeMean(2, 8)
    n2 = compute_mean(13, 7)
    # connecting classes and functions to a Node
    n3 = ComputeMean(n1.results, n2)

graph.run()
print(n3.results)
# >>> 7.5
```

### Dask Support

ZnFlow comes with support for [Dask](https://www.dask.org/) to run your graph:

- in parallel.
- through e.g. SLURM (see https://jobqueue.dask.org/en/latest/api.html).
- with a nice GUI to track progress.

All you need to do is install ZnFlow with Dask `pip install znflow[dask]`. We
can then extend the example from above. This will run `n1` and `n2` in parallel.
You can investigate the graph on the Dask dashboard (typically
http://127.0.0.1:8787/graph or via the client object in Jupyter.)

```python
import znflow
import dataclasses
from dask.distributed import Client

@znflow.nodify
def compute_mean(x, y):
    return (x + y) / 2

@dataclasses.dataclass
class ComputeMean(znflow.Node):
    x: float
    y: float

    results: float = None

    def run(self):
        self.results = (self.x + self.y) / 2


client = Client()
deployment = znflow.deployment.DaskDeployment(client=client)


with znflow.DiGraph(deployment=deployment) as graph:
    n1 = ComputeMean(2, 8)
    n2 = compute_mean(13, 7)
    # connecting classes and functions to a Node
    n3 = ComputeMean(n1.results, n2)

graph.run()

print(n3)
# >>> ComputeMean(x=5.0, y=10.0, results=7.5)
```

### Working with lists

ZnFlow supports some special features for working with lists. In the following
example we want to `combine` two lists.

```python
import znflow

@znflow.nodify
def arange(size: int) -> list:
    return list(range(size))

print(arange(2) + arange(3))
>>> [0, 1, 0, 1, 2]

with znflow.DiGraph() as graph:
    lst = arange(2) + arange(3)

graph.run()
print(lst.result)
>>> [0, 1, 0, 1, 2]
```

This functionality is restricted to lists. There are some further features that
allow combining `data: list[list]` by either using
`data: list = znflow.combine(data)` which has an optional `attribute=None`
argument to be used in the case of classes or you can simply use
`data: list = sum(data, [])`.

### Attributes Access

Inside the `with znflow.DiGraph()` context manager, accessing class attributes
yields `znflow.Connector` objects. Sometimes, it may be required to obtain the
actual attribute value instead of a `znflow.Connector` object. It is not
recommended to run class methods inside the `with znflow.DiGraph()` context
manager since it should be exclusively used for building the graph and not for
actual computation.

In the case of properties or other descriptor-based attributes, it might be
necessary to access the actual attribute value. This can be achieved using the
`znflow.get_attribute` method, which supports all features from `getattr` and
can be imported as such:

```python
from znflow import get_attribute as getattr
```

Here's an example of how to use `znflow.get_attribute`:

```python
import znflow

class POW2(znflow.Node):
    """Compute the square of x."""
    x_factor: float = 0.5
    results: float = None
    _x: float = None

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        # using "self._x = value * self.x_factor" inside "znflow.DiGraph()" would run
        # "value * Connector(self, "x_factor")" which is not possible (TypeError)
        # therefore we use znflow.get_attribute.
        self._x = value * znflow.get_attribute(self, "x_factor")

    def run(self):
        self.results = self.x**2

with znflow.DiGraph() as graph:
    n1 = POW2()
    n1.x = 4.0

graph.run()
assert n1.results == 4.0

```

Instead, you can also use the `znflow.disable_graph` decorator / context manager
to disable the graph for a specific block of code or the `znflow.Property` as a
drop-in replacement for `property`.

### Groups

It is possible to create groups of `znflow.nodify` or `znflow.Nodes` independent
from the graph structure. To create a group you can use
`with graph.group(<name>)`. To access the group members, use
`graph.get_group(<name>) -> znflow.Group`.

```python
import znflow

@znflow.nodify
def compute_mean(x, y):
    return (x + y) / 2

graph = znflow.DiGraph()

with graph.group("grp1"):
    n1 = compute_mean(2, 4)

assert n1.uuid in graph.get_group("grp1")
```

## Supported Frameworks

ZnFlow includes tests to ensure compatibility with:

- "Plain classes"
- `dataclasses`
- `ZnInit`
- `attrs`
- `pydantic` (experimental)

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "znflow",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.9",
    "maintainer_email": null,
    "keywords": null,
    "author": "zincwarecode",
    "author_email": "zincwarecode@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/31/18/d4730e55a02f5c3963f2b30319cae113973aa792f3e19ed2aca65fd5128e/znflow-0.2.1.tar.gz",
    "platform": null,
    "description": "[![zincware](https://img.shields.io/badge/Powered%20by-zincware-darkcyan)](https://github.com/zincware)\n[![Coverage Status](https://coveralls.io/repos/github/zincware/ZnFlow/badge.svg?branch=main)](https://coveralls.io/github/zincware/ZnFlow?branch=main)\n[![PyPI version](https://badge.fury.io/py/znflow.svg)](https://badge.fury.io/py/znflow)\n[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/zincware/ZnFlow/HEAD)\n\n# ZnFlow\n\nThe `ZnFlow` package provides a basic structure for building computational\ngraphs based on functions or classes. It is designed as a lightweight\nabstraction layer to\n\n- learn graph computing.\n- build your own packages on top of it.\n\n## Installation\n\n```shell\npip install znflow\n```\n\n## Usage\n\n### Connecting Functions\n\nWith ZnFlow you can connect functions to each other by using the `@nodify`\ndecorator. Inside the `znflow.DiGraph` the decorator will return a\n`FunctionFuture` object that can be used to connect the function to other nodes.\nThe `FunctionFuture` object will also be used to retrieve the result of the\nfunction. Outside the `znflow.DiGraph` the function behaves as a normal\nfunction.\n\n```python\nimport znflow\n\n@znflow.nodify\ndef compute_mean(x, y):\n    return (x + y) / 2\n\nprint(compute_mean(2, 8))\n# >>> 5\n\nwith znflow.DiGraph() as graph:\n    mean = compute_mean(2, 8)\n\ngraph.run()\nprint(mean.result)\n# >>> 5\n\nwith znflow.DiGraph() as graph:\n    n1 = compute_mean(2, 8)\n    n2 = compute_mean(13, 7)\n    n3 = compute_mean(n1, n2)\n\ngraph.run()\nprint(n3.result)\n# >>> 7.5\n```\n\n### Connecting Classes\n\nIt is also possible to connect classes. They can be connected either directly or\nvia class attributes. This is possible by returning `znflow.Connections` inside\nthe `znflow.DiGraph` context manager. Outside the `znflow.DiGraph` the class\nbehaves as a normal class.\n\nIn the following example we use a dataclass, but it works with all Python\nclasses that inherit from `znflow.Node`.\n\n```python\nimport znflow\nimport dataclasses\n\n@znflow.nodify\ndef compute_mean(x, y):\n    return (x + y) / 2\n\n@dataclasses.dataclass\nclass ComputeMean(znflow.Node):\n    x: float\n    y: float\n\n    results: float = None\n\n    def run(self):\n        self.results = (self.x + self.y) / 2\n\nwith znflow.DiGraph() as graph:\n    n1 = ComputeMean(2, 8)\n    n2 = compute_mean(13, 7)\n    # connecting classes and functions to a Node\n    n3 = ComputeMean(n1.results, n2)\n\ngraph.run()\nprint(n3.results)\n# >>> 7.5\n```\n\n### Dask Support\n\nZnFlow comes with support for [Dask](https://www.dask.org/) to run your graph:\n\n- in parallel.\n- through e.g. SLURM (see https://jobqueue.dask.org/en/latest/api.html).\n- with a nice GUI to track progress.\n\nAll you need to do is install ZnFlow with Dask `pip install znflow[dask]`. We\ncan then extend the example from above. This will run `n1` and `n2` in parallel.\nYou can investigate the graph on the Dask dashboard (typically\nhttp://127.0.0.1:8787/graph or via the client object in Jupyter.)\n\n```python\nimport znflow\nimport dataclasses\nfrom dask.distributed import Client\n\n@znflow.nodify\ndef compute_mean(x, y):\n    return (x + y) / 2\n\n@dataclasses.dataclass\nclass ComputeMean(znflow.Node):\n    x: float\n    y: float\n\n    results: float = None\n\n    def run(self):\n        self.results = (self.x + self.y) / 2\n\n\nclient = Client()\ndeployment = znflow.deployment.DaskDeployment(client=client)\n\n\nwith znflow.DiGraph(deployment=deployment) as graph:\n    n1 = ComputeMean(2, 8)\n    n2 = compute_mean(13, 7)\n    # connecting classes and functions to a Node\n    n3 = ComputeMean(n1.results, n2)\n\ngraph.run()\n\nprint(n3)\n# >>> ComputeMean(x=5.0, y=10.0, results=7.5)\n```\n\n### Working with lists\n\nZnFlow supports some special features for working with lists. In the following\nexample we want to `combine` two lists.\n\n```python\nimport znflow\n\n@znflow.nodify\ndef arange(size: int) -> list:\n    return list(range(size))\n\nprint(arange(2) + arange(3))\n>>> [0, 1, 0, 1, 2]\n\nwith znflow.DiGraph() as graph:\n    lst = arange(2) + arange(3)\n\ngraph.run()\nprint(lst.result)\n>>> [0, 1, 0, 1, 2]\n```\n\nThis functionality is restricted to lists. There are some further features that\nallow combining `data: list[list]` by either using\n`data: list = znflow.combine(data)` which has an optional `attribute=None`\nargument to be used in the case of classes or you can simply use\n`data: list = sum(data, [])`.\n\n### Attributes Access\n\nInside the `with znflow.DiGraph()` context manager, accessing class attributes\nyields `znflow.Connector` objects. Sometimes, it may be required to obtain the\nactual attribute value instead of a `znflow.Connector` object. It is not\nrecommended to run class methods inside the `with znflow.DiGraph()` context\nmanager since it should be exclusively used for building the graph and not for\nactual computation.\n\nIn the case of properties or other descriptor-based attributes, it might be\nnecessary to access the actual attribute value. This can be achieved using the\n`znflow.get_attribute` method, which supports all features from `getattr` and\ncan be imported as such:\n\n```python\nfrom znflow import get_attribute as getattr\n```\n\nHere's an example of how to use `znflow.get_attribute`:\n\n```python\nimport znflow\n\nclass POW2(znflow.Node):\n    \"\"\"Compute the square of x.\"\"\"\n    x_factor: float = 0.5\n    results: float = None\n    _x: float = None\n\n    @property\n    def x(self):\n        return self._x\n\n    @x.setter\n    def x(self, value):\n        # using \"self._x = value * self.x_factor\" inside \"znflow.DiGraph()\" would run\n        # \"value * Connector(self, \"x_factor\")\" which is not possible (TypeError)\n        # therefore we use znflow.get_attribute.\n        self._x = value * znflow.get_attribute(self, \"x_factor\")\n\n    def run(self):\n        self.results = self.x**2\n\nwith znflow.DiGraph() as graph:\n    n1 = POW2()\n    n1.x = 4.0\n\ngraph.run()\nassert n1.results == 4.0\n\n```\n\nInstead, you can also use the `znflow.disable_graph` decorator / context manager\nto disable the graph for a specific block of code or the `znflow.Property` as a\ndrop-in replacement for `property`.\n\n### Groups\n\nIt is possible to create groups of `znflow.nodify` or `znflow.Nodes` independent\nfrom the graph structure. To create a group you can use\n`with graph.group(<name>)`. To access the group members, use\n`graph.get_group(<name>) -> znflow.Group`.\n\n```python\nimport znflow\n\n@znflow.nodify\ndef compute_mean(x, y):\n    return (x + y) / 2\n\ngraph = znflow.DiGraph()\n\nwith graph.group(\"grp1\"):\n    n1 = compute_mean(2, 4)\n\nassert n1.uuid in graph.get_group(\"grp1\")\n```\n\n## Supported Frameworks\n\nZnFlow includes tests to ensure compatibility with:\n\n- \"Plain classes\"\n- `dataclasses`\n- `ZnInit`\n- `attrs`\n- `pydantic` (experimental)\n",
    "bugtrack_url": null,
    "license": "Apache-2.0",
    "summary": "A general purpose framework for building and running computational graphs.",
    "version": "0.2.1",
    "project_urls": null,
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "772d989d72e40b58caf48a79e1794f1fc305c54967b49e3d718ff5f4b6d66570",
                "md5": "f91e2b4ea5aba1d92826bbc4685ff094",
                "sha256": "a4451a202f9b82856e28a64e75615e93fe6a9e54da3e7873103c1bf193e8f759"
            },
            "downloads": -1,
            "filename": "znflow-0.2.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "f91e2b4ea5aba1d92826bbc4685ff094",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.9",
            "size": 22254,
            "upload_time": "2024-08-29T19:30:02",
            "upload_time_iso_8601": "2024-08-29T19:30:02.030606Z",
            "url": "https://files.pythonhosted.org/packages/77/2d/989d72e40b58caf48a79e1794f1fc305c54967b49e3d718ff5f4b6d66570/znflow-0.2.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3118d4730e55a02f5c3963f2b30319cae113973aa792f3e19ed2aca65fd5128e",
                "md5": "2d16dce4ab4829e94e92428e2c545203",
                "sha256": "a273421add51454887cbbbb3bcd0f31f59596f16b3bf21a6217b6235f2584c15"
            },
            "downloads": -1,
            "filename": "znflow-0.2.1.tar.gz",
            "has_sig": false,
            "md5_digest": "2d16dce4ab4829e94e92428e2c545203",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.9",
            "size": 19816,
            "upload_time": "2024-08-29T19:30:03",
            "upload_time_iso_8601": "2024-08-29T19:30:03.405030Z",
            "url": "https://files.pythonhosted.org/packages/31/18/d4730e55a02f5c3963f2b30319cae113973aa792f3e19ed2aca65fd5128e/znflow-0.2.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-08-29 19:30:03",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "znflow"
}
        
Elapsed time: 0.33226s