Primitive Computables

The inquanto.computables.primitive submodule provides primitive computable objects which do not represent physical quantities themselves but function as building blocks for more complex structures. Moreover, primitive computables can evaluate themselves recursively without the need for an evaluator, as they are generally not the leaf nodes in a computable tree.

A basic computable, which is primarily intended for demonstration purposes, is ComputableInt. It stores an integer value and returns this value upon evaluation. As it does not require any quantum measurement, but returns directly the stored integer, no evaluator function needs to be passed to the evaluate() method.

from inquanto.computables.primitive import ComputableInt

cint = ComputableInt(2)
print(cint)
print(cint.evaluate())
ComputableInt(value=2)
2

Other primitive computables extend to more advanced data structures such as lists, arrays, tuples, and callables. These structures can house other computables, with evaluations performed recursively. The ComputableInt will serve as an atomic computable, demonstrating how to build these more complex data structures with it.

Computable Lists and Tuples

In this section, we illustrate the usage of ComputableTuple and ComputableList. These classes mirror Python’s tuple and list data structures, but can also contain other computable structures. When the evaluate() method is called, the corresponding evaluate() methods of the child computables (the elements of the tuple or list that are also computables), are invoked.

from inquanto.computables.primitive import ComputableTuple, ComputableList

ctuple = ComputableTuple(ComputableInt(1), ComputableInt(0), -1)
print(ctuple)
print(ctuple.evaluate())

clist = ComputableList([ComputableInt(3), ComputableInt(4), 5])
print(clist)
print(clist.evaluate())
(ComputableInt(value=1), ComputableInt(value=0), -1)
(1, 0, -1)
[ComputableInt(value=3), ComputableInt(value=4), 5]
[3, 4, 5]

You can explore the child computables contained within these structures using built-in methods:

print(list(ctuple.children()))
[ComputableInt(value=1), ComputableInt(value=0)]

This will print the child computables. Note that the ctuple has three elements, but only two children. Additionally, you can inspect the tree structure of computables using the print_tree() method:

ctuple.print_tree()
(ComputableInt(value=1), ComputableInt(value=0), -1)
  ComputableInt(value=1)
  ComputableInt(value=0)

Iterating Over Computable Trees

The computables can be further composed into larger structures.

co = ComputableTuple(ctuple, clist, "something else")

You can iterate over the nodes of a computable tree using the walk() method, which allows for detailed exploration of the tree structure:

for cnode, depth in co.walk():
    print(cnode, depth)
((ComputableInt(value=1), ComputableInt(value=0), -1), [ComputableInt(value=3), ComputableInt(value=4), 5], 'something else') 0
(ComputableInt(value=1), ComputableInt(value=0), -1) 1
ComputableInt(value=1) 2
ComputableInt(value=0) 2
[ComputableInt(value=3), ComputableInt(value=4), 5] 1
ComputableInt(value=3) 2
ComputableInt(value=4) 2

Computable Multi-dimensional Arrays

The ComputableNDArray class allows for handling multi-dimensional arrays of computables, utilizing numpy’s ndarray. This computable ndarray stores computables as objects and returns an ndarray with evaluated values in their respective locations upon successful evaluation of all child computables. The type of values depends on the computables housed in the computable ndarray; hence, the returned ndarray will retain object dtype.

from inquanto.computables.primitive import ComputableNDArray

carr = ComputableNDArray([[ComputableInt(3), ComputableInt(4)], [ComputableInt(3), ComputableInt(4)]])
print(carr)
print(carr.evaluate())
[[ComputableInt(value=3) ComputableInt(value=4)]
 [ComputableInt(value=3) ComputableInt(value=4)]]
[[3 4]
 [3 4]]

Calling a Function with Computables

The ComputableFunction class enables the conversion of any lambda function that operates on values into a function that operates on computables. For instance, if we have a list of computables and want to evaluate the sum of their values, we might proceed as follows:

from inquanto.computables.primitive import ComputableFunction
csum = ComputableFunction(lambda x: sum(x), clist)

print(csum)
print(csum.evaluate()) # 12
ComputableFunction(<lambda>, [ComputableInt(value=3), ComputableInt(value=4), 5])
12

Another example might be the division of one computable by another:

from inquanto.computables.primitive import ComputableFunction
cfunc = ComputableFunction(lambda x, y: x / y, csum, ComputableInt(4))
print(cfunc)
print(cfunc.evaluate())
ComputableFunction(<lambda>, ComputableFunction(<lambda>, [ComputableInt(value=3), ComputableInt(value=4), 5]), ComputableInt(value=4))
3.0