Example #1
0
 def test_two_qubit_randomized_benchmarking_sequence(self):
     """
     """
     seeds = [0, 100, 200, 300, 400]
     net_cliffs = np.arange(len(seeds))
     for seed, net_cl in zip(seeds, net_cliffs):
         rb.randomized_benchmarking_sequence(
             n_cl=20, desired_net_cl=0, number_of_qubits=2, seed=0)
Example #2
0
 def test_seed_reproduces(self):
     rb_seq_a = rb.randomized_benchmarking_sequence(500, seed=5)
     rb_seq_b = rb.randomized_benchmarking_sequence(500, seed=None)
     rb_seq_c = rb.randomized_benchmarking_sequence(500, seed=5)
     rb_seq_d = rb.randomized_benchmarking_sequence(500, seed=None)
     self.assertTrue((rb_seq_a == rb_seq_c).all())
     self.assertTrue((rb_seq_a != rb_seq_b).any())
     self.assertTrue((rb_seq_c != rb_seq_b).any())
     self.assertTrue((rb_seq_b != rb_seq_d).any())
 def test_seed_reproduces(self):
     rb_seq_a = rb.randomized_benchmarking_sequence(500, seed=5)
     rb_seq_b = rb.randomized_benchmarking_sequence(500, seed=None)
     rb_seq_c = rb.randomized_benchmarking_sequence(500, seed=5)
     rb_seq_d = rb.randomized_benchmarking_sequence(500, seed=None)
     self.assertTrue((rb_seq_a == rb_seq_c).all())
     self.assertTrue((rb_seq_a != rb_seq_b).any)
     self.assertTrue((rb_seq_c != rb_seq_b).any)
     self.assertTrue((rb_seq_b != rb_seq_d).any)
Example #4
0
    def test_interleaved_randomized_benchmarking_sequence_1Q(self):
        seeds = [0, 100, 200, 300, 400]
        net_cliffs = np.arange(len(seeds))
        for seed, net_cl in zip(seeds, net_cliffs):
            intl_cliffords = rb.randomized_benchmarking_sequence(
                n_cl=20, desired_net_cl=0, number_of_qubits=1, seed=0,
                interleaving_cl=0)
            cliffords = rb.randomized_benchmarking_sequence(
                n_cl=20, desired_net_cl=0, seed=0)

            new_cliff = np.empty(cliffords.size*2-1, dtype=int)
            new_cliff[0::2] = cliffords
            new_cliff[1::2] = 0
            assert_array_equal(intl_cliffords, new_cliff)
Example #5
0
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
    p = oqh.create_program(program_name, platf_cfg)

    i = 0
    for seed in range(nr_seeds):
        for j, n_cl in enumerate(nr_cliffords):
            k = oqh.create_kernel('RB_{}Cl_s{}'.format(n_cl, seed), p)
            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)

    p = oqh.compile(p)
    return p
    def prepare(self, upload_tek_seq=True, **kw):
        self.AWG.stop()
        n_cls = self.nr_cliffords
        time_tape = []
        pulse_length = self.LutMan.gauss_width.get()*4
        for seed in range(self.nr_seeds):
            for n_cl in n_cls:
                cliffords = rb.randomized_benchmarking_sequence(n_cl)
                cl_tape = rb.convert_clifford_sequence_to_tape(
                    cliffords,
                    self.LutMan.lut_mapping.get())
                for i, tape_elt in enumerate(cl_tape):
                    if i == 0:
                        # wait_time is in ns
                        wait_time = (self.max_seq_duration*1e9 -
                                     (len(cl_tape)-1)*self.pulse_delay_ns -
                                     pulse_length)
                    else:
                        wait_time = self.pulse_delay_ns - pulse_length
                    end_of_marker = (i == (len(cl_tape)-1))
                    entry = self.CBox.create_timing_tape_entry(
                        wait_time, tape_elt, end_of_marker, prepend_elt=0)
                    time_tape.extend(entry)

            for cal_pt in self.cal_points:
                wait_time = self.max_seq_duration*1e9 - pulse_length
                time_tape.extend(self.CBox.create_timing_tape_entry(
                    wait_time, cal_pt, True, prepend_elt=0))
        # print('Total tape length', len(time_tape))
        for awg in range(3):
            self.CBox.set('AWG{}_mode'.format(awg), 'Segmented')
            self.CBox.set_segmented_tape(awg, time_tape)
            self.CBox.restart_awg_tape(awg)
        if upload_tek_seq:
            self.upload_tek_seq()
Example #7
0
 def test_single_qubit_randomized_benchmarking_sequence(self):
     seeds = [0, 100, 200, 300, 400]
     net_cliffs = np.arange(len(seeds))
     for seed, net_cl in zip(seeds, net_cliffs):
         cliffords_single_qubit_class = rb.randomized_benchmarking_sequence(
             n_cl=20, desired_net_cl=0,  number_of_qubits=1, seed=0)
         cliffords = rb.randomized_benchmarking_sequence_old(
             n_cl=20, desired_net_cl=0, seed=0)
         assert_array_equal(cliffords_single_qubit_class, cliffords)
    def get_resetless_rb_detector(self, nr_cliff, starting_seed=1,
                                  nr_seeds='max', pulse_p_elt='min',
                                  MC=None,
                                  upload=True):
        if MC is None:
            MC = self.MC

        if pulse_p_elt == 'min':
            safety_factor = 5 if nr_cliff < 8 else 3
            pulse_p_elt = int(safety_factor*nr_cliff)
        if nr_seeds == 'max':
            nr_seeds = 29184//pulse_p_elt

        if nr_seeds*pulse_p_elt > 29184:
            raise ValueError(
                'Too many pulses ({}), {} seeds, {} pulse_p_elt'.format(
                    nr_seeds*pulse_p_elt, nr_seeds, pulse_p_elt))

        resetless_interval = (
            np.round(pulse_p_elt*self.pulse_delay.get()*1e6)+2.5)*1e-6

        combined_tape = []
        for i in range(nr_seeds):
            if starting_seed is not None:
                seed = starting_seed*1000*i
            else:
                seed = None
            rb_seq = rb.randomized_benchmarking_sequence(nr_cliff,
                                                         desired_net_cl=3,
                                                         seed=seed)
            tape = rb.convert_clifford_sequence_to_tape(
                rb_seq, self.LutMan.lut_mapping.get())
            if len(tape) > pulse_p_elt:
                raise ValueError(
                    'Too many pulses ({}), {} pulse_p_elt'.format(
                        len(tape),  pulse_p_elt))
            combined_tape += [0]*(pulse_p_elt-len(tape))+tape

        # Rename IF in awg_swf_resetless tape
        s = awg_swf.Resetless_tape(
            n_pulses=pulse_p_elt, tape=combined_tape,
            IF=self.f_RO_mod.get(),
            pulse_delay=self.pulse_delay.get(),
            resetless_interval=resetless_interval,
            RO_pulse_delay=self.RO_pulse_delay.get(),
            RO_pulse_length=self.RO_pulse_length.get(),
            RO_trigger_delay=self.RO_acq_marker_delay.get(),
            AWG=self.AWG, CBox=self.CBox, upload=upload)

        d = cdet.CBox_trace_error_fraction_detector(
            'Resetless rb det',
            MC=MC, AWG=self.AWG, CBox=self.CBox,
            sequence_swf=s,
            threshold=self.RO_threshold.get(),
            save_raw_trace=False)
        return d
Example #9
0
    def get_resetless_rb_detector(self, nr_cliff, starting_seed=1,
                                  nr_seeds='max', pulse_p_elt='min',
                                  MC=None,
                                  upload=True):
        if MC is None:
            MC = self.MC

        if pulse_p_elt == 'min':
            safety_factor = 5 if nr_cliff < 8 else 3
            pulse_p_elt = int(safety_factor*nr_cliff)
        if nr_seeds == 'max':
            nr_seeds = 29184//pulse_p_elt

        if nr_seeds*pulse_p_elt > 29184:
            raise ValueError(
                'Too many pulses ({}), {} seeds, {} pulse_p_elt'.format(
                    nr_seeds*pulse_p_elt, nr_seeds, pulse_p_elt))

        resetless_interval = (
            np.round(pulse_p_elt*self.pulse_delay.get()*1e6)+2.5)*1e-6

        combined_tape = []
        for i in range(nr_seeds):
            if starting_seed is not None:
                seed = starting_seed*1000*i
            else:
                seed = None
            rb_seq = rb.randomized_benchmarking_sequence(nr_cliff,
                                                         desired_net_cl=3,
                                                         seed=seed)
            tape = rb.convert_clifford_sequence_to_tape(
                rb_seq, self.LutMan.lut_mapping.get())
            if len(tape) > pulse_p_elt:
                raise ValueError(
                    'Too many pulses ({}), {} pulse_p_elt'.format(
                        len(tape),  pulse_p_elt))
            combined_tape += [0]*(pulse_p_elt-len(tape))+tape

        # Rename IF in awg_swf_resetless tape
        s = awg_swf.Resetless_tape(
            n_pulses=pulse_p_elt, tape=combined_tape,
            IF=self.f_RO_mod.get(),
            pulse_delay=self.pulse_delay.get(),
            resetless_interval=resetless_interval,
            RO_pulse_delay=self.RO_pulse_delay.get(),
            RO_pulse_length=self.RO_pulse_length.get(),
            RO_trigger_delay=self.RO_acq_marker_delay.get(),
            AWG=self.AWG, CBox=self.CBox, upload=upload)

        d = cdet.CBox_trace_error_fraction_detector(
            'Resetless rb det',
            MC=MC, AWG=self.AWG, CBox=self.CBox,
            sequence_swf=s,
            threshold=self.RO_threshold.get(),
            save_raw_trace=False)
        return d
def randomized_benchmarking(qubit_name, nr_cliffords, nr_seeds,
                            net_clifford=0, restless=False,
                            label='randomized_benchmarking',
                            cal_points=True,
                            double_curves=True):
    '''
    Input pars:
        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 corresponds to Identity and 3 corresponds to X180
        restless:      bool, does not initialize if restless is True
        label:           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:
        qasm_file

    generates a qasm file for single qubit Clifford based randomized
    benchmarking.
    '''
    net_cliffords = [0, 3]  # Exists purely for the double curves mode
    filename = join(base_qasm_path, label+'.qasm')
    qasm_file = mopen(filename, mode='w')
    qasm_file.writelines('qubit {} \n'.format(qubit_name))
    i = 0
    for seed in range(nr_seeds):
        for j, n_cl in enumerate(nr_cliffords):
            if not restless:
                qasm_file.writelines('init_all  \n')
            if cal_points and (j == (len(nr_cliffords)-4) or
                               j == (len(nr_cliffords)-3)):
                qasm_file.writelines('RO {}  \n'.format(qubit_name))
            elif cal_points and (j == (len(nr_cliffords)-2) or
                                 j == (len(nr_cliffords)-1)):
                qasm_file.writelines('X180 {} \n'.format(qubit_name))
                qasm_file.writelines('RO {}  \n'.format(qubit_name))
            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 pulse in pulse_keys:
                    if pulse != 'I':
                        qasm_file.writelines('{} {}\n'.format(
                            pulse, qubit_name))
                qasm_file.writelines('RO {}  \n'.format(qubit_name))
    qasm_file.close()
    return qasm_file
    def rb_block(self, sp1d_idx, sp2d_idx, **kw):
        """
        Creates simultaneous blocks of RB pulses for each task in
        preprocessed_task_list.

        Args:
            sp1d_idx (int): current iteration index in the 1D sweep points array
            sp2d_idx (int): current iteration index in the 2D sweep points array

        Keyword args:
            interleaved_gate (see docstring of parent class)
        """
        interleaved_gate = kw.get('interleaved_gate', None)
        pulse_op_codes_list = []
        tl = [self.preprocessed_task_list[0]] if self.identical_pulses else \
            self.preprocessed_task_list
        for i, task in enumerate(tl):
            param_name = 'seeds' if interleaved_gate is None else 'seeds_irb'
            seed_idx = sp1d_idx if self.sweep_type['seeds'] == 0 else sp2d_idx
            clf_idx = sp1d_idx if self.sweep_type[
                'cliffords'] == 0 else sp2d_idx
            seed = task['sweep_points'].get_sweep_params_property(
                'values', self.sweep_type['seeds'], param_name)[seed_idx,
                                                                clf_idx]
            clifford = task['sweep_points'].get_sweep_params_property(
                'values', self.sweep_type['cliffords'], 'cliffords')[clf_idx]
            cl_seq = rb.randomized_benchmarking_sequence(
                clifford, seed=seed, interleaved_gate=interleaved_gate)
            pulse_list = rb.decompose_clifford_seq(
                cl_seq, gate_decomp=self.gate_decomposition)
            if self.purity:
                idx = sp1d_idx if self.sweep_type['seeds'] == 0 else sp2d_idx
                pulse_list += [self.tomo_pulses[idx % 3]]
            pulse_op_codes_list += [pulse_list]

        rb_block_list = [
            self.block_from_ops(f"rb_{task['qb']}", [
                f"{p} {task['qb']}"
                for p in pulse_op_codes_list[0 if self.identical_pulses else i]
            ]) for i, task in enumerate(self.preprocessed_task_list)
        ]

        return self.simultaneous_blocks(f'sim_rb_{sp1d_idx}{sp1d_idx}',
                                        rb_block_list,
                                        block_align='end',
                                        destroy=self.fast_mode)
Example #12
0
    def test_recovery_Y180_irb(self):
        cliffords = [0, 1, 50, 100]
        nr_seeds = 100

        for cl in cliffords:
            for _ in range(nr_seeds):
                cl_seq = rb.randomized_benchmarking_sequence(
                    cl, desired_net_cl=0, interleaved_gate='Y180')
                for decomp in ['HZ', 'XY']:
                    pulse_keys = rb.decompose_clifford_seq(cl_seq,
                                                           gate_decomp=decomp)

                    gproduct = qtp.tensor(qtp.identity(2))
                    for pk in pulse_keys:
                        gproduct = self.standard_pulses[pk] * gproduct

                    x = gproduct.full() / gproduct.full()[0][0]
                    self.assertTrue(
                        np.all((np.allclose(np.real(x), np.eye(2)),
                                np.allclose(np.imag(x), np.zeros(2)))))
Example #13
0
def character_benchmarking(
    qubits: list,
    platf_cfg: str,
    nr_cliffords,
    nr_seeds: int,
    interleaving_cliffords=[None],
    program_name: str = "character_benchmarking",
    cal_points: bool = True,
    f_state_cal_pts: bool = True,
    flux_codeword="cz",
    recompile: bool = True,
):
    """
    Create OpenQL program to perform two-qubit character benchmarking.

    Character benchmarking is described in:
        https://arxiv.org/abs/1808.00358 (theory)
        https://arxiv.org/abs/1811.04002 (implementation in Si/SiGe spins)

    Two-qubit character benchmarking:
        q0: P - C1 - C2 - C3 - ... - Cn- R - M
        q1: P - C1 - C2 - C3 - ... - Cn- R - M
    P -> Single qubit Pauli's. Single qubit Paulis are chosen so as to
        prepare in |00>, |01>, |10> and |11>.
        N.B. data should be averaged over all single qubit Paulis.
    Ci -> Single qubit Cliffords, different seqs for both qubits.
    R -> Recovery Clifford so that seq of C1 - Cn correspond to Idx.
    M -> Measurement in Z-basis.

    Outcomes should be averaged according to the "character function".

    Averaging scheme:
    seeds (average over different randomizations)
        nr of cliffords (peform for different nr of cliffords)
            paulis (perform for different Paulis)


    """

    assert len(qubits) == 2

    p = oqh.create_program(program_name, platf_cfg)

    this_file = inspect.getfile(inspect.currentframe())

    # Ensure that programs are recompiled when changing the code as well
    recompile_dict = oqh.check_recompilation_needed_hash_based(
        program_fn=p.filename,
        platf_cfg=platf_cfg,
        clifford_rb_oql=this_file,
        recompile=recompile,
    )

    if not recompile_dict["recompile"]:
        os.rename(recompile_dict["tmp_file"], recompile_dict["file"])
        return p

    qubit_map = {"q0": qubits[0], "q1": qubits[1]}
    Cl = TwoQubitClifford

    paulis = {
        "00": ["II", "IZ", "ZI", "ZZ"],
        "01": ["IX", "IY", "ZX", "ZY"],
        "10": ["XI", "XZ", "YI", "YZ"],
        "11": ["XX", "XY", "YX", "YY"],
    }

    for seed in range(nr_seeds):
        for j, n_cl in enumerate(nr_cliffords):
            for interleaving_cl in interleaving_cliffords:
                cl_seq = rb.randomized_benchmarking_sequence(
                    n_cl,
                    number_of_qubits=2,
                    desired_net_cl=0,  # desired to do identity
                    max_clifford_idx=567,
                    # The benchmarking group is the single qubit Clifford group
                    # for two qubits this corresponds to all single qubit like
                    # Cliffords.
                    interleaving_cl=interleaving_cl,
                )

                cl_seq_decomposed = []
                # first element not included in decomposition because it will
                # be merged with the character paulis
                for cl in cl_seq[1:]:
                    # hacking in exception for benchmarking only CZ
                    # (not as a member of CNOT-like group)
                    if cl == 104368:
                        cl_seq_decomposed.append([("CZ", ["q0", "q1"])])
                    else:
                        cl_seq_decomposed.append(Cl(cl).gate_decomposition)

                for pauli_type in paulis:
                    # select a random pauli from the different types
                    pauli = paulis[pauli_type][np.random.randint(4)]
                    # merge the pauli with the first element of the cl seq.
                    cl0 = Cl(common_cliffords[pauli])
                    # N.B. multiplication order is opposite of order in time
                    # -> the first element in time (cl0) is on the right
                    combined_cl0 = Cl(cl_seq[0]) * cl0
                    char_bench_seq_decomposed = [
                        combined_cl0.gate_decomposition
                    ] + cl_seq_decomposed

                    k = oqh.create_kernel(
                        "CharBench_P{}_{}Cl_s{}_inter{}".format(
                            pauli, int(n_cl), seed, interleaving_cl
                        ),
                        p,
                    )

                    for qubit_idx in qubit_map.values():
                        k.prepz(qubit_idx)
                    for gates in char_bench_seq_decomposed:
                        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 is a hack because we cannot
                                # properly trigger CZ gates.
                                k.gate("wait", [], 0)
                                k.gate(flux_codeword, [2, 0])
                                k.gate("wait", [], 0)

                    for qubit_idx in qubit_map.values():
                        k.measure(qubit_idx)

                    p.add_kernel(k)

        if cal_points:
            if f_state_cal_pts:
                combinations = ["00", "01", "10", "11", "02", "20", "22"]
            else:
                combinations = ["00", "01", "10", "11"]
            p = oqh.add_multi_q_cal_points(p, qubits=qubits, combinations=combinations)

    p = oqh.compile(p)
    # Just before returning we rename the hashes file as an indication of the
    # integrity of the RB code
    os.rename(recompile_dict["tmp_file"], recompile_dict["file"])
    return p
Example #14
0
            raise ValueError("Could not find flux duration. Specify manually!")

    for seed in range(nr_seeds):
        for j, n_cl in enumerate(nr_cliffords):
            for interleaving_cl in interleaving_cliffords:
                if (
                    not simultaneous_single_qubit_RB
                    and not simultaneous_single_qubit_parking_RB
                ):
                    # ############ 1 qubit, or 2 qubits using TwoQubitClifford
                    # generate sequence
                    for net_clifford in net_cliffords:
                        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,
                        )
                    net_cl_seq = rb.calculate_net_clifford(cl_seq, Cl)

                    # decompose
                    cl_seq_decomposed = [None] * len(cl_seq)
                    for i,cl in enumerate(cl_seq):
                        # benchmarking only CZ (not as a member of CNOT group)
                        if cl == 104368:  # 104368 = 100_000 + CZ
                            cl_seq_decomposed[i] = [("CZ", ["q0", "q1"])]
                        # benchmarking only idling identity, with duration of cz
                        # see below where wait-time is added
                        elif cl == 100_000:
                            cl_seq_decomposed[i] = [("I", ["q0", "q1"])]
def character_benchmarking(qubits: list,
                           platf_cfg: str,
                           nr_cliffords,
                           nr_seeds: int,
                           interleaving_cliffords=[None],
                           program_name: str = 'character_benchmarking',
                           cal_points: bool = True,
                           f_state_cal_pts: bool = True,
                           flux_codeword='cz',
                           recompile: bool = True):
    """
    Create OpenQL program to perform two-qubit character benchmarking.

    Character benchmarking is described in:
        https://arxiv.org/abs/1808.00358 (theory)
        https://arxiv.org/abs/1811.04002 (implementation in Si/SiGe spins)

    Two-qubit character benchmarking:
        q0: P - C1 - C2 - C3 - ... - Cn- R - M
        q1: P - C1 - C2 - C3 - ... - Cn- R - M
    P -> Single qubit Pauli's. Single qubit Paulis are chosen so as to
        prepare in |00>, |01>, |10> and |11>.
        N.B. data should be averaged over all single qubit Paulis.
    Ci -> Single qubit Cliffords, different seqs for both qubits.
    R -> Recovery Clifford so that seq of C1 - Cn correspond to Idx.
    M -> Measurement in Z-basis.

    Outcomes should be averaged according to the "character function".

    Averaging scheme:
    seeds (average over different randomizations)
        nr of cliffords (peform for different nr of cliffords)
            paulis (perform for different Paulis)


    """

    assert len(qubits) == 2

    p = oqh.create_program(program_name, platf_cfg)

    # attribute get's added to program to help finding the output files
    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

    qubit_map = {'q0': qubits[0], 'q1': qubits[1]}
    Cl = TwoQubitClifford

    paulis = {
        '00': ['II', 'IZ', 'ZI', 'ZZ'],
        '01': ['IX', 'IY', 'ZX', 'ZY'],
        '10': ['XI', 'XZ', 'YI', 'YZ'],
        '11': ['XX', 'XY', 'YX', 'YY']
    }

    for seed in range(nr_seeds):
        for j, n_cl in enumerate(nr_cliffords):
            for interleaving_cl in interleaving_cliffords:
                cl_seq = rb.randomized_benchmarking_sequence(
                    n_cl,
                    number_of_qubits=2,
                    desired_net_cl=0,  # desired to do identity
                    max_clifford_idx=567,
                    # The benchmarking group is the single qubit Clifford group
                    # for two qubits this corresponds to all single qubit like
                    # Cliffords.
                    interleaving_cl=interleaving_cl)

                cl_seq_decomposed = []
                # first element not included in decomposition because it will
                # be merged with the character paulis
                for cl in cl_seq[1:]:
                    # hacking in exception for benchmarking only CZ
                    # (not as a member of CNOT-like group)
                    if cl == -4368:
                        cl_seq_decomposed.append([('CZ', ['q0', 'q1'])])
                    else:
                        cl_seq_decomposed.append(Cl(cl).gate_decomposition)

                for pauli_type in paulis:
                    # select a random pauli from the different types
                    pauli = paulis[pauli_type][np.random.randint(4)]
                    # merge the pauli with the first element of the cl seq.
                    cl0 = Cl(common_cliffords[pauli])
                    # N.B. multiplication order is opposite of order in time
                    # -> the first element in time (cl0) is on the right
                    combined_cl0 = Cl(cl_seq[0]) * cl0
                    char_bench_seq_decomposed = \
                        [combined_cl0.gate_decomposition] + cl_seq_decomposed

                    k = oqh.create_kernel(
                        'CharBench_P{}_{}Cl_s{}_inter{}'.format(
                            pauli, int(n_cl), seed, interleaving_cl), p)

                    for qubit_idx in qubit_map.values():
                        k.prepz(qubit_idx)
                    for gates in char_bench_seq_decomposed:
                        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 is a hack because we cannot
                                # properly trigger CZ gates.
                                k.gate("wait", list(qubit_map.values()), 0)
                                k.gate(flux_codeword, [2, 0])
                                k.gate("wait", list(qubit_map.values()), 0)

                    for qubit_idx in qubit_map.values():
                        k.measure(qubit_idx)

                    p.add_kernel(k)

        if cal_points:
            if f_state_cal_pts:
                combinations = ['00', '01', '10', '11', '02', '20', '22']
            else:
                combinations = ['00', '01', '10', '11']
            p = oqh.add_multi_q_cal_points(p,
                                           qubits=qubits,
                                           combinations=combinations)

    p = oqh.compile(p)
    return p
def randomized_benchmarking(qubits: list,
                            platf_cfg: str,
                            nr_cliffords,
                            nr_seeds: int,
                            net_cliffords: list = [0],
                            max_clifford_idx: int = 11520,
                            flux_codeword: str = 'cz',
                            simultaneous_single_qubit_RB=False,
                            initialize: bool = True,
                            interleaving_cliffords=[None],
                            program_name: str = 'randomized_benchmarking',
                            cal_points: bool = True,
                            f_state_cal_pts: bool = True,
                            sim_cz_qubits: list = None,
                            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.
                            Important clifford indices
                                0 -> Idx
                                3 -> rx180
                                3*24+3 -> {rx180 q0 | rx180 q1}
                                4368 -> CZ

        max_clifford_idx:   Set's the maximum clifford group index from which
                        to sample random cliffords.
                            Important clifford indices
                                24 -> Size of the single qubit Cl group
                                576  -> Size of the single qubit like class
                                    contained in the two qubit Cl group
                                11520 -> Size of the complete two qubit Cl group

        initialize:     if True initializes qubits to 0, disable for restless
                        tuning
        interleaving_cliffords: list of integers which specifies which cliffords
                        to interleave the sequence with (for interleaved RB)
        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)
        sim_cz_qubits:
                        A list of qubit indices on which a simultaneous cz 
                        instruction must be applied. This is for characterizing
                        CZ gates that are intended to be performed in parallel 
                        with other CZ gates. 
        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
                simultaneous_single_qubit_RB=True,
                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))

    '''
    p = oqh.create_program(program_name, platf_cfg)

    # attribute get's added to program to help finding the output files
    p.filename = join(p.output_dir,
                      p.name + '.qisa')  # FIXME: platform dependency

    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 and not simultaneous_single_qubit_RB:
        qubit_map = {'q0': qubits[0], 'q1': qubits[1]}
        number_of_qubits = 2
        Cl = TwoQubitClifford
    elif len(qubits) == 2 and simultaneous_single_qubit_RB:
        qubit_map = {'q0': qubits[0], 'q1': qubits[1]}
        # arguments used to generate 2 single qubit sequences
        number_of_qubits = 2
        Cl = SingleQubitClifford
    else:
        raise NotImplementedError()

    for seed in range(nr_seeds):
        for j, n_cl in enumerate(nr_cliffords):
            for interleaving_cl in interleaving_cliffords:
                if not simultaneous_single_qubit_RB:
                    cl_seq = rb.randomized_benchmarking_sequence(
                        n_cl,
                        number_of_qubits=number_of_qubits,
                        desired_net_cl=None,  # net_clifford,
                        max_clifford_idx=max_clifford_idx,
                        interleaving_cl=interleaving_cl)
                    net_cl_seq = rb.calculate_net_clifford(cl_seq, Cl)
                    cl_seq_decomposed = []
                    for cl in cl_seq:
                        # FIXME: hacking in exception for benchmarking only CZ
                        # (not as a member of CNOT group)
                        if cl == -4368:
                            cl_seq_decomposed.append([('CZ', ['q0', 'q1'])])
                        else:
                            cl_seq_decomposed.append(Cl(cl).gate_decomposition)
                    for net_clifford in net_cliffords:
                        recovery_to_idx_clifford = net_cl_seq.get_inverse()
                        recovery_clifford = Cl(
                            net_clifford) * recovery_to_idx_clifford
                        cl_seq_decomposed_with_net = cl_seq_decomposed + \
                            [recovery_clifford.gate_decomposition]
                        k = oqh.create_kernel(
                            'RB_{}Cl_s{}_net{}_inter{}'.format(
                                int(n_cl), seed, net_clifford,
                                interleaving_cl), p)
                        if initialize:
                            for qubit_idx in qubit_map.values():
                                k.prepz(qubit_idx)

                        for gates in cl_seq_decomposed_with_net:
                            for g, q in gates:
                                if isinstance(q, str):
                                    k.gate(g, [qubit_map[q]])
                                elif isinstance(q, list):
                                    if sim_cz_qubits is None:
                                        k.gate("wait",
                                               list(qubit_map.values()), 0)
                                        k.gate(
                                            flux_codeword,
                                            list(qubit_map.values()),
                                        )  # fix for QCC
                                        k.gate("wait",
                                               list(qubit_map.values()), 0)
                                    else:
                                        # A simultaneous CZ is applied to characterize cz gates that
                                        # have been calibrated to be used in parallel.
                                        k.gate(
                                            "wait",
                                            list(qubit_map.values()) +
                                            sim_cz_qubits, 0)
                                        k.gate(
                                            flux_codeword,
                                            list(qubit_map.values()),
                                        )  # fix for QCC
                                        k.gate(flux_codeword,
                                               sim_cz_qubits)  # fix for QCC
                                        k.gate(
                                            "wait",
                                            list(qubit_map.values()) +
                                            sim_cz_qubits, 0)

                        # FIXME: 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)
                elif simultaneous_single_qubit_RB:
                    for net_clifford in net_cliffords:
                        k = oqh.create_kernel(
                            'RB_{}Cl_s{}_net{}_inter{}'.format(
                                int(n_cl), seed, net_clifford,
                                interleaving_cl), p)
                        if initialize:
                            for qubit_idx in qubit_map.values():
                                k.prepz(qubit_idx)

                        # FIXME: Gate seqs is a hack for failing openql scheduling
                        gate_seqs = [[], []]
                        for gsi, q_idx in enumerate(qubits):
                            cl_seq = rb.randomized_benchmarking_sequence(
                                n_cl,
                                number_of_qubits=1,
                                desired_net_cl=net_clifford,
                                interleaving_cl=interleaving_cl)
                            for cl in cl_seq:
                                gates = Cl(cl).gate_decomposition
                                # for g, q in gates:
                                #     k.gate(g, q_idx)

                                # FIXME: THIS is a hack because of OpenQL
                                # scheduling issues #157

                                gate_seqs[gsi] += gates
                        # OpenQL #157 HACK
                        l = max([len(gate_seqs[0]), len(gate_seqs[1])])

                        for gi in range(l):
                            for gj, q_idx in enumerate(qubits):
                                # gj = 0
                                # q_idx = 0
                                try:  # for possible different lengths in gate_seqs
                                    g = gate_seqs[gj][gi]
                                    k.gate(g[0], [q_idx])
                                except IndexError as e:
                                    pass
                        # end of #157 HACK
                        # FIXME: 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 = oqh.add_single_qubit_cal_points(
                    p, 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 = oqh.add_multi_q_cal_points(p,
                                               qubits=qubits,
                                               combinations=combinations)

    p = oqh.compile(p)
    return p
Example #17
0
def randomized_renchmarking_seqs(qb_name,
                                 operation_dict,
                                 cliffords,
                                 nr_seeds=None,
                                 net_clifford=0,
                                 gate_decomposition='HZ',
                                 interleaved_gate=None,
                                 upload=True,
                                 cl_sequence=None,
                                 sampling_seeds=None,
                                 cal_points=None,
                                 prep_params=dict()):
    """
    Args
        qb_name (str): name of qubit
        operation_dict (dict): dict with all pulse dicts of qubit
        cliffords (array): array of ints specifying the number of random
            Cliffords to generate in each sequence
        nr_seeds (array): array of the form np.arange(nr_seeds_value)
        net_clifford (int): 0 or 1; whether the recovery Clifford returns
            qubits to ground statea (0) or puts them in the excited states (1)
        gate_decomposition (str): the decomposition of Clifford gates
            into primitives; can be "XY", "HZ", or "5Primitives"
        interleaved_gate (str): pycqed name for a gate
        upload (bool): whether to upload sequence to AWGs
        cl_sequence (list): the Clifford sequence to use for all seeds. Can
            also be lists of lists in which case the user must ensure that
            len(nr seeds) % len(cl_sequence) == 0.
        sampling_seeds (array of ints): ints that will be used as seeds for
            the random generation of Cliffords. Should have the same length
            as nr_seeds.
        cal_points (CalibrationPoints): instance of CalibrationPoints
        prep_params (dict): qubit preparation_params dict
    """
    seq_name = '1Qb_RB_sequence'
    if sampling_seeds is None:
        if nr_seeds is None:
            raise ValueError('Please provide either "sampling_seeds" or '
                             '"nr_seeds."')
        sampling_seeds = [None] * len(nr_seeds)
    else:
        nr_seeds = np.arange(len(sampling_seeds))

    if cl_sequence is not None:
        if isinstance(cl_sequence[0], list):
            # if cl_sequence is a list of lists such that
            # len(nr_seeds) != len(cl_sequence) but
            # len(nr_seeds) % len(cl_sequence) == 0,
            # then create as many copies of the lists in cl_sequence until
            # len(cl_sequence) == len(nr_seeds).
            assert len(nr_seeds) % len(cl_sequence) == 0
            k = len(nr_seeds) // len(cl_sequence)
            cl_seq_temp = k * cl_sequence

    sequences = []
    for nCl in cliffords:
        pulse_list_list_all = []
        for s in nr_seeds:
            if cl_sequence is None:
                cl_seq = rb.randomized_benchmarking_sequence(
                    nCl,
                    desired_net_cl=net_clifford,
                    interleaved_gate=interleaved_gate,
                    seed=sampling_seeds[s])
            elif isinstance(cl_sequence[0], list):
                cl_seq = cl_seq_temp[s]
            else:
                cl_seq = cl_sequence

            pulse_keys = rb.decompose_clifford_seq(
                cl_seq, gate_decomp=gate_decomposition)
            # to avoid having only virtual gates in segment:
            pulse_keys = ['I'] + pulse_keys
            pulse_list = [
                operation_dict[x + ' ' + qb_name] for x in pulse_keys
            ]
            pulse_list += [operation_dict['RO ' + qb_name]]
            pulse_list_w_prep = add_preparation_pulses(pulse_list,
                                                       operation_dict,
                                                       [qb_name],
                                                       **prep_params)
            pulse_list_list_all.append(pulse_list_w_prep)
        seq = pulse_list_list_seq(pulse_list_list_all,
                                  seq_name + f'_{nCl}',
                                  upload=False)
        if cal_points is not None:
            seq.extend(
                cal_points.create_segments(operation_dict, **prep_params))
        sequences.append(seq)

    # reuse sequencer memory by repeating readout pattern
    [s.repeat_ro(f"RO {qb_name}", operation_dict) for s in sequences]

    if upload:
        ps.Pulsar.get_instance().program_awgs(sequences[0])

    return sequences, np.arange(sequences[0].n_acq_elements()), \
           np.arange(len(cliffords))
def Randomized_Benchmarking_seq(pulse_pars,
                                RO_pars,
                                nr_cliffords,
                                nr_seeds,
                                net_clifford=0,
                                post_msmt_delay=3e-6,
                                cal_points=True,
                                resetless=False,
                                double_curves=False,
                                seq_name=None,
                                verbose=False,
                                upload=True):
    '''
    Input pars:
        pulse_pars:    dict containing pulse pars
        RO_pars:       dict containing RO pars
        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 corresponds to Identity and 3 corresponds to X180
        post_msmt_delay:
        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)
        resetless:     bool if False will append extra Id element if seq
                       is longer than 50us to ensure proper initialization
        double_curves: Alternates between net clifford 0 and 3
        upload:        Upload to the AWG

    returns:
        seq, elements_list


    Conventional use:
        nr_cliffords = [n1, n2, n3 ....]
        cal_points = True
    Optimization use (resetless or not):
        nr_cliffords = [n] is a list with a single entry
        cal_points = False
        net_clifford = 3 (optional) make sure it does a net pi-pulse
        post_msmt_delay is set (optional)
        resetless (optional)
    '''
    if seq_name is None:
        seq_name = 'RandomizedBenchmarking_sequence'
    seq = sequence.Sequence(seq_name)
    el_list = []
    pulses = get_pulse_dict_from_pars(pulse_pars)
    net_cliffords = [0, 3]  # Exists purely for the double curves mode
    i = 0
    for seed in range(nr_seeds):
        for j, n_cl in enumerate(nr_cliffords):
            if double_curves:
                net_clifford = net_cliffords[i % 2]
            i += 1  # only used for ensuring unique elt names

            if cal_points and (j == (len(nr_cliffords) - 4)
                               or j == (len(nr_cliffords) - 3)):
                el = multi_pulse_elt(i, station, [pulses['I'], RO_pars])
            elif cal_points and (j == (len(nr_cliffords) - 2)
                                 or j == (len(nr_cliffords) - 1)):
                el = multi_pulse_elt(i, station, [pulses['X180'], RO_pars])
            else:
                cl_seq = rb.randomized_benchmarking_sequence(
                    n_cl, desired_net_cl=net_clifford)
                pulse_keys = rb.decompose_clifford_seq(cl_seq)
                pulse_list = [pulses[x] for x in pulse_keys]
                pulse_list += [RO_pars]
                # copy first element and set extra wait
                pulse_list[0] = deepcopy(pulse_list[0])
                pulse_list[0]['pulse_delay'] += post_msmt_delay
                el = multi_pulse_elt(i, station, pulse_list)
            el_list.append(el)
            seq.append_element(el, trigger_wait=True)

            # If the element is too long, add in an extra wait elt
            # to skip a trigger
            if resetless and n_cl * pulse_pars['pulse_delay'] * 1.875 > 50e-6:
                el = multi_pulse_elt(i, station, [pulses['I']])
                el_list.append(el)
                seq.append_element(el, trigger_wait=True)
    if upload:
        station.pulsar.program_awgs(seq, *el_list, verbose=verbose)
        return seq, el_list
    else:
        return seq, el_list
 def test_net_cliff(self):
     for i in range(len(Clifford_group)):
         rb_seq = rb.randomized_benchmarking_sequence(500, desired_net_cl=i)
         net_cliff = rb.calculate_net_clifford(rb_seq)
         self.assertEqual(net_cliff, i)
def Randomized_Benchmarking_seq(pulse_pars, RO_pars,
                                nr_cliffords,
                                nr_seeds,
                                net_clifford=0,
                                post_msmt_delay=3e-6,
                                cal_points=True,
                                resetless=False,
                                double_curves=False,
                                seq_name=None,
                                verbose=False, upload=True):
    '''
    Input pars:
        pulse_pars:    dict containing pulse pars
        RO_pars:       dict containing RO pars
        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 corresponds to Identity and 3 corresponds to X180
        post_msmt_delay:
        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)
        resetless:     bool if False will append extra Id element if seq
                       is longer than 50us to ensure proper initialization
        double_curves: Alternates between net clifford 0 and 3
        upload:        Upload to the AWG

    returns:
        seq, elements_list


    Conventional use:
        nr_cliffords = [n1, n2, n3 ....]
        cal_points = True
    Optimization use (resetless or not):
        nr_cliffords = [n] is a list with a single entry
        cal_points = False
        net_clifford = 3 (optional) make sure it does a net pi-pulse
        post_msmt_delay is set (optional)
        resetless (optional)
    '''
    if seq_name is None:
        seq_name = 'RandomizedBenchmarking_sequence'
    seq = sequence.Sequence(seq_name)
    station.pulsar.update_channel_settings()
    el_list = []
    pulses = get_pulse_dict_from_pars(pulse_pars)
    net_cliffords = [0, 3]  # Exists purely for the double curves mode
    i = 0
    for seed in range(nr_seeds):
        for j, n_cl in enumerate(nr_cliffords):
            if double_curves:
                net_clifford = net_cliffords[i%2]
            i += 1  # only used for ensuring unique elt names

            if cal_points and (j == (len(nr_cliffords)-4) or
                               j == (len(nr_cliffords)-3)):
                el = multi_pulse_elt(i, station,
                                     [pulses['I'], RO_pars])
            elif cal_points and (j == (len(nr_cliffords)-2) or
                                 j == (len(nr_cliffords)-1)):
                el = multi_pulse_elt(i, station,
                                     [pulses['X180'], RO_pars])
            else:
                cl_seq = rb.randomized_benchmarking_sequence(
                    n_cl, desired_net_cl=net_clifford)
                pulse_keys = rb.decompose_clifford_seq(cl_seq)
                pulse_list = [pulses[x] for x in pulse_keys]
                pulse_list += [RO_pars]
                # copy first element and set extra wait
                pulse_list[0] = deepcopy(pulse_list[0])
                pulse_list[0]['pulse_delay'] += post_msmt_delay
                el = multi_pulse_elt(i, station, pulse_list)
            el_list.append(el)
            seq.append_element(el, trigger_wait=True)

            # If the element is too long, add in an extra wait elt
            # to skip a trigger
            if resetless and n_cl*pulse_pars['pulse_delay']*1.875 > 50e-6:
                el = multi_pulse_elt(i, station, [pulses['I']])
                el_list.append(el)
                seq.append_element(el, trigger_wait=True)
    if upload:
        station.components['AWG'].stop()
        station.pulsar.program_awg(seq, *el_list, verbose=verbose)
        return seq, el_list
    else:
        return seq, el_list
Example #21
0
 def test_net_cliff(self):
     for i in range(len(clifford_group_single_qubit)):
         rb_seq = rb.randomized_benchmarking_sequence(500, desired_net_cl=i)
         net_cliff = rb.calculate_net_clifford(rb_seq).idx
         self.assertEqual(net_cliff, i)
Example #22
0
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