def test_print_operation_representation_two_qubits_neg(): qreg = cirq.LineQubit.range(2) ideal = cirq.Circuit(cirq.CNOT(*qreg)) noisy_a = NoisyOperation.from_cirq( circuit=cirq.Circuit(cirq.H.on_each(qreg[0]), cirq.CNOT( *qreg), cirq.H.on_each(qreg[1]))) noisy_b = NoisyOperation.from_cirq( circuit=cirq.Circuit(cirq.Z.on_each(qreg[1]))) decomp = OperationRepresentation( ideal=ideal, basis_expansion={ noisy_a: -0.5, noisy_b: 1.5, }, ) print(str(decomp)) expected = f""" 0: ───@─── │ 1: ───X─── ={" "} -0.500 0: ───H───@─────── │ 1: ───────X───H─── +1.500 1: ───Z───""" # Remove initial newline expected = expected[1:] assert str(decomp) == expected
def test_on_each_bad_types(): ideal = cirq.Circuit(cirq.I(cirq.LineQubit(0))) real = np.identity(4) with pytest.raises(TypeError, match="must be iterable"): NoisyOperation.on_each(ideal, qubits=cirq.NamedQubit("new"), channel_matrix=real)
def test_on_each_multiple_qubits_bad_qubits_shape(): real_cnot = np.zeros(shape=(16, 16)) qubits = [cirq.LineQubit.range(3)] with pytest.raises( ValueError, match="Number of qubits in each register should be" ): NoisyOperation.on_each(cirq.CNOT, qubits=qubits, real=real_cnot)
def test_transform_qubits_wrong_number(): real = np.zeros(shape=(16, 16)) qreg = [cirq.NamedQubit("Dummy 1"), cirq.NamedQubit("Dummy 2")] ideal = cirq.Circuit(cirq.ops.CNOT.on(*qreg)) noisy_op = NoisyOperation(ideal, real) with pytest.raises(ValueError, match="Expected 2 qubits but received"): noisy_op.transform_qubits(qubits=[cirq.NamedQubit("new")])
def test_print_cirq_operation_representation(): ideal = cirq.Circuit(cirq.H(cirq.LineQubit(0))) noisy_xop = NoisyOperation.from_cirq(circuit=cirq.X, channel_matrix=np.zeros(shape=(4, 4))) noisy_zop = NoisyOperation.from_cirq(circuit=cirq.Z, channel_matrix=np.zeros(shape=(4, 4))) # Positive first coefficient decomp = OperationRepresentation( ideal=ideal, basis_expansion={ noisy_xop: 0.5, noisy_zop: 0.5, }, ) expected = r"0: ───H─── = 0.500*(0: ───X───)+0.500*(0: ───Z───)" assert str(decomp) == expected # Negative first coefficient decomp = OperationRepresentation( ideal=ideal, basis_expansion={ noisy_xop: -0.5, noisy_zop: 1.5, }, ) expected = r"0: ───H─── = -0.500*(0: ───X───)+1.500*(0: ───Z───)" assert str(decomp) == expected # Empty representation decomp = OperationRepresentation(ideal=ideal, basis_expansion={}) expected = r"0: ───H─── = 0.000" assert str(decomp) == expected # Small coefficient approximation decomp = OperationRepresentation( ideal=ideal, basis_expansion={ noisy_xop: 1.00001, noisy_zop: 0.00001 }, ) expected = r"0: ───H─── = 1.000*(0: ───X───)" assert str(decomp) == expected # Small coefficient approximation different position decomp = OperationRepresentation( ideal=ideal, basis_expansion={ noisy_xop: 0.00001, noisy_zop: 1.00001 }, ) expected = r"0: ───H─── = 1.000*(0: ───Z───)" # Small coefficient approximation different position decomp = OperationRepresentation( ideal=ideal, basis_expansion={noisy_xop: 0.00001}, ) expected = r"0: ───H─── = 0.000" assert str(decomp) == expected
def test_noisy_basis_add_bad_types(): rng = np.random.RandomState(seed=1) noisy_basis = NoisyBasis( NoisyOperation.from_cirq(ideal=cirq.I, real=rng.rand(4, 4)), NoisyOperation.from_cirq(ideal=cirq.X, real=rng.rand(4, 4)), ) with pytest.raises(TypeError, match="All basis elements must be of type"): noisy_basis.add(cirq.Y)
def test_add_noisy_operation_no_channel_matrix(): noisy_op1 = NoisyOperation(cirq.Circuit([cirq.X.on(cirq.NamedQubit("Q"))])) noisy_op2 = NoisyOperation( cirq.Circuit([cirq.X.on(cirq.NamedQubit("Q"))]), channel_matrix=np.random.rand(4, 4), ) with pytest.raises(ValueError): (noisy_op1 + noisy_op2).channel_matrix
def test_noisy_basis_simple(): rng = np.random.RandomState(seed=1) noisy_basis = NoisyBasis( NoisyOperation.from_cirq(ideal=cirq.I, real=rng.rand(4, 4)), NoisyOperation.from_cirq(ideal=cirq.X, real=rng.rand(4, 4)), NoisyOperation.from_cirq(ideal=cirq.Y, real=rng.rand(4, 4)), NoisyOperation.from_cirq(ideal=cirq.Z, real=rng.rand(4, 4)), ) assert len(noisy_basis) == 4 assert noisy_basis.all_qubits() == {cirq.LineQubit(0)}
def test_extend_to_simple(): rng = np.random.RandomState(seed=1) noisy_basis = NoisyBasis( NoisyOperation.from_cirq(ideal=cirq.I, real=rng.rand(4, 4)), NoisyOperation.from_cirq(ideal=cirq.X, real=rng.rand(4, 4)), ) assert len(noisy_basis.elements) == 2 noisy_basis.extend_to(cirq.LineQubit.range(1, 3)) assert len(noisy_basis.elements) == 6
def test_init_with_cirq_circuit(): qreg = cirq.LineQubit.range(2) circ = cirq.Circuit(cirq.H.on(qreg[0]), cirq.CNOT.on(*qreg)) real = np.zeros(shape=(16, 16)) noisy_op = NoisyOperation(circ, real) assert isinstance(noisy_op._ideal, cirq.Circuit) assert _equal(noisy_op.ideal_circuit(), circ, require_qubit_equality=True) assert set(noisy_op.qubits) == set(qreg) assert np.allclose(noisy_op.ideal_unitary, cirq.unitary(circ)) assert np.allclose(noisy_op.real_matrix, real) assert noisy_op.real_matrix is not real
def test_equal_method_of_representations(): q = cirq.LineQubit(0) ideal = cirq.Circuit(cirq.H(q)) noisy_xop_a = NoisyOperation( ideal=cirq.Circuit(cirq.X(q)), real=np.zeros(shape=(4, 4)), ) noisy_zop_a = NoisyOperation( ideal=cirq.Circuit(cirq.Z(q)), real=np.zeros(shape=(4, 4)), ) rep_a = OperationRepresentation( ideal=ideal, basis_expansion={noisy_xop_a: 0.5, noisy_zop_a: 0.5}, ) noisy_xop_b = NoisyOperation( ideal=cirq.Circuit(cirq.X(q)), real=np.ones(shape=(4, 4)), ) noisy_zop_b = NoisyOperation( ideal=cirq.Circuit(cirq.Z(q)), real=np.ones(shape=(4, 4)), ) rep_b = OperationRepresentation( ideal=ideal, basis_expansion={noisy_xop_b: 0.5, noisy_zop_b: 0.5}, ) # Equal representation up to real superoperators assert rep_a == rep_b # Different ideal ideal_b = cirq.Circuit(cirq.X(q)) rep_b = OperationRepresentation( ideal=ideal_b, basis_expansion={noisy_xop_b: 0.5, noisy_zop_b: 0.5}, ) assert rep_a != rep_b # Different type q_b = qiskit.QuantumRegister(1) ideal_b = qiskit.QuantumCircuit(q_b) ideal_b.x(q_b) rep_b = OperationRepresentation( ideal=ideal_b, basis_expansion={noisy_xop_b: 0.5, noisy_zop_b: 0.5}, ) assert rep_a != rep_b # Different length rep_b = OperationRepresentation( ideal=ideal, basis_expansion={noisy_xop_b: 0.5}, ) assert rep_a != rep_b # Different operations noisy_diff = NoisyOperation(ideal=cirq.Circuit(cirq.H(q))) rep_b = OperationRepresentation( ideal=ideal, basis_expansion={noisy_xop_b: 0.5, noisy_diff: 0.5}, ) assert rep_a != rep_b # Different coefficients rep_b = OperationRepresentation( ideal=ideal, basis_expansion={noisy_xop_b: 0.7, noisy_zop_b: 0.5}, ) assert rep_a != rep_b
def test_with_qubits(): real = np.zeros(shape=(16, 16)) qreg = [cirq.NamedQubit("Dummy 1"), cirq.NamedQubit("Dummy 2")] ideal = cirq.Circuit(cirq.ops.H.on(qreg[0]), cirq.ops.CNOT.on(*qreg)) noisy_op = NoisyOperation(ideal, real) assert set(noisy_op.qubits) == set(qreg) assert np.allclose(noisy_op.real_matrix, real) qubits = cirq.LineQubit.range(2) new_noisy_op = noisy_op.with_qubits(qubits) assert set(new_noisy_op.qubits) == set(qubits) assert np.allclose(new_noisy_op.real_matrix, real)
def test_add_simple(): ideal = cirq.Circuit([cirq.X.on(cirq.NamedQubit("Q"))]) real = np.random.rand(4, 4) noisy_op1 = NoisyOperation(ideal, real) noisy_op2 = NoisyOperation(ideal, real) noisy_op = noisy_op1 + noisy_op2 correct = cirq.Circuit([cirq.X.on(cirq.NamedQubit("Q"))] * 2) assert _equal(noisy_op._ideal, correct, require_qubit_equality=True) assert np.allclose(noisy_op.real_matrix, real @ real)
def test_get_sequences_simple(length): rng = np.random.RandomState(seed=1) noisy_basis = NoisyBasis( NoisyOperation.from_cirq(ideal=cirq.I, real=rng.rand(4, 4)), NoisyOperation.from_cirq(ideal=cirq.X, real=rng.rand(4, 4)), ) sequences = noisy_basis.get_sequences(length=length) assert all(isinstance(s, NoisyOperation) for s in sequences) assert len(sequences) == len(noisy_basis) ** length for sequence in sequences: assert len(sequence.ideal_circuit()) == length
def test_transform_qubits_multiple_qubits(qubits, real): qreg = [cirq.NamedQubit("Dummy 1"), cirq.NamedQubit("Dummy 2")] ideal = cirq.Circuit(cirq.ops.H.on(qreg[0]), cirq.ops.CNOT.on(*qreg)) noisy_op = NoisyOperation(ideal, real) assert set(noisy_op.qubits) != set(qubits) if real is not None: assert np.allclose(noisy_op.real_matrix, real) noisy_op.transform_qubits(qubits) assert set(noisy_op.qubits) == set(qubits) if real is not None: assert np.allclose(noisy_op.real_matrix, real)
def test_noisy_basis_add(): rng = np.random.RandomState(seed=1) noisy_basis = NoisyBasis( NoisyOperation.from_cirq(ideal=cirq.I, real=rng.rand(4, 4)), NoisyOperation.from_cirq(ideal=cirq.X, real=rng.rand(4, 4)), ) assert len(noisy_basis) == 2 noisy_basis.add( NoisyOperation.from_cirq(ideal=cirq.Y, real=rng.rand(4, 4)), NoisyOperation.from_cirq(ideal=cirq.Z, real=rng.rand(4, 4)), ) assert len(noisy_basis) == 4
def test_pyquil_noisy_basis(): rng = np.random.RandomState(seed=1) noisy_basis = NoisyBasis( NoisyOperation(ideal=pyquil.Program(pyquil.gates.I(0)), real=rng.rand(4, 4)), NoisyOperation(ideal=pyquil.Program(pyquil.gates.Y(0)), real=rng.rand(4, 4)), ) assert len(noisy_basis) == 2 for op in noisy_basis.elements: assert isinstance(op.ideal_circuit(), pyquil.Program) assert isinstance(op._ideal, cirq.Circuit)
def test_noisy_basis_simple(): rng = np.random.RandomState(seed=1) noisy_basis = NoisyBasis( NoisyOperation.from_cirq(circuit=cirq.I, channel_matrix=rng.rand(4, 4)), NoisyOperation.from_cirq(circuit=cirq.X, channel_matrix=rng.rand(4, 4)), NoisyOperation.from_cirq(circuit=cirq.Y, channel_matrix=rng.rand(4, 4)), NoisyOperation.from_cirq(circuit=cirq.Z, channel_matrix=rng.rand(4, 4)), ) assert len(noisy_basis) == 4 assert noisy_basis.all_qubits() == {cirq.LineQubit(0)}
def test_add_pyquil_noisy_operations(): ideal = pyquil.Program(pyquil.gates.X(0)) real = np.random.rand(4, 4) noisy_op1 = NoisyOperation(ideal, real) noisy_op2 = NoisyOperation(ideal, real) noisy_op = noisy_op1 + noisy_op2 correct = cirq.Circuit([cirq.X.on(cirq.NamedQubit("Q"))] * 2) assert _equal(noisy_op._ideal, correct, require_qubit_equality=False) assert np.allclose(noisy_op.ideal_unitary, np.identity(2)) assert np.allclose(noisy_op.real_matrix, real @ real)
def test_representation_coeff_of_nonexistant_operation(): qbit = cirq.LineQubit(0) ideal = cirq.Circuit(cirq.X(qbit)) noisy_xop = NoisyOperation.from_cirq(circuit=cirq.X, channel_matrix=np.zeros(shape=(4, 4))) decomp = OperationRepresentation(ideal=ideal, basis_expansion=dict([(noisy_xop, 0.5)])) noisy_zop = NoisyOperation.from_cirq(circuit=cirq.Z, channel_matrix=np.zeros(shape=(4, 4))) with pytest.raises(ValueError, match="does not appear in the basis"): decomp.coeff_of(noisy_zop)
def get_test_representation(): ideal = cirq.Circuit(cirq.H(cirq.LineQubit(0))) noisy_xop = NoisyOperation.from_cirq( ideal=cirq.X, real=np.zeros(shape=(4, 4)) ) noisy_zop = NoisyOperation.from_cirq( ideal=cirq.Z, real=np.zeros(shape=(4, 4)) ) decomp = OperationRepresentation( ideal=ideal, basis_expansion={noisy_xop: 0.5, noisy_zop: -0.5} ) return ideal, noisy_xop, noisy_zop, decomp
def test_print_cirq_operation_representation(): ideal = cirq.Circuit(cirq.H(cirq.LineQubit(0))) noisy_xop = NoisyOperation.from_cirq( ideal=cirq.X, real=np.zeros(shape=(4, 4)) ) noisy_zop = NoisyOperation.from_cirq( ideal=cirq.Z, real=np.zeros(shape=(4, 4)) ) decomp = OperationRepresentation( ideal=ideal, basis_expansion={noisy_xop: 0.5, noisy_zop: 0.5, }, ) expected = r"0: ───H─── = 0.500*0: ───X───+0.500*0: ───Z───" assert str(decomp) == expected
def test_transform_qubits_single_qubit(qubit, real): gate = cirq.H noisy_op = NoisyOperation.from_cirq(gate, real) assert set(noisy_op.qubits) != {qubit} noisy_op.transform_qubits(qubit) assert set(noisy_op.qubits) == {qubit}
def test_find_optimal_representation_no_superoperator_error(): q = LineQubit(0) # Define noisy operation without superoperator matrix noisy_op = NoisyOperation(Circuit(X(q))) noisy_basis = NoisyBasis(noisy_op) with raises(ValueError, match="numerical superoperator matrix"): find_optimal_representation(Circuit(X(q)), noisy_basis)
def _represent_operation_with_amplitude_damping_noise( ideal_operation: Circuit, noise_level: float, ) -> OperationRepresentation: r"""Returns the quasi-probability representation of the input single-qubit ``ideal_operation`` with respect to a basis of noisy operations. Any ideal single-qubit unitary followed by local amplitude-damping noise of equal ``noise_level`` is assumed to be in the basis of implementable operations. The representation is based on the analytical result presented in :cite:`Takagi2020`. Args: ideal_operation: The ideal operation (as a QPROGRAM) to represent. noise_level: The noise level of each amplitude damping channel. Returns: The quasi-probability representation of the ``ideal_operation``. .. note:: The input ``ideal_operation`` is typically a QPROGRAM with a single gate but could also correspond to a sequence of more gates. This is possible as long as the unitary associated to the input QPROGRAM, followed by a single final amplitude damping channel, is physically implementable. .. note:: The input ``ideal_operation`` must be a ``cirq.Circuit``. """ if not isinstance(ideal_operation, Circuit): raise NotImplementedError( "The input ideal_operation must be a cirq.Circuit.", ) qubits = ideal_operation.all_qubits() if len(qubits) == 1: q = tuple(qubits)[0] eta_0 = (1 + np.sqrt(1 - noise_level)) / (2 * (1 - noise_level)) eta_1 = (1 - np.sqrt(1 - noise_level)) / (2 * (1 - noise_level)) eta_2 = -noise_level / (1 - noise_level) etas = [eta_0, eta_1, eta_2] post_ops = [[], Z(q), reset(q)] else: raise ValueError( # pragma: no cover "Only single-qubit operations are supported." # pragma: no cover ) # pragma: no cover # Basis of implementable operations as circuits imp_op_circuits = [ideal_operation + Circuit(op) for op in post_ops] # Build basis_expantion expansion = {NoisyOperation(c): a for c, a in zip(imp_op_circuits, etas)} return OperationRepresentation(ideal_operation, expansion)
def test_add_qiskit_noisy_operations(): qreg = qiskit.QuantumRegister(1) ideal = qiskit.QuantumCircuit(qreg) _ = ideal.x(qreg) real = np.random.rand(4, 4) noisy_op1 = NoisyOperation(ideal, real) noisy_op2 = NoisyOperation(ideal, real) noisy_op = noisy_op1 + noisy_op2 correct = cirq.Circuit([cirq.X.on(cirq.NamedQubit("Q"))] * 2) assert _equal(noisy_op._ideal, correct, require_qubit_equality=False) assert np.allclose(noisy_op.ideal_unitary, np.identity(2)) assert np.allclose(noisy_op.real_matrix, real @ real)
def test_add_bad_type(): ideal = cirq.Circuit([cirq.X.on(cirq.NamedQubit("Q"))]) real = np.random.rand(4, 4) noisy_op = NoisyOperation(ideal, real) with pytest.raises(ValueError, match="must be a NoisyOperation"): noisy_op + ideal
def test_noisy_basis_add(): rng = np.random.RandomState(seed=1) noisy_basis = NoisyBasis( NoisyOperation.from_cirq(circuit=cirq.I, channel_matrix=rng.rand(4, 4)), NoisyOperation.from_cirq(circuit=cirq.X, channel_matrix=rng.rand(4, 4)), ) assert len(noisy_basis) == 2 noisy_basis.add( NoisyOperation.from_cirq(circuit=cirq.Y, channel_matrix=rng.rand(4, 4)), NoisyOperation.from_cirq(circuit=cirq.Z, channel_matrix=rng.rand(4, 4)), ) assert len(noisy_basis) == 4
def test_on_each_multiple_qubits(qubits, real): noisy_ops = NoisyOperation.on_each(cirq.CNOT, qubits=qubits, real=real) assert len(noisy_ops) == 2 for i, op in enumerate(noisy_ops): if real is not None: assert np.allclose(op.real_matrix, real) assert op.num_qubits == 2 assert set(op.qubits) == set(qubits[i])
def test_qiskit_noisy_basis(): rng = np.random.RandomState(seed=1) qreg = qiskit.QuantumRegister(1) xcirc = qiskit.QuantumCircuit(qreg) _ = xcirc.x(qreg) zcirc = qiskit.QuantumCircuit(qreg) _ = zcirc.z(qreg) noisy_basis = NoisyBasis( NoisyOperation(ideal=xcirc, real=rng.rand(4, 4)), NoisyOperation(ideal=zcirc, real=rng.rand(4, 4)), ) assert len(noisy_basis) == 2 for op in noisy_basis.elements: assert isinstance(op.ideal_circuit(), qiskit.QuantumCircuit) assert isinstance(op._ideal, cirq.Circuit)