def pulsed_spec_seq(qubit_idx: int, spec_pulse_length: float, platf_cfg: str): """ Sequence for pulsed spectroscopy. Important notes: because of the way the CCL functions this sequence is made by repeating multiple "spec" pulses of 20ns back to back. As such the spec_pulse_lenght must be a multiple of 20e-9. If this is not the case the spec_pulse_length will be rounded. """ platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname="pulsed_spec_seq", nqubits=platf.get_qubit_number(), p=platf) k = Kernel("main", p=platf) nr_clocks = int(spec_pulse_length / 20e-9) for i in range(nr_clocks): # The spec pulse is a pulse that lasts 20ns, because of the way the VSM # control works. By repeating it the duration can be controlled. k.gate('spec', qubit_idx) k.measure(qubit_idx) p.add_kernel(k) with suppress_stdout(): p.compile(verbose=False) # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p
def CW_RO_sequence(qubit_idx: int, platf_cfg: str): """ A sequence that performs readout back to back without initialization. The separation of the readout triggers is done by specifying the duration of the readout parameter in the configuration file used for compilation. qubit_idx can also be a list, then the corresponding set of measurements is triggered. """ platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname="CW_RO_sequence", nqubits=platf.get_qubit_number(), p=platf) k = Kernel("main", p=platf) if not hasattr(qubit_idx, "__iter__"): qubit_idx = [qubit_idx] k.gate('wait', qubit_idx, 0) for qi in qubit_idx: k.measure(qi) k.gate('wait', qubit_idx, 0) p.add_kernel(k) with suppress_stdout(): p.compile(verbose=False) # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p
def openql_kernel_from_gatestring(gatestring, qubits: list, kernel_name:str, platf): """ Generates an openQL kernel for a pygsti gatestring. """ k = Kernel(kernel_name, p=platf) for q in qubits: k.prepz(q) for gate in gatestring: assert gate[0] == 'G' # for valid pyGSTi gatestrings if len(gate[1:]) == 1: # 1Q pygsti format e.g.,: Gi or Gy k.gate(gatemap[gate[1]], qubits[0]) elif len(gate[1:]) == 2: # 2Q pygsti format e.g.,: Gix or Gyy k.gate(gatemap[gate[1]], qubits[0]) k.gate(gatemap[gate[2]], qubits[1]) elif gate == 'Gcphase': # only two qubit gate supported k.gate(gatemap[gate[1:]], qubits[0], qubits[1]) else: raise NotImplementedError('Gate {} not supported'.format(gate)) for q in qubits: k.measure(q) # ensures timing of readout is aligned k.gate('wait', qubits, 0) return k
def motzoi_XY(qubit_idx: int, platf_cfg: str, program_name: str = 'motzoi_XY'): ''' Sequence used for calibrating the motzoi parameter. Consists of yX and xY Beware that the elements alternate, if you want to measure both Xy and Yx at each motzoi you need repeating motzoi parameters. This was chosen to be more easily compatible with standard detector functions and sweep pts ''' platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname=program_name, nqubits=platf.get_qubit_number(), p=platf) k = Kernel("yX", p=platf) k.prepz(qubit_idx) k.gate('ry90', qubit_idx) k.gate('rx180', qubit_idx) k.measure(qubit_idx) p.add_kernel(k) k = Kernel("xY", p=platf) k.prepz(qubit_idx) k.gate('rx90', qubit_idx) k.gate('ry180', qubit_idx) k.measure(qubit_idx) p.add_kernel(k) with suppress_stdout(): p.compile(verbose=False) # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p
def flipping(qubit_idx: int, number_of_flips, platf_cfg: str, equator: bool = False, cal_points: bool = True): """ Generates a flipping sequence that performs multiple pi-pulses Basic sequence: - (X)^n - RO Input pars: qubit_idx: int specifying the target qubit (starting at 0) number_of_flips: array of ints specifying the sweep points platf_cfg: filename of the platform config file equator: if True add an extra pi/2 pulse at the end to make the state end at the equator. cal_points: replaces last 4 points by calibration points Returns: p: OpenQL Program object """ platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname="Flipping", nqubits=platf.get_qubit_number(), p=platf) for i, n in enumerate(number_of_flips): k = Kernel("Flipping_" + str(i), p=platf) k.prepz(qubit_idx) if cal_points and (i == (len(number_of_flips) - 4) or i == (len(number_of_flips) - 3)): k.measure(qubit_idx) elif cal_points and (i == (len(number_of_flips) - 2) or i == (len(number_of_flips) - 1)): k.x(qubit_idx) k.measure(qubit_idx) else: if equator: k.gate('rx90', qubit_idx) for j in range(n): k.x(qubit_idx) k.measure(qubit_idx) p.add_kernel(k) with suppress_stdout(): p.compile(verbose=False) # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p
def AllXY(qubit_idx: int, platf_cfg: str, double_points: bool = True): """ Single qubit AllXY sequence. Writes output files to the directory specified in openql. Output directory is set as an attribute to the program for convenience. Input pars: qubit_idx: int specifying the target qubit (starting at 0) platf_cfg: filename of the platform config file double_points: if true repeats every element twice Returns: p: OpenQL Program object containing """ platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname="AllXY", nqubits=platf.get_qubit_number(), p=platf) allXY = [['i', 'i'], ['rx180', 'rx180'], ['ry180', 'ry180'], ['rx180', 'ry180'], ['ry180', 'rx180'], ['rx90', 'i'], ['ry90', 'i'], ['rx90', 'ry90'], ['ry90', 'rx90'], ['rx90', 'ry180'], ['ry90', 'rx180'], ['rx180', 'ry90'], ['ry180', 'rx90'], ['rx90', 'rx180'], ['rx180', 'rx90'], ['ry90', 'ry180'], ['ry180', 'ry90'], ['rx180', 'i'], ['ry180', 'i'], ['rx90', 'rx90'], ['ry90', 'ry90']] # this should be implicit p.set_sweep_points(np.arange(len(allXY), dtype=float), len(allXY)) for i, xy in enumerate(allXY): if double_points: js = 2 else: js = 1 for j in range(js): k = Kernel("AllXY_" + str(i + j / 2), p=platf) k.prepz(qubit_idx) k.gate(xy[0], qubit_idx) k.gate(xy[1], qubit_idx) k.measure(qubit_idx) p.add_kernel(k) with suppress_stdout(): p.compile(verbose=False) # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p
def off_on(qubit_idx: int, pulse_comb: str, initialize: bool, platf_cfg: str): """ Performs an 'off_on' sequence on the qubit specified. off: (RO) - prepz - - RO on: (RO) - prepz - x180 - RO Args: qubit_idx (int) : pulse_comb (str): What pulses to play valid options are "off", "on", "off_on" initialize (bool): if True does an extra initial measurement to post select data. platf_cfg (str) : filepath of OpenQL platform config file Pulses can be optionally enabled by putting 'off', respectively 'on' in the pulse_comb string. """ platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname="OffOn_RO_sequence", nqubits=platf.get_qubit_number(), p=platf) # # Off if 'off' in pulse_comb.lower(): k = Kernel("off", p=platf) k.prepz(qubit_idx) if initialize: k.measure(qubit_idx) k.measure(qubit_idx) p.add_kernel(k) if 'on' in pulse_comb.lower(): k = Kernel("on", p=platf) k.prepz(qubit_idx) if initialize: k.measure(qubit_idx) k.gate('rx180', qubit_idx) k.measure(qubit_idx) p.add_kernel(k) if ('on' not in pulse_comb.lower()) and ('off' not in pulse_comb.lower()): raise ValueError() with suppress_stdout(): p.compile(verbose=False) # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p
def CW_tone(qubit_idx: int, platf_cfg: str): """ Sequence to generate an "always on" pulse or "ContinuousWave" (CW) tone. This is a sequence that goes a bit against the paradigm of openql. """ platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname="CW_tone", nqubits=platf.get_qubit_number(), p=platf) k = Kernel("main", p=platf) for i in range(40): k.gate('square', qubit_idx) p.add_kernel(k) with suppress_stdout(): p.compile(verbose=False) p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p
def add_single_qubit_cal_points(p, platf, qubit_idx, f_state_cal_pts: bool = False): """ Adds single qubit calibration points to an OpenQL program Args: p platf qubit_idx """ for i in np.arange(2): k = Kernel("cal_gr_" + str(i), p=platf) k.prepz(qubit_idx) k.measure(qubit_idx) p.add_kernel(k) for i in np.arange(2): k = Kernel("cal_ex_" + str(i), p=platf) k.prepz(qubit_idx) k.gate('rx180', qubit_idx) k.measure(qubit_idx) p.add_kernel(k) if f_state_cal_pts: for i in np.arange(2): k = Kernel("cal_f_" + str(i), p=platf) k.prepz(qubit_idx) k.gate('rx180', qubit_idx) k.gate('rx12', qubit_idx) k.measure(qubit_idx) p.add_kernel(k) return p
def echo(times, qubit_idx: int, platf_cfg: str): """ Single qubit Echo sequence. Writes output files to the directory specified in openql. Output directory is set as an attribute to the program for convenience. Input pars: times: the list of waiting times for each Echo element qubit_idx: int specifying the target qubit (starting at 0) platf_cfg: filename of the platform config file Returns: p: OpenQL Program object containing """ platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname="echo", nqubits=platf.get_qubit_number(), p=platf) for i, time in enumerate(times[:-4]): k = Kernel("echo_" + str(i), p=platf) k.prepz(qubit_idx) # nr_clocks = int(time/20e-9/2) wait_nanoseconds = int(round(time / 1e-9 / 2)) k.gate('rx90', qubit_idx) k.gate("wait", [qubit_idx], wait_nanoseconds) k.gate('rx180', qubit_idx) k.gate("wait", [qubit_idx], wait_nanoseconds) k.gate('rx90', qubit_idx) k.measure(qubit_idx) p.add_kernel(k) # adding the calibration points add_single_qubit_cal_points(p, platf=platf, qubit_idx=qubit_idx) with suppress_stdout(): p.compile(verbose=False) # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p
def vsm_timing_cal_sequence(qubit_idx: int, platf_cfg: str): """ A sequence for calibrating the VSM timing delay. The marker idx is a qubit number for which a dummy pulse is played. This can be used as a reference. """ platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname="vsm_timing_cal_sequence", nqubits=platf.get_qubit_number(), p=platf) k = Kernel("main", p=platf) k.prepz(qubit_idx) # to ensure enough separation in timing k.gate('spec', qubit_idx) p.add_kernel(k) with suppress_stdout(): p.compile(verbose=False) # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p
def T1_second_excited_state(times, qubit_idx: int, platf_cfg: str): """ Single qubit T1 sequence for the second excited states. Writes output files to the directory specified in openql. Output directory is set as an attribute to the program for convenience. Input pars: times: the list of waiting times for each T1 element qubit_idx: int specifying the target qubit (starting at 0) platf_cfg: filename of the platform config file Returns: p: OpenQL Program object containing """ platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname="T1_2nd_exc", nqubits=platf.get_qubit_number(), p=platf) for i, time in enumerate(times): for j in range(2): k = Kernel("T1_2nd_exc_{}_{}".format(i, j), p=platf) k.prepz(qubit_idx) wait_nanoseconds = int(round(time / 1e-9)) k.gate('rx180', qubit_idx) k.gate('rx12', qubit_idx) k.gate("wait", [qubit_idx], wait_nanoseconds) if j == 1: k.gate('rx180', qubit_idx) k.measure(qubit_idx) p.add_kernel(k) # adding the calibration points add_single_qubit_cal_points(p, platf=platf, qubit_idx=qubit_idx, f_state_cal_pts=True) with suppress_stdout(): p.compile(verbose=False) dt = times[1] - times[0] sweep_points = np.concatenate( [np.repeat(times, 2), times[-1] + dt * np.arange(6) + dt]) # attribute get's added to program to help finding the output files p.sweep_points = sweep_points p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p
def ef_rabi_seq(q0: int, amps: list, platf_cfg: str, recovery_pulse: bool = True, add_cal_points: bool = True): """ Sequence used to calibrate pulses for 2nd excited state (ef/12 transition) Timing of the sequence: q0: -- X180 -- X12 -- (X180) -- RO Args: q0 (str): name of the addressed qubit amps (list): amps for the two state pulse, note that these are only used to label the kernels. Load the pulse in the LutMan recovery_pulse (bool): if True adds a recovery pulse to enhance contrast in the measured signal. """ if len(amps) > 18: raise ValueError('Only 18 free codewords available for amp pulses') platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname="ef_rabi_seq", nqubits=platf.get_qubit_number(), p=platf) # These angles correspond to special pi/2 pulses in the lutman for i, amp in enumerate(amps): # cw_idx corresponds to special hardcoded pulses in the lutman cw_idx = i + 9 k = Kernel("ef_A{}".format(amp), p=platf) k.prepz(q0) k.gate('rx180', q0) k.gate('cw_{:02}'.format(cw_idx), q0) if recovery_pulse: k.gate('rx180', q0) k.measure(q0) p.add_kernel(k) if add_cal_points: p = add_single_qubit_cal_points(p, platf=platf, qubit_idx=q0) with suppress_stdout(): p.compile() # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') if add_single_qubit_cal_points: cal_pts_idx = [ amps[-1] + .1, amps[-1] + .15, amps[-1] + .2, amps[-1] + .25 ] else: cal_pts_idx = [] p.sweep_points = np.concatenate([amps, cal_pts_idx]) p.set_sweep_points(p.sweep_points, len(p.sweep_points)) return p
def FluxTimingCalibration(qubit_idx: int, times, platf_cfg: str, cal_points: bool = True): """ A Ramsey sequence with varying waiting times `times` around a flux pulse. """ platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname="FluxTimingCalibration", nqubits=platf.get_qubit_number(), p=platf) # don't use last 4 points if calibration points are used if cal_points: times = times[:-4] for t in times: t_nanoseconds = int(round(t / 1e-9)) k = Kernel("pifluxpi", p=platf) k.prepz(qubit_idx) k.gate('rx90', qubit_idx) k.gate('fl_cw_02', 2, 0) if t_nanoseconds > 10: k.gate("wait", [qubit_idx], t_nanoseconds) k.gate('rx90', qubit_idx) k.measure(qubit_idx) p.add_kernel(k) if cal_points: add_single_qubit_cal_points(p, platf=platf, qubit_idx=qubit_idx) with suppress_stdout(): p.compile(verbose=False) # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p
def FastFeedbackControl(lantecy, qubit_idx: int, platf_cfg: str): """ Single qubit sequence to test fast feedback control (fast conditional execution). Writes output files to the directory specified in openql. Output directory is set as an attribute to the program for convenience. Input pars: lantecy: the waiting time between measurement and the feedback pulse, which should be longer than the feedback latency. feedback: if apply qubit_idx: int specifying the target qubit (starting at 0) platf_cfg: filename of the platform config file Returns: p: OpenQL Program object containing """ platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname="FastFdbkCtrl", nqubits=platf.get_qubit_number(), p=platf) k = Kernel("FastFdbkCtrl_nofb", p=platf) k.prepz(qubit_idx) k.gate('rx90', qubit_idx) # k.gate('rx180', qubit_idx) k.measure(qubit_idx) wait_nanoseconds = int(round(lantecy / 1e-9)) k.gate("wait", [qubit_idx], wait_nanoseconds) k.gate("i", qubit_idx) k.measure(qubit_idx) p.add_kernel(k) k = Kernel("FastFdbkCtrl_fb0", p=platf) k.prepz(qubit_idx) k.gate('rx90', qubit_idx) # k.gate('rx180', qubit_idx) k.measure(qubit_idx) wait_nanoseconds = int(round(lantecy / 1e-9)) k.gate("wait", [qubit_idx], wait_nanoseconds) k.gate('C0rx180', qubit_idx) # fast feedback control here k.measure(qubit_idx) p.add_kernel(k) k = Kernel("FastFdbkCtrl_fb1", p=platf) k.prepz(qubit_idx) k.gate('rx90', qubit_idx) # k.gate('rx180', qubit_idx) k.measure(qubit_idx) wait_nanoseconds = int(round(lantecy / 1e-9)) k.gate("wait", [qubit_idx], wait_nanoseconds) k.gate('C1rx180', qubit_idx) # fast feedback control here k.measure(qubit_idx) p.add_kernel(k) # adding the calibration points add_single_qubit_cal_points(p, platf=platf, qubit_idx=qubit_idx) with suppress_stdout(): p.compile(verbose=False) # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p
def FluxTimingCalibration_2q(q0, q1, buffer_time1, times, platf_cfg: str): """ A Ramsey sequence with varying waiting times `times` around a flux pulse. """ platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname="FluxTimingCalibration2q", nqubits=platf.get_qubit_number(), p=platf) buffer_nanoseconds1 = int(round(buffer_time1 / 1e-9)) for t in times: t_nanoseconds = int(round(t / 1e-9)) k = Kernel("pifluxpi", p=platf) k.prepz(q0) k.prepz(q1) k.gate('rx180', q0) k.gate('rx180', q1) if buffer_nanoseconds1 > 10: k.gate("wait", [2, 0], buffer_nanoseconds1) k.gate('fl_cw_02', 2, 0) if t_nanoseconds > 10: k.gate("wait", [2, 0], t_nanoseconds) #k.gate('rx180', q0) #k.gate('rx180', q1) k.gate("wait", [2, 0], 1) k.measure(q0) k.gate("wait", [2, 0], 1) p.add_kernel(k) with suppress_stdout(): p.compile(verbose=False) # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p
def RTE(qubit_idx: int, sequence_type: str, platf_cfg: str, net_gate: str, feedback=False): """ """ platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname="RTE", nqubits=platf.get_qubit_number(), p=platf) k = Kernel('RTE', p=platf) if sequence_type == 'echo': k.gate('rx90', qubit_idx) k.gate('i', qubit_idx) k.gate('i', qubit_idx) k.gate('i', qubit_idx) k.gate('i', qubit_idx) k.gate('rx180', qubit_idx) k.gate('i', qubit_idx) k.gate('i', qubit_idx) k.gate('i', qubit_idx) k.gate('i', qubit_idx) if net_gate == 'pi': k.gate('rxm90', qubit_idx) elif net_gate == 'i': k.gate('rx90', qubit_idx) else: raise ValueError( 'net_gate ({})should be "i" or "pi"'.format(net_gate)) if feedback: k.gate('Crx180', qubit_idx) elif sequence_type == 'pi': if net_gate == 'pi': k.gate('rx180', qubit_idx) elif net_gate == 'i': pass else: raise ValueError( 'net_gate ({})should be "i" or "pi"'.format(net_gate)) if feedback: k.gate('Crx180', qubit_idx) else: raise ValueError( 'sequence_type ({})should be "echo" or "pi"'.format(sequence_type)) k.measure(qubit_idx) p.add_kernel(k) with suppress_stdout(): p.compile(verbose=False) # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p
def idle_error_rate_seq(nr_of_idle_gates, states: list, gate_duration_ns: int, echo: bool, qubit_idx: int, platf_cfg: str, post_select=True): """ Sequence to perform the idle_error_rate_sequence. Virtually identical to a T1 experiment (Z-basis) or a ramsey/echo experiment (X-basis) Input pars: nr_of_idle_gates : list of integers specifying the number of idle gates corresponding to each data point. gate_duration_ns : integer specifying the duration of the wait gate. states : list of states to prepare qubit_idx: int specifying the target qubit (starting at 0) platf_cfg: filename of the platform config file Returns: p: OpenQL Program object containing """ allowed_states = ['0', '1', '+'] platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname="idle_error_rate", nqubits=platf.get_qubit_number(), p=platf) sweep_points = [] for N in nr_of_idle_gates: for state in states: if state not in allowed_states: raise ValueError('State must be in {}'.format(allowed_states)) k = Kernel("idle_prep{}_N{}".format(state, N), p=platf) # 1. Preparing in the right basis k.prepz(qubit_idx) if post_select: # adds an initialization measurement used to post-select k.measure(qubit_idx) if state == '1': k.gate('rx180', qubit_idx) elif state == '+': k.gate('rym90', qubit_idx) # 2. The "waiting" gates wait_nanoseconds = N * gate_duration_ns if state == '+' and echo: k.gate("wait", [qubit_idx], wait_nanoseconds // 2) k.gate('rx180', qubit_idx) k.gate("wait", [qubit_idx], wait_nanoseconds // 2) else: k.gate("wait", [qubit_idx], wait_nanoseconds) # 3. Reading out in the proper basis if state == '+' and echo: k.gate('rym90', qubit_idx) elif state == '+': k.gate('ry90', qubit_idx) k.measure(qubit_idx) p.add_kernel(k) sweep_points.append(N) p.set_sweep_points(sweep_points, num_sweep_points=len(sweep_points)) p.sweep_points = sweep_points with suppress_stdout(): p.compile(verbose=False) # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p
def randomized_benchmarking(qubits: list, platf_cfg: str, nr_cliffords, nr_seeds: int, net_cliffords: list = [0], max_clifford_idx: int = 11520, initialize: bool = True, interleaving_cliffords=[None], program_name: str = 'randomized_benchmarking', cal_points: bool = True, f_state_cal_pts: bool = True, recompile: bool = True): ''' Input pars: qubits: list of ints specifying qubit indices. based on the length this function detects if it should generate a single or two qubit RB sequence. platf_cfg: filename of the platform config file nr_cliffords: list nr_cliffords for which to generate RB seqs nr_seeds: int nr_seeds for which to generate RB seqs net_cliffords: list of ints index of net clifford the sequence should perform. See examples below on how to use this. 0 -> Idx 3 -> rx180 3*24+3 -> {rx180 q0 | rx180 q1} initialize: if True initializes qubits to 0, disable for restless tuning program_name: some string that can be used as a label. cal_points: bool whether to replace the last two elements with calibration points, set to False if you want to measure a single element (for e.g. optimization) recompile: True -> compiles the program, 'as needed' -> compares program to timestamp of config and existence, if required recompile. False -> compares program to timestamp of config. if compilation is required raises a ValueError If the program is more recent than the config it returns an empty OpenQL program object with the intended filename that can be used to upload the previously compiled file. Returns: p: OpenQL Program object *************************************************************************** Examples: 1. Single qubit randomized benchmarking: p = cl_oql.randomized_benchmarking( qubits=[0], nr_cliffords=[2, 4, 8, 16, 32, 128, 512, 1024], nr_seeds=1, # for CCL memory reasons platf_cfg=qubit.cfg_openql_platform_fn(), program_name='RB_{}'.format(i)) 2. Two qubit simultaneous randomized benchmarking: p = cl_oql.randomized_benchmarking( qubits=[0, 1], # simultaneous RB on both qubits max_clifford_idx = 576, # to ensure only SQ Cliffords are drawn nr_cliffords=[2, 4, 8, 16, 32, 128, 512, 1024], nr_seeds=1, # for CCL memory reasons platf_cfg=qubit.cfg_openql_platform_fn(), program_name='RB_{}'.format(i)) 3. Single qubit interleaved randomized benchmarking: p = cl_oql.randomized_benchmarking( qubits=[0], interleaving_cliffords=[None, 0, 16, 3], cal_points=False # relevant here because of data binning nr_cliffords=[2, 4, 8, 16, 32, 128, 512, 1024], nr_seeds=1, platf_cfg=qubit.cfg_openql_platform_fn(), program_name='Interleaved_RB_s{}_int{}_ncl{}_{}'.format(i)) ''' platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname=program_name, nqubits=platf.get_qubit_number(), p=platf) # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') if not oqh.check_recompilation_needed( program_fn=p.filename, platf_cfg=platf_cfg, recompile=recompile): return p if len(qubits) == 1: qubit_map = {'q0': qubits[0]} number_of_qubits = 1 Cl = SingleQubitClifford elif len(qubits) == 2: qubit_map = {'q0': qubits[0], 'q1': qubits[1]} number_of_qubits = 2 Cl = TwoQubitClifford else: raise NotImplementedError() for seed in range(nr_seeds): for j, n_cl in enumerate(nr_cliffords): for interleaving_cl in interleaving_cliffords: for net_clifford in net_cliffords: k = Kernel('RB_{}Cl_s{}_net{}_inter{}'.format( n_cl, seed, net_clifford, interleaving_cl), p=platf) if initialize: for qubit_idx in qubit_map.values(): k.prepz(qubit_idx) cl_seq = rb.randomized_benchmarking_sequence( n_cl, number_of_qubits=number_of_qubits, desired_net_cl=net_clifford, max_clifford_idx=max_clifford_idx, interleaving_cl=interleaving_cl) for cl in cl_seq: gates = Cl(cl).gate_decomposition for g, q in gates: if isinstance(q, str): k.gate(g, qubit_map[q]) elif isinstance(q, list): # proper codeword k.gate(g, [qubit_map[q[0]], qubit_map[q[1]]]) # This hack is required to align multiplexed RO in openQL.. k.gate("wait", list(qubit_map.values()), 0) for qubit_idx in qubit_map.values(): k.measure(qubit_idx) k.gate("wait", list(qubit_map.values()), 0) p.add_kernel(k) if cal_points: if number_of_qubits == 1: p = add_single_qubit_cal_points( p, platf=platf, qubit_idx=qubits[0], f_state_cal_pts=f_state_cal_pts) elif number_of_qubits == 2: if f_state_cal_pts: combinations = ['00', '01', '10', '11', '02', '20', '22'] else: combinations = ['00', '01', '10', '11'] p = add_multi_q_cal_points(p, platf=platf, qubits=qubits, combinations=combinations) with suppress_stdout(): p.compile(verbose=False) return p
def randomized_benchmarking(qubit_idx: int, platf_cfg: str, nr_cliffords, nr_seeds: int, net_clifford: int = 0, restless: bool = False, program_name: str = 'randomized_benchmarking', cal_points: bool = True, double_curves: bool = False): ''' Input pars: qubit_idx: int specifying the target qubit (starting at 0) platf_cfg: filename of the platform config file nr_cliffords: list nr_cliffords for which to generate RB seqs nr_seeds: int nr_seeds for which to generate RB seqs net_clifford: int index of net clifford the sequence should perform 0 -> Idx 3 -> rx180 restless: bool, does not initialize if restless is True program_name: some string that can be used as a label. cal_points: bool whether to replace the last two elements with calibration points, set to False if you want to measure a single element (for e.g. optimization) double_curves: Alternates between net clifford 0 and 3 Returns: p: OpenQL Program object generates a program for single qubit Clifford based randomized benchmarking. ''' net_cliffords = [0, 3] # Exists purely for the double curves mode platf = Platform('OpenQL_Platform', platf_cfg) p = Program(pname=program_name, nqubits=platf.get_qubit_number(), p=platf) i = 0 for seed in range(nr_seeds): for j, n_cl in enumerate(nr_cliffords): k = Kernel('RB_{}Cl_s{}'.format(n_cl, seed), p=platf) if not restless: k.prepz(qubit_idx) if cal_points and (j == (len(nr_cliffords) - 4) or j == (len(nr_cliffords) - 3)): k.measure(qubit_idx) elif cal_points and (j == (len(nr_cliffords) - 2) or j == (len(nr_cliffords) - 1)): k.x(qubit_idx) k.measure(qubit_idx) else: if double_curves: net_clifford = net_cliffords[i % 2] i += 1 cl_seq = rb.randomized_benchmarking_sequence( n_cl, desired_net_cl=net_clifford) # pulse_keys = rb.decompose_clifford_seq(cl_seq) for cl in cl_seq: k.gate('cl_{}'.format(cl), qubit_idx) k.measure(qubit_idx) p.add_kernel(k) with suppress_stdout(): p.compile(verbose=False) # attribute get's added to program to help finding the output files p.output_dir = ql.get_output_dir() p.filename = join(p.output_dir, p.name + '.qisa') return p