def AllXY(q: qreg): # QGL2 version of QGL2 Basic Sequence # Must be compiled & given a QRegister # 6/12/19: Verified this looks same as QGL1 version twentyOnePulsePairs = [ # no pulses to measure |0> (Id, Id), # pulse around same axis (X, X), (Y, Y), # pulse around orthogonal axes (X, Y), (Y, X), # These produce a |+> or |i> state (X90, Id), (Y90, Id), # pulse pairs around orthogonal axes with 1e error sensititivity (X90, Y90), (Y90, X90), # pulse pairs with 2e sensitivity (X90, Y), (Y90, X), (X, Y90), (Y, X90), # pulse pairs around common axis with 3e error sensitivity (X90, X), (X, X90), (Y90, Y), (Y, Y90), # These create the |1> state (X, Id), (Y, Id), (X90, X90), (Y90, Y90) ] # For each of the 21 pulse pairs for (f1, f2) in twentyOnePulsePairs: # Repeat it twice and do a MEAS at the end of each for i in range(2): init(q) f1(q) f2(q) MEAS(q)
def InversionRecovery(qubit: qreg, delays, calRepeats=2): """ Inversion recovery experiment to measure qubit T1 Parameters ---------- qubit : logical channel to implement sequence (LogicalChannel) delays : delays after inversion before measurement (iterable; seconds) calRepeats : how many repetitions of calibration pulses (int) """ # Original: # # Create the basic sequences # seqs = [[X(qubit), Id(qubit, d), MEAS(qubit)] for d in delays] # # Tack on the calibration scalings # seqs += create_cal_seqs((qubit,), calRepeats) # fileNames = compile_to_hardware(seqs, 'T1'+('_'+qubit.label)*suffix+'/T1'+('_'+qubit.label)*suffix) # print(fileNames) # if showPlot: # plot_pulse_files(fileNames) for d in delays: init(qubit) X(qubit) Id(qubit, length=d) MEAS(qubit) # Tack on calibration create_cal_seqs(qubit, calRepeats)
def RabiAmp_NQubits(qubits: qreg, amps, phase=0, measChans: qreg = None, docals=False, calRepeats=2): """ Variable amplitude Rabi nutation experiment for an arbitrary number of qubits simultaneously Parameters ---------- qubits : tuple of logical channels to implement sequence (LogicalChannel) amps : pulse amplitudes to sweep over for all qubits (iterable) phase : phase of the pulses (radians) measChans : tuple of qubits to be measured (use qubits if not specified) (LogicalChannel) docals, calRepeats: enable calibration sequences, repeated calRepeats times """ if measChans is None: measChans = qubits allChans = QRegister(qubits, measChans) for amp in amps: init(allChans) Utheta(qubits, amp=amp, phase=phase) measConcurrently(measChans) if docals: create_cal_seqs(qubits, calRepeats, measChans)
def doSingleShot(q: qreg): init(q) Id(q) MEAS(q) init(q) X(q) MEAS(q)
def doPulsedSpec(q: qreg, specOn): init(q) if specOn: X(q) else: Id(q) MEAS(q)
def doRabiWidth(q: qreg, widths): # FIXME: Note the local re-definition of tanh shapeFun = qgl2.basic_sequences.pulses.local_tanh for l in widths: init(q) Utheta(q, length=l, amp=1, phase=0, shapeFun=shapeFun) MEAS(q)
def doRabiAmpPi(qr: qreg, amps): for l in amps: init(qr) X(qr[1]) Utheta(qr[0], amp=l, phase=0) X(qr[1]) MEAS(qr[1])
def doPulsedSpec(qubit: qreg, specOn): init(qubit) if specOn: X(qubit) else: Id(qubit) MEAS(qubit)
def CRtomo_seq(controlQ: qreg, targetQ: qreg, lengths, ph, amp=0.8, riseFall=20e-9): """ Variable length CX experiment, for Hamiltonian tomography. Parameters ---------- controlQ : logical channel for the control qubit (LogicalChannel) targetQ: logical channel for the target qubit (LogicalChannel) lengths : pulse lengths of the CR pulse to sweep over (iterable) riseFall : rise/fall time of the CR pulse (s) ph : phase of the CR pulse (rad) """ # Rather than do EdgeFactory and regular flat_top_gaussian, # define a new QGL2 stub where the QGL1 implementation does that, # so QGL2 can avoid dealing with the edge # CRchan = EdgeFactory(controlQ, targetQ) # flat_top_gaussian is an addition of 3 UTheta pulses cNt = QRegister(controlQ, targetQ) tomo_pulses = [Y90m, X90, Id] # Sequence 1 for l, tomo_pulse in product(lengths, tomo_pulses): init(cNt) Id(controlQ) flat_top_gaussian_edge(controlQ, targetQ, riseFall=riseFall, length=l, amp=amp, phase=ph, label="CR") Barrier(cNt) Id(controlQ) tomo_pulse(targetQ) MEAS(targetQ) # Sequence 2 for l, tomo_pulse in product(lengths, tomo_pulses): init(cNt) X(controlQ) flat_top_gaussian_edge(controlQ, targetQ, riseFall=riseFall, length=l, amp=amp, phase=ph, label="CR") Barrier(cNt) X(controlQ) tomo_pulse(targetQ) MEAS(targetQ) create_cal_seqs(targetQ, 2)
def settle(q: qreg) -> pulse: init(q) while True: MEAS(q) if vmeas: break Id(q)
def func_a(a: qreg, b: qreg, c: qreg): with concur: for q in [a, b, c]: init(q) for x in [1, 2]: for f in [func_c, func_d]: f(a, x)
def doInversionRecovery(q:qreg, delays, calRepeats): for d in delays: init(q) X(q) Id(q, length=d) MEAS(q) # Tack on calibration create_cal_seqs(q, calRepeats)
def doRabiAmpPi(qubit: qreg, mqubit: qreg, amps, phase): for amp in amps: with concur: init(qubit) init(mqubit) X(mqubit) Utheta(qubit, amp=amp, phase=phase) X(mqubit) MEAS(mqubit)
def PiRabi(controlQ: qreg, targetQ: qreg, lengths, riseFall=40e-9, amp=1, phase=0, calRepeats=2): """ Variable length CX experiment. Parameters ---------- controlQ : logical channel for the control qubit (LogicalChannel) targetQ: logical channel for the target qubit (LogicalChannel) lengths : pulse lengths of the CR pulse to sweep over (iterable) riseFall : rise/fall time of the CR pulse (s) amp : amplitude of the CR pulse phase : phase of the CR pulse (rad) calRepeats : number repetitions of calibration sequences (int) """ # Rather than do EdgeFactory and regular flat_top_gaussian, # define a new QGL2 stub where the QGL1 implementation does that, # so QGL2 can avoid dealing with the edge # CRchan = EdgeFactory(controlQ, targetQ) # flat_top_gaussian is an addition of 3 UTheta pulses cNt = QRegister(controlQ, targetQ) # Sequence 1: Id(control), gaussian(l), measure both for l in lengths: init(cNt) Id(controlQ) flat_top_gaussian_edge(controlQ, targetQ, riseFall, length=l, amp=amp, phase=phase) measConcurrently(cNt) # Sequence 2: X(control), gaussian(l), X(control), measure both for l in lengths: init(cNt) X(controlQ) flat_top_gaussian_edge(controlQ, targetQ, riseFall, length=l, amp=amp, phase=phase) X(controlQ) measConcurrently(cNt) # Then do calRepeats calibration sequences create_cal_seqs(cNt, calRepeats)
def doSwap(qr: qreg, delays): for d in delays: init(qr) X(qr) Id(qr[1], length=d) Barrier(qr) MEAS(qr) create_cal_seqs(qr, 2)
def FlipFlop(qubit: qreg, dragParamSweep, maxNumFFs=10, showPlot=False): """ Flip-flop sequence (X90-X90m)**n to determine off-resonance or DRAG parameter optimization. Parameters ---------- qubit : logical channel to implement sequence (LogicalChannel) dragParamSweep : drag parameter values to sweep over (iterable) maxNumFFs : maximum number of flip-flop pairs to do showPlot : whether to plot (boolean) """ # Original: # def flipflop_seqs(dragScaling): # """ Helper function to create a list of sequences with a specified drag parameter. """ # qubit.pulse_params['dragScaling'] = dragScaling # return [[X90(qubit)] + [X90(qubit), X90m(qubit)]*rep + [Y90(qubit)] for rep in range(maxNumFFs)] # # Insert an identity at the start of every set to mark them off # originalScaling = qubit.pulse_params['dragScaling'] # seqs = list(chain.from_iterable([[[Id(qubit)]] + flipflop_seqs(dragParam) for dragParam in dragParamSweep])) # qubit.pulse_params['dragScaling'] = originalScaling # # Add a final pi for reference # seqs.append([X(qubit)]) # # Add the measurment block to every sequence # measBlock = MEAS(qubit) # for seq in seqs: # seq.append(measBlock) # fileNames = compile_to_hardware(seqs, 'FlipFlop/FlipFlop') # print(fileNames) # if showPlot: # plot_pulse_files(fileNames) # Insert an identity at the start of every set to mark them off # Want a result something like: # [['Id'], ['X9', 'Y9'], ['X9', 'X9', 'X9m', 'Y9'], ['X9', 'X9', 'X9m', 'X9', 'X9m', 'Y9'], ['Id'], ['X9', 'Y9'], ['X9', 'X9', 'X9m', 'Y9'], ['X9', 'X9', 'X9m', 'X9', 'X9m', 'Y9'], ['Id'], ['X9', 'Y9'], ['X9', 'X9', 'X9m', 'Y9'], ['X9', 'X9', 'X9m', 'X9', 'X9m', 'Y9']] originalScaling = qubit.pulse_params['dragScaling'] for dragParam in dragParamSweep: init(qubit) Id(qubit) MEAS(qubit) # FIXME: Need original dragScaling? # FIXME: In original this was [[Id]] + flipflop - is this # right? flipflop_seqs(dragParam, maxNumFFs, qubit) qubit.pulse_params['dragScaling'] = originalScaling # Add a final pi for reference init(qubit) X(qubit) MEAS(qubit)
def FlipFlop(qubit: qreg, dragParamSweep, maxNumFFs=10): """ Flip-flop sequence (X90-X90m)**n to determine off-resonance or DRAG parameter optimization. Parameters ---------- qubit : logical channel to implement sequence (LogicalChannel) dragParamSweep : drag parameter values to sweep over (iterable) maxNumFFs : maximum number of flip-flop pairs to do """ # Original: # def flipflop_seqs(dragScaling): # """ Helper function to create a list of sequences with a specified drag parameter. """ # qubit.pulse_params['dragScaling'] = dragScaling # return [[X90(qubit)] + [X90(qubit), X90m(qubit)]*rep + [Y90(qubit)] for rep in range(maxNumFFs)] # # Insert an identity at the start of every set to mark them off # originalScaling = qubit.pulse_params['dragScaling'] # seqs = list(chain.from_iterable([[[Id(qubit)]] + flipflop_seqs(dragParam) for dragParam in dragParamSweep])) # qubit.pulse_params['dragScaling'] = originalScaling # # Add a final pi for reference # seqs.append([X(qubit)]) # # Add the measurment block to every sequence # measBlock = MEAS(qubit) # for seq in seqs: # seq.append(measBlock) # fileNames = compile_to_hardware(seqs, 'FlipFlop/FlipFlop') # print(fileNames) # if showPlot: # plot_pulse_files(fileNames) # Insert an identity at the start of every set to mark them off # Want a result something like: # [['Id'], ['X9', 'Y9'], ['X9', 'X9', 'X9m', 'Y9'], ['X9', 'X9', 'X9m', 'X9', 'X9m', 'Y9'], ['Id'], ['X9', 'Y9'], ['X9', 'X9', 'X9m', 'Y9'], ['X9', 'X9', 'X9m', 'X9', 'X9m', 'Y9'], ['Id'], ['X9', 'Y9'], ['X9', 'X9', 'X9m', 'Y9'], ['X9', 'X9', 'X9m', 'X9', 'X9m', 'Y9']] # QGL2 qubits are read only, so can't modify qubit.pulse_params[dragScaling], # Instead of modifying qubit, we'll just supply the drag param explicitly to each pulse # So no need to save this off and reset afterwards # originalScaling = qubit.pulse_params['dragScaling'] for dragParam in dragParamSweep: init(qubit) Id(qubit) MEAS(qubit) flipflop_seqs(dragParam, maxNumFFs, qubit) # qubit.pulse_params['dragScaling'] = originalScaling # Add a final pi for reference init(qubit) X(qubit) MEAS(qubit)
def PulsedSpec(qubit: qreg, specOn=True): """ Measurement preceded by a X pulse if specOn """ init(qubit) if specOn: X(qubit) else: Id(qubit) MEAS(qubit)
def SingleShot(qubit: qreg): """ 2-segment sequence with qubit prepared in |0> and |1>, useful for single-shot fidelity measurements and kernel calibration """ init(qubit) Id(qubit) MEAS(qubit) init(qubit) X(qubit) MEAS(qubit)
def doRabiAmp_NQubits(qr: qreg, amps, docals, calRepeats): p = 0 for a in amps: init(qr) Utheta(qr, amp=a, phase=p) MEAS(qr) if docals: create_cal_seqs(qr, calRepeats)
def doAllXY(q:qreg): # For each of the 21 pulse pairs for func in [IdId, XX, YY, XY, YX, X90Id, Y90Id, X90Y90, Y90X90, X90Y, Y90X, XY90, YX90, X90X, XX90, Y90Y, YY90, XId, YId, X90X90, Y90Y90]: # Repeat it twice and do a MEAS at the end of each for i in range(2): init(q) func(q) MEAS(q)
def edgeTest3(): q1 = QRegister('q1') q2 = QRegister('q2') for q in [q1, q2]: init(q) echoCR(q1, q2) X(q2) Y(q2) Id(q2) X(q2)
def SimultaneousRB_AC(qubits: qreg, seqs, add_cals=True): """ Simultaneous randomized benchmarking on multiple qubits using atomic Clifford pulses. Parameters ---------- qubits : QRegister of logical channels to implement seqs on seqs : a tuple of sequences created for each qubit in qubits Example ------- >>> q1 = QubitFactory('q1') >>> q2 = QubitFactory('q2') >>> seqs1 = create_RB_seqs(1, [2, 4, 8, 16]) >>> seqs2 = create_RB_seqs(1, [2, 4, 8, 16]) >>> qr = QRegister(q1, q2) >>> SimultaneousRB_AC(qr, (seqs1, seqs2)) """ # Original: # seqsBis = [] # for seq in zip(*seqs): # seqsBis.append([reduce(operator.__mul__, [AC(q,c) for q,c in zip(qubits, # pulseNums)]) for pulseNums in zip(*seq)]) # # Add the measurement to all sequences # for seq in seqsBis: # seq.append(reduce(operator.mul, [MEAS(q) for q in qubits])) # axis_descriptor = [{ # 'name': 'length', # 'unit': None, # 'points': list(map(len, seqs)), # 'partition': 1 # }] # # Tack on the calibration sequences # if add_cals: # seqsBis += create_cal_seqs((qubits), 2) # axis_descriptor.append(cal_descriptor((qubits), 2)) # metafile = compile_to_hardware(seqsBis, 'RB/RB', axis_descriptor = axis_descriptor, extra_meta = {'sequences':seqs}) for seq in zip(*seqs): # Start sequence init(qubits) for pulseNums in zip(*seq): Barrier(qubits) for q, c in zip(qubits, pulseNums): AC(q, c) # Measure at end of each sequence measConcurrently(qubits) if add_cals: # Tack on calibration create_cal_seqs(qubits, 2)
def SingleQubitRB_AC(qubit: qreg, seqs, purity=False, add_cals=True): """ Single qubit randomized benchmarking using atomic Clifford pulses. Parameters ---------- qubit : logical channel to implement sequence (LogicalChannel) seqFile : file containing sequence strings """ # Original: # seqsBis = [] # op = [Id(qubit, length=0), Y90m(qubit), X90(qubit)] # for ct in range(3 if purity else 1): # for seq in seqs: # seqsBis.append([AC(qubit, c) for c in seq]) # #append tomography pulse to measure purity # seqsBis[-1].append(op[ct]) # #append measurement # seqsBis[-1].append(MEAS(qubit)) # # Tack on the calibration sequences # if add_cals: # seqsBis += create_cal_seqs((qubit,), 2) # axis_descriptor = [{ # 'name': 'length', # 'unit': None, # 'points': list(map(len, seqs)), # 'partition': 1 # }] # metafile = compile_to_hardware(seqsBis, 'RB/RB', axis_descriptor = axis_descriptor, extra_meta = {'sequences':seqs}) # AC() gives a single pulse on qubit op = [Id, Y90m, X90] for ct in range(3 if purity else 1): for seq in seqs: init(qubit) for c in seq: AC(qubit, c) # append tomography pulse to measure purity # See issue #: 53 func = op[ct] if ct == 0: func(qubit, length=0) else: func(qubit) # append measurement MEAS(qubit) if add_cals: # Tack on calibration sequences create_cal_seqs(qubit, 2)
def SimultaneousRB_AC(qubits: qreg, seqs, showPlot=False): """ Simultaneous randomized benchmarking on multiple qubits using atomic Clifford pulses. Parameters ---------- qubits : iterable of logical channels to implement seqs on (list or tuple) seqs : a tuple of sequences created for each qubit in qubits showPlot : whether to plot (boolean) Example ------- >>> q1 = QubitFactory('q1') >>> q2 = QubitFactory('q2') >>> seqs1 = create_RB_seqs(1, [2, 4, 8, 16]) >>> seqs2 = create_RB_seqs(1, [2, 4, 8, 16]) >>> SimultaneousRB_AC((q1, q2), (seqs1, seqs2), showPlot=False) """ # Original: # seqsBis = [] # for seq in zip(*seqs): # seqsBis.append([reduce(operator.__mul__, [AC(q,c) for q,c in zip(qubits, # pulseNums)]) for pulseNums in zip(*seq)]) # # Add the measurement to all sequences # for seq in seqsBis: # seq.append(reduce(operator.mul, [MEAS(q) for q in qubits])) # # Tack on the calibration sequences # seqsBis += create_cal_seqs((qubits), 2) # fileNames = compile_to_hardware(seqsBis, 'RB/RB') # print(fileNames) # if showPlot: # plot_pulse_files(fileNames) for seq in zip(*seqs): # Start sequence with concur: for q in qubits: init(q) for pulseNums in zip(*seq): with concur: for q, c in zip(qubits, pulseNums): AC(q, c) # Measure at end of each sequence with concur: for q in qubits: MEAS(q) # FIXME: Not working in QGL2 yet # Tack on calibration create_cal_seqs((qubits), 2)
def spam_seqs(angle, q: qreg, maxSpamBlocks=10): for rep in range(maxSpamBlocks): init(q) Y90(q) for _ in range(rep): X(q) U(q, phase=pi / 2 + angle) X(q) U(q, phase=pi / 2 + angle) X90(q) MEAS(q)
def SingleShotNoArg(): """ Sample 0-argument 2-segment sequence with qubit prepared in |0> and |1>, useful for single-shot fidelity measurements and kernel calibration """ qubit = QRegister(1) init(qubit) Id(qubit) MEAS(qubit) init(qubit) X(qubit) MEAS(qubit)
def doRabiAmp_NQubits(qubits: qreg, amps, phase, measChans: qreg, docals, calRepeats): for amp in amps: with concur: for q in qubits: init(q) Utheta(q, amp=amp, phase=phase) with concur: for m in measChans: MEAS(m) if docals: create_cal_seqs(qubits, calRepeats, measChans)
def doAllXY2(q:qreg): # one layer of indirection on "func" in the loop below twentyOnepulseFuncs = [IdId, XX, YY, XY, YX, X90Id, Y90Id, X90Y90, Y90X90, X90Y, Y90X, XY90, YX90, X90X, XX90, Y90Y, YY90, XId, YId, X90X90, Y90Y90] # For each of the 21 pulse pairs for func in twentyOnepulseFuncs: # Repeat it twice and do a MEAS at the end of each for i in range(2): init(q) func(q) MEAS(q)
def flipflop_seqs(dragScaling, maxNumFFs, qubit: qreg): """ Helper function to create a list of sequences with a specified drag parameter. """ # FIXME: cause qubit is a placeholder, can't access pulse_params # qubit.pulse_params['dragScaling'] = dragScaling for rep in range(maxNumFFs): init(qubit) X90(qubit, dragScaling=dragScaling) # FIXME: Original used [X90] + [X90, X90m]... is this right? for _ in range(rep): X90(qubit, dragScaling=dragScaling) X90m(qubi, dragScaling=dragScaling) Y90(qubit, dragScaling=dragScaling) MEAS(qubit) # FIXME: Need original dragScaling?