def test_channel_propagates_to_gate(): class TestGate(cirq.SingleQubitGate): def _channel_(self) -> np.ndarray: return (np.eye(2), ) def _has_channel_(self) -> bool: return True def assert_kraus_eq(ks1: Tuple[np.ndarray, ...], ks2: Tuple[np.ndarray, ...]) -> None: assert len(ks1) == len(ks2) for k1, k2 in zip(ks1, ks2): assert np.all(k1 == k2) identity_kraus = (np.eye(2), ) q = cirq.LineQubit(0) gate = TestGate() gate_op = TestGate().on(q) with cirq.testing.assert_deprecated(deadline='v0.13', count=None): assert cirq.has_channel(gate) assert cirq.has_channel(gate_op) assert_kraus_eq(cirq.channel(gate), identity_kraus) assert_kraus_eq(cirq.channel(gate_op), identity_kraus)
def test_measurement_channel(): np.testing.assert_allclose( cirq.channel(cirq.MeasurementGate(1)), (np.array([[1, 0], [0, 0]]), np.array([[0, 0], [0, 1]]))) # yapf: disable np.testing.assert_allclose( cirq.channel(cirq.MeasurementGate(2)), (np.array([[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]), np.array([[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]), np.array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]]), np.array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1]])))
def test_channel_generates_deprecation_warning(): class UsesDeprecatedChannelMethod: def _has_channel_(self): return True def _channel_(self): return (np.eye(2), ) val = UsesDeprecatedChannelMethod() with pytest.warns(DeprecationWarning, match='_has_kraus_'): assert cirq.has_channel(val) with pytest.warns(DeprecationWarning, match='_kraus_'): ks = cirq.channel(val) assert len(ks) == 1 assert np.all(ks[0] == np.eye(2))
def test_multi_asymmetric_depolarizing_channel(): d = cirq.asymmetric_depolarize(error_probabilities={'II': 0.8, 'XX': 0.2}) np.testing.assert_almost_equal( cirq.channel(d), (np.sqrt(0.8) * np.eye(4), np.sqrt(0.2) * np.kron(X, X))) assert cirq.has_channel(d) np.testing.assert_equal(d._num_qubits_(), 2) with pytest.raises(ValueError, match="num_qubits should be 1"): assert d.p_i == 1.0 with pytest.raises(ValueError, match="num_qubits should be 1"): assert d.p_x == 0.0 with pytest.raises(ValueError, match="num_qubits should be 1"): assert d.p_y == 0.0 with pytest.raises(ValueError, match="num_qubits should be 1"): assert d.p_z == 0.0
def test_minimize_one_norm_with_amp_damp_choi(): for noise_level in [0.01, 0.02, 0.03]: q = LineQubit(0) ideal_matrix = _operation_to_choi(H(q)) basis_matrices = [ _operation_to_choi( [H(q), gate(q), AmplitudeDampingChannel(noise_level)(q)]) for gate in [I, Z] ] # Append reset channel reset_kraus = channel(ResetChannel()) basis_matrices.append(kraus_to_choi(reset_kraus)) optimal_coeffs = minimize_one_norm(ideal_matrix, basis_matrices) represented_mat = sum( [eta * mat for eta, mat in zip(optimal_coeffs, basis_matrices)]) assert np.allclose(ideal_matrix, represented_mat) # Optimal analytic result by Takagi (arXiv:2006.12509) expected = (1.0 + noise_level) / (1.0 - noise_level) assert np.isclose(np.linalg.norm(optimal_coeffs, 1), expected)
def test_depolarizing_channel_two_qubits(): d = cirq.depolarize(0.15, n_qubits=2) np.testing.assert_almost_equal(cirq.channel(d), ( np.sqrt(0.85) * np.eye(4), np.sqrt(0.01) * np.kron(np.eye(2), X), np.sqrt(0.01) * np.kron(np.eye(2), Y), np.sqrt(0.01) * np.kron(np.eye(2), Z), np.sqrt(0.01) * np.kron(X, np.eye(2)), np.sqrt(0.01) * np.kron(X, X), np.sqrt(0.01) * np.kron(X, Y), np.sqrt(0.01) * np.kron(X, Z), np.sqrt(0.01) * np.kron(Y, np.eye(2)), np.sqrt(0.01) * np.kron(Y, X), np.sqrt(0.01) * np.kron(Y, Y), np.sqrt(0.01) * np.kron(Y, Z), np.sqrt(0.01) * np.kron(Z, np.eye(2)), np.sqrt(0.01) * np.kron(Z, X), np.sqrt(0.01) * np.kron(Z, Y), np.sqrt(0.01) * np.kron(Z, Z), )) assert cirq.has_channel(d)
def test_channel_protocol_is_deprecated(): with cirq.testing.assert_deprecated(deadline='v0.13'): assert np.allclose(cirq.channel(cirq.X), cirq.kraus(cirq.X))
def translate_cirq_to_qtrajectory( self, qubit_order: cirq.ops.QubitOrderOrList = cirq.ops.QubitOrder.DEFAULT ) -> qsim.NoisyCircuit: """ Translates this noisy Cirq circuit to the qsim representation. :qubit_order: Ordering of qubits :return: a C++ qsim NoisyCircuit object """ qsim_ncircuit = qsim.NoisyCircuit() ordered_qubits = cirq.ops.QubitOrder.as_qubit_order( qubit_order).order_for(self.all_qubits()) # qsim numbers qubits in reverse order from cirq ordered_qubits = list(reversed(ordered_qubits)) qsim_ncircuit.num_qubits = len(ordered_qubits) def has_qsim_kind(op: cirq.ops.GateOperation): return _cirq_gate_kind(op.gate) != None def to_matrix(op: cirq.ops.GateOperation): mat = cirq.unitary(op.gate, None) if mat is None: return NotImplemented return cirq.ops.MatrixGate(mat).on(*op.qubits) qubit_to_index_dict = {q: i for i, q in enumerate(ordered_qubits)} time_offset = 0 for moment in self: moment_length = 0 ops_by_gate = [] ops_by_mix = [] ops_by_channel = [] # Capture ops of each type in the appropriate list. for qsim_op in moment: if cirq.has_unitary(qsim_op) or cirq.is_measurement(qsim_op): oplist = cirq.decompose(qsim_op, fallback_decomposer=to_matrix, keep=has_qsim_kind) ops_by_gate.append(oplist) moment_length = max(moment_length, len(oplist)) pass elif cirq.has_mixture(qsim_op): ops_by_mix.append(qsim_op) moment_length = max(moment_length, 1) pass elif cirq.has_channel(qsim_op): ops_by_channel.append(qsim_op) moment_length = max(moment_length, 1) pass else: raise ValueError(f'Encountered unparseable op: {qsim_op}') # Gates must be added in time order. for gi in range(moment_length): # Handle gate output. for gate_ops in ops_by_gate: if gi >= len(gate_ops): continue qsim_op = gate_ops[gi] time = time_offset + gi gate_kind = _cirq_gate_kind(qsim_op.gate) add_op_to_circuit(qsim_op, time, qubit_to_index_dict, qsim_ncircuit) # Handle mixture output. for mixture in ops_by_mix: mixdata = [] for prob, mat in cirq.mixture(mixture): square_mat = np.reshape(mat, (int(np.sqrt(mat.size)), -1)) unitary = cirq.is_unitary(square_mat) # Package matrix into a qsim-friendly format. mat = np.reshape(mat, (-1, )).astype(np.complex64, copy=False) mixdata.append((prob, mat.view(np.float32), unitary)) qubits = [qubit_to_index_dict[q] for q in mixture.qubits] qsim.add_channel(time_offset, qubits, mixdata, qsim_ncircuit) # Handle channel output. for channel in ops_by_channel: chdata = [] for i, mat in enumerate(cirq.channel(channel)): square_mat = np.reshape(mat, (int(np.sqrt(mat.size)), -1)) unitary = cirq.is_unitary(square_mat) singular_vals = np.linalg.svd(square_mat)[1] lower_bound_prob = min(singular_vals)**2 # Package matrix into a qsim-friendly format. mat = np.reshape(mat, (-1, )).astype(np.complex64, copy=False) chdata.append( (lower_bound_prob, mat.view(np.float32), unitary)) qubits = [qubit_to_index_dict[q] for q in channel.qubits] qsim.add_channel(time_offset, qubits, chdata, qsim_ncircuit) time_offset += moment_length return qsim_ncircuit
def test_bit_flip_channel(): d = cirq.bit_flip(0.3) np.testing.assert_almost_equal( cirq.channel(d), (np.sqrt(1.0 - 0.3) * np.eye(2), np.sqrt(0.3) * X)) assert cirq.has_channel(d)
def test_asymmetric_depolarizing_channel(): d = cirq.asymmetric_depolarize(0.1, 0.2, 0.3) np.testing.assert_almost_equal(cirq.channel(d), (np.sqrt(0.4) * np.eye(2), np.sqrt(0.1) * X, np.sqrt(0.2) * Y, np.sqrt(0.3) * Z)) assert cirq.has_channel(d)
def test_depolarizing_channel(): d = cirq.depolarize(0.3) np.testing.assert_almost_equal(cirq.channel(d), (np.sqrt(0.7) * np.eye(2), np.sqrt(0.1) * X, np.sqrt(0.1) * Y, np.sqrt(0.1) * Z)) assert cirq.has_channel(d)
def test_phase_damping_channel(): d = cirq.phase_damp(0.3) np.testing.assert_almost_equal( cirq.channel(d), (np.array([[1.0, 0.], [0., np.sqrt(1 - 0.3)]]), np.array([[0., 0.], [0., np.sqrt(0.3)]])))
def test_amplitude_damping_channel(): d = cirq.amplitude_damp(0.3) np.testing.assert_almost_equal( cirq.channel(d), (np.array([[1., 0.], [0., np.sqrt(1. - 0.3)]]), np.array([[0., np.sqrt(0.3)], [0., 0.]])))
def test_channel(): class NoDetailsGate(cirq.Gate): def num_qubits(self) -> int: return 1 assert not cirq.has_channel(NoDetailsGate().with_probability(0.5)) assert cirq.channel(NoDetailsGate().with_probability(0.5), None) is None assert cirq.channel(cirq.X.with_probability(sympy.Symbol('x')), None) is None assert_channel_sums_to_identity(cirq.X.with_probability(0.25)) assert_channel_sums_to_identity(cirq.bit_flip(0.75).with_probability(0.25)) assert_channel_sums_to_identity( cirq.amplitude_damp(0.75).with_probability(0.25)) m = cirq.channel(cirq.X.with_probability(0.25)) assert len(m) == 2 np.testing.assert_allclose( m[0], cirq.unitary(cirq.X) * np.sqrt(0.25), atol=1e-8, ) np.testing.assert_allclose( m[1], cirq.unitary(cirq.I) * np.sqrt(0.75), atol=1e-8, ) m = cirq.channel(cirq.bit_flip(0.75).with_probability(0.25)) assert len(m) == 3 np.testing.assert_allclose( m[0], cirq.unitary(cirq.I) * np.sqrt(0.25) * np.sqrt(0.25), atol=1e-8, ) np.testing.assert_allclose( m[1], cirq.unitary(cirq.X) * np.sqrt(0.25) * np.sqrt(0.75), atol=1e-8, ) np.testing.assert_allclose( m[2], cirq.unitary(cirq.I) * np.sqrt(0.75), atol=1e-8, ) m = cirq.channel(cirq.amplitude_damp(0.75).with_probability(0.25)) assert len(m) == 3 np.testing.assert_allclose( m[0], np.array([[1, 0], [0, np.sqrt(1 - 0.75)]]) * np.sqrt(0.25), atol=1e-8, ) np.testing.assert_allclose( m[1], np.array([[0, np.sqrt(0.75)], [0, 0]]) * np.sqrt(0.25), atol=1e-8, ) np.testing.assert_allclose( m[2], cirq.unitary(cirq.I) * np.sqrt(0.75), atol=1e-8, )
def assert_channel_sums_to_identity(val): m = cirq.channel(val) s = sum(np.conj(e.T) @ e for e in m) np.testing.assert_allclose(s, np.eye(np.product(cirq.qid_shape(val))), atol=1e-8)
def _channel_(self): # coverage: ignore return cirq.channel(cirq.X)
def test_rotation_error_channel(): d = cirq.rotation_error(np.pi / 6., np.pi / 12., np.pi / 8.) np.testing.assert_almost_equal( cirq.channel(d), (np.exp(X * 0.5 * (0.0 - 1.0j) * np.pi / 6.), np.exp(Y * 0.5 * (0.0 - 1.0j) * np.pi / 12.), np.exp(Z * 0.5 * (0.0 - 1.0j) * np.pi / 8.)))
def test_phase_flip_channel(): d = cirq.phase_flip(0.3) np.testing.assert_almost_equal( cirq.channel(d), (np.sqrt(1. - 0.3) * np.eye(2), np.sqrt(0.3) * Z)) assert cirq.has_channel(d)
def test_reset_channel(): np.testing.assert_almost_equal( cirq.channel(cirq.RESET), (np.array([[1., 0.], [0., 0]]), np.array([[0., 1.], [0., 0.]]))) assert cirq.has_channel(cirq.RESET) assert not cirq.has_mixture_channel(cirq.RESET)
def _channel_(self): return cirq.channel(cirq.X)
def circuit_to_density_matrix_tensors( circuit: cirq.Circuit, qubits: Optional[Sequence[cirq.LineQubit]] = None ) -> Tuple[List[qtn.Tensor], Dict['cirq.Qid', int], Dict[Tuple[str, str], Tuple[float, float]]]: """Given a circuit with mixtures or channels, construct a tensor network representation of the density matrix. This assumes you start in the |0..0><0..0| state. Indices are named "nf{i}_q{x}" and "nb{i}_q{x}" where i is a time index and x is a qubit index. nf- and nb- refer to the "forwards" and "backwards" copies of the circuit. Kraus indices are named "k{j}" where j is an independent "kraus" internal index which you probably never need to access. Args: circuit: The circuit containing operations that support the cirq.unitary() or cirq.channel() protocols. qubits: The qubits in the circuit. Returns: tensors: A list of Quimb Tensor objects qubit_frontier: A mapping from qubit to time index at the end of the circuit. This can be used to deduce the names of the free tensor indices. positions: A positions dictionary suitable for passing to tn.graph()'s `fix` argument to draw the resulting tensor network similar to a quantum circuit. """ if qubits is None: # coverage: ignore qubits = sorted(cast(Iterable[cirq.LineQubit], circuit.all_qubits())) qubit_frontier: Dict[cirq.Qid, int] = {q: 0 for q in qubits} kraus_frontier = 0 positions: Dict[Tuple[str, str], Tuple[float, float]] = {} tensors: List[qtn.Tensor] = [] x_scale = 2 y_scale = 3 x_nudge = 0.3 n_qubits = len(qubits) yb_offset = (n_qubits + 0.5) * y_scale def _positions(mi, qubits): return _add_to_positions( positions, mi, qubits, tot_n_qubits=n_qubits, x_scale=x_scale, y_scale=y_scale, x_nudge=x_nudge, yb_offset=yb_offset, ) # Initialize forwards and backwards qubits into the 0 state, i.e. prepare # rho_0 = |0><0|. for q in qubits: tensors += [ qtn.Tensor(data=quimb.up().squeeze(), inds=(f'nf0_q{q.x}', ), tags={'Q0', 'i0f', _qpos_tag(q)}), qtn.Tensor(data=quimb.up().squeeze(), inds=(f'nb0_q{q.x}', ), tags={'Q0', 'i0b', _qpos_tag(q)}), ] _positions(0, q) for mi, moment in enumerate(circuit.moments): for op in moment.operations: start_inds_f = [f'nf{qubit_frontier[q]}_q{q.x}' for q in op.qubits] start_inds_b = [f'nb{qubit_frontier[q]}_q{q.x}' for q in op.qubits] for q in op.qubits: qubit_frontier[q] += 1 end_inds_f = [f'nf{qubit_frontier[q]}_q{q.x}' for q in op.qubits] end_inds_b = [f'nb{qubit_frontier[q]}_q{q.x}' for q in op.qubits] if cirq.has_unitary(op): U = cirq.unitary(op).reshape( (2, ) * 2 * len(op.qubits)).astype(np.complex128) tensors.append( qtn.Tensor( data=U, inds=end_inds_f + start_inds_f, tags={ f'Q{len(op.qubits)}', f'i{mi + 1}f', _qpos_tag(op.qubits) }, )) tensors.append( qtn.Tensor( data=np.conj(U), inds=end_inds_b + start_inds_b, tags={ f'Q{len(op.qubits)}', f'i{mi + 1}b', _qpos_tag(op.qubits) }, )) elif cirq.has_channel(op): K = np.asarray(cirq.channel(op), dtype=np.complex128) kraus_inds = [f'k{kraus_frontier}'] tensors.append( qtn.Tensor( data=K, inds=kraus_inds + end_inds_f + start_inds_f, tags={ f'kQ{len(op.qubits)}', f'i{mi + 1}f', _qpos_tag(op.qubits) }, )) tensors.append( qtn.Tensor( data=np.conj(K), inds=kraus_inds + end_inds_b + start_inds_b, tags={ f'kQ{len(op.qubits)}', f'i{mi + 1}b', _qpos_tag(op.qubits) }, )) kraus_frontier += 1 else: raise ValueError(repr(op)) # coverage: ignore _positions(mi + 1, op.qubits) return tensors, qubit_frontier, positions
def test_tagged_operation_forwards_protocols(): """The results of all protocols applied to an operation with a tag should be equivalent to the result without tags. """ q1 = cirq.GridQubit(1, 1) q2 = cirq.GridQubit(1, 2) h = cirq.H(q1) tag = 'tag1' tagged_h = cirq.H(q1).with_tags(tag) np.testing.assert_equal(cirq.unitary(tagged_h), cirq.unitary(h)) assert cirq.has_unitary(tagged_h) assert cirq.decompose(tagged_h) == cirq.decompose(h) assert cirq.pauli_expansion(tagged_h) == cirq.pauli_expansion(h) assert cirq.equal_up_to_global_phase(h, tagged_h) assert np.isclose(cirq.channel(h), cirq.channel(tagged_h)).all() assert cirq.measurement_key(cirq.measure(q1, key='blah').with_tags(tag)) == 'blah' parameterized_op = cirq.XPowGate(exponent=sympy.Symbol('t'))(q1).with_tags(tag) assert cirq.is_parameterized(parameterized_op) resolver = cirq.study.ParamResolver({'t': 0.25}) assert cirq.resolve_parameters(parameterized_op, resolver) == cirq.XPowGate(exponent=0.25)( q1 ).with_tags(tag) assert cirq.resolve_parameters_once(parameterized_op, resolver) == cirq.XPowGate(exponent=0.25)( q1 ).with_tags(tag) y = cirq.Y(q1) tagged_y = cirq.Y(q1).with_tags(tag) assert tagged_y ** 0.5 == cirq.YPowGate(exponent=0.5)(q1) assert tagged_y * 2 == (y * 2) assert 3 * tagged_y == (3 * y) assert cirq.phase_by(y, 0.125, 0) == cirq.phase_by(tagged_y, 0.125, 0) controlled_y = tagged_y.controlled_by(q2) assert controlled_y.qubits == ( q2, q1, ) assert isinstance(controlled_y, cirq.Operation) assert not isinstance(controlled_y, cirq.TaggedOperation) clifford_x = cirq.SingleQubitCliffordGate.X(q1) tagged_x = cirq.SingleQubitCliffordGate.X(q1).with_tags(tag) assert cirq.commutes(clifford_x, clifford_x) assert cirq.commutes(tagged_x, clifford_x) assert cirq.commutes(clifford_x, tagged_x) assert cirq.commutes(tagged_x, tagged_x) assert cirq.trace_distance_bound(y ** 0.001) == cirq.trace_distance_bound( (y ** 0.001).with_tags(tag) ) flip = cirq.bit_flip(0.5)(q1) tagged_flip = cirq.bit_flip(0.5)(q1).with_tags(tag) assert cirq.has_mixture(tagged_flip) assert cirq.has_channel(tagged_flip) flip_mixture = cirq.mixture(flip) tagged_mixture = cirq.mixture(tagged_flip) assert len(tagged_mixture) == 2 assert len(tagged_mixture[0]) == 2 assert len(tagged_mixture[1]) == 2 assert tagged_mixture[0][0] == flip_mixture[0][0] assert np.isclose(tagged_mixture[0][1], flip_mixture[0][1]).all() assert tagged_mixture[1][0] == flip_mixture[1][0] assert np.isclose(tagged_mixture[1][1], flip_mixture[1][1]).all() qubit_map = {q1: 'q1'} qasm_args = cirq.QasmArgs(qubit_id_map=qubit_map) assert cirq.qasm(h, args=qasm_args) == cirq.qasm(tagged_h, args=qasm_args) cirq.testing.assert_has_consistent_apply_unitary(tagged_h)
def compute_channel_matrix(channel: cirq.SupportsChannel) -> np.ndarray: ks = cirq.channel(channel) d_out, d_in = ks[0].shape m = np.zeros((d_out * d_out, d_in * d_in), dtype=np.complex128) for k, e_in in enumerate(generate_standard_operator_basis(d_in, d_in)): m[:, k] = np.reshape(apply_channel(channel, e_in), d_out * d_out) return m @pytest.mark.parametrize( 'kraus_operators, expected_choi', ( ([np.eye(2)], np.array([[1, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0], [1, 0, 0, 1]])), (cirq.channel(cirq.depolarize(0.75)), np.eye(4) / 2), ( [ np.array([[1, 0, 0], [0, 0, 1]]) / np.sqrt(2), np.array([[1, 0, 0], [0, 0, -1]]) / np.sqrt(2), ], np.diag([1, 0, 0, 0, 0, 1]), ), ), ) def test_kraus_to_choi(kraus_operators, expected_choi): """Verifies that cirq.kraus_to_choi computes the correct Choi matrix.""" assert np.allclose(cirq.kraus_to_choi(kraus_operators), expected_choi) @pytest.mark.parametrize(