"""\********************************************************************************* Copyright (c) 2025 the Qrisp authors** This program and the accompanying materials are made available under the* terms of the Eclipse Public License 2.0 which is available at* http://www.eclipse.org/legal/epl-2.0.** This Source Code may also be made available under the following Secondary* Licenses when the conditions for such availability set forth in the Eclipse* Public License, v. 2.0 are satisfied: GNU General Public License, version 2* with the GNU Classpath Exception which is* available at https://www.gnu.org/software/classpath/license.html.** SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0********************************************************************************/"""fromitertoolsimportproductimportnumpyasnpfromqrisp.circuitimporttranspilefromqrisp.coreimportQuantumVariable,qompilerfromqrisp.miscimportbin_rep
[docs]classQuantumArray(np.ndarray):""" This class allows the convenient management of multiple QuantumVariables of one type. As a subclass of `numpy's ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, 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 :meth:`encode <qrisp.QuantumArray.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} The shape of a QuantumArray does not have to be specified at creation. We can either set it through the :meth:`set_shape <qrisp.QuantumArray.set_shape>` method or by initiating: >>> q_array_1 = QuantumArray(qtype = qtype) >>> q_array_1.set_shape((2,2)) >>> print(q_array_1) {OutcomeArray([[0., 0.], [0., 0.]]): 1.0} >>> q_array_2 = QuantumArray(qtype = qtype) >>> q_array_2[:] = np.eye(2) >>> print(q_array_2) {OutcomeArray([[1., 0.], [0., 1.]]): 1.0} **Quantum indexing** QuantumArrays can be dereferenced by :ref:`QuantumFloats <QuantumFloat>`. This returns a :ref:`QuantumEnvironment` in which the corresponding entry is avaliable as a QuantumVariable. :: from qrisp import QuantumBool, QuantumArray, QuantumFloat, h, multi_measurement q_array = QuantumArray(QuantumBool(), 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: entry.flip() >>> 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) >>> q_array_2 = QuantumArray(qtype) >>> 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) >>> 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} """def__new__(subtype,qtype,shape=0,qs=None,name=None):ifisinstance(shape,QuantumVariable):raiseExceptionobj=super().__new__(subtype,shape,dtype="object")ifshape==0:obj.shape_specified=Falseelse:obj.shape_specified=Truefromqrisp.miscimportfind_calling_lineifnameisNone:iftype(obj)isQuantumArray:line=find_calling_line(1)else:line=find_calling_line(2)split_line=line.split("=")iflen(split_line)==2andFalse:python_var_name=split_line[0]python_var_name=python_var_name.split(" ")[0]python_var_name=python_var_name.split(" ")[-1]name=python_var_nameelse:fromqrispimportQuantumBool,QuantumChar,QuantumFloatifisinstance(qtype,QuantumFloat):name=qtype.get_unique_name("qf_array")elifisinstance(qtype,QuantumBool):name=qtype.get_unique_name("qbl_array")elifisinstance(qtype,QuantumChar):name=qtype.get_unique_name("qch_array")else:name=qtype.get_unique_name("qv_array")else:ifname[-1]=="*":name=qtype.get_unique_name(name[:-1])obj.name=qtype.name# Set data sizeobj.msize=qtype.sizeobj.qtype=qtypeifqsisNone:fromqrisp.coreimportQuantumSessionqs=QuantumSession()ifisinstance(shape,int):shape=(shape,)indices=list(product(*[list(range(i))foriinshape]))foriinindices:temp_dup=qtype.duplicate(name=obj.name+"*",qs=qs)np.ndarray.__setitem__(obj,i,temp_dup)obj.qs=qsreturnobj
[docs]defdecoder(self,code_int):""" The decoder method specifies how a QuantumArray turns the outcomes of measurements into human-readable values. It recieves an integer i and returns an OutcomeArray. Parameters ---------- i : int Integer representing the outcome of a measurement of the qubits of this QuantumArray. Returns ------- res : np.ndarray An array with entries of the type of the results of the .decoder of the qtype of this array. Examples -------- We create a QuantumFloat and inspect its decoder: >>> from qrisp import QuantumArray, QuantumFloat >>> qtype = QuantumFloat(3) >>> q_array = QuantumArray(qtype, (2,2)) >>> print(q_array.decoder(1)) [[0 0] [0 1]] """flattened_array=self.flatten()fromqrisp.qtypes.quantum_floatimportQuantumFloatifisinstance(self.qtype,QuantumFloat):ifself.qtype.exponent>=0:res=np.zeros(len(flattened_array),dtype=np.int32)else:res=np.zeros(len(flattened_array))else:res=np.zeros(len(flattened_array))n=len(self.qtype)bin_string=bin_rep(code_int,len(flattened_array)*n)foriinrange(len(flattened_array)):ifisinstance(self.qtype,QuantumFloat):res[i]=self.qtype.decoder(int(bin_string[i*n:(i+1)*n],2))else:res=res.astype("object")res[i]=self.qtype.decoder(int(bin_string[i*n:(i+1)*n],2))returnOutcomeArray(res.reshape(self.shape))
[docs]defencoder(self,encoding_array):""" The encoder reverses the decoder, it turns arrays into integers based on the encoder of the ``qtype`` of this array. Parameters ---------- encoding_array : An array where the entries can be read by the decoder of qtype. Raises ------ Exception Tried to call encoder on array with mismatching shape. Returns ------- i : int The integer encoding the given array. Examples -------- We create a QuantumArray and inspect it's encoder: >>> from qrisp import QuantumArray, QuantumFloat >>> qtype = QuantumFloat(3) >>> q_array = QuantumArray(qtype, (2,2)) >>> print(q_array.encoder(np.eye(2))) 513 """ifisinstance(encoding_array,list):encoding_array=np.array(encoding_array)ifself.shape!=encoding_array.shape:raiseException("Tried to call encoder on array with mismatching shape")flattened_encoding_array=encoding_array.flatten()flattened_quantum_array=self.flatten()encoding_int_str=""foriinrange(len(flattened_encoding_array)):qv=flattened_quantum_array[i]encoding_int_str+=bin_rep(qv.encoder(flattened_encoding_array[i]),qv.size)[::-1]returnint(encoding_int_str[::-1],2)
[docs]defset_shape(self,shape):""" Method to specify a shape for arrays which have been created without one. Parameters ---------- shape : tuple Desired shape. Raises ------ Exception QuantumArray already has a shape. """ifself.shape_specified:raiseException("Tried to set shape of QuantumArray, which already has a shape")ifisinstance(shape,int):shape=(shape,)self.resize(shape,refcheck=False)indices=list(product(*[list(range(i))foriinshape]))foriinindices:temp_dup=self.qtype.duplicate(qs=self.qs)np.ndarray.__setitem__(self,i,temp_dup)self.shape_specified=True
def__setitem__(self,key,value):ifnotself.shape_specified:ifnothasattr(value,"shape"):value=np.array(value)self.set_shape(value.shape)ifisinstance(value,QuantumArray):self[key].init_from(value)returnifisinstance(value,QuantumVariable):returnself[key].encode(value)returnselfdef__array_function__(self,func,types,args,kwargs):iffunc.__name__=="concatenate":fromqrispimportmergeifid(args[0][0].qtype)!=id(args[0][1].qtype):raiseException("Tried to concatenate arrays with differing qtype objects")merge(args)ndarray_res=np.ndarray.__array_function__(self,func,types,args,kwargs)res=QuantumArray(self.qtype,shape=ndarray_res.shape)indices=product(*[list(range(i))foriinndarray_res.shape])foriinindices:np.ndarray.__setitem__(res,i,ndarray_res[i])res.qs=args[0][0].qsres.qtype=args[0][0].qtypereturnreselse:returnnp.ndarray.__array_function__(self,func,types,args,kwargs)# Retrieves a measurement of the arrays state# Returns a list of tuples of the type (array, count)# ie. [(array([1,1,0]), 232), (array([1,1,3]), 115), ...]
[docs]defget_measurement(self,backend=None,shots=None,compile=True,compilation_kwargs={},subs_dic={},circuit_preprocessor=None,precompiled_qc=None,):""" Method for acquiring measurement results for the given array. The semantics are similar to the :meth:`get_measurement <qrisp.QuantumVariable.get_measurement>` method of QuantumVariable. The results are returned as a dictionary of another numpy subtype called OutcomeArray. Parameters ---------- backend : BackendClient, optional The backend on which to evaluate the quantum circuit. The default can be specified in the file default_backend.py. shots : integer, optional The amount of shots to evaluate the circuit. The default is given by the backend used. compile : bool, optional Boolean indicating if the .compile method of the underlying QuantumSession should be called before. The default is True. compilation_kwargs : dict, optional Keyword arguments for the compile method. For more details check :meth:`QuantumSession.compile <qrisp.QuantumSession.compile>`. The default is ``{}``. subs_dic : dict, optional A dictionary of sympy symbols and floats to specify parameters in the case of a circuit with unspecified, abstract parameters. The default is {}. circuit_preprocessor : Python function, optional A function which recieves a QuantumCircuit and returns one, which is applied after compilation and parameter substitution. The default is None. Raises ------ Exception Tried to get measurement within open environment. Returns ------- list of tuples The measurement results in the form [(outcome_label, probability), ...]. Examples -------- >>> from qrisp import QuantumFloat, QuantumArray >>> qtype = QuantumFloat(3) >>> q_array = QuantumArray(qtype) >>> q_array[:] = [[1,0],[0,1]] >>> res = q_array.get_measurement() >>> print(res) {OutcomeArray([[1, 0], [0, 1]]): 1.0} """forqvinself.flatten():ifqv.is_deleted():raiseException("Tried to measure QuantumArray containing deleted QuantumVariables")ifnotself.shape_specified:raiseException("Tried to measure QuantumArray without shape specification")ifbackendisNone:ifself.qs.backendisNone:fromqrisp.default_backendimportdef_backendbackend=def_backendelse:backend=self.qs.backendiflen(self.qs.env_stack)!=0:raiseException("Tried to get measurement within open environment")qubits=sum([qv.regforqvinself.flatten()[::-1]],[])# Copy circuit in over to prevent modification# from qrisp.quantum_network import QuantumNetworkClientifprecompiled_qcisNone:ifcompile:qc=qompiler(self.qs,intended_measurements=qubits,**compilation_kwargs)else:qc=self.qs.copy()# Transpile circuitqc=transpile(qc)else:qc=precompiled_qc.copy()# Bind parametersifsubs_dic:qc=qc.bind_parameters(subs_dic)fromqrisp.core.compilationimportcombine_single_qubit_gatesqc=combine_single_qubit_gates(qc)# Execute user specified circuit_preprocessorifcircuit_preprocessorisnotNone:qc=circuit_preprocessor(qc)fromqrisp.miscimportget_measurement_from_qccounts=get_measurement_from_qc(qc,qubits,backend,shots)# Insert outcome labels (if available and hashable)new_counts_dic={}forkeyincounts.keys():outcome_label=self.decoder(key)new_counts_dic[outcome_label]=counts[key]counts=new_counts_dic# Sort keyssorted_key_list=list(counts.keys())sorted_key_list.sort(key=lambdax:-counts[x])counts={key:counts[key]forkeyinsorted_key_list}returncounts
[docs]defencode(self,encoding_array):""" The encode method allows to quickly bring a QuantumArray in a desired computational basis state. For this, it performs a circuit, bringing fresh qubits into the integer state specified by the encoder. A shorthand for this method is given by the ``[:]`` operator. Note that the qubits to initialize have to be fresh (i.e. no operations performed on them). Parameters ---------- value : A value supported by the encoder. Examples -------- We create a QuantumArray and encode the identity matrix. >>> from qrisp import QuantumArray, QuantumFloat >>> qtype = QuantumFloat(5) >>> q_array = QuantumArray(qtype, (4,4)) >>> q_array.encode(np.eye(4)) >>> print(q_array) {OutcomeArray([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]): 1.0} Using the slice operator we can also encode on slices of QuantumArrays >>> q_array = QuantumArray(qtype, (4,4)) >>> q_array[:,:2] = np.ones((4,2)) >>> print(q_array) {OutcomeArray([[1, 1, 0, 0], [1, 1, 0, 0], [1, 1, 0, 0], [1, 1, 0, 0]]): 1.0} """flattened_array=self.flatten()qubit_list=sum([qv.regforqvinflattened_array],[])fromqrisp.miscimportcheck_if_fresh,int_encoderifnotcheck_if_fresh(qubit_list,self.qs):raiseException("Tried to initialize qubits which are not fresh anymore.")int_encoder(qubit_list,self.encoder(encoding_array))return
[docs]defdelete(self,verify=False):r""" Performs the :meth:`delete <qrisp.QuantumVariable.delete>` method on all QuantumVariables in this array. Parameters ---------- verify : bool, optional If this keyword is set to true, a simulator is queried to check if the deleted qubits are in the $\ket{0}$ state. The default is False. """forqvinself.flatten():qv.delete(verify=verify)
def__repr__(self):returnstr(self.get_measurement())def__str__(self):returnstr(self.get_measurement())def__matmul__(self,other):fromqrispimportQuantumFloatifisinstance(self.qtype,QuantumFloat):ifisinstance(other,QuantumArray):fromqrisp.alg_primitives.arithmeticimportq_matmulreturnq_matmul(self,other)elifisinstance(other,np.ndarray):fromqrisp.alg_primitives.arithmeticimportsemi_classic_matmulreturnsemi_classic_matmul(self,other)raiseException("Matrix multiplication for non-float types not implemented")def__rmatmul__(self,other):fromqrispimportQuantumFloatifisinstance(self.qtype,QuantumFloat):return(self.transpose()@other.transpose()).transpose()def__getitem__(self,index):fromqrisp.environmentsimportconjugateifisinstance(index,QuantumVariable):returnconjugate(manipulate_array)(self,index)ifisinstance(index,tuple):ifisinstance(index[0],QuantumVariable):returnconjugate(manipulate_array)(self,index)returnnp.ndarray.__getitem__(self,index)
[docs]definit_state(self,tuple_list):""" Method to initialize arbitrary quantum states in this array. The semantics are similar to the :meth:`QuantumVariable equivalent <qrisp.QuantumVariable.init_state>` of this method. The given state will be normalized. Parameters ---------- tuple_list : list of tuples The list of tuples describing the quantum state. The first component of the tuples needs to represent the array and the second the required amplitude. Raises ------ Exception Tried to initialize quantum state on qubits which are not fresh anymore. Examples -------- We initialize a quantum state on an array and evaluate the measurement probabilities. >>> import numpy as np >>> from qrisp import QuantumArray, QuantumFloat >>> qtype = QuantumFloat(3) >>> q_array = QuantumArray(qtype, shape = 3) >>> q_array.init_state([(np.array([1, 2, 3]), 1), (np.array([1, 2, 2]), 0.5j)]) >>> print(q_array) {OutcomeArray([1, 2, 3]): 0.8, OutcomeArray([1, 2, 2]): 0.2} """ifisinstance(tuple_list,dict):tuple_list=[(k,v)fork,vintuple_list.items()]flattened_array=self.flatten()qubit_list=[]forqvinflattened_array:qubit_list+=qv.regfromqrisp.miscimportcheck_if_freshifnotcheck_if_fresh(qv.reg,qv.qs):raiseException("Tried to initialize quantum state on qubits,""which are not fresh anymore")target_array=np.zeros(2**len(qubit_list),dtype=np.complex128)foriinrange(len(tuple_list)):target_array[self.encoder(tuple_list[i][0])]=tuple_list[i][1]target_array=target_array/np.vdot(target_array,target_array)**0.5fromqrispimportinit_stateinit_state(self,target_array)
[docs]definit_from(self,other):""" Performs the :meth:`init_from <qrisp.QuantumVariable.init_from>` method on all containing QuantumVariables based on their equivalent in the QuantumArray other. Note that this method does not copy the quantum state. For more details check the documentation of the init_from method of QuantumVariable. A shorthand for this method is the slicing operator ``[:]``. Parameters ---------- other : QuantumArray The QuantumArray from which to initiate. Raises ------ Exception Tried to initialize from array of invalied shape or qtype. Examples -------- We create a QuantumArray and bring it into superposition >>> import numpy as np >>> from qrisp import QuantumArray, QuantumFloat, h, multi_measurement >>> qtype = QuantumFloat(5) >>> q_array_a = QuantumArray(qtype) >>> q_array_a[:] = np.eye(3) >>> h(q_array_a[0,0][0]) >>> print(q_array_a) {OutcomeArray([[0, 0, 0], [0, 1, 0], [0, 0, 1]]): 0.5, OutcomeArray([[1, 0, 0], [0, 1, 0], [0, 0, 1]]): 0.5} We now duplicate this array and initiate >>> q_array_b = q_array_a.duplicate() >>> q_array_b.init_from(q_array_a) >>> print(multi_measurement([q_array_a, q_array_b])) {(OutcomeArray([[0, 0, 0], [0, 1, 0], [0, 0, 1]]), OutcomeArray([[0, 0, 0], [0, 1, 0], [0, 0, 1]])): 0.5, (OutcomeArray([[1, 0, 0], [0, 1, 0], [0, 0, 1]]), OutcomeArray([[1, 0, 0], [0, 1, 0], [0, 0, 1]])): 0.5} We can achieve the same with the slicing operator >>> q_array_c = QuantumArray(qtype, shape = (3, 3)) >>> q_array_c[1,:] = q_array_a[0,:] >>> print(multi_measurement([q_array_b, q_array_c])) {(OutcomeArray([[0, 0, 0], [0, 0, 0], [0, 0, 0]]), OutcomeArray([[0, 0, 0], [0, 0, 0], [0, 0, 0]])): 0.5, (OutcomeArray([[0, 0, 0], [1, 0, 0], [0, 0, 0]]), OutcomeArray([[0, 0, 0], [1, 0, 0], [0, 0, 0]])): 0.5} """ifid(self.qtype)!=id(other.qtype)orself.shape!=other.shape:raiseException("Tried to initialize from array of invalid shape or qtype")self_fl=self.flatten()other_fl=other.flatten()foriinrange(len(self_fl)):self_fl[i].init_from(other_fl[i])
[docs]defduplicate(self,init=False,qs=None):""" This method returns a fresh QuantumArray, with equal ``qtype`` and shape. Parameters ---------- init : bool, optional If set to True, the :meth:`init_from <qrisp.QuantumArray.init_from>` method will be called after creation. The default is False. qs : QuantumSession, optional The QuantumSession where the duplicate should be registered. By default, the duplicate will be registered in a new QuantumSession. Returns ------- res : QuantumArray The duplicated array. Examples -------- We duplicate a QuantumArray consisting of QuantumFloats with and without initiation. >>> from qrisp import QuantumArray, QuantumFloat >>> qtype = QuantumFloat(4) >>> q_array_0 = QuantumArray(qtype, (2,2)) >>> q_array_0[:] = np.ones((2,2)) >>> print(q_array_0) {OutcomeArray([[1, 1], [1, 1]]): 1.0} >>> q_array_1 = q_array_0.duplicate() >>> print(q_array_1) {OutcomeArray([[0, 0], [0, 0]]): 1.0} Note that no values have been carried over: >>> q_array_2 = q_array_0.duplicate(init = True) >>> print(q_array_2) {OutcomeArray([[1, 1], [1, 1]]): 1.0} Now the values have been carried over. Note that this does NOT copy the state. For more information on this check the documentation of the :meth:`init_from <qrisp.QuantumVariable.init_from>` method of QuantumVariable. """res=self.copy()ifqsisNone:fromqrispimportQuantumSessionqs=QuantumSession()indices=product(*[list(range(i))foriinself.shape])foriinindices:temp_dup=self.qtype.duplicate(qs=qs)np.ndarray.__setitem__(res,i,temp_dup)res.msize=self.qtype.sizeres.qtype=self.qtyperes.qs=qsifinit:res.init_from(self)returnres
[docs]defmost_likely(self,**kwargs):""" Performs a measurement and returns the most likely outcome. Parameters ---------- **kwargs : Keyword arguments for the get_measurement call. Examples -------- >>> from qrisp import QuantumFloat, QuantumArray, ry >>> import numpy as np >>> qa = QuantumArray(QuantumFloat(3), shape = 4) >>> ry(np.pi*9/8, qa[0][0]) >>> print(qa) {OutcomeArray([1, 0, 0, 0]): 0.9619, OutcomeArray([0, 0, 0, 0]): 0.0381} >>> qa.most_likely() OutcomeArray([1, 0, 0, 0]) """returnlist(self.get_measurement(**kwargs))[0]
defmanipulate_array(q_array,index):fromqrispimportQuantumFloat,demux,invertifisinstance(index,tuple):iflen(q_array.shape)!=len(index):raiseException("Tried to quantum deref QuantumArray with index of mismatching shape")forqfinindex:ifisinstance(qf,QuantumFloat):ifqf.signed:raiseException("Tried to quantum deref with a signed QuantumFloat")ifqf.exponent!=0:raiseException("Tried to quantum deref with a non-integer")index=sum([qv.regforqvinindex[::-1]],[])q_array=q_array.flatten()withinvert():demux(q_array[0],index,q_array)returnq_array[0]classOutcomeArray(np.ndarray):def__new__(subtype,ndarray):ifisinstance(ndarray,list):ndarray=np.array(ndarray)ifndarray.dtype==np.int64:ndarray=np.array(ndarray,dtype=np.int32)obj=super().__new__(subtype,ndarray.shape,dtype=ndarray.dtype)indices=product(*[list(range(i))foriinndarray.shape])foriinindices:np.ndarray.__setitem__(obj,i,ndarray[i])obj.flags.writeable=Falsereturnobjdef__hash__(self):returnhash(self.ravel().data.tobytes())# return hash(str(self))def__eq__(self,other):returnnp.array_equal(self,other)def__repr__(self):res=np.ndarray.__repr__(self).replace(", dtype=int32","")returnres
Get in touch!
If you are interested in Qrisp or high-level quantum algorithm research in general connect with us on our
Slack workspace.