Skip to main content

lazy graph framework

Project description

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

lazy_graph-0.3.16-py3-none-any.whl (7.3 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page