This class allows the convenient management of multiple QuantumVariables of one type. Inspired by the well known numpy ndarray, the QuantumArray supports many convenient array manipulation methods. Similar to the numpy equivalent, creating a QuantumArray can be achieved by specifying a shape and a qtype
:
>>> import numpy as np >>> from qrisp import QuantumArray, QuantumFloat >>> qtype = QuantumFloat(5, -2) >>> q_array = QuantumArray(qtype = qtype, shape = (2, 2, 2))
Note that qtype
is not a type object but a QuantumVariable which serves as an âexampleâ.
To retrieve the entries (i.e. QuantumVariables) from the QuantumArray, we simply index as with regular numpy arrays:
>>> from qrisp import h >>> qv = q_array[0,0,1] >>> h(qv[0]) >>> print(q_array) {OutcomeArray([[[0., 0.], [0., 0.]], [[0., 0.], [0., 0.]]]): 0.5, OutcomeArray([[[0. , 0.25], [0. , 0. ]], [[0. , 0. ], [0. , 0. ]]]): 0.5}
We see the value 0.25 in the second entry because we applied an H-gate onto the 0-th qubit of the QuantumVariable at position (0,0,1). Since the type of this array is a QuantumFloat, with exponent -2, the significance of this qubit is 0.25.
Note that the keys of the dictionary returned by the get_measurement method are no regular numpy arrays, as key objects need to be hashable. Instead, they are objects of an immutable subclass of np.ndarray called OutcomeArray, that supports hashing.
For QuantumArrays, many methods known from numpy arrays work here too:
>>> q_array = q_array.reshape(2,4)
Not only do the ndarray methods work but also many other convenience functions from the numpy module:
>>> q_array_swap = np.swapaxes(q_array, 0, 1) >>> print(q_array_swap) {OutcomeArray([[0., 0.], [0., 0.], [0., 0.], [0., 0.]]): 0.5, OutcomeArray([[0. , 0. ], [0.25, 0. ], [0. , 0. ], [0. , 0. ]]): 0.5}
To initiate the array, we use the encode
method. Similar to QuantumVariables, we can also use the slicing operator, but this time non-trivial slices are possible as well:
>>> q_array[1:,:] = 2*np.ones((1,4)) >>> print(q_array) {OutcomeArray([[0., 0., 0., 0.], [2., 2., 2., 2.]]): 0.5, OutcomeArray([[0. , 0.25, 0. , 0. ], [2. , 2. , 2. , 2. ]]): 0.5}
Quantum indexing
QuantumArrays can be dereferenced by QuantumFloats. This returns a QuantumEnvironment in which the corresponding entry is avaliable as a QuantumVariable.
from qrisp import QuantumBool, QuantumArray, QuantumFloat, h, x, multi_measurement q_array = QuantumArray(QuantumFloat(1), shape = (4,4)) index_0 = QuantumFloat(2) index_1 = QuantumFloat(2) index_0[:] = 2 index_1[:] = 1 h(index_0[0]) with q_array[index_0, index_1] as entry: x(entry)
>>> print(multi_measurement([index_0, index_1, q_array])) {(2, 1, OutcomeArray([[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 0., 0.]])): 0.5, (3, 1, OutcomeArray([[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 1., 0., 0.]])): 0.5}
Note
This only works for arrays which have a size of an integer power of 2.
Matrix multiplication
For QuantumArrays with qtype
QuantumFloat, matrix multiplication is available.
>>> q_array_1 = QuantumArray(qtype, (2,2)) >>> q_array_2 = QuantumArray(qtype, (2,2)) >>> q_array_1[:] = 2*np.eye(2) >>> q_array_2[:] = [[1,2],[3,4]] >>> print(q_array_1 @ q_array_2) {OutcomeArray([[2., 4.], [6., 0.]]): 1.0}
Note
By default, the output matrix will have the same qtype
as the first input matrix. Here, the qtype
is a QuantumFloat with 5 mantissa bits and exponent -2, implying that the result 8 yields overflow. Since qrisps unsigend arithmetic is modular, we get a 0.
It is also possible to multiply classical and quantum matrices
>>> q_array = QuantumArray(qtype, (2,2)) >>> q_array[:] = 3*np.eye(2) >>> cl_array = np.array([[1,2],[3,4]]) >>> print(q_array @ cl_array) {OutcomeArray([[3., 6.], [1., 4.]]): 1.0}
Note
These methods never allocate additional qubits and instead return a âviewâ.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4