def snell(n_1, n_2, th_1): """ return angle theta in layer 2 with refractive index n_2, assuming it has angle th_1 in layer with refractive index n_1. Use Snell's law. Note that "angles" may be complex!! """ # Important that the arcsin here is numpy.lib.scimath.arcsin, not # numpy.arcsin! (They give different results e.g. for arcsin(2).) th_2_guess = arcsin(n_1 * np.sin(th_1) / n_2) if is_forward_angle(n_2, th_2_guess): return th_2_guess else: return pi - th_2_guess
def recalculateEquatorialPosition(self): Lat = self._observer.lat Alt = self._fixedBody.alt Az = self._fixedBody.az # Ra = self._fixedBody.ra # print("ALT-AZ", self._fixedBody.alt, self._fixedBody.az, "LST",self._observer.getLST()) Dec = arcsin(sin(Alt) * sin(Lat) + cos(Alt) * cos(Lat) * cos(Az)) HA = arccos((sin(Alt) - sin(Dec)*sin(Lat)) / (cos(Dec)*cos(Lat))) # HA2 = arcsin(-sin(Az)*cos(Alt)/cos(Dec)) Ra = self._observer.getLST() -HA # print('Calculated','HA',getHours(HA),'RA-DEC',getCoordinates(Ra,Dec)) # print('Original', 'HA', self.getCurrentHA(), 'RA-DEC',self._fixedBody.ra, self._fixedBody.dec) # getHours(self._observer.long) return getCoordinates(Ra,Dec)
def list_snell(n_list, th_0): """ return list of angle theta in each layer based on angle th_0 in layer 0, using Snell's law. n_list is index of refraction of each layer. Note that "angles" may be complex!! """ # Important that the arcsin here is numpy.lib.scimath.arcsin, not # numpy.arcsin! (They give different results e.g. for arcsin(2).) angles = arcsin(n_list[0] * np.sin(th_0) / n_list) # The first and last entry need to be the forward angle (the intermediate # layers don't matter, see https://arxiv.org/abs/1603.02720 Section 5) if not is_forward_angle(n_list[0], angles[0]): angles[0] = pi - angles[0] if not is_forward_angle(n_list[-1], angles[-1]): angles[-1] = pi - angles[-1] return angles
def cost_sparse(n: int, lam: float, d: int, dE: float, chi: int, stps: int) -> Tuple[int, int, int]: """ Determine fault-tolerant costs using sparse decomposition in quantum chemistry Args: n (int) - the number of spin-orbitals lam (float) - the lambda-value for the Hamiltonian dE (float) - allowable error in phase estimation L (int) - the rank of the first decomposition Lxi (int) - the total number of eigenvectors chi (int) - equivalent to aleph_1 and aleph_2 in the document, the number of bits for the representation of the coefficients beta (int) - equivalent to beth in the document, the number of bits for the rotations stps (int) - an approximate number of steps to choose the precision of single qubit rotations in preparation of the equal superposition state Returns: step_cost (int) - Toffolis per step total_cost (int) - Total number of Toffolis ancilla_cost (int) - Total ancilla cost """ # I think there is a bug in the mathematica notebook. It does not check if # 2 is a factor first, which it should, cf. the similar function in # costingdf.nb Below is correct using the power_two() function, to give # power of 2 that is a factor of d. eta = power_two(d) nN = np.ceil(np.log2(n // 2)) m = chi + 8 * nN + 4 # Eq (A13) oh = [0] * 20 nM = (np.ceil(np.log2(d)) - eta) / 2 for p in range(2, 22): # JJG note: arccos arg may be > 1 v = np.round(np.power(2,p+1) / (2 * np.pi) * arccos(np.power(2,nM) /\ np.sqrt(d/2**eta)/2)) oh[p-2] = np.real(stps * (1 / (np.sin(3 * arcsin(np.cos(v * 2 * np.pi /\ np.power(2,p+1)) * \ np.sqrt(d/2**eta) / np.power(2,nM)))**2) - 1) + 4 * (p + 1)) # Bits of precision for rotation br = int(np.argmin(oh) + 1) + 2 # Hand selecting the k expansion factor k1 = 32 # Equation (A17) cost = np.ceil(d/k1) + m * (k1 -1) + QI(d)[1] + 4 * n + 8 * nN + 2 * chi + \ 7 * np.ceil(np.log2(d)) - 6 * eta + 4 * br - 19 # Number of iterations needed for the phase estimation. iters = np.ceil(np.pi * lam / (dE * 2)) # The following are the number of qubits from the listing on page 39. # Control registers for phase estimation and iteration on them. ac1 = 2 * np.ceil(np.log2(iters)) - 1 # System qubits ac2 = n # The register used for the QROM ac3 = np.ceil(np.log2(d)) # The qubit used for flagging the success of the equal superposition state # preparation and the ancilla qubit for rotation ac45 = 2 # The phase gradient state ac6 = br # The equal superposition state for coherent alias sampling ac7 = chi # The ancillas used for QROM ac8 = np.ceil(np.log2(d / k1)) + m * k1 ancilla_cost = ac1 + ac2 + ac3 + ac45 + ac6 + ac7 + ac8 # Sanity checks before returning as int assert cost.is_integer() assert iters.is_integer() assert ancilla_cost.is_integer() step_cost = int(cost) total_cost = int(cost * iters) ancilla_cost = int(ancilla_cost) return step_cost, total_cost, ancilla_cost
def qubit_vs_toffoli(lam, dE, eps, n, chi, beta, M, algorithm='half', verbose=False): """ Args: lam (float) - the lambda-value for the Hamiltonian dE (float) - allowable error in phase estimation. usually 0.001 eps (float) - allowable error for synthesis (dE/(10 * lam)) usually n (int) - number of spin orbitals. chi (int) - number of bits of precision for state prep beta (int) - number of bits of precision for rotations M (int) - THC rank or r_{Thc} algorithm (str) - 'half', where half of the phasing angles are loaded at a time 'full', where angles loaded from QROM to perform phasing operations are loaded at the same time Note: In arXiv:2011.03494, 'half' corresponds to Fig 11, while 'full' corresponds to Fig 12. verbose (bool) - do additional printing of intermediates? """ # only valid algorithms accepted assert algorithm in ['half', 'full'] # The number of iterations for the phase estimation. iters = np.ceil(pi * lam / (dE * 2)) # The number of bits used for each register. nM = np.ceil(np.log2(M + 1)) #This is number of distinct items of data we need to output, see Eq. (28). d = M * (M + 1) / 2 + n / 2 # The number of bits used for the contiguous register. nc = np.ceil(np.log2(d)) # The output size is 2*Log[M] for the alt values, χ for the keep value, # and 2 for the two sign bits. m = 2 * nM + 2 + chi # The next block of code finds the optimal number of bits to use for the # rotation angle for the amplitude amplification taking into account the # probability of failure and the cost of the rotations. oh = np.zeros(20, dtype=float) for p in range(1, 20 + 1): cos_term = arccos(np.power(2, nM) / np.sqrt(d) / 2) # print(cos_term) v = np.round(np.power(2, p) / (2 * pi) * cos_term) asin_term = arcsin( np.cos(v * 2 * pi / np.power(2, p)) * np.sqrt(d) / np.power(2, nM)) sin_term = np.sin(3 * asin_term)**2 oh[p - 1] = (20_000 * (1 / sin_term - 1) + 4 * p).real # br is the number of bits used in the rotation. br = np.argmin(oh) + 1 # Next are the costs for the state preparation. cp1 = 2 * (10 * nM + 2 * br - 9) # There is cost 10*Log[M] for preparing the equal superposition over the # input registers. This is the costing from above Eq. (29). # This is the cost of computing the contiguous register and inverting it. # This is with a sophisticated scheme adding together triplets of bits. # This is the cost of step 2 in the list on page 14. cp2 = 2 * (nM**2 + nM - 1) # This is the cost of the QROM for the state preparation and its inverse. cp3 = QR(d, m)[1] + QI(d)[1] # The cost for the inequality test. cp4 = 2 * chi # The cost 2*nM for the controlled swaps. cp5 = 4 * nM # Then there is a cost of nM+1 for swapping the μ and ν registers, where # the +3 is because we need to control on two registers, and # control swap of the spin registers. cp6 = 2 * nM + 3 # The total cost in Eq. (33). CPCP = cp1 + cp2 + cp3 + cp4 + cp5 + cp6 # Next are the costs for the select operation. # This is the cost of swapping based on the spin register. These costs are # from the list on page 15, and this is steps 1 and 7. cs1 = 2 * n k1 = 2**QI(M + n / 2)[0] cs2a = M + n / 2 - 2 + np.ceil(M / k1) + np.ceil(n / 2 / k1) + k1 # The QROM for the rotation angles the first time. Here M+n/2-2 is the cost # for generating them, and the second part is the cost for inverting them # with advanced QROM. The QROM for the rotation angles the second time. # Here the cost M-2 is for generating the angles the second time, and QI[M] # is for inverting the QROM. Steps 2 and 6. cs2b = M - 2 + QI(M)[1] # The cost of the rotations steps 3 and 5. cs3 = 4 * n * (beta - 2) # Cost for extra part in making the Z doubly controlled step 4. cs4 = 1 # The total select cost in Eq. (43). CS = cs1 + cs2a + cs2b + cs3 + cs4 # The cost given slightly above Eq. (44) is 2*nM+5. That is a typo and it # should have the aleph (equivalent to χ here) like at the top of the # column. Here we have +3 in this line, +1 in the next line and +1 for cs4, # to give the same total. costref = 2 * nM + chi + 3 cost = CPCP + CS + costref + 1 # Next are qubit costs. ac1 = 2 * np.ceil(np.log2(iters + 1)) - 1 ac2 = n ac3 = 2 * nM ac47 = 5 ac8 = beta ac9 = nc kt = 2**QR(d, m)[0] ac10 = m * kt + np.ceil(np.log2(d / kt)) # This is for the equal superposition state to perform the inequality test # with the keep register. ac11 = chi # The qubit to control the swap of the μ and ν registers. ac12 = 1 aca = ac1 + ac2 + ac3 + ac47 + ac8 + ac9 + ac11 + ac12 # This is the data needed for the rotations. ac13 = beta * n / 2 # These are the ancillas needed for adding into the phase gradient state. ac14 = beta - 2 # These are the temporary ancillas in between erasing the first QROM # ancillas and inverting that QROM. The +m is for output of first QROM. acc = ac13 + ac14 + m if verbose: print("Total Toffoli cost ", cost * iters) print("Ancilla for first QROM ", aca + ac10) print("Actual ancilla ... ", np.max([aca + ac10, aca + acc])) print("Spacetime volume ", np.max([aca + ac10, aca + acc]) * cost) # First are the numbers of qubits that must be kept throughout the # computation. See page 18. if algorithm == 'half': # The qubits used as the control registers for the phase estimation, # that must be kept the whole way through. If we used independent # controls each time that would increase the Toffoli cost by # np.ceil(np.log2iters+1]]-3, while saving # np.ceil(np.log2iters+1]]-1 qubits ac1 = np.ceil(np.log2(iters + 1)) elif algorithm == 'full': # The qubits used as the control registers for the phase estimation, # that must be kept the whole way through. If we used independent # controls each time that would increase the Toffoli cost by # np.ceil(np.log2iters+1]]-3, while saving # np.ceil(np.log2iters+1]]-1 qubits. ac1 = 2 * np.ceil(np.log2(iters + 1)) - 1 # The system qubits that must always be included. ac2 = n # The μ and ν registers, that must be kept because they are control # registers that aren't fully erased and must be reflected on. ac3 = 2 * nM # These are the qubits for the spin in the control state as well as the # qubit that is rotated for the preparation of the equal superposition # state, AND the qubit that is used to control. # None of these are fully inversely prepared. ac4512 = 4 # The qubits for the phase gradient state. ac8 = beta # This is for the equal superposition state to perform the inequality test # with the keep register. It must be kept around and reflected upon. ac11 = chi # The total number of permanent qubits. perm = ac1 + ac2 + ac3 + ac4512 + ac8 + ac11 # In preparing the equal superposition state there are 6 temporary qubits # used in the rotation of the ancilla. There are another three that are # needed for the temporary results of inequality tests. By far the largest # number, however, come from keeping the temporary ancillas from the # inequality tests. That should be 3*nM+nN-4. There are an other two # qubits in output at the end that will be kept until this step is undone. # Note: not used? #nN = np.ceil(np.log2(n / 2)) # This is the maximum number of qubits used while preparing the equal # superposition state. qu1 = perm + 4 * nM - 1 # To explain the number of temporary ancillas, we have nM+1 to perform the # inequality test on mu and nu with out-of-place addition. We have another # nM-2 for the equality test. Then we can do the inequality tests on # mu and nu with constants (temporarily) overwriting these variables, and # keeping nM-1 qubits on each. Then there are another 2 temporary qubits # used for the reflection. That gives 4*nM-1 total. # This is the number of Toffolis during this step. tof1 = 10 * nM + 2 * br - 9 # This is increasing the running number of permanent ancillas by 2 for the # ν=M+1 flag qubit and the success flag qubit. perm = perm + 2 # The number of temporary qubits used in this computation is the the same # as the number of Toffolis plus one. qu2 = perm + nM**2 + nM # The Toffoli cost of computing the contiguous register. tof2 = nM**2 + nM - 1 # The running number of qubits is increased by the number needed for the # contiguous register. perm = perm + nc if algorithm == 'half': # Here I'm setting the k-value for the QROM by hand instead of choosing # the optimal one for Toffolis. kt = 16 elif algorithm == 'full': # Here I'm setting the k-value for the QROM by hand instead of choosing # the optimal one for Toffolis. kt = 32 # This is the number of qubits needed during the QROM. qu3 = perm + m * kt + np.ceil(np.log2(d / kt)) # The number of Toffolis for the QROM. tof3 = np.ceil(d / kt) + m * (kt - 1) # The number of ancillas used increases by the actual output size of QROM. perm = perm + m # The number of ancilla qubits used for the subtraction for the ineql test. qu4 = perm + chi # We can use one of the qubits from the registers that are subtracted as # the flag qubit so we don't need an extra flag qubit. # The number of Toffolis needed for the inequality test. The number of # permanent ancillas is unchanged.*) tof4 = chi # We don't need any extra ancillas for the controlled swaps. qu5 = perm # We are swapping pairs of registers of size nM tof5 = 2 * nM # One extra ancilla for the controlled swap of mu and nu because it is # controlled on two qubits. qu6 = perm # One more Toffoli for the double controls. tof6 = nM + 1 # Swapping based on the spin register. qu7 = perm tof7 = n / 2 if algorithm == 'half': # We use these temporary ancillas for the first QROM for the rot angles. qu8 = perm + nM + beta * n / 4 elif algorithm == 'full': # We use these temporary ancillas for the first QROM for the rot angles. qu8 = perm + nM + beta * n / 2 # The cost of outputting the rotation angles including those for the # one-electron part. tof8 = M + n / 2 - 2 if algorithm == 'half': # We are now need the output rotation angles, though we don't need the # temporary qubits from the unary iteration. perm = perm + beta * n / 4 elif algorithm == 'full': # We are now need the output rotation angles, though we don't need the # temporary qubits from the unary iteration. perm = perm + beta * n / 2 # We need a few temp registers for adding into the phase grad register. qu9 = perm + (beta - 2) if algorithm == 'half': # The cost of the rotations. tof9 = n * (beta - 2) / 2 # Make a list where we keep subtr the data qubits that can be erased. # Table[-j*beta,{j,0,n/4-1}]+perm+(beta-2) qu10 = np.array([-j * beta for j in range(int(n / 4))]) \ + perm + beta - 2 # The cost of the rotations. # Table[2*(beta-2),{j,0,n/4-1}] tof10 = np.array([2 * (beta - 2) for j in range(int(n / 4))]) # We've erased the data perm = perm - beta * n / 4 elif algorithm == 'full': # The cost of the rotations. tof9 = n * (beta - 2) # Make a list where we keep subtr the data qubits that can be erased. # Table[-j*beta,{j,0,n/2-1}]+perm+(beta-2) qu10 = np.array([-j * beta for j in range(int(n / 2))]) \ + perm + beta - 2 # The cost of the rotations. # Table[2*(beta-2),{j,0,n/2-1}] tof10 = np.array([2 * (beta - 2) for j in range(int(n / 2))]) # We've erased the data perm = perm - beta * n / 2 # Find the k for the phase fixup for the erasure of the rotations. k1 = 2**QI(M + n / 2)[0] # Temp qubits used. Data qubits were already erased, so don't change perm. qu11 = perm + k1 + np.ceil(np.log2(M / k1)) tof11 = np.ceil(M / k1) + np.ceil(n / 2 / k1) + k1 # Swapping based on the spin register. qu12 = perm tof12 = n / 2 # Swapping the spin registers. qu12a = perm tof12a = 1 # Swapping based on the spin register. qu13 = perm tof13 = n / 2 if algorithm == 'half': # We use these temp ancillas for the second QROM for the rot angles. qu14 = perm + nM - 1 + beta * n / 4 perm = perm + beta * n / 4 elif algorithm == 'full': # We use these temp ancillas for the second QROM for the rot angles. qu14 = perm + nM - 1 + beta * n / 2 perm = perm + beta * n / 2 tof14 = M - 2 # We need a few temporary registers for adding into the phase grad register qu15 = perm + (beta - 2) if algorithm == 'half': # The cost of the rotations. tof15 = n * (beta - 2) / 2 elif algorithm == 'full': # The cost of the rotations. tof15 = n * (beta - 2) # Just one Toffoli to do the controlled Z1. qu16 = perm tof16 = 1 if algorithm == 'half': # Make a list where we keep subtr the data qubits that can be erased. # Table[-j*beta,{j,0,n/4-1}]+perm+(beta-2) qu17 = np.array([-j * beta for j in range(int(n / 4))]) + perm + beta - 2 # The cost of the rotations. # Table[2*(beta-2),{j,0,n/4-1}] tof17 = np.array([2 * (beta - 2) for j in range(int(n / 4))]) # We've erased the data. perm = perm - beta * n / 4 elif algorithm == 'full': # Make a list where we keep subtr the data qubits that can be erased. # Table[-j*beta,{j,0,n/2-1}]+perm+(beta-2) qu17 = np.array([-j * beta for j in range(int(n / 2))]) \ + perm + beta - 2 # The cost of the rotations. # Table[2*(beta-2),{j,0,n/2-1}] tof17 = np.array([2 * (beta - 2) for j in range(int(n / 2))]) # We've erased the data perm = perm - beta * n / 2 # Find the k for the phase fixup for the erasure of the rotations. k1 = 2**QI(M)[0] # The temp qubits used. The data qubits were already erased, # so don't change perm. qu18 = perm + k1 + np.ceil(np.log2(M / k1)) tof18 = np.ceil(M / k1) + k1 # Swapping based on the spin register. qu19 = perm tof19 = n / 2 # One extra ancilla for the controlled swap of mu and nu because it is # controlled on two qubits. qu20 = perm + 1 # One extra Toffoli, because we are controlling on two qubits. tof20 = nM + 1 # We don't need any extra ancillas for the controlled swaps. qu21 = perm # We are swapping pairs of registers of size nM tof21 = 2 * nM # The number of ancilla qubits used for the subtraction for the inequal test qu22 = perm + chi # We can use one of the qubits from the registers that are subtracted as # the flag qubit so we don't need an extra flag qubit. # The number of Toffolis needed for inverting the inequality test. # The number of permanent ancillas is unchanged. tof22 = chi # We can erase the data for the QROM for inverting the state preparation, # then do the phase fixup. perm = perm - m kt = 2**QI(d)[0] # This is the number of qubits needed during the QROM. qu23 = perm + kt + np.ceil(np.log2(d / kt)) # The number of Toffolis for the QROM. tof23 = np.ceil(d / kt) + kt # The number of temporary qubits used in this computation is the same as # the number of Toffolis plus one. We are erasing the contiguous register # as we go so can subtract nc. qu24 = perm - nc + nM**2 + nM # The Toffoli cost of computing the contiguous register. tof24 = nM**2 + nM - 1 # The contiguous register has now been deleted. perm = perm - nc # This is the maximum number of qubits used while preparing the equal # superposition state. qu25 = perm + 4 * nM - 1 # This is the number of Toffolis during this step. tof25 = 10 * nM + 2 * br - 9 # This is increasing the running number of permanent ancillas by 2 for # the ν=M+1 flag qubit and the success flag qubit. perm = perm - 2 if algorithm == 'half': # We need some ancillas to perform a reflection on multiple qubits. # We are including one more Toffoli to make it controlled. qu26 = perm + costref + np.ceil(np.log2(iters + 1)) tof26 = costref + np.ceil(np.log2(iters + 1)) elif algorithm == 'full': # We need some ancillas to perform a reflection on multiple qubits. # We are including one more Toffoli to make it controlled. qu26 = perm + costref tof26 = costref qu27 = perm # Iterate the control register. tof27 = 1 # Labels sm = 'small element' pq = 'preparation QROM' rq = 'rotation QROM' ri = r'R$^{\dag}$' ro = 'R' iq = 'inverse QROM' color_dict = { sm: '#435CE8', pq: '#E83935', rq: '#F59236', ri: '#E3D246', ro: '#36B83E', iq: '#E83935' } if algorithm == 'half': tgates = np.hstack((np.array([ tof1, tof2, tof3, tof4, tof5, tof6, tof7, tof8, tof9, tof8, tof9, tof9, tof8 ]), tof10, np.array([ tof11, tof12, tof12a, tof13, tof14, tof15, tof14, tof15, tof16, tof15, tof14 ]), tof17, np.array([ tof18, tof19, tof20, tof21, tof22, tof23, tof24, tof25, tof26, tof27 ]))) qubits = np.hstack((np.array( [qu1, qu2, qu3, qu4, qu5, qu6, qu7, qu8, qu9, qu8, qu9, qu9, qu8]), qu10, np.array([ qu11, qu12, qu12a, qu13, qu14, qu15, qu14, qu15, qu16, qu15, qu14 ]), qu17, np.array([ qu18, qu19, qu20, qu21, qu22, qu23, qu24, qu25, qu26, qu27 ]))) labels = [sm, sm, pq, sm, sm, sm, sm, rq, ri, rq, ri, ro, rq] + \ [ro] * len(qu10) + \ [rq, sm, sm, sm, rq, ri, rq, ri, sm, ro, rq] + \ [ro] * len(qu17) + \ [rq, sm, sm, sm, sm, iq, sm, sm, sm, sm] colors = [color_dict[i] for i in labels] elif algorithm == 'full': tgates = np.hstack( (np.array([tof1, tof2, tof3, tof4, tof5, tof6, tof7, tof8, tof9]), tof10, np.array([tof11, tof12, tof12a, tof13, tof14, tof15, tof16]), tof17, np.array([ tof18, tof19, tof20, tof21, tof22, tof23, tof24, tof25, tof26, tof27 ]))) qubits = np.hstack( (np.array([qu1, qu2, qu3, qu4, qu5, qu6, qu7, qu8, qu9]), qu10, np.array([qu11, qu12, qu12a, qu13, qu14, qu15, qu16]), qu17, np.array( [qu18, qu19, qu20, qu21, qu22, qu23, qu24, qu25, qu26, qu27]))) labels = [sm, sm, pq, sm, sm, sm, sm, rq, ri] + \ [ro] * len(qu10) + \ [rq, sm, sm, sm, rq, ri, sm] + \ [ro] * len(qu17) + \ [rq, sm, sm, sm, sm, iq, sm, sm, sm, sm] colors = [color_dict[i] for i in labels] # check lists are at least consistent assert all( len(element) == len(tgates) for element in [qubits, labels, colors]) return tgates, qubits, labels, colors
def coh_tmm(n_list, d_list, th_0, lam_vac): """ Code adapted by that of Byrnes - see https://arxiv.org/abs/1603.02720 n_list is the list of refractive indices, in the order that the light would pass through them. The 0'th element of the list should be the semi-infinite medium from which the light enters, the last element should be the semi- infinite medium to which the light exits (if any exits). th_0 is the angle of incidence: 0 for normal, pi/2 for glancing. Remember, for a dissipative incoming medium (n_list[0] is not real), th_0 should be complex so that n0 sin(th0) is real (intensity is constant as a function of lateral position). d_list is the list of layer thicknesses (front to back). Should correspond one-to-one with elements of n_list. First and last elements should be "inf". lam_vac is vacuum wavelength of the light. """ # Convert lists to numpy arrays if they're not already. n_list = np.asarray(n_list) d_list = np.asfarray(d_list) num_layers = n_list.size th_0, lam_vac = [np.array(a) for a in np.broadcast_arrays(th_0, lam_vac)] orig_shp = th_0.shape th_0 = np.ravel(th_0) lam_vac = np.ravel(lam_vac) # Input tests assert (np.abs(np.imag(n_list[0] * np.sin(th_0))) < 100 * EPSILON).all(), "Error in n0 or th0!" # th_list is a list with, for each layer, the angle that the light travels # through the layer. Computed with Snell's law. Note that the "angles" may # be complex! # Important that the arcsin here is numpy.lib.scimath.arcsin, not # numpy.arcsin! (They give different results e.g. for arcsin(2).) # (NLAYERS, NUMPNTS) th_list = arcsin(n_list[0] * np.sin(th_0[None, :]) / n_list[:, None]) # The first and last entry need to be the forward angle (the intermediate # layers don't matter, see https://arxiv.org/abs/1603.02720 Section 5) # TODO VECTORISE FORWARD ANGLE # if not is_forward_angle(n_list[0], th_list[0]): # th_list[0] = pi - th_list[0] # if not is_forward_angle(n_list[-1], th_list[-1]): # th_list[-1] = pi - th_list[-1] # kz is the z-component of (complex) angular wavevector for forward-moving # wave. Positive imaginary part means decaying. # kz_list.shape = (NLAYERS, NUMPOINTS) kz_list = 2 * np.pi * n_list[:, None] * np.cos(th_list) / lam_vac if num_layers > 2: # delta is the total phase accrued by traveling through a given layer. # don't work it out for the fronting/backing media # delta.shape = (NLAYERS - 2, NUMPNTS) delta = kz_list[1:-1] * d_list[1:-1, None] # For a very opaque layer, reset delta to avoid divide-by-0 and similar # errors. The criterion imag(delta) > 35 corresponds to single-pass # transmission < 1e-30 --- small enough that the exact value doesn't # matter. if (np.imag(delta) > 35).any(): np.clip(delta.imag, -np.inf, 35, out=delta.imag) results = {} polarisations = [ ["s", interface_r_s, interface_t_s], ["p", interface_r_p, interface_t_p], ] for pol, interface_r, interface_t in polarisations: # t_list and r_list are transmission and reflection amplitudes, # respectively, coming from i, going to j. # r_list.shape = (NLAYERS - 1, NUMPOINTS) r_list = interface_r(n_list[:-1, None], n_list[1:, None], th_list[:-1], th_list[1:]) t_list = interface_t(n_list[:-1, None], n_list[1:, None], th_list[:-1], th_list[1:]) # At the interface between the (n-1)st and nth material, let v_n be the # amplitude of the wave on the nth side heading forwards (away from the # boundary), and let w_n be the amplitude on the nth side heading backwards # (towards the boundary). Then (v_n,w_n) = M_n (v_{n+1},w_{n+1}). M_n is # M_list[n]. M_0 and M_{num_layers-1} are not defined. # My M is a bit different than Sernelius's, but Mtilde is the same. if num_layers > 2: # calculate the characteristic matrices for all the layers beta = np.exp(1j * delta) beta_inv = 1 / beta # M_list00.shape = (NUM_LAYERS - 2, NUMPOINTS) M_list00 = beta_inv / t_list[1:] M_list01 = r_list[1:] * beta_inv / t_list[1:] M_list10 = r_list[1:] * beta / t_list[1:] M_list11 = beta / t_list[1:] # initial interface # Mtilde00.shape = (NUMPOINTS,) Mtilde00 = 1 / t_list[0] Mtilde01 = r_list[0] / t_list[0] Mtilde10 = Mtilde01 Mtilde11 = Mtilde00 # propagate characteristic matrices for i in range(0, num_layers - 2): # matrix multiply Mtilde by characteristic matrix p00 = Mtilde00 * M_list00[i, :] + Mtilde01 * M_list10[i, :] p01 = Mtilde00 * M_list01[i, :] + Mtilde01 * M_list11[i, :] p10 = Mtilde10 * M_list00[i, :] + Mtilde11 * M_list10[i, :] p11 = Mtilde10 * M_list01[i, :] + Mtilde11 * M_list11[i, :] Mtilde00 = p00 Mtilde01 = p01 Mtilde10 = p10 Mtilde11 = p11 # Net complex transmission and reflection amplitudes r = Mtilde10 / Mtilde00 t = 1 / Mtilde00 results[f"r_{pol}"] = r results[f"t_{pol}"] = t return results
def compute_cost(n: int, lam: float, dE: float, L: int, Lxi: int, chi: int, beta: int, stps: int, verbose: bool = False) -> Tuple[int, int, int]: """ Determine fault-tolerant costs using DF decomposition in quantum chem Args: n (int) - the number of spin-orbitals lam (float) - the lambda-value for the Hamiltonian dE (float) - allowable error in phase estimation L (int) - the rank of the first decomposition Lxi (int) - the total number of eigenvectors chi (int) - equivalent to aleph_1 and aleph_2 in the document, the number of bits for the representation of the coefficients beta (int) - equivalent to beth in the document, the number of bits for the rotations stps (int) - an approximate number of steps to choose the precision of single qubit rotations in preparation of the equal superpositn state verbose (bool) - do additional printing of intermediates? Returns: step_cost (int) - Toffolis per step total_cost (int) - Total number of Toffolis ancilla_cost (int) - Total ancilla cost """ # The number of bits used for the second register. nxi = np.ceil(np.log2(n // 2)) # The number of bits for the contiguous register. nLxi = np.ceil(np.log2(Lxi + n // 2)) # The number of bits used for the first register. nL = np.ceil(np.log2(L + 1)) # The power of 2 that is a factor of L + 1 eta = power_two(L + 1) oh = [0] * 20 for p in range(20): # JJG note: arccos arg may be > 1 v = np.round(np.power(2,p+1) / (2 * np.pi) * arccos(np.power(2,nL) /\ np.sqrt((L + 1)/2**eta)/2)) oh[p] = np.real(stps * (1 / (np.sin(3 * arcsin(np.cos(v * 2 * np.pi / \ np.power(2,p+1)) * \ np.sqrt((L + 1)/2**eta) / np.power(2,nL)))**2) - 1) + 4 * (p + 1)) # Bits of precision for rotation br = int(np.argmin(oh) + 1) # The following costs are from the list starting on page 50. # The cost for preparing an equal superposition for preparing the first # register in step 1 (a). We double this cost to account for the inverse. cost1a = 2 * (3 * nL + 2 * br - 3 * eta - 9) # The output size for the QROM for the first state preparation in Eq. (C27) bp1 = nL + chi # The cost of the QROM for the first state preparation in step 1 (b) and # its inverse. cost1b = QR(L + 1, bp1)[1] + QI(L + 1)[1] # The cost for the inequality test, controlled swap and their inverse in # steps 1 (c) and (d) cost1cd = 2 * (chi + nL) # The total cost for preparing the first register in step 1. cost1 = cost1a + cost1b + cost1cd # The output size for the QROM for the data to prepare the equal # superposition on the second register, as given in Eq. (C29). bo = nxi + nLxi + br + 1 # This is step 2. This is the cost of outputting the data to prepare the # equal superposition on the second register. We will assume it is not # uncomputed, because we want to keep the offset for applying the QROM for # outputting the rotations. cost2 = QR(L + 1, bo)[1] + QI(L + 1)[1] # The number of bits for rotating the ancilla for the second preparation. # We are just entering this manually because it is a typical value. br = 7 # The cost of preparing an equal superposition over the second register in # a controlled way. We pay this cost 4 times. cost3a = 4 * (7 * nxi + 2 * br - 6) # The cost of the offset to apply the QROM for state preparation on the # second register. cost3b = 4 * (nLxi - 1) bp2 = nxi + chi + 2 # The cost of the QROMs and inverse QROMs for the state preparation, where # in the first one we need + n/2 to account for the one-electron terms. cost3c = QR(Lxi + n // 2, bp2)[1] + QI(Lxi + n // 2)[1] + QR( Lxi, bp2)[1] + QI(Lxi)[1] # The inequality test and state preparations. cost3d = 4 * (nxi + chi) # The total costs for state preparations on register 2. cost3 = cost3a + cost3b + cost3c + cost3d # The cost of adding offsets in steps 4 (a) and (h). cost4ah = 4 * (nLxi - 1) # The costs of the QROMs and their inverses in steps 4 (b) and (g). cost4bg = QR(Lxi + n // 2, n * beta // 2)[1] + QI(Lxi + n // 2)[1] + QR( Lxi, n * beta // 2)[1] + QI(Lxi)[1] # The cost of the controlled swaps based on the spin qubit in steps 4c and f cost4cf = 2 * n # The controlled rotations in steps 4 (d) and (f). cost4df = 4 * n * (beta - 2) # The controlled Z operations in the middle for step 4 (e). cost4e = 3 # This is the cost of the controlled rotations for step 4. cost4 = cost4ah + cost4bg + cost4cf + cost4df + cost4e # This is the cost of the reflection on the second register from step 6. cost6 = nxi + chi + 2 # The cost of the final reflection req'd to construct the step of the # quantum walk from step 9. cost9 = nL + nxi + chi + 1 # The extra two qubits for unary iteration and making the rflxn controlled. cost10 = 2 # The Toffoli cost for a single step cost = cost1 + cost2 + cost3 + cost4 + cost6 + cost9 + cost10 # The number of steps needed iters = np.ceil(np.pi * lam / (2 * dE)) # Now the number of qubits from the list on page 54. k1 = np.power(2, QR(Lxi + n // 2, n * beta // 2)[0]) # The control register for phase estimation and iteration on it. ac1 = np.ceil(np.log2(iters + 1)) * 2 - 1 # The system qubits ac2 = n # The first register prepared, a rotated qubit and a flag qubit. ac3 = nL + 2 # The output of the QROM, the equal superposition state and a flag qubit. ac4 = nL + chi * 2 + 1 # The data used for preparing the equal superposition state on the 2nd reg ac5 = bo # The second register, a rotated qubit and a flag qubit. ac6 = nxi + 2 # The second preparation QROM output. ac8 = bp2 # The equal superposition state and the result of the inequality test. ac9 = chi + 1 # The angles for rotations. ac10 = k1 * n * beta // 2 # The phase gradient state. ac11 = beta # A control qubit for the spin. ac12 = 1 # A T state. ac13 = 1 if verbose: print("[*] Top of routine") print(" [+] nxi = ", nxi) print(" [+] nLxi = ", nLxi) print(" [+] nL = ", nL) print(" [+] eta = ", eta) print(" [+] cost3 = ", cost3) print(" [+] cost4 = ", cost4) print(" [+] cost = ", cost) print(" [+] iters = ", iters) ancilla_cost = ac1 + ac2 + ac3 + ac4 + ac5 + ac6 + ac8 + ac9 + ac10 + ac11\ + ac12 + ac13 # Sanity checks before returning as int assert cost.is_integer() assert iters.is_integer() assert ancilla_cost.is_integer() step_cost = int(cost) total_cost = int(cost * iters) ancilla_cost = int(ancilla_cost) return step_cost, total_cost, ancilla_cost
def convlim(Ss, Vs, Vc, Ztf, Bf, Zc, Icmax, Vcmax, Vcmin, convi, epslim, printopt): """ Check for converter operation within ac voltage and current limits. Converter operation checked with the converter's PQ capability diagram that are calculated based on the converter limits and grid operation state. @author:Jef Beerten (KU Leuven) @author:Roni Irnawan (Aalborg University) """ ## voltage limits order check if Vcmax<Vcmin: stderr.write('Vcmin is larger than Vcmax for converter %d\n'%convi) elif Vcmax==Vcmin: stderr.write('Vcmin is equal to Vcmax for converter %d\n'%convi) Ssold = Ss.copy() ## define voltage magnitudes, angles, powers Vsm = abs(Vs) Vsa = angle(Vs) Vcm = abs(Vc) Vca = angle(Vc) Ps = real(Ss) Qs = imag(Ss) ##--- initialization --- ## load existing parameters Zf = 1/(j*(Bf + eps)) Ytf = 1/(Ztf + eps) ## avoid division by zero. If Ztf=0, Ytf=inf is not used Yf = j*(Bf + eps) Yc = 1/(Zc + eps) ## pi-equivalent parameters of the converter station (voltage limits) # Implementation based on: # J. Beerten, S. Cole and R. Belmans: "Generalized Steady-State VSC MTDC # Model for Sequential AC/DC Power Flow Algorithms", IEEE Trans. Pow. # Syst., vol. 27, no. 2, 2012, pp. 821 - 829. # ====== # --------| Z2 |--------- # || ====== || # ==== ==== # | | | | # | Z1 | | Z3 | # | | | | # ==== ==== # || || # # Expressions for Z3 and Y3 are not included, since not used in program if Ztf!=0 and Bf!=0: Z1 = (Ztf*Zc+Zc*Zf+Zf*Ztf)/Zc Z2 = (Ztf*Zc+Zc*Zf+Zf*Ztf)/Zf elif Ztf==0 and Bf!=0: Z1 = Zf Z2 = Zc elif Ztf!= 0 and Bf==0: Z1 = inf Z2 = Ztf+Zc else: ## Ztf == 0 && Bf ==0 Z1 = inf Z2 = Zc Y1 = 1/Z1 Y2 = 1/Z2 G2 = real(Y2) B2 = imag(Y2) Y12 = Y1 + Y2 G12 = real(Y12) B12 = imag(Y12) ##--- voltage and current limit parameters ---- ## maximum current limit circle parameters MPL1 = -Vsm**2*(1/(conj(Zf) + conj(Ztf)))*(Bf!=0)\ + (0+j*0)*(Bf==0) ## center of the current limit rL1 = Vsm*Icmax*(\ (Ztf!=0)*(abs(conj(Ytf)/(conj(Yf) + conj(Ytf))))\ + (Ztf==0)*1) ## radius of current limit circle ## maximum and minimal active power on current limit PmaxL1 = real(MPL1) + rL1 PminL1 = real(MPL1) - rL1 QPmaxL1 = imag(MPL1) QPminL1 = imag(MPL1) ## minimum and maximum voltage limit circle parameters MPL2 = -Vsm**2*conj(Y1+Y2) ## center of the voltage limits rL2 = Vsm*r_[Vcmin,Vcmax]*abs(Y2) ## radius of voltage limits ##--- voltage limit compliance of min/max active power point --- ## find intersection points of Vcmin/Vcmax and current limit circles dL12 = sqrt((real(MPL2)-real(MPL1))**2+(imag(MPL2)-imag(MPL1))**2) alpha = arctan((imag(MPL2)-imag(MPL1))/(real(MPL2)-real(MPL1))) beta = arccos(((dL12**2+rL1**2)*ones((1,2))-rL2**2)/(2*dL12*rL1)) delta = arccos(((dL12**2-rL1**2)*ones((1,2))+rL2**2)/(2*dL12*rL2)) gamma = c_[alpha-beta,pi-alpha-beta] eta = c_[alpha+delta,alpha-delta] ## possible intersection points (current limit) => intersection vector x1 = zeros((2,gamma.shape[1]),dtype=complex) y1 = zeros((2,gamma.shape[1]),dtype=complex) x1[0,:] = real(MPL1) + rL1*cos(gamma) x1[1,:] = real(MPL1) - rL1*cos(gamma) y1[0,:] = imag(MPL1) + rL1*sin(gamma) y1[1,:] = imag(MPL1) - rL1*sin(gamma) ## possible intersection points (voltage limits) => intersection vector x2 = zeros((2,gamma.shape[1]),dtype=complex) y2 = zeros((2,gamma.shape[1]),dtype=complex) x2[0,:] = real(MPL2) + r_[rL2,rL2]*cos(eta); x2[1,:] = real(MPL2) - r_[rL2,rL2]*cos(eta); y2[0,:] = imag(MPL2) + r_[rL2,rL2]*sin(eta); y2[1,:] = imag(MPL2) - r_[rL2,rL2]*sin(eta); ## vectorize intersection point matrices x1v = x1.reshape((x1.size,1),order='F') x2v = x2.reshape((x2.size,1),order='F') y1v = y1.reshape((y1.size,1),order='F') y2v = y2.reshape((y2.size,1),order='F') ## decrease accuracy to detect the intersection points eps2 = 1e-8; x1r = round(x1v/eps2)*eps2; x2r = round(x2v/eps2)*eps2; y1r = round(y1v/eps2)*eps2; y2r = round(y2v/eps2)*eps2; ## corresponding elements in intersections vectors x12r = intersect1d(x1r,x2r) x1i = array([idx for y in x12r for idx,x in enumerate(x1r) if x==y]) x2i = array([idx for y in x12r for idx,x in enumerate(x2r) if x==y]) y12r = intersect1d(y1r,y2r) y1i = array([idx for y in y12r for idx,x in enumerate(y1r) if x==y]) y2i = array([idx for y in y12r for idx,x in enumerate(y2r) if x==y]) x12r1 = c_[x1i,x12r] x12r1 = x12r1[x12r1[:,0].argsort()] x12r2 = c_[x2i,x12r] x12r2 = x12r2[x12r2[:,0].argsort()] y12r1 = c_[y1i,y12r] y12r1 = y12r1[y12r1[:,0].argsort()] y12r2 = c_[y2i,y12r] y12r2 = y12r2[y12r2[:,0].argsort()] ## define intersection points (full accuracy) VcminPQ1 = x1v[int(real(x12r1[0,0]))] + j*y1v[int(real(y12r1[0,0]))] VcminPQ2 = x1v[int(real(x12r1[2,0]))] + j*y1v[int(real(y12r1[2,0]))] VcmaxPQ1 = x1v[int(real(x12r1[1,0]))] + j*y1v[int(real(y12r1[1,0]))] VcmaxPQ2 = x1v[int(real(x12r1[3,0]))] + j*y1v[int(real(y12r1[3,0]))] ## Remove imaginary intersection points (no intersections, due to low/high voltage limits) if isreal(x12r2[0,1])==0 or isreal(y12r2[0,1])==0 or isreal(x12r2[2,1])==0 \ or isreal(y12r2[2,1])==0 : VcminPQ1 = array([]) ## Imaginary intersection points are found in pairs VcminPQ2 = array([]) if printopt == 1 : stdout.write('\n Lower voltage limit at converter %d : No intersections with current limit were found.\n'%convi) if isreal(x12r2[1,1])==0 or isreal(y12r2[1,1])==0 or isreal(x12r2[3,1])==0 \ or isreal(y12r2[3,1])==0 : VcmaxPQ1 = array([]) VcmaxPQ2 = array([]) if printopt == 1 : stdout.write('\n Upper voltage limit at converter %d : No intersections with current limit were found.\n'%convi ) ## Define maximum and minimum power points # Initialisation Pmin = PminL1 QPmin = QPminL1 Pmax = PmaxL1 QPmax = QPmaxL1 # Redefine max and min power points if min/max voltage limits are high/low if not VcminPQ1.size == 0 or not VcminPQ2.size == 0 : if printopt == 1 and (imag(VcminPQ1) > QPminL1 or imag(VcminPQ2) > QPmaxL1) : stdout.write('\n High lower voltage limit detected at converter %d. \n'% convi) if imag(VcminPQ1) > QPminL1 : Pmin = real(VcminPQ1) QPmin = imag(VcminPQ1) if imag(VcminPQ2) > QPmaxL1: Pmax = real(VcminPQ2) QPmax = imag(VcminPQ2) if not VcmaxPQ1.size == 0 or not VcmaxPQ2.size == 0 : if printopt == 1 and (imag(VcmaxPQ1) < QPminL1 or imag(VcmaxPQ2) < QPmaxL1): stdout.write('\n Low upper voltage limit detected at converter %d. \n '% convi) if imag(VcmaxPQ1) < QPminL1: Pmin = real(VcmaxPQ1) QPmin = imag(VcmaxPQ1) if imag(VcmaxPQ2) < QPmaxL1: Pmax = real(VcmaxPQ2) QPmax = imag(VcmaxPQ2) ##--- Limit check --- if Pmin < Ps and Ps < Pmax: ## maximum current limit (L1) if imag(MPL1)<Qs : Qs1 = imag(MPL1) + sqrt(rL1**2-(Ps-real(MPL1))**2) else: ## if Qs<imag(MPL1) Qs1 = imag(MPL1) - sqrt(rL1**2-(Ps-real(MPL1))**2) ## minimum and maximum voltage limits (L2) Qs2 = imag(MPL2)+sqrt(rL2**2-(Ps-real(MPL2))**2) a = 1+(B2/G2)**2*ones((1,2)) b = -2*B2/G2*(Ps+Vsm**2*G12)/(Vsm*r_[Vcmin,Vcmax]*G2) c = ((Ps+Vsm**2*G12)/(Vsm*r_[Vcmin,Vcmax]*G2))**2-1 ## only positive solution retained (neg. solution refers to lower part) sinDd = (-b + sqrt(b**2-4*a*c))/(2*a) Dd = arcsin(sinDd) cosDd = cos(Dd) Qs2 = Vsm**2*B12+Vsm*r_[Vcmin,Vcmax]*(G2*sinDd-B2*cosDd); ## adopt working point to limits if Qs > imag(MPL1) : if Qs > r_[Qs1,Qs2[:,1]].min() : convlimviol = 1 Qs = r_[Qs1,Qs2[:,1]].min() elif Qs < Qs2[:,0] : convlimviol = 1 Qs = Qs2[:,0] else: ## Qs < min([Qs1 Qs2[1]]) convlimviol = 0 else: ## Qs < imag(MPL1) if Qs < r_[Qs1,Qs2[:,0]].max(): convlimviol = 1 Qs = r_[Qs1,Qs2[:,0]].max() elif Qs > Qs2[:,1] : convlimviol = 1 Qs = Qs2[:,1] else: ## Qs < min([Qs1 Qs2[1]]) convlimviol = 0 ## active power outside of current limit active power range elif Ps <= Pmin: ## grid injected active power lower than minimum value ## set active power to minimum convlimviol = 2 Ps = Pmin Qs = QPmin else: ## if PmaxL1 <= Ps %% grid injected active power higher than maximum value ## set active power to maximum convlimviol = 2 Ps = Pmax Qs = QPmax ## define output argument Ss Ss = Ps + j*Qs ## remove violation when difference is small if abs(Ssold-Ss)<epslim: convlimviol = 0 ## define plot arguments if convlimviol == 1: SsIcmax = Ps+j*Qs1 SsVcmin = Ps+j*Qs2.min() SsVcmax = Ps+j*Qs2.max() plotarg = r_[convlimviol, Ztf, Zf, Zc, Y1, Y2, Yf, Ytf, Vs, Icmax,\ Vcmax, Vcmin, Ssold, Ss, SsIcmax, SsVcmax, SsVcmin] else: plotarg = r_[convlimviol, Ztf, Zf, Zc, Y1, Y2, Yf, Ytf, Vs, Icmax,\ Vcmax, Vcmin, Ssold, Ss, 0, 0, 0]; return convlimviol, Ss, plotarg
def f(x=(0,2)): print(np.abs(sc.arcsin(x)))
"""`numpy.lib.scimath` imported as `sc` here, is from `sc?`: > Wrapper functions to more user-friendly calling of certain math functions whose output data-type is different than the input data-type in certain domains of the input. Function with some parts of its domain in the complex plane like, `sqrt`, `log`, `log2`, `logn`, `log10`, `power`, `arccos`, `arcsin`, and `arctanh`. """ np.lib.scimath.arcsin(2) import ipywidgets as widgets @widgets.interact def f(x=(0,2)): print(np.abs(sc.arcsin(x))) sc.arcsin([2,3]) """# Lists, Tuples and Dictionaries ## Lists Lists are useful when you want to store and manipulate a set of elements (even of different types). """ #A list is declared using [] and may content different type of objects lista = ["abc", 42, 3.1415] lista #First element of the list lista[0]
def compute_cost(n: int, lam: float, dE: float, L: int, chi: int, stps: int, verbose: bool = False) -> Tuple[int, int, int]: """ Determine fault-tolerant costs using SF decomposition in quantum chem Args: n (int) - the number of spin-orbitals lam (float) - the lambda-value for the Hamiltonian dE (float) - allowable error in phase estimation L (int) - the rank of the first decomposition chi (int) - equivalent to aleph_1 and aleph_2 in the document, the number of bits for the representation of the coefficients stps (int) - an approximate number of steps to choose the precision of single qubit rotations in preparation of the equal superposn state verbose (bool) - do additional printing of intermediates? Returns: step_cost (int) - Toffolis per step total_cost (int) - Total number of Toffolis total_qubit_count (int) - Total qubit count """ # Number of trailing zeros eta = power_two(L + 1) # Number of qubits for the first register nL = np.ceil(np.log2(L + 1)) # Number of qubits for p and q registers nN = np.ceil(np.log2(n // 2)) oh = [0] * 20 for p in range(20): # JJG note: arccos arg may be > 1 v = np.round(np.power(2,p+1) / (2 * np.pi) * arccos(np.power(2,nL) /\ np.sqrt((L + 1)/2**eta)/2)) oh[p] = np.real(stps * (1 / (np.sin(3 * arcsin(np.cos(v * 2 * np.pi / \ np.power(2,p+1)) * \ np.sqrt((L + 1)/2**eta) / np.power(2,nL)))**2) - 1) + 4 * (p + 1)) # Bits of precision for rotation br = int(np.argmin(oh) + 1) # The following costs are given in the list of steps on pages 43 and 44. # Cost of preparing the equal superposition state on the 1st register in 1a cost1a = 2 * (3 * nL - 3 * eta + 2 * br - 9) # We have added two qubits bL = nL + chi + 2 # QROM costs for first register preparation in step 1(b) cost1b = QR(L + 1, bL)[-1] + QI(L + 1)[-1] # The inequality test of cost chi in step 1(c) and the controlled swap with # cost nL + 1 in step 1(d) (and their inversions). cost1cd = 2 * (chi + nL + 1) oh = [0] * 20 nprime = int(n**2 // 8 + n // 4) for p in range(20): v = np.round( np.power(2, p + 1) / (2 * np.pi) * arccos(np.power(2, 2 * nN) / np.sqrt(nprime) / 2)) oh[p] = np.real(20000 * (1 / (np.sin(3 * arcsin(np.cos(v * 2 * np.pi / \ np.power(2, p+1)) * \ np.sqrt(nprime) / np.power(2,2*nN)))**2) - 1) + 4 * (p + 1)) # Bits of precision for rotation for preparing the next equal # superposition states br = int(np.argmin(oh) + 1) # Cost of preparing equal superposition of p and q registers in step 2(a). cost2a = 4 * (6 * nN + 2 * br - 7) # Cost of computing contiguous register in step 2 (b). cost2b = 4 * (nN**2 + nN - 1) # Number of coefficients for first state preparation on p & q. n1 = (L + 1) * nprime # Number of coefficients for second state preparation on p & q. n2 = L * nprime # The output size for the QROM for the second state preparation. bp = int(2 * nN + chi + 2) # Cost of QROMs for state preparation on p and q in step 2 (c). cost2c = QR2(L + 1, nprime, bp)[-1] + QI(n1)[-1] + QR2(L, nprime, bp)[-1] + QI(n2)[-1] # The cost of the inequality test and controlled swap for the quantum alias # sampling in steps 2 (d) and (e). cost2de = 4 * (chi + 2 * nN) # Swapping the p & q registers for symmetry in step 3. This needs to be # done and inverted twice, hence the factor of 4. cost3 = 4 * nN # The SELECT operation in step 4, which needs to be done twice. cost4 = 4 * n - 8 # The reflection used on the 2nd register in the middle, with cost in step 6 cost6 = 2 * nN + chi + 3 # Step 7 involves performing steps 2 to 5 again, which are accounted for # above, but there is one extra Toffoli for checking that l <> 0. cost7 = 1 # The total reflection for the quantum walk in step 9. cost9 = nL + 2 * nN + 2 * chi + 2 # The two Toffolis for the control for phase estimation and iteration, # given in step 10. cost10 = 2 # The number of steps. iters = np.ceil(np.pi * lam / (dE * 2)) # The total Toffoli costs for a step. cost = cost1a + cost1b + cost1cd + cost2a + cost2b + cost2c + cost2de + \ cost3 + cost4 + cost6 + cost7 + cost9 + cost10 # Control for phase estimation and its iteration ac1 = 2 * np.ceil(np.log2(iters)) - 1 # System qubits ac2 = n # First ell register that rotated and the flag for success ac3 = nL + 2 # State preparation on the first register ac4 = nL + 2 * chi + 3 # For p and q registers, the rotated qubit and the success flag ac5 = 2 * nN + 2 # The size of the contiguous register ac6 = np.ceil(np.log2(nprime)) kp = QR2(L + 1, nprime, bp)[:2] # The equal superposition state for the second state preparation ac7 = chi # The phase gradient state ac8 = br # The QROM on the p & q registers ac9 = kp[0] * kp[1] * bp + np.ceil(np.log2( (L + 1) / kp[0])) + np.ceil(np.log2(nprime / kp[1])) if verbose: print("[*] Top of routine") print(" [+] eta = ", eta) print(" [+] nL = ", nL) print(" [+] nN = ", nN) total_qubit_count = ac1 + ac2 + ac3 + ac4 + ac5 + ac6 + ac7 + ac8 + ac9 # Sanity checks before returning as int assert cost.is_integer() assert iters.is_integer() assert total_qubit_count.is_integer() step_cost = int(cost) total_cost = int(cost * iters) total_qubit_count = int(total_qubit_count) return step_cost, total_cost, total_qubit_count
def compute_cost(n: int, lam: float, dE: float, chi: int, beta: int, M: int, stps: int, verbose: bool = False) -> Tuple[int, int, int]: """ Determine fault-tolerant costs using THC decomposition in quantum chem Args: n (int) - the number of spin-orbitals lam (float) - the lambda-value for the Hamiltonian dE (float) - allowable error in phase estimation chi (int) - equivalent to aleph in the document, the number of bits for the representation of the coefficients beta (int) - equivalent to beth in the document, the number of bits for the rotations M (int) - the dimension for the THC decomposition stps (int) - an approximate number of steps to choose the precision of single qubit rotations in preparation of the equal superpositn state Returns: step_cost (int) - Toffolis per step total_cost (int) - Total number of Toffolis ancilla_cost (int) - Total ancilla cost """ # The number of bits used for each register. nM = np.ceil(np.log2(M + 1)) # This is the number of distinct items of data we need to output. d = M * (M + 1) // 2 + n // 2 # The number of bits used for the contiguous register. nc = np.ceil(np.log2(d)) # The output size is 2*log[M] for the alt values, chi for the keep value, # and 2 for the two sign bits, as in Eq. (29). m = 2 * nM + 2 + chi oh = [0] * 20 for p in range(20): # arccos arg may be > 1 v = np.round( np.power(2, p + 1) / (2 * np.pi) * arccos(np.power(2, nM) / np.sqrt(d) / 2)) oh[p] = stps * (1 / (np.sin(3 * arcsin(np.cos(v * 2 * np.pi / \ np.power(2,p+1)) * \ np.sqrt(d) / np.power(2,nM)))**2) - 1) + 4 * (p + 1) # Set it to be the number of bits that minimises the cost, usually 7. # Python is 0-index, so need to add the one back in vs mathematica nb br = np.argmin(oh) + 1 # This is the costing for preparing the equal superposition over the # input registers from below Eq. (27). cp1 = 2 * (10 * nM + 2 * br - 9) # This is the cost of computing the contiguous register and inverting it. # This is with a sophisticated scheme adding together triplets of bits. # This is the cost of step 2 in the list on pages 15 and 16, with a factor # of 2 to account for its inverse. cp2 = 2 * (nM**2 + nM - 1) # This is the cost of the QROM for the state preparation in step 3 and its # inverse. Note: arg_min is first value, min is second value cp3 = QR(d, m)[1] + QI(d)[1] # The cost for the inequality test in step 4 and its inverse. cp4 = 2 * chi # The cost 2*nM for the controlled swap in step 5 and its inverse. cp5 = 4 * nM # Then there is a cost of nM + 1 for swapping the mu and nu registers in # step 6, where the + 1 is because we need to control on two registers. # There is the same cost for the inverse. cp6 = 2 * nM + 2 # This is the total cost in Eq. (32). CPCP = cp1 + cp2 + cp3 + cp4 + cp5 + cp6 # This is the cost of swapping based on the spin register. It is steps 1 # and 7 from the list of steps on pages 16 and 17. cs1 = 2 * n # The QROM for the rotation angles the first time in step 2. and the second # part is the cost for inverting them with advanced QROM (step 6). cs2a = M + n / 2 - 2 # The QROM for the rotation angles the second time. Here the cost M - 2 is # for generating the angles the second time (again step 2), and QI[M] is # for inverting the QROM (step 6). cs2b = M - 2 # The cost of the rotations in steps 3 and 5, which must be done 4 times. cs3 = 4 * n * (beta - 2) # Cost for making the Z doubly controlled in step 4, and a controlled swap # of the spin qubits. Note: is this really just "2"? Does this need a var? cs4 = 2 # want the argument, so that is first element k1 = np.power(2, QI(M + n // 2)[0]) # The cost for inverting the rotation angles the first time in step 6. cs6a = np.ceil(M / k1) + np.ceil(n / 2 / k1) + k1 # The cost for inverting the rotation angles the second time in step 6. # value is in second output argument of QI cs6b = QI(M)[1] # The total select cost in Eq. (42). CS = cs1 + cs2a + cs2b + cs3 + cs4 + cs6a + cs6b # The cost of the reflections used in Eq. (43). costref = 2 * nM + chi + 4 # The total cost in Eq. (43). cost = CPCP + CS + costref # The cost of the control register and temporary registers given for part 1. ac1 = lambda iters: 2 * np.ceil(np.log2(iters + 1)) - 1 # Qubits for the system register in part 2. ac2 = n # The cost for the \[Mu] and \[Nu] registers in part 3. ac3 = 2 * nM # This is for the equal superposition state to perform the inequal # test with the keep register. ac4 = chi # Some minor qubit counts in parts 5 to 9. ac59 = 7 # The phase gradient state in part 10. ac10 = beta # The contiguous register in part 11 ac11 = nc # we want arg_min so first argument kt = np.power(2, QR(d, m))[0] # The qubits used for the QROM in part 12, of which only m are persistent. ac12 = m * kt + np.ceil(np.log2(d / kt)) # This is the data needed for the rotations in part 13. ac13 = beta * n / 2 # These are the ancillas needed for adding into the phase gradient state # in part 14. ac14 = beta - 2 # These are the temporary ancillas in between erasing the first QROM # ancillas and inverting that QROM. The + m is for output of the first QROM. acc = ac13 + ac14 + m # Total number of iterations iters = np.ceil(np.pi * lam / (dE * 2)) aca = ac1(iters) + ac2 + ac3 + ac4 + ac59 + ac10 + ac11 if verbose: print("[*] Top of routine") print(" [+] nM = ", nM) print(" [+] d = ", d) print(" [+] nc = ", nc) print(" [+] m = ", m) print(" [+] oh = ", oh) print(" [+] br = ", br) print(" [+] cp1 = ", cp1) print(" [+] cp2 = ", cp2) print(" [*] QR[d,m] = ", QR(d, m)) print(" [*] QI[d] = ", QI(d)) print(" [+] cp3 = ", cp3) print(" [+] cp4 = ", cp4) print(" [+] cp5 = ", cp5) print(" [+] cp6 = ", cp6) print(" [+] CPCP = ", CPCP) print(" [+] cs1 = ", cs1) print(" [+] cs2a = ", cs2a) print(" [+] cs2b = ", cs2b) print(" [+] cs3 = ", cs3) print(" [+] k1 = ", k1) print(" [+] cs6a = ", cs6a) print(" [+] cs6b = ", cs6b) print(" [+] CS = ", CS) print(" [+] costref = ", costref) print(" [+] cost = ", cost) print(" [+] ac1 = ", ac1) print(" [+] ac2 = ", ac2) print(" [+] ac3 = ", ac3) print(" [+] ac4 = ", ac4) print(" [+] ac10 = ", ac10) print(" [+] ac11 = ", ac11) print(" [+] kt = ", kt) print(" [+] ac12 = ", ac12) print(" [+] ac13 = ", ac13) print(" [+] ac14 = ", ac14) print(" [+] acc = ", acc) print(" [+] iters = ", iters) print(" [+] aca = ", aca) # Sanity checks before returning as int # assert cost.is_integer() # assert iters.is_integer() # assert aca.is_integer() # assert ac12.is_integer() # assert acc.is_integer() step_cost = int(cost) total_cost = int(cost * iters) ancilla_cost = int(np.max([aca + ac12, aca + acc])) # step-cost, Toffoli count, logical qubits return step_cost, total_cost, ancilla_cost