"""\********************************************************************************* 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********************************************************************************/"""importnumpyasnpimportsympyasspfromqrisp.coreimportQuantumVariable,cxfromqrisp.miscimportgate_wrapfromqrisp.environmentsimportinvert,conjugatefromqrisp.jaspimportcheck_for_tracing_modedefsigned_int_iso_2(x,n):returnx%(int(1)<<(n))defsigned_int_iso(x,n):ifint(x)<-(2**n)orint(x)>=2**n:raiseException("Applying signed integer isomorphism resulted in overflow")ifx>=0:returnx%2**nelse:return-abs(x)%2**(n+1)defsigned_int_iso_inv(y,n):y=y%2**(n+1)ify<2**n:returnyelse:return-(2**(n+1))+y# Truncates a polynomial of the form p(x) = 2**k_0*x*i_0 + 2**k_1*x**i_1 ...# where every summand where the power of the coefficients does not lie in the interval# trunc bounds is removeddeftrunc_poly(poly,trunc_bounds):# Convert to sympy polynomialpoly=sp.poly(poly)# Clip upper boundpoly=poly.trunc(2.0**(trunc_bounds[1]))# Clip lower boundpoly=poly/2.0**trunc_bounds[0]poly=poly-sp.poly(poly).trunc(1)poly=poly*2.0**trunc_bounds[0]returnpoly.expr.expand()
[docs]classQuantumFloat(QuantumVariable):r""" This subclass of :ref:`QuantumVariable` can represent floating point numbers (signed and unsigned) up to an arbitrary precision. The technical details of the employed arithmetic can be found in this `article <https://ieeexplore.ieee.org/document/9815035>`_. To create a QuantumFloat we call the constructor: >>> from qrisp import QuantumFloat >>> a = QuantumFloat(3, -1, signed = False) Here, the 3 indicates the amount of mantissa qubits and the -1 indicates the exponent. For unsigned QuantumFloats, the decoder function is given by .. math:: f_{k}(i) = i2^{k} Where $k$ is the exponent. We can check which values can be represented: >>> for i in range(2**a.size): print(a.decoder(i)) 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 We see $2^3 = 8$ values, because we have 3 mantissa qubits. The exponent is -1, implying the precision is $0.5 = 2^{-1}$. For signed QuantumFloats, the decoder function is .. math:: f_{k}^{n}(i) = \begin{cases} i2^{k} & \text{if } i < 2^n \\ (i - 2^{n+1})2^k & \text{else} \end{cases} Where $k$ is again, the exponent and $n$ is the mantissa size. Another example: >>> b = QuantumFloat(2, -2, signed = True) >>> for i in range(2**b.size): print(b.decoder(i)) 0.0 0.25 0.5 0.75 -1.0 -0.75 -0.5 -0.25 Here, we have $2^2 = 4$ values and their signed equivalents. Their precision is $0.25 = 2^{-2}$. **Arithmetic** Many operations known from classical arithmetic work for QuantumFloats in infix notation. Addition: >>> a[:] = 1.5 >>> b[:] = 0.25 >>> c = a + b >>> print(c) {1.75: 1.0} Subtraction: >>> d = a - c >>> print(d) {-0.25: 1.0} Multiplication: >>> e = d * b >>> print(e) {-0.0625: 1.0} And even division: >>> a = QuantumFloat(3) >>> b = QuantumFloat(3) >>> a[:] = 7 >>> b[:] = 2 >>> c = a/b >>> print(c) {3.5: 1.0} Floor division: >>> d = a//b >>> print(d) {3.0: 1.0} Inversion: >>> a = QuantumFloat(3, -1) >>> a[:] = 3.5 >>> b = a**-1 >>> print(b) {0.25: 1.0} Note that the latter is only an approximate result. This is because in many cases, the results of division can not be stored in a finite amount of qubits, forcing us to approximate. To get a better approximation we can use the :meth:`q_div <qrisp.q_div>` and :meth:`qf_inversion <qrisp.qf_inversion>` functions and specify the precision: >>> from qrisp import q_div, qf_inversion >>> a = QuantumFloat(3) >>> a[:] = 1 >>> b = QuantumFloat(3) >>> b[:] = 7 >>> c = q_div(a, b, prec = 6) >>> print(c) {0.140625: 1.0} Comparing with the classical result (0.1428571428): >>> 1/7 - 0.140625 0.002232142857142849 We see that the result is inside the expected precision of $2^{-6} = 0.015625$. **In-place Operations** Further supported operations are inplace addition, subtraction (with both classical and quantum values): >>> a = QuantumFloat(4, signed = True) >>> a[:] = 4 >>> b = QuantumFloat(4) >>> b[:] = 3 >>> a += b >>> print(a) {7: 1.0} >>> a -= 2 >>> print(a) {5: 1.0} .. warning:: Additions that would result in overflow, raise no errors. Instead, the additions are performed `modular <https://en.wikipedia.org/wiki/Modular_arithmetic>`_. >>> c = QuantumFloat(3) >>> c += 9 >>> print(c) {1: 1.0} For inplace multiplications, only classical values are allowed: >>> a *= -3 >>> print(a) {-15: 1.0} .. note:: In-place multiplications can change the mantissa size to prevent overflow errors. If you want to prevent this behavior, look into :meth:`inpl_mult <qrisp.inpl_mult>`. >>> a.size 7 **Bitshifts** Bitshifts can be executed for free (i.e. not requiring any quantum gates). We can either use the :meth:`exp_shift <qrisp.QuantumFloat.exp_shift>` method or use the infix operators. Note that the bitshifts work in-place. >>> a.exp_shift(3) >>> print(a) {-120: 1.0} >>> a >>= 5 >>> print(a) {-3.75: 1.0} **Comparisons** QuantumFloats can be compared to Python floats using the established operators. The return values are :ref:`QuantumBools <QuantumBool>`: >>> from qrisp import h >>> a = QuantumFloat(4) >>> h(a[2]) >>> print(a) {0: 0.5, 4: 0.5} >>> comparison_qbl_0 = (a < 4 ) >>> print(comparison_qbl_0) {False: 0.5, True: 0.5} Comparison to other QuantumFloats also works: >>> b = QuantumFloat(3) >>> b[:] = 4 >>> comparison_qbl_1 = (a == b) >>> comparison_qbl_1.qs.statevector() sqrt(2)*(|0>*|True>*|4>*|False> + |4>*|False>*|4>*|True>)/2 The first tensor factor containing a boolean value is corresponding to ``comparison_qbl_0`` and the second one is ``comparison_qbl_1``. """def__init__(self,msize,exponent=0,qs=None,name=None,signed=False):# Boolean to indicate if the float is signedself.signed=signed# Exponentself.exponent=exponent# Initialize QuantumVariableifsigned:super().__init__(msize+1,qs,name=name)else:super().__init__(msize,qs,name=name)self.traced_attributes=["exponent","signed"]@propertydefmsize(self):returnself.size-self.signed@propertydefmshape(self):# Tuple that consists of (log2(min), log2(max)) where min and max are the# minimal and maximal values of the absolutes that the QuantumFloat can# represent.return(self.exponent,self.exponent+self.msize)# Define outcome_labelsdefdecoder(self,i):ifisinstance(self.signed,bool)andself.signed:res=signed_int_iso_inv(i,self.size-1)*2.0**self.exponentifself.exponent>=0:ifisinstance(res,(int,float)):returnint(res)else:returnres.astype(int)else:returnreselse:fromjax.numpyimportfloat64fromjax.coreimportTracerifisinstance(i,Tracer):res=i*float64(2)**self.exponentelse:res=i*2**self.exponentreturnresdefjdecoder(self,i):ifisinstance(self.exponent,int)andself.exponent==0:returnireturnself.decoder(i)defencoder(self,i):fromjax.numpyimportfloat64res=signed_int_iso_2(i/(float64(2)**self.exponent),self.size)# if self.signed:# res = signed_int_iso(i/2**self.exponent, self.size-1)# else:# res = i/2**self.exponentifisinstance(res,(int,float)):returnint(res)else:returnres.astype(int)
[docs]defsb_poly(self,m=0):""" Returns the semi-boolean polynomial of this `QuantumFloat` where `m` specifies the image extension parameter. For the technical details we refer to: https://ieeexplore.ieee.org/document/9815035 Parameters ---------- m : int, optional Image extension parameter. The default is 0. Returns ------- Sympy expression The semi-boolean polynomial of this QuantumFloat. Examples -------- >>> from qrisp import QuantumFloat >>> x = QuantumFloat(3, -1, signed = True, name = "x") >>> print(x.sb_poly(5)) 0.5*x_0 + 1.0*x_1 + 2.0*x_2 + 28.0*x_3 """ifm==0:m=self.sizesymbols=[sp.symbols(str(hash(self))+"_"+str(i))foriinrange(self.size)]poly=sum([2.0**(i)*symbols[i]foriinrange(self.size)])ifself.signed:poly+=(2.0**(m+1)-2.0**(self.size))*symbols[-1]return2**self.exponent*poly
defencode(self,encoding_number,rounding=False,permit_dirtyness=False):ifrounding:# Round value to closest fitting numberoutcome_labels=[self.decoder(i)foriinrange(2**self.size)]encoding_number=outcome_labels[np.argmin(np.abs(encoding_number-np.array(outcome_labels)))]super().encode(encoding_number,permit_dirtyness=permit_dirtyness)@gate_wrap(permeability="args",is_qfree=True)def__mul__(self,other):fromqrisp.jaspimportcheck_for_tracing_modeifcheck_for_tracing_mode():fromqrisp.alg_primitives.arithmeticimportjasp_multiplyerifisinstance(other,QuantumFloat):returnjasp_multiplyer(other,self)else:raiseException(f"Tried to multiply class {type(other)} with QuantumFloat")fromqrisp.alg_primitives.arithmeticimportq_mult,polynomial_encoderifisinstance(other,QuantumFloat):returnq_mult(self,other)elifisinstance(other,int):bit_shift=0whilenotother%2:other=other>>1bit_shift+=1ifself.signedorother<0:output_qf=QuantumFloat(self.msize+int(np.ceil(np.log2(abs(other)))),self.exponent,signed=True,)else:output_qf=QuantumFloat(self.msize+int(np.ceil(np.log2(abs(other)))),self.exponent,signed=False,)polynomial_encoder([self],output_qf,other*sp.Symbol("x"))output_qf<<bit_shiftreturnoutput_qfelse:raiseException("QuantumFloat multiplication for type "+str(type(other))+""" not implemented (available are QuantumFloat and int)")@gate_wrap(permeability="args",is_qfree=True)def__add__(self,other):fromqrisp.alg_primitives.arithmeticimportsbp_addfromqrispimportcheck_for_tracing_modeifisinstance(other,QuantumFloat):ifcheck_for_tracing_mode():res=self.duplicate()cx(self,res)res+=otherreturnreselse:returnsbp_add(self,other)elifisinstance(other,(int,float)):res=self.duplicate()cx(self,res)res+=otherreturnreselse:raiseException("Addition with type "+str(type(other))+" not implemented")@gate_wrap(permeability="args",is_qfree=True)def__sub__(self,other):fromqrisp.alg_primitives.arithmeticimportsbp_subfromqrispimportcheck_for_tracing_modeifisinstance(other,QuantumFloat):ifcheck_for_tracing_mode():res=self.duplicate()cx(self,res)res-=otherreturnreselse:returnsbp_sub(self,other)elifisinstance(other,(int,float)):res=self.duplicate()cx(self,res)res-=otherreturnreselse:raiseException("Subtraction with type "+str(type(other))+" not implemented")__radd__=__add____rmul__=__mul__@gate_wrap(permeability="args",is_qfree=True)def__rsub__(self,other):fromqrispimportxfromqrisp.alg_primitives.arithmeticimportsbp_subifisinstance(other,QuantumFloat):returnsbp_sub(other,self)elifisinstance(other,(int,float)):res=self.duplicate(init=True)ifnotres.signed:res.add_sign()x(res)res+=other+2**res.exponentreturnreselse:raiseException("Subtraction with type "+str(type(other))+" not implemented")@gate_wrap(permeability="args",is_qfree=True)def__truediv__(self,other):fromqrisp.alg_primitives.arithmeticimportq_divreturnq_div(self,other)@gate_wrap(permeability="args",is_qfree=True)def__floordiv__(self,other):ifself.signedorother.signed:raiseException("Floor division not implemented for signed QuantumFloats")ifself.exponent<0orother.exponent<0:raiseException("Tried to perform floor division on non-integer QuantumFloats")fromqrisp.alg_primitives.arithmeticimportq_divreturnq_div(self,other,prec=0)@gate_wrap(permeability="args",is_qfree=True)def__pow__(self,power):ifpower==-1:fromqrisp.alg_primitives.arithmeticimportqf_inversionreturnqf_inversion(self)elifpower==0:res=self.duplicate()res[:]=1returnreselse:fromqrispimportjasp_multiplyerdefpower_conjugator(base,power,temp_results):cx(base,temp_results[0])foriinrange(power-1):(temp_results[i+1]<<jasp_multiplyer)(base,temp_results[i])# (temp_results[i+1] << (lambda a, b : a * b))(base, temp_results[i])temp_results=[QuantumFloat((i+1)*self.size)foriinrange(power)]res=QuantumFloat(self.size*power)withconjugate(power_conjugator)(self,power,temp_results):cx(temp_results[-1],res)forqvintemp_results:qv.delete()returnres@gate_wrap(permeability=[1],is_qfree=True)def__iadd__(self,other):fromqrisp.jaspimportcheck_for_tracing_modeifcheck_for_tracing_mode():fromqrisp.alg_primitives.arithmeticimportgidney_addergidney_adder(other,self)returnselffromqrisp.alg_primitives.arithmeticimportpolynomial_encoderifisinstance(other,QuantumFloat):input_qf_list=[other]poly=sp.symbols("x")polynomial_encoder(input_qf_list,self,poly)elifisinstance(other,(int,float)):# self.incr(other)ifnotint(other/2**self.exponent)==other/2**self.exponent:raiseException("Tried to perform in-place addition with invalid number. ""QuantumFloat precision too low.")input_qf_list=[]poly=sp.sympify(other)polynomial_encoder(input_qf_list,self,poly)else:raiseException("In-place addition for type "+str(type(other))+" not implemented")returnself@gate_wrap(permeability=[1],is_qfree=True)def__isub__(self,other):fromqrisp.alg_primitives.arithmeticimportpolynomial_encoderfromqrisp.jaspimportcheck_for_tracing_modeifcheck_for_tracing_mode():fromqrisp.alg_primitives.arithmeticimportgidney_adderwithinvert():gidney_adder(other,self)returnselfifisinstance(other,QuantumFloat):input_qf_list=[other]poly=-sp.symbols("x")polynomial_encoder(input_qf_list,self,poly)elifisinstance(other,(int,float)):ifnotint(other/2**self.exponent)==other/2**self.exponent:raiseException("Tried to perform in-place subtraction with invalid number. ""QuantumFloat precision too low.")input_qf_list=[]poly=-sp.sympify(other)polynomial_encoder(input_qf_list,self,poly)else:raiseException("In-place substraction for type "+str(type(other))+" not implemented")returnself@gate_wrap(permeability=[],is_qfree=True)def__imul__(self,other):fromqrisp.alg_primitives.arithmeticimportinpl_multinpl_mult(self,other)returnselfdef__irshift__(self,k):self.exp_shift(-k)returnselfdef__ilshift__(self,k):self.exp_shift(k)returnselfdef__lt__(self,other):fromqrisp.alg_primitives.arithmeticimportlt,uint_lt,gidney_adderifcheck_for_tracing_mode():returnuint_lt(self,other,gidney_adder)else:ifnotisinstance(other,(QuantumFloat,int,float)):raiseException(f"Comparison with type {type(other)} not implemented")returnlt(self,other)def__gt__(self,other):fromqrisp.alg_primitives.arithmeticimportgt,uint_gt,gidney_adderifcheck_for_tracing_mode():returnuint_gt(self,other,gidney_adder)else:ifnotisinstance(other,(QuantumFloat,int,float)):raiseException(f"Comparison with type {type(other)} not implemented")returngt(self,other)def__le__(self,other):fromqrisp.alg_primitives.arithmeticimportleq,uint_le,gidney_adderifcheck_for_tracing_mode():returnuint_le(self,other,gidney_adder)else:ifnotisinstance(other,(QuantumFloat,int,float)):raiseException(f"Comparison with type {type(other)} not implemented")returnleq(self,other)def__ge__(self,other):fromqrisp.alg_primitives.arithmeticimportgeq,uint_ge,gidney_adderifcheck_for_tracing_mode():returnuint_ge(self,other,gidney_adder)else:ifnotisinstance(other,(QuantumFloat,int,float)):raiseException(f"Comparison with type {type(other)} not implemented")returngeq(self,other)def__eq__(self,other):fromqrisp.alg_primitives.arithmeticimporteqifnotisinstance(other,(QuantumFloat,int,float)):raiseException(f"Comparison with type {type(other)} not implemented")returneq(self,other)def__ne__(self,other):fromqrisp.alg_primitives.arithmeticimportneqifnotisinstance(other,(QuantumFloat,int,float)):raiseException(f"Comparison with type {type(other)} not implemented")returnneq(self,other)
[docs]defexp_shift(self,shift):""" Performs an internal bit shift. Note that this method doesn't cost any quantum gates. For the quantum version of this method, see :meth:`quantum_bit_shift<qrisp.QuantumFloat.quantum_bitshift>`. Parameters ---------- shift : int The amount to shift. Raises ------ Exception Tried to shift QuantumFloat exponent by non-integer value Examples -------- We create a QuantumFloat and perform a bitshift: >>> from qrisp import QuantumFloat >>> a = QuantumFloat(4) >>> a[:] = 2 >>> a.exp_shift(2) >>> print(a) {8: 1.0} >>> print(a.qs) :: QuantumCircuit: -------------- a.0: ───── ┌───┐ a.1: ┤ X ├ └───┘ a.2: ───── <BLANKLINE> a.3: ───── Live QuantumVariables: --------------------- QuantumFloat a """ifnotisinstance(shift,int):raiseException("Tried to shift QuantumFloat exponent by non-integer value")self.exponent+=shift
[docs]defadd_sign(self):""" Turns an unsigned QuantumFloat into its signed version. Raises ------ Exception Tried to add sign to signed QuantumFloat. Examples -------- >>> from qrisp import QuantumFloat >>> qf = QuantumFloat(4) >>> qf.signed False >>> qf.add_sign() >>> qf.signed True """ifself.signed:raiseException(r'Tried to add sign to signed QuantumFloat')self.extend(1,self.size)self.signed=True
[docs]defsign(self):r""" Returns the sign qubit. This qubit is in state $\ket{1}$ if the QuantumFloat holds a negative value and in state $\ket{0}$ otherwise. For more information about the encoding of negative numbers check the `publication <https://ieeexplore.ieee.org/document/9815035>`_. .. warning:: Performing an X gate on this qubit does not flip the sign! Use inplace multiplication instead. >>> from qrisp import QuantumFloat >>> qf = QuantumFloat(3, signed = True) >>> qf[:] = 3 >>> qf *= -1 >>> print(qf) {-3: 1.0} Raises ------ Exception Tried to retrieve sign qubit of unsigned QuantumFloat. Returns ------- Qubit The qubit holding the sign. Examples -------- We create a QuantumFloat, initiate a state that has probability 2/3 of being negative and entangle a QuantumBool with the sign qubit. >>> from qrisp import QuantumFloat, QuantumBool, cx >>> qf = QuantumFloat(4, signed = True) >>> n_amp = 1/3**0.5 >>> qf[:] = {-1 : n_amp, -2 : n_amp, 1 : n_amp} >>> qbl = QuantumBool() >>> cx(qf.sign(), qbl) >>> print(qbl) {True: 0.6667, False: 0.3333} """ifnotself.signed:raiseException("Tried to retrieve sign qubit of unsigned QuantumFloat")returnself[-1]
[docs]defsignificant(self,k):""" Returns the qubit with significance $k$. Parameters ---------- k : int The significance. Raises ------ Exception Tried to retrieve invalid significant from QuantumFloat Returns ------- Qubit The Qubit with significance $k$. Examples -------- We create a QuantumFloat and flip a qubit of specified significance. >>> from qrisp import QuantumFloat, x >>> qf = QuantumFloat(6, -3) >>> x(qf.significant(-2)) >>> print(qf) {0.25: 1.0} The qubit with significance $-2$ corresponds to the value $0.25 = 2^{-2}$. >>> x(qf.significant(2)) {4.25: 1.0} The qubit with significance $2$ corresponds to the value $4 = 2^{2}$. """sig_list=list(range(self.mshape[0],self.mshape[1]))ifknotinsig_list:raiseException(f"Tried to retrieve invalid significant {k} "f"from QuantumFloat with mantissa shape {self.mshape}")returnself[sig_list.index(k)]
[docs]deftruncate(self,x):""" Receives a regular float and returns the float that is closest to the input but can still be encoded. Parameters ---------- x : float A float that is supposed to be truncated. Returns ------- float The truncated float. Examples -------- We create a QuantumFloat and round a value to fit the encoder and subsequently initiate: >>> from qrisp import QuantumFloat >>> qf = QuantumFloat(4, -1) >>> value = 0.5102341 >>> qf[:] = value Exception: Value 0.5102341 not supported by encoder. >>> rounded_value = qf.truncate(value) >>> rounded_value 0.5 >>> qf[:] = rounded_value >>> print(qf) {0.5: 1.0} """decoder_values=np.array([self.decoder(i)foriinrange(2**self.size)])returndecoder_values[np.argmin(np.abs(decoder_values-x))]
[docs]defget_ev(self,**mes_kwargs):""" Retrieves the expectation value of self. Parameters ---------- **mes_kwargs : dict Keyword arguments for the measurement. See :meth:`qrisp.QuantumVariable.get_measurement` for more information. Returns ------- float The expectation value. Examples -------- We set up a QuantumFloat in uniform superposition and retrieve the expectation value: >>> from qrisp import QuantumFloat, h >>> qf = QuantumFloat(4) >>> h(qf) >>> qf.get_ev() 7.5 """mes_res=self.get_measurement(**mes_kwargs)returnsum([k*vfork,vinmes_res.items()])
[docs]defquantum_bit_shift(self,shift_amount):""" Performs a bit shift in the quantum device. While :meth:`exp_shift<qrisp.QuantumFloat.exp_shift>` performs a bit shift in the compiler (thus costing no quantum gates) this method performs the bitshift on the hardware. This has the advantage, that it can be controlled if called within a :ref:`ControlEnvironment` and furthermore admits bit shifts based on the state of a QuantumFloat .. note:: Bit bit shifts based on a QuantumFloat are currently only possible if both self and ``shift_amount`` are unsigned. .. warning:: Quantum bit shifting extends the QuantumFloat (ie. it allocates additional qubits). Parameters ---------- shift_amount : int or QuantumFloat The amount to shift. Raises ------ Exception Tried to shift QuantumFloat exponent by non-integer value Exception Quantum-quantum bitshifting is currently only supported for unsigned arguments Examples -------- We create a QuantumFloat and a QuantumBool to perform a controlled bit shift. :: from qrisp import QuantumFloat, QuantumBool, h qf = QuantumFloat(4) qf[:] = 1 qbl = QuantumBool() h(qbl) with qbl: qf.quantum_bit_shift(2) Evaluate the result >>> print(qf.qs.statevector()) sqrt(2)*(|1>*|False> + |4>*|True>)/2 """fromqrisp.alg_primitives.arithmeticimportquantum_bit_shiftquantum_bit_shift(self,shift_amount)
defcreate_output_qf(operands,op):ifisinstance(op,sp.core.expr.Expr):fromqrisp.alg_primitives.arithmetic.poly_toolsimportexpr_to_listexpr_list=expr_to_list(op)foriinrange(len(expr_list)):ifnotisinstance(expr_list[i][0],sp.Symbol):expr_list[i].pop(0)operands.sort(key=lambdax:x.name)defprod(iter):iter=list(iter)a=iter[0]foriinrange(1,len(iter)):a*=iter[i]returnafromsympyimportAbs,Poly,Symbolpoly=Poly(op)monom_list=[a*prod(x**kforx,kinzip(poly.gens,mon))fora,moninzip(poly.coeffs(),poly.monoms())]max_value_dic={Symbol(qf.name):2.0**qf.mshape[1]forqfinoperands}min_value_dic={Symbol(qf.name):2.0**qf.mshape[0]forqfinoperands}abs_poly=sum([Abs(monom)formonominmonom_list],0)min_poly_value=min([float(Abs(monom).subs(min_value_dic))formonominmonom_list])max_poly_value=float(abs_poly.subs(max_value_dic))min_sig=int(np.floor(np.log2(min_poly_value)))max_sig=int(np.ceil(np.log2(max_poly_value)))msize=max_sig-min_sigexponent=min_sigsigned=bool(sum([int(operand.signed)foroperandinoperands]))returnQuantumFloat(msize,exponent=exponent,signed=signed)ifop=="add":signed=operands[0].signedoroperands[1].signedexponent=min(operands[0].exponent,operands[1].exponent)max_sig=int(np.ceil(np.log2(int(2**operands[0].mshape[1]+2**operands[1].mshape[1]))))msize=max_sig-exponent+1returnQuantumFloat(msize,exponent,operands[0].qs,signed=signed,name="add_res*")ifop=="mul":signed=operands[0].signedoroperands[1].signedifoperands[0].reg==operands[1].regand(operands[0].signedandoperands[1].signed):signed=FalsereturnQuantumFloat(operands[0].msize+operands[1].msize+operands[0].signed*operands[1].signed,operands[0].exponent+operands[1].exponent,operands[0].qs,signed=signed,name="mul_res*",)ifop=="sub":exponent=min(operands[0].exponent,operands[1].exponent)max_sig=int(np.ceil(np.log2(int(2**operands[0].mshape[1]+2**operands[1].mshape[1]))))msize=max_sig-exponent+1returnQuantumFloat(msize,exponent,operands[0].qs,signed=True,name="sub_res*")# Initiates the value of qf2 into qf1 where qf1 has to hold the value 0defcopy_qf(qf1,qf2,ignore_overflow_errors=False,ignore_rounding_errors=False):# Lists that translate Qubit index => Significanceqf1_sign_list=[qf1.exponent+iforiinrange(qf1.size)]qf2_sign_list=[qf2.exponent+iforiinrange(qf2.size)]# Check overflow/underflowifmax(qf1_sign_list)<max(qf2_sign_list)andnotignore_overflow_errors:raiseException("Copy operation would result in overflow ""(use ignore_overflow_errors = True)")ifmin(qf1_sign_list)>min(qf2_sign_list)andnotignore_rounding_errors:raiseException("Copy operation would result in rounding ""(use ignore_rounding_errors = True)")qs=qf1.qsifqf2.signed:ifnotqf1.signed:raiseException("Tried to copy signed into unsigend float")# Remove last entry from significance list (last qubit is the sign qubit)qf2_sign_list.pop(-1)qf1_sign_list.pop(-1)foriinrange(len(qf1_sign_list)):# If we are in a realm where both floats have overlapping significance# => CNOT into each otherifqf1_sign_list[i]inqf2_sign_list:qf2_index=qf2_sign_list.index(qf1_sign_list[i])qs.cx(qf2[qf2_index],qf1[i])continue# Otherwise copy the sign bit into the bits of higher significance than qf2ifqf1_sign_list[i]>max(qf2_sign_list)andqf2.signed:qs.cx(qf2[-1],qf1[i])# Copy the sign bitifqf2.signed:qs.cx(qf2[-1],qf1[-1])
Get in touch!
If you are interested in Qrisp or high-level quantum algorithm research in general connect with us on our
Slack workspace.