lazy-graph


Namelazy-graph JSON
Version 0.3.17 PyPI version JSON
download
home_pageNone
Summarylazy graph framework
upload_time2024-09-06 07:35:09
maintainerNone
docs_urlNone
authorNone
requires_python>=3.7
licenseGPLv3
keywords framework lazy evaluation computational graph
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            The lazy-graph is a Python library designed to facilitate lazy evaluation,
offering additional functionality for updating upstream values
and allowing easy duplication of the entire lazy graph structure.


# Install

Please either copy or create a soft link for the directory in the `site-packages` directory.
Alternatively, you can utilize pip to install the lazy-graph package by running the command `pip install lazy_graph`.


# Documents


## A simple example

We can create a root node `a` with the value `1` and another root node `b` with the value `2`,
followed by creating a new node called `c`, where its value is equal to the sum of the values of nodes `a` and `b`.
The function for adding these nodes will be triggered whenever we attempt to retrieve the value of node `c`.

    from lazy import Root, Node
    
    
    def add(a, b):
        print(f"calculating {a} + {b}")
        return a + b
    
    
    print("create nodes")
    a = Root(1)
    b = Root(2)
    c = Node(add, a, b)
    print("get the value")
    print(f"c is {c()}")

    create nodes
    get the value
    calculating 1 + 2
    c is 3

As demonstrated earlier, to create a root node containing the value of `x`, we use the expression `Root(x)`.
On the other hand, to obtain a node with its value determined by the function `func` along with any additional arguments or keyword arguments (if provided),
you would call `Node(func, *args, **kwargs)`.
This will generate a node whose value is computed as `func(*args, **kwargs)`.

To obtain the value of a given node `n`, simply use the function `n()`.
This calculates the value for the initial run and then utilizes caching for subsequent calls, ensuring efficiency in your code.


## Check if a node has already been computed

To determine if the value of a specified node `n` has already been computed and stored in cache,
you can utilize `bool(n)`.
This function returns `True` if the node's value exists in the cache, and `False` otherwise.

    a = Root(1)
    b = Root(2)
    c = Node(add, a, b)
    print(bool(c))
    print("c is", c())
    print(bool(c))

    False
    calculating 1 + 2
    c is 3
    True


## An example of updating an upstream node

    print("create nodes")
    a = Root(1)
    b = Root(2)
    c = Node(add, a, b)
    print("get the value")
    print(f"c is {c()}")
    print("get the value again")
    print(f"c is {c()}")
    print("update upstream")
    a.reset(4)
    print("get the new value")
    print(f"c is {c()}")

    create nodes
    get the value
    calculating 1 + 2
    c is 3
    get the value again
    c is 3
    update upstream
    get the new value
    calculating 4 + 2
    c is 6

In the provided code snippet, prior to resetting the upstream node `a`,
the value of node `c` is computed only once during its initial execution and subsequently utilizes a cache mechanism for subsequent calls.
Then, by calling `a.reset(v)`, where `v` equals `4` here, the value of node `a` can be reset to this new value.
After this operation, invoking `c()` will cause the function to be executed once more in order to obtain the updated value of node `c`.


## Both positional and keyword arguments are accepted, as well as regular values and lazy nodes

Both positional and keyword arguments are supported, allowing for a flexible approach when creating nodes.
You can mix these arguments with regular values as needed.
In the example provided, we utilize various types of arguments,
such as positional regular values, positional lazy nodes, keyword regular values, and keyword lazy nodes,
to construct node `z`.

    def add4(a, b, c, d):
        print(f"calculating {a} + {b} + {c} + {d}")
        return a + b + c + d
    
    
    print("create nodes")
    a = Root(1)
    c = Root(3)
    z = Node(add4, a, 2, c=c, d=4)
    print("get the value")
    print(f"c is {z()}")

    create nodes
    get the value
    calculating 1 + 2 + 3 + 4
    c is 10


## Copy the graph of lazy nodes

    from lazy import Copy
    
    print("create nodes")
    a = Root(1)
    b = Root(2)
    c = Node(add, a, b)
    print("get the value")
    print(f"c is {c()}")
    
    print("copy lazy graph")
    copy = Copy()
    new_a = copy(a)
    new_b = copy(b)
    new_c = copy(c)
    
    print("get the new value")
    print(f"new c is {new_c()}")

    create nodes
    get the value
    calculating 1 + 2
    c is 3
    copy lazy graph
    get the new value
    new c is 3

In addition to the previously simple example, we duplicate the graph,
copying `a` to `new_a`, `b` to `new_b`, and `c` to `new_c`.
This is done using a copy handle acquired through the `Copy()` function.
Once you have obtained the handle with `copy = Copy()`,
you can then utilize `copy(old_node)` to obtain the corresponding `new_node`.

After copying the graph, the cache is also reused whenever possible.
For instance, the `add` function isn't called when retrieving the value of node `new_c`.

    print("reset value")
    a.reset(4)
    new_a.reset(8)
    print("get the old value and new value")
    print(f"c is {c()}, new c is {new_c()}")

    reset value
    get the old value and new value
    calculating 4 + 2
    calculating 8 + 2
    c is 6, new c is 10

In the copied graph, the relationships between nodes are identical to those in the original graph,
along with the cache when feasible.
However, resetting the value of a node in one graph does not impact any other graphs.

In some cases, users might wish to duplicate just a portion of an entire graph.
In such instances, both graphs will share the same upstream nodes for those that haven't been replicated.
For instance, consider the example below where node `a` is shared between the two graphs.
However, the second graph contains unique nodes `new_b` and `new_c`, which correspond to `a` and `b` respectively in the initial graph.

    copy = Copy()
    new_b = copy(b)
    new_c = copy(c)
    
    print(f"a is {a()}")
    print(f"b is {b()}, new b is {new_b()}")
    print(f"c is {c()}, new c is {new_c()}")
    b.reset(8)
    print(f"c is {c()}, new c is {new_c()}")
    new_b.reset(10)
    print(f"c is {c()}, new c is {new_c()}")
    a.reset(6)
    print(f"c is {c()}, new c is {new_c()}")

    a is 4
    b is 2, new b is 2
    c is 6, new c is 6
    calculating 4 + 8
    c is 12, new c is 6
    calculating 4 + 10
    c is 12, new c is 14
    calculating 6 + 8
    calculating 6 + 10
    c is 14, new c is 16

In order to prevent misuse, if a user attempts to duplicate the same node multiple times, the copy handler will provide the same new node each time.

    new_c = copy(c)
    new_c_2 = copy(c)
    print(id(new_c) == id(new_c_2))

    True

When duplicating a lazy graph,
it is essential to replicate the upstream nodes prior to proceeding with the downstream nodes.
This guarantees that the package can effectively handle the dependencies among the various nodes of the graph.


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "lazy-graph",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": null,
    "keywords": "framework, lazy evaluation, computational graph",
    "author": null,
    "author_email": "Hao Zhang <zh970205@mail.ustc.edu.cn>",
    "download_url": null,
    "platform": null,
    "description": "The lazy-graph is a Python library designed to facilitate lazy evaluation,\noffering additional functionality for updating upstream values\nand allowing easy duplication of the entire lazy graph structure.\n\n\n# Install\n\nPlease either copy or create a soft link for the directory in the `site-packages` directory.\nAlternatively, you can utilize pip to install the lazy-graph package by running the command `pip install lazy_graph`.\n\n\n# Documents\n\n\n## A simple example\n\nWe can create a root node `a` with the value `1` and another root node `b` with the value `2`,\nfollowed by creating a new node called `c`, where its value is equal to the sum of the values of nodes `a` and `b`.\nThe function for adding these nodes will be triggered whenever we attempt to retrieve the value of node `c`.\n\n    from lazy import Root, Node\n    \n    \n    def add(a, b):\n        print(f\"calculating {a} + {b}\")\n        return a + b\n    \n    \n    print(\"create nodes\")\n    a = Root(1)\n    b = Root(2)\n    c = Node(add, a, b)\n    print(\"get the value\")\n    print(f\"c is {c()}\")\n\n    create nodes\n    get the value\n    calculating 1 + 2\n    c is 3\n\nAs demonstrated earlier, to create a root node containing the value of `x`, we use the expression `Root(x)`.\nOn the other hand, to obtain a node with its value determined by the function `func` along with any additional arguments or keyword arguments (if provided),\nyou would call `Node(func, *args, **kwargs)`.\nThis will generate a node whose value is computed as `func(*args, **kwargs)`.\n\nTo obtain the value of a given node `n`, simply use the function `n()`.\nThis calculates the value for the initial run and then utilizes caching for subsequent calls, ensuring efficiency in your code.\n\n\n## Check if a node has already been computed\n\nTo determine if the value of a specified node `n` has already been computed and stored in cache,\nyou can utilize `bool(n)`.\nThis function returns `True` if the node's value exists in the cache, and `False` otherwise.\n\n    a = Root(1)\n    b = Root(2)\n    c = Node(add, a, b)\n    print(bool(c))\n    print(\"c is\", c())\n    print(bool(c))\n\n    False\n    calculating 1 + 2\n    c is 3\n    True\n\n\n## An example of updating an upstream node\n\n    print(\"create nodes\")\n    a = Root(1)\n    b = Root(2)\n    c = Node(add, a, b)\n    print(\"get the value\")\n    print(f\"c is {c()}\")\n    print(\"get the value again\")\n    print(f\"c is {c()}\")\n    print(\"update upstream\")\n    a.reset(4)\n    print(\"get the new value\")\n    print(f\"c is {c()}\")\n\n    create nodes\n    get the value\n    calculating 1 + 2\n    c is 3\n    get the value again\n    c is 3\n    update upstream\n    get the new value\n    calculating 4 + 2\n    c is 6\n\nIn the provided code snippet, prior to resetting the upstream node `a`,\nthe value of node `c` is computed only once during its initial execution and subsequently utilizes a cache mechanism for subsequent calls.\nThen, by calling `a.reset(v)`, where `v` equals `4` here, the value of node `a` can be reset to this new value.\nAfter this operation, invoking `c()` will cause the function to be executed once more in order to obtain the updated value of node `c`.\n\n\n## Both positional and keyword arguments are accepted, as well as regular values and lazy nodes\n\nBoth positional and keyword arguments are supported, allowing for a flexible approach when creating nodes.\nYou can mix these arguments with regular values as needed.\nIn the example provided, we utilize various types of arguments,\nsuch as positional regular values, positional lazy nodes, keyword regular values, and keyword lazy nodes,\nto construct node `z`.\n\n    def add4(a, b, c, d):\n        print(f\"calculating {a} + {b} + {c} + {d}\")\n        return a + b + c + d\n    \n    \n    print(\"create nodes\")\n    a = Root(1)\n    c = Root(3)\n    z = Node(add4, a, 2, c=c, d=4)\n    print(\"get the value\")\n    print(f\"c is {z()}\")\n\n    create nodes\n    get the value\n    calculating 1 + 2 + 3 + 4\n    c is 10\n\n\n## Copy the graph of lazy nodes\n\n    from lazy import Copy\n    \n    print(\"create nodes\")\n    a = Root(1)\n    b = Root(2)\n    c = Node(add, a, b)\n    print(\"get the value\")\n    print(f\"c is {c()}\")\n    \n    print(\"copy lazy graph\")\n    copy = Copy()\n    new_a = copy(a)\n    new_b = copy(b)\n    new_c = copy(c)\n    \n    print(\"get the new value\")\n    print(f\"new c is {new_c()}\")\n\n    create nodes\n    get the value\n    calculating 1 + 2\n    c is 3\n    copy lazy graph\n    get the new value\n    new c is 3\n\nIn addition to the previously simple example, we duplicate the graph,\ncopying `a` to `new_a`, `b` to `new_b`, and `c` to `new_c`.\nThis is done using a copy handle acquired through the `Copy()` function.\nOnce you have obtained the handle with `copy = Copy()`,\nyou can then utilize `copy(old_node)` to obtain the corresponding `new_node`.\n\nAfter copying the graph, the cache is also reused whenever possible.\nFor instance, the `add` function isn't called when retrieving the value of node `new_c`.\n\n    print(\"reset value\")\n    a.reset(4)\n    new_a.reset(8)\n    print(\"get the old value and new value\")\n    print(f\"c is {c()}, new c is {new_c()}\")\n\n    reset value\n    get the old value and new value\n    calculating 4 + 2\n    calculating 8 + 2\n    c is 6, new c is 10\n\nIn the copied graph, the relationships between nodes are identical to those in the original graph,\nalong with the cache when feasible.\nHowever, resetting the value of a node in one graph does not impact any other graphs.\n\nIn some cases, users might wish to duplicate just a portion of an entire graph.\nIn such instances, both graphs will share the same upstream nodes for those that haven't been replicated.\nFor instance, consider the example below where node `a` is shared between the two graphs.\nHowever, the second graph contains unique nodes `new_b` and `new_c`, which correspond to `a` and `b` respectively in the initial graph.\n\n    copy = Copy()\n    new_b = copy(b)\n    new_c = copy(c)\n    \n    print(f\"a is {a()}\")\n    print(f\"b is {b()}, new b is {new_b()}\")\n    print(f\"c is {c()}, new c is {new_c()}\")\n    b.reset(8)\n    print(f\"c is {c()}, new c is {new_c()}\")\n    new_b.reset(10)\n    print(f\"c is {c()}, new c is {new_c()}\")\n    a.reset(6)\n    print(f\"c is {c()}, new c is {new_c()}\")\n\n    a is 4\n    b is 2, new b is 2\n    c is 6, new c is 6\n    calculating 4 + 8\n    c is 12, new c is 6\n    calculating 4 + 10\n    c is 12, new c is 14\n    calculating 6 + 8\n    calculating 6 + 10\n    c is 14, new c is 16\n\nIn order to prevent misuse, if a user attempts to duplicate the same node multiple times, the copy handler will provide the same new node each time.\n\n    new_c = copy(c)\n    new_c_2 = copy(c)\n    print(id(new_c) == id(new_c_2))\n\n    True\n\nWhen duplicating a lazy graph,\nit is essential to replicate the upstream nodes prior to proceeding with the downstream nodes.\nThis guarantees that the package can effectively handle the dependencies among the various nodes of the graph.\n\n",
    "bugtrack_url": null,
    "license": "GPLv3",
    "summary": "lazy graph framework",
    "version": "0.3.17",
    "project_urls": {
        "Changelog": "https://github.com/USTC-TNS/TNSP/blob/main/CHANGELOG.org",
        "Homepage": "https://github.com/USTC-TNS/TNSP/tree/main/lazy_graph",
        "Issues": "https://github.com/USTC-TNS/TNSP/issues",
        "Repository": "https://github.com/USTC-TNS/TNSP.git"
    },
    "split_keywords": [
        "framework",
        " lazy evaluation",
        " computational graph"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "5d95958ee2fb84170de82f9e279e4ad10c13937687f6b5db168a4d226e4b47fd",
                "md5": "8b5d1faaf88acb0644dc642b7e6c60f3",
                "sha256": "c28f6867b27deb61f5309d0a2868e4cb1a3ac117c7c65e678ce50fc032543e14"
            },
            "downloads": -1,
            "filename": "lazy_graph-0.3.17-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "8b5d1faaf88acb0644dc642b7e6c60f3",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 7263,
            "upload_time": "2024-09-06T07:35:09",
            "upload_time_iso_8601": "2024-09-06T07:35:09.496150Z",
            "url": "https://files.pythonhosted.org/packages/5d/95/958ee2fb84170de82f9e279e4ad10c13937687f6b5db168a4d226e4b47fd/lazy_graph-0.3.17-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-09-06 07:35:09",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "USTC-TNS",
    "github_project": "TNSP",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "lazy-graph"
}
        
Elapsed time: 0.31892s