def __init__(self): # |0> Zp rotation prep_zp = QuantumCircuit(1, name="PauliPrepZp") # |1> Zm rotation prep_zm = QuantumCircuit(1, name="PauliPrepZm") prep_zm.append(XGate(), [0]) # |+> Xp rotation prep_xp = QuantumCircuit(1, name="PauliPrepXp") prep_xp.append(HGate(), [0]) # |-> Xm rotation prep_xm = QuantumCircuit(1, name="PauliPrepXm") prep_xm.append(HGate(), [0]) prep_xm.append(ZGate(), [0]) # |+i> Yp rotation prep_yp = QuantumCircuit(1, name="PauliPrepYp") prep_yp.append(HGate(), [0]) prep_yp.append(SGate(), [0]) # |-i> Ym rotation prep_ym = QuantumCircuit(1, name="PauliPrepYm") prep_ym.append(HGate(), [0]) prep_ym.append(SdgGate(), [0]) super().__init__( [ prep_zp, prep_zm, prep_xp, prep_xm, prep_yp, prep_ym, ] )
def test_1_qubit_identities(self): """Tests identities for 1-qubit gates""" # T*X*T = X circ1 = QuantumCircuit(1) circ1.t(0) circ1.x(0) circ1.t(0) elem1 = CNOTDihedral(circ1) elem = CNOTDihedral(XGate()) self.assertEqual(elem1, elem, "Error: 1-qubit identity does not hold") # X*T*X = Tdg circ1 = QuantumCircuit(1) circ1.x(0) circ1.t(0) circ1.x(0) elem1 = CNOTDihedral(circ1) elem = CNOTDihedral(TdgGate()) self.assertEqual(elem1, elem, "Error: 1-qubit identity does not hold") # X*Tdg*X = T circ1 = QuantumCircuit(1) circ1.x(0) circ1.tdg(0) circ1.x(0) elem1 = CNOTDihedral(circ1) elem = CNOTDihedral(TGate()) self.assertEqual(elem1, elem, "Error: 1-qubit identity does not hold") # X*S*X = Sdg circ1 = QuantumCircuit(1) circ1.x(0) circ1.s(0) circ1.x(0) elem1 = CNOTDihedral(circ1) elem = CNOTDihedral(SdgGate()) self.assertEqual(elem1, elem, "Error: 1-qubit identity does not hold") # X*Sdg*X = S circ1 = QuantumCircuit(1) circ1.x(0) circ1.sdg(0) circ1.x(0) elem1 = CNOTDihedral(circ1) elem = CNOTDihedral(SGate()) self.assertEqual(elem1, elem, "Error: 1-qubit identity does not hold") # T*X*Tdg = S*X circ1 = QuantumCircuit(1) circ1.t(0) circ1.x(0) circ1.tdg(0) circ2 = QuantumCircuit(1) circ2.s(0) circ2.x(0) elem1 = CNOTDihedral(circ1) elem2 = CNOTDihedral(circ2) self.assertEqual(elem1, elem2, "Error: 1-qubit identity does not hold")
def make_oneq_cliffords(): """Make as list of 1q Cliffords""" ixyz_list = [g().to_matrix() for g in (IGate, XGate, YGate, ZGate)] ih_list = [g().to_matrix() for g in (IGate, HGate)] irs_list = [IGate().to_matrix(), SdgGate().to_matrix() @ HGate().to_matrix(), HGate().to_matrix() @ SGate().to_matrix()] oneq_cliffords = [Operator(ixyz @ ih @ irs) for ixyz in ixyz_list for ih in ih_list for irs in irs_list] return oneq_cliffords
def __init__(self): """Initialize Pauli measurement basis""" # Z-meas rotation meas_z = QuantumCircuit(1, name="PauliMeasZ") # X-meas rotation meas_x = QuantumCircuit(1, name="PauliMeasX") meas_x.append(HGate(), [0]) # Y-meas rotation meas_y = QuantumCircuit(1, name="PauliMeasY") meas_y.append(SdgGate(), [0]) meas_y.append(HGate(), [0]) super().__init__([meas_z, meas_x, meas_y])
def random_clifford_circuit(num_qubits, num_gates, gates='all', seed=None): """Generate a pseudo random Clifford circuit.""" if gates == 'all': if num_qubits == 1: gates = ['i', 'x', 'y', 'z', 'h', 's', 'sdg', 'v', 'w'] else: gates = [ 'i', 'x', 'y', 'z', 'h', 's', 'sdg', 'v', 'w', 'cx', 'cz', 'swap' ] instructions = { 'i': (IGate(), 1), 'x': (XGate(), 1), 'y': (YGate(), 1), 'z': (ZGate(), 1), 'h': (HGate(), 1), 's': (SGate(), 1), 'sdg': (SdgGate(), 1), 'v': (VGate(), 1), 'w': (WGate(), 1), 'cx': (CXGate(), 2), 'cz': (CZGate(), 2), 'swap': (SwapGate(), 2) } if isinstance(seed, np.random.Generator): rng = seed else: rng = np.random.default_rng(seed) samples = rng.choice(gates, num_gates) circ = QuantumCircuit(num_qubits) for name in samples: gate, nqargs = instructions[name] qargs = rng.choice(range(num_qubits), nqargs, replace=False).tolist() circ.append(gate, qargs) return circ
def random_clifford_circuit(num_qubits, num_gates, gates="all", seed=None): """Generate a pseudo random Clifford circuit.""" if gates == "all": if num_qubits == 1: gates = ["i", "x", "y", "z", "h", "s", "sdg", "v", "w"] else: gates = [ "i", "x", "y", "z", "h", "s", "sdg", "v", "w", "cx", "cz", "swap" ] instructions = { "i": (IGate(), 1), "x": (XGate(), 1), "y": (YGate(), 1), "z": (ZGate(), 1), "h": (HGate(), 1), "s": (SGate(), 1), "sdg": (SdgGate(), 1), "v": (VGate(), 1), "w": (WGate(), 1), "cx": (CXGate(), 2), "cz": (CZGate(), 2), "swap": (SwapGate(), 2), } if isinstance(seed, np.random.Generator): rng = seed else: rng = np.random.default_rng(seed) samples = rng.choice(gates, num_gates) circ = QuantumCircuit(num_qubits) for name in samples: gate, nqargs = instructions[name] qargs = rng.choice(range(num_qubits), nqargs, replace=False).tolist() circ.append(gate, qargs) return circ
def _gradient_states( self, state_op: StateFn, meas_op: Optional[OperatorBase] = None, target_params: Optional[Union[ParameterExpression, ParameterVector, List[ParameterExpression]]] = None ) -> ListOp: """Generate the gradient states. Args: state_op: The operator representing the quantum state for which we compute the gradient. meas_op: The operator representing the observable for which we compute the gradient. target_params: The parameters we are taking the gradient wrt: ω Returns: ListOp of StateFns as quantum circuits which are the states w.r.t. which we compute the gradient. If a parameter appears multiple times, one circuit is created per parameterized gates to compute the product rule. Raises: AquaError: If one of the circuits could not be constructed. TypeError: If the operators is of unsupported type. """ state_qc = deepcopy(state_op.primitive) # Define the working qubit to realize the linear combination of unitaries qr_work = QuantumRegister(1, 'work_qubit_lin_comb_grad') work_q = qr_work[0] if not isinstance(target_params, (list, np.ndarray)): target_params = [target_params] if len(target_params) > 1: states = None additional_qubits: Tuple[List[Qubit], List[Qubit]] = ([work_q], []) for param in target_params: if param not in state_qc._parameter_table.get_keys(): op = ~Zero @ One else: param_gates = state_qc._parameter_table[param] for m, param_occurence in enumerate(param_gates): coeffs, gates = self._gate_gradient_dict( param_occurence[0])[param_occurence[1]] # construct the states for k, gate_to_insert in enumerate(gates): grad_state = QuantumCircuit(*state_qc.qregs, qr_work) grad_state.compose(state_qc, inplace=True) # apply Hadamard on work_q self.insert_gate(grad_state, param_occurence[0], HGate(), qubits=[work_q]) # Fix work_q phase coeff_i = coeffs[k] sign = np.sign(coeff_i) is_complex = np.iscomplex(coeff_i) if sign == -1: if is_complex: self.insert_gate(grad_state, param_occurence[0], SdgGate(), qubits=[work_q]) else: self.insert_gate(grad_state, param_occurence[0], ZGate(), qubits=[work_q]) else: if is_complex: self.insert_gate(grad_state, param_occurence[0], SGate(), qubits=[work_q]) # Insert controlled, intercepting gate - controlled by |0> if isinstance(param_occurence[0], UGate): if param_occurence[1] == 0: self.insert_gate( grad_state, param_occurence[0], RZGate(param_occurence[0].params[2])) self.insert_gate(grad_state, param_occurence[0], RXGate(np.pi / 2)) self.insert_gate( grad_state, param_occurence[0], gate_to_insert, additional_qubits=additional_qubits) self.insert_gate(grad_state, param_occurence[0], RXGate(-np.pi / 2)) self.insert_gate( grad_state, param_occurence[0], RZGate(-param_occurence[0].params[2])) elif param_occurence[1] == 1: self.insert_gate( grad_state, param_occurence[0], gate_to_insert, after=True, additional_qubits=additional_qubits) else: self.insert_gate( grad_state, param_occurence[0], gate_to_insert, additional_qubits=additional_qubits) else: self.insert_gate( grad_state, param_occurence[0], gate_to_insert, additional_qubits=additional_qubits) grad_state.h(work_q) state = np.sqrt( np.abs(coeff_i)) * state_op.coeff * CircuitStateFn( grad_state) # Chain Rule parameter expressions gate_param = param_occurence[0].params[ param_occurence[1]] if meas_op: if gate_param == param: state = meas_op @ state else: if isinstance(gate_param, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param, param) state = (expr_grad * meas_op) @ state else: state = ~Zero @ One else: if gate_param == param: state = ListOp([state], combo_fn=partial( self._grad_combo_fn, state_op=state_op)) else: if isinstance(gate_param, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param, param) state = expr_grad * ListOp( [state], combo_fn=partial(self._grad_combo_fn, state_op=state_op)) else: state = ~Zero @ One if m == 0 and k == 0: op = state else: # Product Rule op += state if len(target_params) > 1: if not states: states = [op] else: states += [op] else: return op if len(target_params) > 1: return ListOp(states) else: return op
def _define(self): """V Gate definition.""" q = QuantumRegister(1, 'q') qc = QuantumCircuit(q) qc.data = [(SdgGate(), [q[0]], []), (HGate(), [q[0]], [])] self.definition = qc
def apply_grad_gate(circuit, gate, param_index, grad_gate, grad_coeff, qr_superpos, open_ctrl=False, trim_after_grad_gate=False): """Util function to apply a gradient gate for the linear combination of unitaries method. Replaces the ``gate`` instance in ``circuit`` with ``grad_gate`` using ``qr_superpos`` as superposition qubit. Also adds the appropriate sign-fix gates on the superposition qubit. Args: circuit (QuantumCircuit): The circuit in which to do the replacements. gate (Gate): The gate instance to replace. param_index (int): The index of the parameter in ``gate``. grad_gate (Gate): A controlled gate encoding the gradient of ``gate``. grad_coeff (float): A coefficient to the gradient component. Might not be one if the gradient contains multiple summed terms. qr_superpos (QuantumRegister): A ``QuantumRegister`` of size 1 contained in ``circuit`` that is used as control for ``grad_gate``. open_ctrl (bool): If True use an open control for ``grad_gate`` instead of closed. trim_after_grad_gate (bool): If True remove all gates after the ``grad_gate``. Can be used to reduce the circuit depth in e.g. computing an overlap of gradients. Returns: QuantumCircuit: A copy of the original circuit with the gradient gate added. Raises: RuntimeError: If ``gate`` is not in ``circuit``. """ # copy the input circuit taking the gates by reference out = QuantumCircuit(*circuit.qregs) out._data = circuit._data.copy() out._parameter_table = ParameterTable({ param: values.copy() for param, values in circuit._parameter_table.items() }) # get the data index and qubits of the target gate TODO use built-in gate_idx, gate_qubits = None, None for i, (op, qarg, _) in enumerate(out._data): if op is gate: gate_idx, gate_qubits = i, qarg break if gate_idx is None: raise RuntimeError('The specified gate could not be found in the circuit data.') # initialize replacement instructions replacement = [] # insert the phase fix before the target gate better documentation sign = np.sign(grad_coeff) is_complex = np.iscomplex(grad_coeff) if sign < 0 and is_complex: replacement.append((SdgGate(), qr_superpos[:], [])) elif sign < 0: replacement.append((ZGate(), qr_superpos[:], [])) elif is_complex: replacement.append((SGate(), qr_superpos[:], [])) # else no additional gate required # open control if specified if open_ctrl: replacement += [(XGate(), qr_superpos[:], [])] # compute the replacement if isinstance(gate, UGate) and param_index == 0: theta = gate.params[2] rz_plus, rz_minus = RZGate(theta), RZGate(-theta) replacement += [(rz_plus, [qubit], []) for qubit in gate_qubits] replacement += [(RXGate(np.pi / 2), [qubit], []) for qubit in gate_qubits] replacement.append((grad_gate, qr_superpos[:] + gate_qubits, [])) replacement += [(RXGate(-np.pi / 2), [qubit], []) for qubit in gate_qubits] replacement += [(rz_minus, [qubit], []) for qubit in gate_qubits] # update parametertable if necessary if isinstance(theta, ParameterExpression): out._update_parameter_table(rz_plus) out._update_parameter_table(rz_minus) if open_ctrl: replacement += [(XGate(), qr_superpos[:], [])] if not trim_after_grad_gate: replacement.append((gate, gate_qubits, [])) elif isinstance(gate, UGate) and param_index == 1: # gradient gate is applied after the original gate in this case replacement.append((gate, gate_qubits, [])) replacement.append((grad_gate, qr_superpos[:] + gate_qubits, [])) if open_ctrl: replacement += [(XGate(), qr_superpos[:], [])] else: replacement.append((grad_gate, qr_superpos[:] + gate_qubits, [])) if open_ctrl: replacement += [(XGate(), qr_superpos[:], [])] if not trim_after_grad_gate: replacement.append((gate, gate_qubits, [])) # replace the parameter we compute the derivative of with the replacement # TODO can this be done more efficiently? if trim_after_grad_gate: # remove everything after the gradient gate out._data[gate_idx:] = replacement # reset parameter table table = ParameterTable() for op, _, _ in out._data: for idx, param_expression in enumerate(op.params): if isinstance(param_expression, ParameterExpression): for param in param_expression.parameters: if param not in table.keys(): table[param] = [(op, idx)] else: table[param].append((op, idx)) out._parameter_table = table else: out._data[gate_idx:gate_idx + 1] = replacement return out
def apply_grad_gate( circuit, gate, param_index, grad_gate, grad_coeff, qr_superpos, open_ctrl=False, trim_after_grad_gate=False, ): """Util function to apply a gradient gate for the linear combination of unitaries method. Replaces the ``gate`` instance in ``circuit`` with ``grad_gate`` using ``qr_superpos`` as superposition qubit. Also adds the appropriate sign-fix gates on the superposition qubit. Args: circuit (QuantumCircuit): The circuit in which to do the replacements. gate (Gate): The gate instance to replace. param_index (int): The index of the parameter in ``gate``. grad_gate (Gate): A controlled gate encoding the gradient of ``gate``. grad_coeff (float): A coefficient to the gradient component. Might not be one if the gradient contains multiple summed terms. qr_superpos (QuantumRegister): A ``QuantumRegister`` of size 1 contained in ``circuit`` that is used as control for ``grad_gate``. open_ctrl (bool): If True use an open control for ``grad_gate`` instead of closed. trim_after_grad_gate (bool): If True remove all gates after the ``grad_gate``. Can be used to reduce the circuit depth in e.g. computing an overlap of gradients. Returns: QuantumCircuit: A copy of the original circuit with the gradient gate added. Raises: RuntimeError: If ``gate`` is not in ``circuit``. """ qr_superpos_qubits = tuple(qr_superpos) # copy the input circuit taking the gates by reference out = QuantumCircuit(*circuit.qregs) out._data = circuit._data.copy() out._parameter_table = ParameterTable({ param: values.copy() for param, values in circuit._parameter_table.items() }) # get the data index and qubits of the target gate TODO use built-in gate_idx, gate_qubits = None, None for i, instruction in enumerate(out._data): if instruction.operation is gate: gate_idx, gate_qubits = i, instruction.qubits break if gate_idx is None: raise RuntimeError( "The specified gate could not be found in the circuit data.") # initialize replacement instructions replacement = [] # insert the phase fix before the target gate better documentation sign = np.sign(grad_coeff) is_complex = np.iscomplex(grad_coeff) if sign < 0 and is_complex: replacement.append( CircuitInstruction(SdgGate(), qr_superpos_qubits, ())) elif sign < 0: replacement.append( CircuitInstruction(ZGate(), qr_superpos_qubits, ())) elif is_complex: replacement.append( CircuitInstruction(SGate(), qr_superpos_qubits, ())) # else no additional gate required # open control if specified if open_ctrl: replacement += [ CircuitInstruction(XGate(), qr_superpos_qubits, []) ] # compute the replacement if isinstance(gate, UGate) and param_index == 0: theta = gate.params[2] rz_plus, rz_minus = RZGate(theta), RZGate(-theta) replacement += [ CircuitInstruction(rz_plus, (qubit, ), ()) for qubit in gate_qubits ] replacement += [ CircuitInstruction(RXGate(np.pi / 2), (qubit, ), ()) for qubit in gate_qubits ] replacement.append( CircuitInstruction(grad_gate, qr_superpos_qubits + gate_qubits, [])) replacement += [ CircuitInstruction(RXGate(-np.pi / 2), (qubit, ), ()) for qubit in gate_qubits ] replacement += [ CircuitInstruction(rz_minus, (qubit, ), ()) for qubit in gate_qubits ] # update parametertable if necessary if isinstance(theta, ParameterExpression): # This dangerously subverts ParameterTable by abusing the fact that binding will # mutate the exact instruction instance, and relies on all instances of `rz_plus` # that were added before being the same in memory, which QuantumCircuit usually # ensures is not the case. I'm leaving this as close to its previous form as # possible, to avoid introducing further complications, but this whole method # accesses internal attributes of `QuantumCircuit` and needs rewriting. # - Jake Lishman, 2022-03-02. out._update_parameter_table( CircuitInstruction(rz_plus, (gate_qubits[0], ), ())) out._update_parameter_table( CircuitInstruction(rz_minus, (gate_qubits[0], ), ())) if open_ctrl: replacement.append( CircuitInstruction(XGate(), qr_superpos_qubits, ())) if not trim_after_grad_gate: replacement.append(CircuitInstruction(gate, gate_qubits, ())) elif isinstance(gate, UGate) and param_index == 1: # gradient gate is applied after the original gate in this case replacement.append(CircuitInstruction(gate, gate_qubits, ())) replacement.append( CircuitInstruction(grad_gate, qr_superpos_qubits + gate_qubits, ())) if open_ctrl: replacement.append( CircuitInstruction(XGate(), qr_superpos_qubits, ())) else: replacement.append( CircuitInstruction(grad_gate, qr_superpos_qubits + gate_qubits, ())) if open_ctrl: replacement.append( CircuitInstruction(XGate(), qr_superpos_qubits, ())) if not trim_after_grad_gate: replacement.append(CircuitInstruction(gate, gate_qubits, ())) # replace the parameter we compute the derivative of with the replacement # TODO can this be done more efficiently? if trim_after_grad_gate: # remove everything after the gradient gate out._data[gate_idx:] = replacement # reset parameter table table = ParameterTable() for instruction in out._data: for idx, param_expression in enumerate( instruction.operation.params): if isinstance(param_expression, ParameterExpression): for param in param_expression.parameters: if param not in table.keys(): table[param] = ParameterReferences( ((instruction.operation, idx), )) else: table[param].add((instruction.operation, idx)) out._parameter_table = table else: out._data[gate_idx:gate_idx + 1] = replacement return out
from qiskit.circuit.library import HGate, XGate, YGate, ZGate, CXGate, CYGate, CZGate, CHGate from qiskit.circuit.library import SwapGate, IGate, SGate, TGate, TdgGate, SdgGate, RYGate from qiskit import QuantumCircuit, Aer, execute, QuantumRegister, ClassicalRegister import numpy as np SINGLE_GATE_DICT = { 'I' : IGate(), 'H' : HGate(), 'X' : XGate(), 'Y' : YGate(), 'Z' : ZGate(), 'S' : SGate(), 'T' : TGate(), 'T_dg' : TdgGate(), 'S_dg' : SdgGate(), 'Ry' : RYGate(np.pi / 4) } CONTROLLED_GATE_DICT = { 'CX0' : CXGate(), 'CX1' : CXGate(), 'CY0' : CYGate(), 'CY1' : CYGate(), 'CZ0' : CZGate(), 'CZ1' : CYGate(), 'CH0' : CHGate(), 'CH1' : CHGate() } def _state_to_gates(state):
class TestPauli(QiskitTestCase): """Tests for Pauli operator class.""" @data(*pauli_group_labels(2)) def test_conjugate(self, label): """Test conjugate method.""" value = Pauli(label).conjugate() target = operator_from_label(label).conjugate() self.assertEqual(Operator(value), target) @data(*pauli_group_labels(2)) def test_transpose(self, label): """Test transpose method.""" value = Pauli(label).transpose() target = operator_from_label(label).transpose() self.assertEqual(Operator(value), target) @data(*pauli_group_labels(2)) def test_adjoint(self, label): """Test adjoint method.""" value = Pauli(label).adjoint() target = operator_from_label(label).adjoint() self.assertEqual(Operator(value), target) @data(*pauli_group_labels(2)) def test_inverse(self, label): """Test inverse method.""" pauli = Pauli(label) value = pauli.inverse() target = pauli.adjoint() self.assertEqual(value, target) @data(*it.product(pauli_group_labels(2, full_group=False), repeat=2)) @unpack def test_dot(self, label1, label2): """Test dot method.""" p1 = Pauli(label1) p2 = Pauli(label2) value = Operator(p1.dot(p2)) op1 = operator_from_label(label1) op2 = operator_from_label(label2) target = op1.dot(op2) self.assertEqual(value, target) @data(*pauli_group_labels(1)) def test_dot_qargs(self, label2): """Test dot method with qargs.""" label1 = '-iXYZ' p1 = Pauli(label1) p2 = Pauli(label2) qargs = [0] value = Operator(p1.dot(p2, qargs=qargs)) op1 = operator_from_label(label1) op2 = operator_from_label(label2) target = op1.dot(op2, qargs=qargs) self.assertEqual(value, target) @data(*it.product(pauli_group_labels(2, full_group=False), repeat=2)) @unpack def test_compose(self, label1, label2): """Test compose method.""" p1 = Pauli(label1) p2 = Pauli(label2) value = Operator(p1.compose(p2)) op1 = operator_from_label(label1) op2 = operator_from_label(label2) target = op1.compose(op2) self.assertEqual(value, target) @data(*pauli_group_labels(1)) def test_compose_qargs(self, label2): """Test compose method with qargs.""" label1 = '-XYZ' p1 = Pauli(label1) p2 = Pauli(label2) qargs = [0] value = Operator(p1.compose(p2, qargs=qargs)) op1 = operator_from_label(label1) op2 = operator_from_label(label2) target = op1.compose(op2, qargs=qargs) self.assertEqual(value, target) @data(*it.product(pauli_group_labels(1, full_group=False), repeat=2)) @unpack def test_tensor(self, label1, label2): """Test tensor method.""" p1 = Pauli(label1) p2 = Pauli(label2) value = Operator(p1.tensor(p2)) op1 = operator_from_label(label1) op2 = operator_from_label(label2) target = op1.tensor(op2) self.assertEqual(value, target) @data(*it.product(pauli_group_labels(1, full_group=False), repeat=2)) @unpack def test_expand(self, label1, label2): """Test expand method.""" p1 = Pauli(label1) p2 = Pauli(label2) value = Operator(p1.expand(p2)) op1 = operator_from_label(label1) op2 = operator_from_label(label2) target = op1.expand(op2) self.assertEqual(value, target) @data('II', 'XI', 'YX', 'ZZ', 'YZ') def test_power(self, label): """Test power method.""" iden = Pauli('II') op = Pauli(label) self.assertTrue(op**2, iden) @data(1, 1.0, -1, -1.0, 1j, -1j) def test_multiply(self, val): """Test multiply method.""" op = val * Pauli(([True, True], [False, False], 0)) phase = (-1j)**op.phase self.assertEqual(phase, val) def test_multiply_except(self): """Test multiply method raises exceptions.""" op = Pauli('XYZ') self.assertRaises(QiskitError, op._multiply, 2) @data(0, 1, 2, 3) def test_negate(self, phase): """Test negate method""" op = Pauli(([False], [True], phase)) neg = -op self.assertTrue(op.equiv(neg)) self.assertEqual(neg.phase, (op.phase + 2) % 4) @data(*it.product(pauli_group_labels(1, False), repeat=2)) @unpack def test_commutes(self, p1, p2): """Test commutes method""" P1 = Pauli(p1) P2 = Pauli(p2) self.assertEqual(P1.commutes(P2), P1.dot(P2) == P2.dot(P1)) @data(*it.product(pauli_group_labels(1, False), repeat=2)) @unpack def test_anticommutes(self, p1, p2): """Test anticommutes method""" P1 = Pauli(p1) P2 = Pauli(p2) self.assertEqual(P1.anticommutes(P2), P1.dot(P2) == -P2.dot(P1)) @data(*it.product( (IGate(), XGate(), YGate(), ZGate(), HGate(), SGate(), SdgGate()), pauli_group_labels(1, False))) @unpack def test_evolve_clifford1(self, gate, label): """Test evolve method for 1-qubit Clifford gates.""" cliff = Clifford(gate) op = Operator(gate) pauli = Pauli(label) value = Operator(pauli.evolve(cliff)) target = op.adjoint().dot(pauli).dot(op) self.assertEqual(value, target) @data(*it.product((CXGate(), CYGate(), CZGate(), SwapGate()), pauli_group_labels(2, False))) @unpack def test_evolve_clifford2(self, gate, label): """Test evolve method for 2-qubit Clifford gates.""" cliff = Clifford(gate) op = Operator(gate) pauli = Pauli(label) value = Operator(pauli.evolve(cliff)) target = op.adjoint().dot(pauli).dot(op) self.assertEqual(value, target) def test_evolve_clifford_qargs(self): """Test evolve method for random Clifford""" cliff = random_clifford(3, seed=10) op = Operator(cliff) pauli = random_pauli(5, seed=10) qargs = [3, 0, 1] value = Operator(pauli.evolve(cliff, qargs=qargs)) target = Operator(pauli).compose(op.adjoint(), qargs=qargs).dot(op, qargs=qargs) self.assertEqual(value, target)
def _define(self): """V Gate definition.""" q = QuantumRegister(1, 'q') self.definition = [(SdgGate(), [q[0]], []), (HGate(), [q[0]], [])]
def convert( self, operator: CircuitStateFn, params: Optional[Union[ParameterExpression, ParameterVector, List[ParameterExpression]]] = None, ) -> ListOp: r""" Args: operator: The operator corresponding to the quantum state :math:`|\psi(\omega)\rangle` for which we compute the QFI. params: The parameters :math:`\omega` with respect to which we are computing the QFI. Returns: A ``ListOp[ListOp]`` where the operator at position ``[k][l]`` corresponds to the matrix element :math:`k, l` of the QFI. Raises: AquaError: If one of the circuits could not be constructed. TypeError: If ``operator`` is an unsupported type. """ # QFI & phase fix observable qfi_observable = ~StateFn(4 * Z ^ (I ^ operator.num_qubits)) phase_fix_observable = ~StateFn((X + 1j * Y) ^ (I ^ operator.num_qubits)) # see https://arxiv.org/pdf/quant-ph/0108146.pdf # Check if the given operator corresponds to a quantum state given as a circuit. if not isinstance(operator, CircuitStateFn): raise TypeError( 'LinCombFull is only compatible with states that are given as CircuitStateFn' ) # If a single parameter is given wrap it into a list. if not isinstance(params, (list, np.ndarray)): params = [params] state_qc = operator.primitive # First, the operators are computed which can compensate for a potential phase-mismatch # between target and trained state, i.e.〈ψ|∂lψ〉 phase_fix_states = None # Add working qubit qr_work = QuantumRegister(1, 'work_qubit') work_q = qr_work[0] additional_qubits: Tuple[List[Qubit], List[Qubit]] = ([work_q], []) for param in params: # Get the gates of the given quantum state which are parameterized by param param_gates = state_qc._parameter_table[param] # Loop through the occurrences of param in the quantum state for m, param_occurence in enumerate(param_gates): # Get the coefficients and gates for the linear combination gradient for each # occurrence, see e.g. https://arxiv.org/abs/2006.06004 coeffs_i, gates_i = LinComb._gate_gradient_dict( param_occurence[0])[param_occurence[1]] # Construct the quantum states which are then evaluated for the respective QFI # element. for k, gate_to_insert_i in enumerate(gates_i): grad_state = state_qc.copy() grad_state.add_register(qr_work) # apply Hadamard on work_q LinComb.insert_gate(grad_state, param_occurence[0], HGate(), qubits=[work_q]) # Fix work_q phase such that the gradient is correct. coeff_i = coeffs_i[k] sign = np.sign(coeff_i) is_complex = np.iscomplex(coeff_i) if sign == -1: if is_complex: LinComb.insert_gate(grad_state, param_occurence[0], SdgGate(), qubits=[work_q]) else: LinComb.insert_gate(grad_state, param_occurence[0], ZGate(), qubits=[work_q]) else: if is_complex: LinComb.insert_gate(grad_state, param_occurence[0], SGate(), qubits=[work_q]) # Insert controlled, intercepting gate - controlled by |0> if isinstance(param_occurence[0], UGate): if param_occurence[1] == 0: LinComb.insert_gate( grad_state, param_occurence[0], RZGate(param_occurence[0].params[2])) LinComb.insert_gate(grad_state, param_occurence[0], RXGate(np.pi / 2)) LinComb.insert_gate( grad_state, param_occurence[0], gate_to_insert_i, additional_qubits=additional_qubits) LinComb.insert_gate(grad_state, param_occurence[0], RXGate(-np.pi / 2)) LinComb.insert_gate( grad_state, param_occurence[0], RZGate(-param_occurence[0].params[2])) elif param_occurence[1] == 1: LinComb.insert_gate( grad_state, param_occurence[0], gate_to_insert_i, after=True, additional_qubits=additional_qubits) else: LinComb.insert_gate( grad_state, param_occurence[0], gate_to_insert_i, additional_qubits=additional_qubits) else: LinComb.insert_gate( grad_state, param_occurence[0], gate_to_insert_i, additional_qubits=additional_qubits) # Remove unnecessary gates. grad_state = self.trim_circuit(grad_state, param_occurence[0]) # Apply the final Hadamard on the working qubit. grad_state.h(work_q) # Add the coefficient needed for the gradient as well as the original # coefficient of the given quantum state. state = np.sqrt(np.abs( coeff_i)) * operator.coeff * CircuitStateFn(grad_state) # Check if the gate parameter corresponding to param is a parameter expression gate_param = param_occurence[0].params[param_occurence[1]] if gate_param == param: state = phase_fix_observable @ state else: # If the gate parameter is a parameter expressions the chain rule needs # to be taken into account. if isinstance(gate_param, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param, param) state = (expr_grad * phase_fix_observable) @ state else: state *= 0 if m == 0 and k == 0: phase_fix_state = state else: # Take the product rule into account phase_fix_state += state # Create a list for the phase fix states if not phase_fix_states: phase_fix_states = [phase_fix_state] else: phase_fix_states += [phase_fix_state] # Get 4 * Re[〈∂kψ|∂lψ] qfi_operators = [] # Add a working qubit qr_work_qubit = QuantumRegister(1, 'work_qubit') work_qubit = qr_work_qubit[0] additional_qubits = ([work_qubit], []) # create a copy of the original circuit with an additional work_qubit register circuit = state_qc.copy() circuit.add_register(qr_work_qubit) # Apply a Hadamard on the working qubit LinComb.insert_gate(circuit, state_qc._parameter_table[params[0]][0][0], HGate(), qubits=[work_qubit]) # Get the circuits needed to compute〈∂iψ|∂jψ〉 for i, param_i in enumerate(params): # loop over parameters qfi_ops = None for j, param_j in enumerate(params): # Get the gates of the quantum state which are parameterized by param_i param_gates_i = state_qc._parameter_table[param_i] for m_i, param_occurence_i in enumerate(param_gates_i): coeffs_i, gates_i = LinComb._gate_gradient_dict( param_occurence_i[0])[param_occurence_i[1]] for k_i, gate_to_insert_i in enumerate(gates_i): coeff_i = coeffs_i[k_i] # Get the gates of the quantum state which are parameterized by param_j param_gates_j = state_qc._parameter_table[param_j] for m_j, param_occurence_j in enumerate(param_gates_j): coeffs_j, gates_j = \ LinComb._gate_gradient_dict(param_occurence_j[0])[ param_occurence_j[1]] for k_j, gate_to_insert_j in enumerate(gates_j): coeff_j = coeffs_j[k_j] # create a copy of the original circuit with the same registers qfi_circuit = QuantumCircuit(*circuit.qregs) qfi_circuit.data = circuit.data # Correct the phase of the working qubit according to coefficient i # and coefficient j sign = np.sign(np.conj(coeff_i) * coeff_j) is_complex = np.iscomplex( np.conj(coeff_i) * coeff_j) if sign == -1: if is_complex: LinComb.insert_gate( qfi_circuit, param_occurence_i[0], SdgGate(), qubits=[work_qubit]) else: LinComb.insert_gate( qfi_circuit, param_occurence_i[0], ZGate(), qubits=[work_qubit]) else: if is_complex: LinComb.insert_gate( qfi_circuit, param_occurence_i[0], SGate(), qubits=[work_qubit]) LinComb.insert_gate(qfi_circuit, param_occurence_i[0], XGate(), qubits=[work_qubit]) # Insert controlled, intercepting gate i - controlled by |1> if isinstance(param_occurence_i[0], UGate): if param_occurence_i[1] == 0: LinComb.insert_gate( qfi_circuit, param_occurence_i[0], RZGate(param_occurence_i[0]. params[2])) LinComb.insert_gate( qfi_circuit, param_occurence_i[0], RXGate(np.pi / 2)) LinComb.insert_gate( qfi_circuit, param_occurence_i[0], gate_to_insert_i, additional_qubits=additional_qubits ) LinComb.insert_gate( qfi_circuit, param_occurence_i[0], RXGate(-np.pi / 2)) LinComb.insert_gate( qfi_circuit, param_occurence_i[0], RZGate(-param_occurence_i[0]. params[2])) elif param_occurence_i[1] == 1: LinComb.insert_gate( qfi_circuit, param_occurence_i[0], gate_to_insert_i, after=True, additional_qubits=additional_qubits ) else: LinComb.insert_gate( qfi_circuit, param_occurence_i[0], gate_to_insert_i, additional_qubits=additional_qubits ) else: LinComb.insert_gate( qfi_circuit, param_occurence_i[0], gate_to_insert_i, additional_qubits=additional_qubits) LinComb.insert_gate(qfi_circuit, gate_to_insert_i, XGate(), qubits=[work_qubit], after=True) # Insert controlled, intercepting gate j - controlled by |0> if isinstance(param_occurence_j[0], UGate): if param_occurence_j[1] == 0: LinComb.insert_gate( qfi_circuit, param_occurence_j[0], RZGate(param_occurence_j[0]. params[2])) LinComb.insert_gate( qfi_circuit, param_occurence_j[0], RXGate(np.pi / 2)) LinComb.insert_gate( qfi_circuit, param_occurence_j[0], gate_to_insert_j, additional_qubits=additional_qubits ) LinComb.insert_gate( qfi_circuit, param_occurence_j[0], RXGate(-np.pi / 2)) LinComb.insert_gate( qfi_circuit, param_occurence_j[0], RZGate(-param_occurence_j[0]. params[2])) elif param_occurence_j[1] == 1: LinComb.insert_gate( qfi_circuit, param_occurence_j[0], gate_to_insert_j, after=True, additional_qubits=additional_qubits ) else: LinComb.insert_gate( qfi_circuit, param_occurence_j[0], gate_to_insert_j, additional_qubits=additional_qubits ) else: LinComb.insert_gate( qfi_circuit, param_occurence_j[0], gate_to_insert_j, additional_qubits=additional_qubits) # Remove redundant gates if j <= i: qfi_circuit = self.trim_circuit( qfi_circuit, param_occurence_i[0]) else: qfi_circuit = self.trim_circuit( qfi_circuit, param_occurence_j[0]) # Apply final Hadamard gate qfi_circuit.h(work_qubit) # Convert the quantum circuit into a CircuitStateFn and add the # coefficients i, j and the original operator coefficient term = np.sqrt( np.abs(coeff_i) * np.abs(coeff_j)) * operator.coeff term = term * CircuitStateFn(qfi_circuit) # Check if the gate parameters i and j are parameter expressions gate_param_i = param_occurence_i[0].params[ param_occurence_i[1]] gate_param_j = param_occurence_j[0].params[ param_occurence_j[1]] meas = deepcopy(qfi_observable) # If the gate parameter i is a parameter expression use the chain # rule. if isinstance(gate_param_i, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param_i, param_i) meas *= expr_grad # If the gate parameter j is a parameter expression use the chain # rule. if isinstance(gate_param_j, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param_j, param_j) meas *= expr_grad term = meas @ term if m_i == 0 and k_i == 0 and m_j == 0 and k_j == 0: qfi_op = term else: # Product Rule qfi_op += term # Compute −4 * Re(〈∂kψ|ψ〉〈ψ|∂lψ〉) def phase_fix_combo_fn(x): return 4 * (-0.5) * (x[0] * np.conjugate(x[1]) + x[1] * np.conjugate(x[0])) phase_fix = ListOp([phase_fix_states[i], phase_fix_states[j]], combo_fn=phase_fix_combo_fn) # Add the phase fix quantities to the entries of the QFI # Get 4 * Re[〈∂kψ|∂lψ〉−〈∂kψ|ψ〉〈ψ|∂lψ〉] if not qfi_ops: qfi_ops = [qfi_op + phase_fix] else: qfi_ops += [qfi_op + phase_fix] qfi_operators.append(ListOp(qfi_ops)) # Return the full QFI return ListOp(qfi_operators)
def _hessian_states( self, state_op: StateFn, meas_op: Optional[OperatorBase] = None, target_params: Optional[Union[Tuple[ParameterExpression, ParameterExpression], List[Tuple[ParameterExpression, ParameterExpression]]]] = None ) -> OperatorBase: """Generate the operator states whose evaluation returns the Hessian (items). Args: state_op: The operator representing the quantum state for which we compute the Hessian. meas_op: The operator representing the observable for which we compute the gradient. target_params: The parameters we are computing the Hessian wrt: ω Returns: Operators which give the Hessian. If a parameter appears multiple times, one circuit is created per parameterized gates to compute the product rule. Raises: AquaError: If one of the circuits could not be constructed. TypeError: If ``operator`` is of unsupported type. """ state_qc = deepcopy(state_op.primitive) if isinstance(target_params, list) and isinstance( target_params[0], tuple): tuples_list = deepcopy(target_params) target_params = [] for tuples in tuples_list: if all([ param in state_qc._parameter_table.get_keys() for param in tuples ]): for param in tuples: if param not in target_params: target_params.append(param) elif isinstance(target_params, tuple): tuples_list = deepcopy([target_params]) target_params = [] for tuples in tuples_list: if all([ param in state_qc._parameter_table.get_keys() for param in tuples ]): for param in tuples: if param not in target_params: target_params.append(param) else: raise TypeError( 'Please define in the parameters for which the Hessian is evaluated either ' 'as parameter tuple or a list of parameter tuples') qr_add0 = QuantumRegister(1, 'work_qubit0') work_q0 = qr_add0[0] qr_add1 = QuantumRegister(1, 'work_qubit1') work_q1 = qr_add1[0] # create a copy of the original circuit with an additional working qubit register circuit = state_qc.copy() circuit.add_register(qr_add0, qr_add1) # Get the circuits needed to compute the Hessian hessian_ops = None for param_a, param_b in tuples_list: if param_a not in state_qc._parameter_table.get_keys() or param_b \ not in state_qc._parameter_table.get_keys(): hessian_op = ~Zero @ One else: param_gates_a = state_qc._parameter_table[param_a] param_gates_b = state_qc._parameter_table[param_b] for i, param_occurence_a in enumerate(param_gates_a): coeffs_a, gates_a = self._gate_gradient_dict( param_occurence_a[0])[param_occurence_a[1]] # apply Hadamard on working qubit self.insert_gate(circuit, param_occurence_a[0], HGate(), qubits=[work_q0]) self.insert_gate(circuit, param_occurence_a[0], HGate(), qubits=[work_q1]) for j, gate_to_insert_a in enumerate(gates_a): coeff_a = coeffs_a[j] hessian_circuit_temp = QuantumCircuit(*circuit.qregs) hessian_circuit_temp.data = circuit.data # Fix working qubit 0 phase sign = np.sign(coeff_a) is_complex = np.iscomplex(coeff_a) if sign == -1: if is_complex: self.insert_gate(hessian_circuit_temp, param_occurence_a[0], SdgGate(), qubits=[work_q0]) else: self.insert_gate(hessian_circuit_temp, param_occurence_a[0], ZGate(), qubits=[work_q0]) else: if is_complex: self.insert_gate(hessian_circuit_temp, param_occurence_a[0], SGate(), qubits=[work_q0]) # Insert controlled, intercepting gate - controlled by |1> if isinstance(param_occurence_a[0], UGate): if param_occurence_a[1] == 0: self.insert_gate( hessian_circuit_temp, param_occurence_a[0], RZGate(param_occurence_a[0].params[2])) self.insert_gate(hessian_circuit_temp, param_occurence_a[0], RXGate(np.pi / 2)) self.insert_gate(hessian_circuit_temp, param_occurence_a[0], gate_to_insert_a, additional_qubits=([work_q0], [])) self.insert_gate(hessian_circuit_temp, param_occurence_a[0], RXGate(-np.pi / 2)) self.insert_gate( hessian_circuit_temp, param_occurence_a[0], RZGate(-param_occurence_a[0].params[2])) elif param_occurence_a[1] == 1: self.insert_gate(hessian_circuit_temp, param_occurence_a[0], gate_to_insert_a, after=True, additional_qubits=([work_q0], [])) else: self.insert_gate(hessian_circuit_temp, param_occurence_a[0], gate_to_insert_a, additional_qubits=([work_q0], [])) else: self.insert_gate(hessian_circuit_temp, param_occurence_a[0], gate_to_insert_a, additional_qubits=([work_q0], [])) for m, param_occurence_b in enumerate(param_gates_b): coeffs_b, gates_b = self._gate_gradient_dict( param_occurence_b[0])[param_occurence_b[1]] for n, gate_to_insert_b in enumerate(gates_b): coeff_b = coeffs_b[n] # create a copy of the original circuit with the same registers hessian_circuit = QuantumCircuit( *hessian_circuit_temp.qregs) hessian_circuit.data = hessian_circuit_temp.data # Fix working qubit 1 phase sign = np.sign(coeff_b) is_complex = np.iscomplex(coeff_b) if sign == -1: if is_complex: self.insert_gate(hessian_circuit, param_occurence_b[0], SdgGate(), qubits=[work_q1]) else: self.insert_gate(hessian_circuit, param_occurence_b[0], ZGate(), qubits=[work_q1]) else: if is_complex: self.insert_gate(hessian_circuit, param_occurence_b[0], SGate(), qubits=[work_q1]) # Insert controlled, intercepting gate - controlled by |1> if isinstance(param_occurence_b[0], UGate): if param_occurence_b[1] == 0: self.insert_gate( hessian_circuit, param_occurence_b[0], RZGate(param_occurence_b[0]. params[2])) self.insert_gate( hessian_circuit, param_occurence_b[0], RXGate(np.pi / 2)) self.insert_gate( hessian_circuit, param_occurence_b[0], gate_to_insert_b, additional_qubits=([work_q1], [])) self.insert_gate( hessian_circuit, param_occurence_b[0], RXGate(-np.pi / 2)) self.insert_gate( hessian_circuit, param_occurence_b[0], RZGate(-param_occurence_b[0]. params[2])) elif param_occurence_b[1] == 1: self.insert_gate( hessian_circuit, param_occurence_b[0], gate_to_insert_b, after=True, additional_qubits=([work_q1], [])) else: self.insert_gate( hessian_circuit, param_occurence_b[0], gate_to_insert_b, additional_qubits=([work_q1], [])) else: self.insert_gate( hessian_circuit, param_occurence_b[0], gate_to_insert_b, additional_qubits=([work_q1], [])) hessian_circuit.h(work_q0) hessian_circuit.cz(work_q1, work_q0) hessian_circuit.h(work_q1) term = state_op.coeff * np.sqrt(np.abs(coeff_a) * np.abs(coeff_b)) \ * CircuitStateFn(hessian_circuit) # Chain Rule Parameter Expression gate_param_a = param_occurence_a[0].params[ param_occurence_a[1]] gate_param_b = param_occurence_b[0].params[ param_occurence_b[1]] if meas_op: meas = deepcopy(meas_op) if isinstance(gate_param_a, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param_a, param_a) meas *= expr_grad if isinstance(gate_param_b, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param_a, param_a) meas *= expr_grad term = meas @ term else: term = ListOp([term], combo_fn=partial( self._hess_combo_fn, state_op=state_op)) if isinstance(gate_param_a, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param_a, param_a) term *= expr_grad if isinstance(gate_param_b, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param_a, param_a) term *= expr_grad if i == 0 and j == 0 and m == 0 and n == 0: hessian_op = term else: # Product Rule hessian_op += term # Create a list of Hessian elements w.r.t. the given parameter tuples if len(tuples_list) == 1: return hessian_op else: if not hessian_ops: hessian_ops = [hessian_op] else: hessian_ops += [hessian_op] return ListOp(hessian_ops)
class TestRBUtilities(QiskitExperimentsTestCase): """ A test class for additional functionality provided by the StandardRB class. """ instructions = { "i": IGate(), "x": XGate(), "y": YGate(), "z": ZGate(), "h": HGate(), "s": SGate(), "sdg": SdgGate(), "cx": CXGate(), "cz": CZGate(), "swap": SwapGate(), } seed = 42 @data( [1, {((0,), "x"): 3, ((0,), "y"): 2, ((0,), "h"): 1}], [5, {((1,), "x"): 3, ((4,), "y"): 2, ((1,), "h"): 1, ((1, 4), "cx"): 7}], ) @unpack def test_count_ops(self, num_qubits, expected_counts): """Testing the count_ops utility function this function receives a circuit and counts the number of gates in it, counting gates for different qubits separately""" circuit = QuantumCircuit(num_qubits) gates_to_add = [] for gate, count in expected_counts.items(): gates_to_add += [gate for _ in range(count)] rng = np.random.default_rng(self.seed) rng.shuffle(gates_to_add) for qubits, gate in gates_to_add: circuit.append(self.instructions[gate], qubits) counts = rb.RBUtils.count_ops(circuit) self.assertDictEqual(expected_counts, counts) def test_calculate_1q_epg(self): """Testing the calculation of 1 qubit error per gate The EPG is computed based on the error per clifford determined in the RB experiment, the gate counts, and an estimate about the relations between the errors of different gate types """ epc_1_qubit = FitVal(0.0037, 0) qubits = [0] gate_error_ratio = {((0,), "id"): 1, ((0,), "rz"): 0, ((0,), "sx"): 1, ((0,), "x"): 1} gates_per_clifford = {((0,), "rz"): 10.5, ((0,), "sx"): 8.15, ((0,), "x"): 0.25} epg = rb.RBUtils.calculate_1q_epg(epc_1_qubit, qubits, gate_error_ratio, gates_per_clifford) error_dict = { ((0,), "rz"): FitVal(0, 0), ((0,), "sx"): FitVal(0.0004432101747785104, 0), ((0,), "x"): FitVal(0.0004432101747785104, 0), } for gate in ["x", "sx", "rz"]: expected_epg = error_dict[((0,), gate)] actual_epg = epg[(0,)][gate] self.assertTrue(np.allclose(expected_epg.value, actual_epg.value, atol=0.001)) self.assertTrue(np.allclose(expected_epg.stderr, actual_epg.stderr, atol=0.001)) def test_calculate_2q_epg(self): """Testing the calculation of 2 qubit error per gate The EPG is computed based on the error per clifford determined in the RB experiment, the gate counts, and an estimate about the relations between the errors of different gate types """ epc_2_qubit = FitVal(0.034184849962675984, 0) qubits = [1, 4] gate_error_ratio = { ((1,), "id"): 1, ((4,), "id"): 1, ((1,), "rz"): 0, ((4,), "rz"): 0, ((1,), "sx"): 1, ((4,), "sx"): 1, ((1,), "x"): 1, ((4,), "x"): 1, ((4, 1), "cx"): 1, ((1, 4), "cx"): 1, } gates_per_clifford = { ((1, 4), "barrier"): 1.032967032967033, ((1,), "rz"): 15.932967032967033, ((1,), "sx"): 12.382417582417583, ((4,), "rz"): 18.681946624803768, ((4,), "sx"): 14.522605965463109, ((1, 4), "cx"): 1.0246506515936569, ((4, 1), "cx"): 0.5212064090480678, ((4,), "x"): 0.24237661112857592, ((1,), "measure"): 0.01098901098901099, ((4,), "measure"): 0.01098901098901099, ((1,), "x"): 0.2525918944392083, } epg_1_qubit = [ AnalysisResultData("EPG_rz", 0.0, device_components=[1]), AnalysisResultData("EPG_rz", 0.0, device_components=[4]), AnalysisResultData("EPG_sx", 0.00036207066403884814, device_components=[1]), AnalysisResultData("EPG_sx", 0.0005429962529239195, device_components=[4]), AnalysisResultData("EPG_x", 0.00036207066403884814, device_components=[1]), AnalysisResultData("EPG_x", 0.0005429962529239195, device_components=[4]), ] epg = rb.RBUtils.calculate_2q_epg( epc_2_qubit, qubits, gate_error_ratio, gates_per_clifford, epg_1_qubit ) error_dict = { ((1, 4), "cx"): FitVal(0.012438847900902494, 0), } expected_epg = error_dict[((1, 4), "cx")] actual_epg = epg[(1, 4)]["cx"] self.assertTrue(np.allclose(expected_epg.value, actual_epg.value, atol=0.001)) self.assertTrue(np.allclose(expected_epg.stderr, actual_epg.stderr, atol=0.001)) def test_coherence_limit(self): """Test coherence_limit.""" t1 = 100.0 t2 = 100.0 gate_2_qubits = 0.5 gate_1_qubit = 0.1 twoq_coherence_err = rb.RBUtils.coherence_limit(2, [t1, t1], [t2, t2], gate_2_qubits) oneq_coherence_err = rb.RBUtils.coherence_limit(1, [t1], [t2], gate_1_qubit) self.assertAlmostEqual(oneq_coherence_err, 0.00049975, 6, "Error: 1Q Coherence Limit") self.assertAlmostEqual(twoq_coherence_err, 0.00597, 5, "Error: 2Q Coherence Limit") def test_clifford_1_qubit_generation(self): """Verify 1-qubit clifford indeed generates the correct group""" clifford_dicts = [ {"stabilizer": ["+Z"], "destabilizer": ["+X"]}, {"stabilizer": ["+X"], "destabilizer": ["+Z"]}, {"stabilizer": ["+Y"], "destabilizer": ["+X"]}, {"stabilizer": ["+X"], "destabilizer": ["+Y"]}, {"stabilizer": ["+Z"], "destabilizer": ["+Y"]}, {"stabilizer": ["+Y"], "destabilizer": ["+Z"]}, {"stabilizer": ["-Z"], "destabilizer": ["+X"]}, {"stabilizer": ["+X"], "destabilizer": ["-Z"]}, {"stabilizer": ["-Y"], "destabilizer": ["+X"]}, {"stabilizer": ["+X"], "destabilizer": ["-Y"]}, {"stabilizer": ["-Z"], "destabilizer": ["-Y"]}, {"stabilizer": ["-Y"], "destabilizer": ["-Z"]}, {"stabilizer": ["-Z"], "destabilizer": ["-X"]}, {"stabilizer": ["-X"], "destabilizer": ["-Z"]}, {"stabilizer": ["+Y"], "destabilizer": ["-X"]}, {"stabilizer": ["-X"], "destabilizer": ["+Y"]}, {"stabilizer": ["-Z"], "destabilizer": ["+Y"]}, {"stabilizer": ["+Y"], "destabilizer": ["-Z"]}, {"stabilizer": ["+Z"], "destabilizer": ["-X"]}, {"stabilizer": ["-X"], "destabilizer": ["+Z"]}, {"stabilizer": ["-Y"], "destabilizer": ["-X"]}, {"stabilizer": ["-X"], "destabilizer": ["-Y"]}, {"stabilizer": ["+Z"], "destabilizer": ["-Y"]}, {"stabilizer": ["-Y"], "destabilizer": ["+Z"]}, ] cliffords = [Clifford.from_dict(i) for i in clifford_dicts] utils = rb.CliffordUtils() for n in range(24): clifford = utils.clifford_1_qubit(n) self.assertEqual(clifford, cliffords[n]) def test_clifford_2_qubit_generation(self): """Verify 2-qubit clifford indeed generates the correct group""" utils = rb.CliffordUtils() pauli_free_elements = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 5760, 5761, 5762, 5763, 5764, 5765, 5766, 5767, 5768, 5769, 5770, 5771, 5772, 5773, 5774, 5775, 5776, 5777, 5778, 5779, 5780, 5781, 5782, 5783, 5784, 5785, 5786, 5787, 5788, 5789, 5790, 5791, 5792, 5793, 5794, 5795, 5796, 5797, 5798, 5799, 5800, 5801, 5802, 5803, 5804, 5805, 5806, 5807, 5808, 5809, 5810, 5811, 5812, 5813, 5814, 5815, 5816, 5817, 5818, 5819, 5820, 5821, 5822, 5823, 5824, 5825, 5826, 5827, 5828, 5829, 5830, 5831, 5832, 5833, 5834, 5835, 5836, 5837, 5838, 5839, 5840, 5841, 5842, 5843, 5844, 5845, 5846, 5847, 5848, 5849, 5850, 5851, 5852, 5853, 5854, 5855, 5856, 5857, 5858, 5859, 5860, 5861, 5862, 5863, 5864, 5865, 5866, 5867, 5868, 5869, 5870, 5871, 5872, 5873, 5874, 5875, 5876, 5877, 5878, 5879, 5880, 5881, 5882, 5883, 5884, 5885, 5886, 5887, 5888, 5889, 5890, 5891, 5892, 5893, 5894, 5895, 5896, 5897, 5898, 5899, 5900, 5901, 5902, 5903, 5904, 5905, 5906, 5907, 5908, 5909, 5910, 5911, 5912, 5913, 5914, 5915, 5916, 5917, 5918, 5919, 5920, 5921, 5922, 5923, 5924, 5925, 5926, 5927, 5928, 5929, 5930, 5931, 5932, 5933, 5934, 5935, 5936, 5937, 5938, 5939, 5940, 5941, 5942, 5943, 5944, 5945, 5946, 5947, 5948, 5949, 5950, 5951, 5952, 5953, 5954, 5955, 5956, 5957, 5958, 5959, 5960, 5961, 5962, 5963, 5964, 5965, 5966, 5967, 5968, 5969, 5970, 5971, 5972, 5973, 5974, 5975, 5976, 5977, 5978, 5979, 5980, 5981, 5982, 5983, 5984, 5985, 5986, 5987, 5988, 5989, 5990, 5991, 5992, 5993, 5994, 5995, 5996, 5997, 5998, 5999, 6000, 6001, 6002, 6003, 6004, 6005, 6006, 6007, 6008, 6009, 6010, 6011, 6012, 6013, 6014, 6015, 6016, 6017, 6018, 6019, 6020, 6021, 6022, 6023, 6024, 6025, 6026, 6027, 6028, 6029, 6030, 6031, 6032, 6033, 6034, 6035, 6036, 6037, 6038, 6039, 6040, 6041, 6042, 6043, 6044, 6045, 6046, 6047, 6048, 6049, 6050, 6051, 6052, 6053, 6054, 6055, 6056, 6057, 6058, 6059, 6060, 6061, 6062, 6063, 6064, 6065, 6066, 6067, 6068, 6069, 6070, 6071, 6072, 6073, 6074, 6075, 6076, 6077, 6078, 6079, 6080, 6081, 6082, 6083, 10944, 10945, 10946, 10947, 10948, 10949, 10950, 10951, 10952, 10953, 10954, 10955, 10956, 10957, 10958, 10959, 10960, 10961, 10962, 10963, 10964, 10965, 10966, 10967, 10968, 10969, 10970, 10971, 10972, 10973, 10974, 10975, 10976, 10977, 10978, 10979, ] cliffords = [] for n in pauli_free_elements: clifford = utils.clifford_2_qubit(n) phase = clifford.table.phase for i in range(4): self.assertFalse(phase[i]) for other_clifford in cliffords: self.assertNotEqual(clifford, other_clifford) cliffords.append(clifford) pauli_check_elements_list = [ [0, 36, 72, 108, 144, 180, 216, 252, 288, 324, 360, 396, 432, 468, 504, 540], [ 576, 900, 1224, 1548, 1872, 2196, 2520, 2844, 3168, 3492, 3816, 4140, 4464, 4788, 5112, 5436, ], [ 5760, 6084, 6408, 6732, 7056, 7380, 7704, 8028, 8352, 8676, 9000, 9324, 9648, 9972, 10296, 10620, ], [ 10944, 10980, 11016, 11052, 11088, 11124, 11160, 11196, 11232, 11268, 11304, 11340, 11376, 11412, 11448, 11484, ], ] for pauli_check_elements in pauli_check_elements_list: phases = [] table = None for n in pauli_check_elements: clifford = utils.clifford_2_qubit(n) if table is None: table = clifford.table.array else: self.assertTrue(np.all(table == clifford.table.array)) phase = tuple(clifford.table.phase) for other_phase in phases: self.assertNotEqual(phase, other_phase) phases.append(phase)
class TestPauli(QiskitTestCase): """Tests for Pauli operator class.""" @data(*pauli_group_labels(2)) def test_conjugate(self, label): """Test conjugate method.""" value = Pauli(label).conjugate() target = operator_from_label(label).conjugate() self.assertEqual(Operator(value), target) @data(*pauli_group_labels(2)) def test_transpose(self, label): """Test transpose method.""" value = Pauli(label).transpose() target = operator_from_label(label).transpose() self.assertEqual(Operator(value), target) @data(*pauli_group_labels(2)) def test_adjoint(self, label): """Test adjoint method.""" value = Pauli(label).adjoint() target = operator_from_label(label).adjoint() self.assertEqual(Operator(value), target) @data(*pauli_group_labels(2)) def test_inverse(self, label): """Test inverse method.""" pauli = Pauli(label) value = pauli.inverse() target = pauli.adjoint() self.assertEqual(value, target) @data(*it.product(pauli_group_labels(2, full_group=False), repeat=2)) @unpack def test_dot(self, label1, label2): """Test dot method.""" p1 = Pauli(label1) p2 = Pauli(label2) value = Operator(p1.dot(p2)) op1 = operator_from_label(label1) op2 = operator_from_label(label2) target = op1.dot(op2) self.assertEqual(value, target) @data(*pauli_group_labels(1)) def test_dot_qargs(self, label2): """Test dot method with qargs.""" label1 = "-iXYZ" p1 = Pauli(label1) p2 = Pauli(label2) qargs = [0] value = Operator(p1.dot(p2, qargs=qargs)) op1 = operator_from_label(label1) op2 = operator_from_label(label2) target = op1.dot(op2, qargs=qargs) self.assertEqual(value, target) @data(*it.product(pauli_group_labels(2, full_group=False), repeat=2)) @unpack def test_compose(self, label1, label2): """Test compose method.""" p1 = Pauli(label1) p2 = Pauli(label2) value = Operator(p1.compose(p2)) op1 = operator_from_label(label1) op2 = operator_from_label(label2) target = op1.compose(op2) self.assertEqual(value, target) @data(*pauli_group_labels(1)) def test_compose_qargs(self, label2): """Test compose method with qargs.""" label1 = "-XYZ" p1 = Pauli(label1) p2 = Pauli(label2) qargs = [0] value = Operator(p1.compose(p2, qargs=qargs)) op1 = operator_from_label(label1) op2 = operator_from_label(label2) target = op1.compose(op2, qargs=qargs) self.assertEqual(value, target) @data(*it.product(pauli_group_labels(1, full_group=False), repeat=2)) @unpack def test_tensor(self, label1, label2): """Test tensor method.""" p1 = Pauli(label1) p2 = Pauli(label2) value = Operator(p1.tensor(p2)) op1 = operator_from_label(label1) op2 = operator_from_label(label2) target = op1.tensor(op2) self.assertEqual(value, target) @data(*it.product(pauli_group_labels(1, full_group=False), repeat=2)) @unpack def test_expand(self, label1, label2): """Test expand method.""" p1 = Pauli(label1) p2 = Pauli(label2) value = Operator(p1.expand(p2)) op1 = operator_from_label(label1) op2 = operator_from_label(label2) target = op1.expand(op2) self.assertEqual(value, target) @data("II", "XI", "YX", "ZZ", "YZ") def test_power(self, label): """Test power method.""" iden = Pauli("II") op = Pauli(label) self.assertTrue(op**2, iden) @data(1, 1.0, -1, -1.0, 1j, -1j) def test_multiply(self, val): """Test multiply method.""" op = val * Pauli(([True, True], [False, False], 0)) phase = (-1j)**op.phase self.assertEqual(phase, val) def test_multiply_except(self): """Test multiply method raises exceptions.""" op = Pauli("XYZ") self.assertRaises(QiskitError, op._multiply, 2) @data(0, 1, 2, 3) def test_negate(self, phase): """Test negate method""" op = Pauli(([False], [True], phase)) neg = -op self.assertTrue(op.equiv(neg)) self.assertEqual(neg.phase, (op.phase + 2) % 4) @data(*it.product(pauli_group_labels(1, False), repeat=2)) @unpack def test_commutes(self, p1, p2): """Test commutes method""" P1 = Pauli(p1) P2 = Pauli(p2) self.assertEqual(P1.commutes(P2), P1.dot(P2) == P2.dot(P1)) @data(*it.product(pauli_group_labels(1, False), repeat=2)) @unpack def test_anticommutes(self, p1, p2): """Test anticommutes method""" P1 = Pauli(p1) P2 = Pauli(p2) self.assertEqual(P1.anticommutes(P2), P1.dot(P2) == -P2.dot(P1)) @data(*it.product( (IGate(), XGate(), YGate(), ZGate(), HGate(), SGate(), SdgGate()), pauli_group_labels(1, False), )) @unpack def test_evolve_clifford1(self, gate, label): """Test evolve method for 1-qubit Clifford gates.""" op = Operator(gate) pauli = Pauli(label) value = Operator(pauli.evolve(gate)) value_h = Operator(pauli.evolve(gate, frame="h")) value_s = Operator(pauli.evolve(gate, frame="s")) value_inv = Operator(pauli.evolve(gate.inverse())) target = op.adjoint().dot(pauli).dot(op) self.assertEqual(value, target) self.assertEqual(value, value_h) self.assertEqual(value_inv, value_s) @data(*it.product((CXGate(), CYGate(), CZGate(), SwapGate()), pauli_group_labels(2, False))) @unpack def test_evolve_clifford2(self, gate, label): """Test evolve method for 2-qubit Clifford gates.""" op = Operator(gate) pauli = Pauli(label) value = Operator(pauli.evolve(gate)) value_h = Operator(pauli.evolve(gate, frame="h")) value_s = Operator(pauli.evolve(gate, frame="s")) value_inv = Operator(pauli.evolve(gate.inverse())) target = op.adjoint().dot(pauli).dot(op) self.assertEqual(value, target) self.assertEqual(value, value_h) self.assertEqual(value_inv, value_s) def test_evolve_clifford_qargs(self): """Test evolve method for random Clifford""" cliff = random_clifford(3, seed=10) op = Operator(cliff) pauli = random_pauli(5, seed=10) qargs = [3, 0, 1] value = Operator(pauli.evolve(cliff, qargs=qargs)) value_h = Operator(pauli.evolve(cliff, qargs=qargs, frame="h")) value_s = Operator(pauli.evolve(cliff, qargs=qargs, frame="s")) value_inv = Operator(pauli.evolve(cliff.adjoint(), qargs=qargs)) target = Operator(pauli).compose(op.adjoint(), qargs=qargs).dot(op, qargs=qargs) self.assertEqual(value, target) self.assertEqual(value, value_h) self.assertEqual(value_inv, value_s) def test_barrier_delay_sim(self): """Test barrier and delay instructions can be simulated""" target_circ = QuantumCircuit(2) target_circ.x(0) target_circ.y(1) target = Pauli(target_circ) circ = QuantumCircuit(2) circ.x(0) circ.delay(100, 0) circ.barrier([0, 1]) circ.y(1) value = Pauli(circ) self.assertEqual(value, target)