def generate_sequence(self, config):
        """Generate sequence by adding gates/pulses to waveforms."""
        # get parameters
        n_pulse = int(config['# of pi pulses'])
        pi_to_q = config['Add pi pulses to Q']
        duration = config['Sequence duration']
        edge_to_edge = config['Edge-to-edge pulses']
        manipulated_qubits = config['Manipulated Qubits']
        
        d = dict(
            Zero=0,
            One=1,
            Two=2,
            Three=3,
            Four=4,
            Five=5,
            Six=6,
            Seven=7,
            Eight=8,
            Nine=9)
        if manipulated_qubits=='All':
            qubit = list(range((self.n_qubit)))
        else:
            qubit = [d[manipulated_qubits]-1]
        
        # select type of refocusing pi pulse
        gate_pi = [(gates.Yp if pi_to_q else gates.Xp) for n in range(len(qubit))]
        gate_X2p = [gates.X2p for n in range(len(qubit))]
        
        # always do T1 same way, regardless if edge-to-edge or center-center
        if n_pulse < 0:
            self.add_gate(qubit,gate_pi)
            self.add_gate_to_all(gates.IdentityGate(width=duration), dt=0)

        elif edge_to_edge:
            # edge-to-edge pulsing, set pulse separations
            self.add_gate(qubit,gate_X2p)
            # for ramsey, just add final pulse
            if n_pulse == 0:
                self.add_gate(qubit,gate_X2p, dt=duration)
            else:
                dt = duration / n_pulse
                # add first pi pulse after half duration
                self.add_gate(qubit,gate_pi, dt=dt/2)
                # add rest of pi pulses
                for i in range(n_pulse - 1):
                    self.add_gate(qubit,gate_pi, dt=dt)
                # add final pi/2 pulse
                self.add_gate(qubit,gate_X2p, dt=dt/2)

        else:
            # center-to-center spacing, set absolute pulse positions
            self.add_gate(qubit,gate_X2p, t0=0)
            # add pi pulses at right position
            for i in range(n_pulse):
                self.add_gate(qubit,gate_pi,
                                     t0=(i + 0.5) * (duration / n_pulse))
            # add final pi/2 pulse
            self.add_gate(qubit,gate_X2p, t0=duration)
    def generate_sequence(self, config):
        """Generate sequence by adding gates/pulses to waveforms."""
        # get parameters
        n_pulse = int(config['# of pi pulses'])
        pi_to_q = config['Add pi pulses to Q']
        duration = config['Sequence duration']
        edge_to_edge = config['Edge-to-edge pulses']
        if config['Add last pi/2 pulse to Q']:
            pi2_final = gates.Y2p
        else:
            pi2_final = gates.X2p

        # select type of refocusing pi pulse
        gate_pi = gates.Yp if pi_to_q else gates.Xp

        # always do T1 same way, regardless if edge-to-edge or center-center
        if n_pulse < 0:
            self.add_gate_to_all(gate_pi)
            self.add_gate_to_all(gates.IdentityGate(width=duration), dt=0)

        elif edge_to_edge:
            # edge-to-edge pulsing, set pulse separations
            self.add_gate_to_all(gates.X2p)
            # for ramsey, just add final pulse
            if n_pulse == 0:
                self.add_gate_to_all(gates.X2p, dt=duration)
            else:
                dt = duration / n_pulse
                # add first pi pulse after half duration
                self.add_gate_to_all(gate_pi, dt=dt / 2)
                # add rest of pi pulses
                for i in range(n_pulse - 1):
                    self.add_gate_to_all(gate_pi, dt=dt)
                # add final pi/2 pulse
                self.add_gate_to_all(pi2_final, dt=dt / 2)

        else:
            # center-to-center spacing, set absolute pulse positions
            self.add_gate_to_all(gates.X2p, t0=0)
            # add pi pulses at right position
            for i in range(n_pulse):
                self.add_gate_to_all(gate_pi,
                                     t0=(i + 0.5) * (duration / n_pulse))
            # add final pi/2 pulse
            self.add_gate_to_all(pi2_final, t0=duration)
    def generate_sequence(self, config):
        """
        Generate sequence by adding gates/pulses to waveforms.

        Parameters
        ----------
        config: dict
            configuration

        Returns
        -------
        """

        # get parameters

        sequence = config['Sequence']
        qubits_to_benchmark = [int(config['Qubits to Benchmark'][0]) - 1,
                               int(config['Qubits to Benchmark'][2]) - 1]
        # Number of Cliffords to generate
        N_cliffords = int(config['Number of Cliffords'])
        randomize = config['Randomize']
        interleave = config['Interleave 2-QB Gate']
        multi_seq = config.get('Output multiple sequences', False)
        write_seq = config.get('Write sequence as txt file', False)
        generator = config.get('Native 2-QB gate', 'CZ')
        
        rnd.seed(randomize)
        if interleave is True:
            interleaved_gate = config['Interleaved 2-QB Gate']
        else:
            interleaved_gate = np.inf

        # generate new randomized clifford gates only if configuration changes
        if (self.prev_sequence != sequence or
                self.prev_randomize != randomize or
                self.prev_N_cliffords != N_cliffords or
                self.prev_interleave != interleave or
                multi_seq or
                self.prev_interleaved_gate != interleaved_gate or
                self.generator != generator):

            self.prev_randomize = randomize
            self.prev_N_cliffords = N_cliffords
            self.prev_interleave = interleave
            self.prev_sequence = sequence
            self.generator = generator

            multi_gate_seq = []

            # Generate 2QB RB sequence
            cliffordSeq1 = []
            cliffordSeq2 = []
            for j in range(N_cliffords):
                log.info('Seed number: %d'%(randomize))
                rndnum = rnd.randint(0, 11519)
                # rndnum = rnd.randint(0, 576) #Only applying single qubit gates
                add_twoQ_clifford(rndnum, cliffordSeq1, cliffordSeq2, generator = generator)
                # If interleave gate,
                if interleave is True:
                    self.prev_interleaved_gate = interleaved_gate
                    if interleaved_gate == 'CZ':
                        cliffordSeq1.append(gates.I)
                        cliffordSeq2.append(gates.CZ)
                    elif interleaved_gate == 'CZEcho':
                        # CZEcho is a composite gate, so get each gate
                        gate = gates.CZEcho
                        for g in gate.sequence:
                            cliffordSeq1.append(g[1])
                            cliffordSeq2.append(g[0])
                    elif interleaved_gate == 'iSWAP':
                        gate = gates.iSWAP
                        for g in gate.sequence:
                            cliffordSeq1.append(g[1])
                            cliffordSeq2.append(g[0])
                    elif interleaved_gate == 'I':
                        # TBA: adjust the duration of I gates?
                        # log.info('Qubits to benchmark: ' + str(qubits_to_benchmark))
                        # gate = gates.I(width = self.pulses_2qb[qubit]).value
                        I_2QB = gates.IdentityGate(width =config.get('Width, 2QB'))

                        cliffordSeq1.append(I_2QB)
                        cliffordSeq2.append(I_2QB)
                        # cliffordSeq1.append(gates.I)
                        # cliffordSeq2.append(gates.I)


            # remove redundant Identity gates for cliffordSeq1
            index_identity_clifford = [] # find where Identity gates are
            for p in range(len(cliffordSeq1)):
                if (cliffordSeq1[p] == gates.I and cliffordSeq2[p] == gates.I):
                    index_identity_clifford.append(p)
            cliffordSeq1 = [m for n, m in enumerate(cliffordSeq1) if n not in index_identity_clifford]
            cliffordSeq2 = [m for n, m in enumerate(cliffordSeq2) if n not in index_identity_clifford]

            # get recovery gate seq
            (recoverySeq1, recoverySeq2) = self.get_recovery_gate(
                cliffordSeq1, cliffordSeq2, config, generator = generator)

            # Remove redundant identity gates in recovery gate seq
            index_identity_recovery = [] # find where Identity gates are
            for p in range(len(recoverySeq1)):
                if (recoverySeq1[p] == gates.I and recoverySeq2[p] == gates.I):
                    index_identity_recovery.append(p)
            recoverySeq1 = [m for n, m in enumerate(recoverySeq1) if n not in index_identity_recovery]
            recoverySeq2 = [m for n, m in enumerate(recoverySeq2) if n not in index_identity_recovery]

            # Construct the total gate sequence.
            gateSeq1 = []
            gateSeq2 = []
            gateSeq1.extend(cliffordSeq1)
            gateSeq1.extend(recoverySeq1)
            gateSeq2.extend(cliffordSeq2)
            gateSeq2.extend(recoverySeq2)

            # Avoid Error: zero-size array to reduction operation maximum which has no identity (05/05/2019)
            if (gateSeq1 == [] and gateSeq2 == []):
                gateSeq1.append(gates.I)
                gateSeq2.append(gates.I)

            # test the recovery gate
            psi_gnd = np.matrix('1; 0; 0; 0') # ground state |00>
            if write_seq == True:
                import os
                from datetime import datetime
                directory = os.path.join(path_currentdir,'2QB_RBseq')
                if not os.path.exists(directory):
                    os.makedirs(directory)
                filename = datetime.now().strftime('%Y-%m-%d %H-%M-%S-f')[:-3] + '_N_cliffords=%d_seed=%d.txt'%(N_cliffords,randomize)
                # filename = datetime.now().strftime('%Y-%m-%d %H-%M-%S-%f')[:-3] + '_N_cliffords=%d_seed=%d.txt'%(N_cliffords,randomize)
                filepath = os.path.join(directory,filename)
                log.info('make file: ' + filepath)
                with open(filepath, "w") as text_file:
                    print('New Sequence', file=text_file)
                    for i in range(len(gateSeq1)):
                        print("Index: %d, Gate: ["%(i) + cliffords.Gate_to_strGate(gateSeq1[i]) + ", " + cliffords.Gate_to_strGate(gateSeq2[i]) +']', file=text_file)
                    for i in range(len(cliffordSeq1)):
                         print("CliffordIndex: %d, Gate: ["%(i) + cliffords.Gate_to_strGate(cliffordSeq1[i]) + ", " + cliffords.Gate_to_strGate(cliffordSeq2[i]) +']', file=text_file)
                    for i in range(len(recoverySeq1)):
                         print("RecoveryIndex: %d, Gate: ["%(i) + cliffords.Gate_to_strGate(recoverySeq1[i]) + ", " + cliffords.Gate_to_strGate(recoverySeq2[i]) +']', file=text_file)
            psi = np.matmul(self.evaluate_sequence(gateSeq1, gateSeq2), psi_gnd)

            np.set_printoptions(precision=2)
            log.info('The matrix of the overall gate sequence:')
            log.info(self.evaluate_sequence(gateSeq1, gateSeq2))

            log.info('--- TESTING THE RECOVERY GATE ---')
            log.info('The probability amplitude of the final state vector: ' + str(np.matrix(psi).flatten()))
            log.info('The population of the ground state after the gate sequence: %.4f'%(np.abs(psi[0,0])**2))
            log.info('-------------------------------------------')

            # Assign two qubit gate sequence to where we want
            # for i in range(qubits_to_benchmark[0] - 1):
            #     multi_gate_seq.append([None] * len(gateSeq1))
            multi_gate_seq.append(gateSeq2)
            multi_gate_seq.append(gateSeq1)
            # for i in range(self.n_qubit - qubits_to_benchmark[1]):
            #     multi_gate_seq.append([None] * len(gateSeq1))

            # transpose list of lists
            multi_gate_seq = list(map(list, itertools.zip_longest(*multi_gate_seq, fillvalue=gates.I))) # Not to chop

            # self.add_gates(multi_gate_seq)
            for gate_seq in multi_gate_seq:
                if ((gate_seq[0] == gates.CZ) or (gate_seq[0] == gates.iSWAP)):
                    self.add_gate(qubit=qubits_to_benchmark, gate=gate_seq[0])
                else:
                    self.add_gate(qubit=qubits_to_benchmark, gate=gate_seq)
            self.prev_gate_seq = multi_gate_seq
        else:
            for gate_seq in self.prev_gate_seq:
                if gate_seq[0] == gates.CZ:
                    self.add_gate(qubit=qubits_to_benchmark, gate=gate_seq[0])
                else:
                    self.add_gate(qubit=qubits_to_benchmark, gate=gate_seq)
    def generate_sequence(self, config):
        """Generate sequence by adding gates/pulses to waveforms."""
        # get parameters
        n_pulse = int(config['# of pi pulses'])
        pi_to_q = config['Add pi pulses to Q']
        duration = config['Sequence duration']
        edge_to_edge = config['Edge-to-edge pulses']
        measure_2nd = bool(config['Measure T1 for 2nd Excited State'])
        # select type of refocusing pi pulse
        if (measure_2nd == True):
            gate_pi = gates.Yp_12 if pi_to_q else gates.Xp_12
        else:
            gate_pi = gates.Yp if pi_to_q else gates.Xp

        # always do T1 same way, regardless if edge-to-edge or center-center
        if n_pulse < 0:
            self.add_gate_to_all(gates.Xp)
            if (measure_2nd == True):
                self.add_gate_to_all(gates.Xp_12)
                # self.add_gate_to_all(gate_pi)
                # self.add_gate(qubit=2, gate=gates.Xp_12)
            self.add_gate_to_all(gates.IdentityGate(width=duration), dt=0)

        elif edge_to_edge:
            # edge-to-edge pulsing, set pulse separations
            if (measure_2nd == True):
                self.add_gate_to_all(gates.Xp)
                self.add_gate_to_all(gates.X2p_12)
            else:
                self.add_gate_to_all(gates.X2p)
            # for ramsey, just add final pulse
            if n_pulse == 0:
                if (measure_2nd == True):
                    self.add_gate_to_all(gates.X2p_12, dt=duration)
                else:
                    self.add_gate_to_all(gates.X2p, dt=duration)
            else:
                dt = duration / n_pulse
                # add first pi pulse after half duration
                self.add_gate_to_all(gate_pi, dt=dt / 2)
                # add rest of pi pulses
                for i in range(n_pulse - 1):
                    self.add_gate_to_all(gate_pi, dt=dt)

                if (measure_2nd == True):
                    # add final pi/2 pulse
                    self.add_gate_to_all(gates.X2p_12, dt=dt / 2)
                else:
                    # add final pi/2 pulse
                    self.add_gate_to_all(gates.X2p, dt=dt / 2)

        else:
            if (measure_2nd == True):
                self.add_gate_to_all(gates.Xp)
                # center-to-center spacing, set absolute pulse positions
                self.add_gate_to_all(gates.X2p_12, t0=0)
                # add pi pulses at right position
                for i in range(n_pulse):
                    self.add_gate_to_all(gate_pi,
                                         t0=(i + 0.5) * (duration / n_pulse))
                # add final pi/2 pulse
                self.add_gate_to_all(gates.X2p_12, t0=duration)

            else:
                # center-to-center spacing, set absolute pulse positions
                self.add_gate_to_all(gates.X2p, t0=0)
                # add pi pulses at right position
                for i in range(n_pulse):
                    self.add_gate_to_all(gate_pi,
                                         t0=(i + 0.5) * (duration / n_pulse))
                # add final pi/2 pulse
                self.add_gate_to_all(gates.X2p, t0=duration)