def test_channel(): a = cirq.NamedQubit('a') op = cirq.bit_flip(0.5).on(a) np.testing.assert_allclose(cirq.kraus(op), cirq.kraus(op.gate)) assert cirq.has_kraus(op) assert cirq.kraus(cirq.SingleQubitGate()(a), None) is None assert not cirq.has_kraus(cirq.SingleQubitGate()(a))
def test_parameterized(resolve_fn): op = cirq.X.with_probability(sympy.Symbol('x')) assert cirq.is_parameterized(op) assert not cirq.has_kraus(op) assert not cirq.has_mixture(op) op2 = resolve_fn(op, {'x': 0.5}) assert op2 == cirq.X.with_probability(0.5) assert not cirq.is_parameterized(op2) assert cirq.has_kraus(op2) assert cirq.has_mixture(op2)
def test_state_prep_channel_kraus_small(): gate = cirq.StatePreparationChannel(np.array([0.0, 1.0]))(cirq.LineQubit(0)) np.testing.assert_almost_equal(cirq.kraus(gate), (np.array( [[0.0, 0.0], [1.0, 0.0]]), np.array([[0.0, 0.0], [0.0, 1.0]]))) assert cirq.has_kraus(gate) assert not cirq.has_mixture(gate) gate = cirq.StatePreparationChannel(np.array([1.0, 0.0]))(cirq.LineQubit(0)) np.testing.assert_almost_equal(cirq.kraus(gate), (np.array( [[1.0, 0.0], [0.0, 0.0]]), np.array([[0.0, 1.0], [0.0, 0.0]]))) assert cirq.has_kraus(gate) assert not cirq.has_mixture(gate)
def test_kraus(): I = np.eye(2) X = np.array([[0, 1], [1, 0]]) Y = np.array([[0, -1j], [1j, 0]]) Z = np.diag([1, -1]) a, b = cirq.LineQubit.range(2) m = cirq.Moment() assert cirq.has_kraus(m) k = cirq.kraus(m) assert len(k) == 1 assert np.allclose(k[0], np.array([[1.0]])) m = cirq.Moment(cirq.S(a)) assert cirq.has_kraus(m) k = cirq.kraus(m) assert len(k) == 1 assert np.allclose(k[0], np.diag([1, 1j])) m = cirq.Moment(cirq.CNOT(a, b)) assert cirq.has_kraus(m) k = cirq.kraus(m) print(k[0]) assert len(k) == 1 assert np.allclose( k[0], np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]])) p = 0.1 m = cirq.Moment(cirq.depolarize(p).on(a)) assert cirq.has_kraus(m) k = cirq.kraus(m) assert len(k) == 4 assert np.allclose(k[0], np.sqrt(1 - p) * I) assert np.allclose(k[1], np.sqrt(p / 3) * X) assert np.allclose(k[2], np.sqrt(p / 3) * Y) assert np.allclose(k[3], np.sqrt(p / 3) * Z) p = 0.2 q = 0.3 m = cirq.Moment(cirq.bit_flip(p).on(a), cirq.phase_flip(q).on(b)) assert cirq.has_kraus(m) k = cirq.kraus(m) assert len(k) == 4 assert np.allclose(k[0], np.sqrt((1 - p) * (1 - q)) * np.kron(I, I)) assert np.allclose(k[1], np.sqrt(q * (1 - p)) * np.kron(I, Z)) assert np.allclose(k[2], np.sqrt(p * (1 - q)) * np.kron(X, I)) assert np.allclose(k[3], np.sqrt(p * q) * np.kron(X, Z))
def test_depolarizing_channel_two_qubits(): d = cirq.depolarize(0.15, n_qubits=2) np.testing.assert_almost_equal( cirq.kraus(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_kraus(d) assert d.num_qubits() == 2 cirq.testing.assert_has_diagram( cirq.Circuit(d(*cirq.LineQubit.range(2))), """ 0: ───D(0.15)─── │ 1: ───#2──────── """, )
def test_kraus_too_big(): m = cirq.Moment(cirq.IdentityGate(11).on(*cirq.LineQubit.range(11))) assert not cirq.has_kraus(m) assert not m._has_superoperator_() assert m._kraus_() is NotImplemented assert m._superoperator_() is NotImplemented assert cirq.kraus(m, default=None) is None
def test_asymmetric_depolarizing_channel(): d = cirq.asymmetric_depolarize(0.1, 0.2, 0.3) np.testing.assert_almost_equal( cirq.kraus(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_kraus(d)
def test_channel(): class NoDetailsGate(cirq.Gate): def num_qubits(self) -> int: return 1 assert not cirq.has_kraus(NoDetailsGate().with_probability(0.5)) assert cirq.kraus(NoDetailsGate().with_probability(0.5), None) is None assert cirq.kraus(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.kraus(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.kraus(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.kraus(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_not_implemented(val): with pytest.raises(TypeError, match='returned NotImplemented'): _ = cirq.kraus(val) assert cirq.kraus(val, None) is None assert cirq.kraus(val, NotImplemented) is NotImplemented assert cirq.kraus(val, (1, )) == (1, ) assert cirq.kraus(val, LOCAL_DEFAULT) is LOCAL_DEFAULT assert not cirq.has_kraus(val)
def test_op_has_no_kraus(): class EmptyGate(cirq.testing.SingleQubitGate): pass m = cirq.Moment(EmptyGate().on(cirq.NamedQubit("a"))) assert not cirq.has_kraus(m) assert not m._has_superoperator_() assert m._kraus_() is NotImplemented assert m._superoperator_() is NotImplemented assert cirq.kraus(m, default=None) is None
def assert_consistent_channel(gate: Any, rtol: float = 1e-5, atol: float = 1e-8): """Asserts that a given gate has Kraus operators and that they are properly normalized.""" assert cirq.has_kraus( gate), f"Given gate {gate!r} does not return True for cirq.has_kraus." kraus_ops = cirq.kraus(gate) assert cirq.is_cptp(kraus_ops=kraus_ops, rtol=rtol, atol=atol), ( f"Kraus operators for {gate!r} did not sum to identity up to expected tolerances. " f"Summed to {sum(m.T.conj() @ m for m in kraus_ops)}")
def test_asymmetric_depolarizing_channel(): d = cirq.asymmetric_depolarize(0.1, 0.2, 0.3) np.testing.assert_almost_equal( cirq.kraus(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_kraus(d) assert cirq.AsymmetricDepolarizingChannel(p_x=0, p_y=0.1, p_z=0).num_qubits() == 1
def test_op_has_no_kraus(): class EmptyGate(cirq.Gate): def _num_qubits_(self) -> int: return 1 m = cirq.Moment(EmptyGate().on(cirq.NamedQubit("a"))) assert not cirq.has_kraus(m) assert not m._has_superoperator_() assert m._kraus_() is NotImplemented assert m._superoperator_() is NotImplemented assert cirq.kraus(m, default=None) is None
def test_phase_damping_channel(): d = cirq.phase_damp(0.3) np.testing.assert_almost_equal( cirq.kraus(d), ( np.array([[1.0, 0.0], [0.0, np.sqrt(1 - 0.3)]]), np.array([[0.0, 0.0], [0.0, np.sqrt(0.3)]]), ), ) assert cirq.has_kraus(d) assert not cirq.has_mixture(d)
def test_depolarizing_channel(): d = cirq.depolarize(0.3) np.testing.assert_almost_equal( cirq.kraus(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_kraus(d)
def test_reset_channel(): r = cirq.reset(cirq.LineQubit(0)) np.testing.assert_almost_equal( cirq.kraus(r), (np.array([[1.0, 0.0], [0.0, 0]]), np.array([[0.0, 1.0], [0.0, 0.0]]))) assert cirq.has_kraus(r) assert not cirq.has_mixture(r) assert cirq.qid_shape(r) == (2, ) r = cirq.reset(cirq.LineQid(0, dimension=3)) np.testing.assert_almost_equal( cirq.kraus(r), ( np.array([[1, 0, 0], [0, 0, 0], [0, 0, 0]]), np.array([[0, 1, 0], [0, 0, 0], [0, 0, 0]]), np.array([[0, 0, 1], [0, 0, 0], [0, 0, 0]]), ), ) # yapf: disable assert cirq.has_kraus(r) assert not cirq.has_mixture(r) assert cirq.qid_shape(r) == (3, )
def test_generalized_amplitude_damping_channel(): d = cirq.generalized_amplitude_damp(0.1, 0.3) np.testing.assert_almost_equal( cirq.kraus(d), ( np.sqrt(0.1) * np.array([[1.0, 0.0], [0.0, np.sqrt(1.0 - 0.3)]]), np.sqrt(0.1) * np.array([[0.0, np.sqrt(0.3)], [0.0, 0.0]]), np.sqrt(0.9) * np.array([[np.sqrt(1.0 - 0.3), 0.0], [0.0, 1.0]]), np.sqrt(0.9) * np.array([[0.0, 0.0], [np.sqrt(0.3), 0.0]]), ), ) assert cirq.has_kraus(d) assert not cirq.has_mixture(d)
def test_channel_no_methods(): class NoMethod: pass with pytest.raises(TypeError, match='no _kraus_ or _mixture_ or _unitary_ method'): _ = cirq.kraus(NoMethod()) assert cirq.kraus(NoMethod(), None) is None assert cirq.kraus(NoMethod, NotImplemented) is NotImplemented assert cirq.kraus(NoMethod(), (1,)) == (1,) assert cirq.kraus(NoMethod(), LOCAL_DEFAULT) is LOCAL_DEFAULT assert not cirq.has_kraus(NoMethod())
def test_channel_fallback_to_unitary(): u = np.array([[1, 0], [1, 0]]) class ReturnsUnitary: def _unitary_(self) -> np.ndarray: return u np.testing.assert_equal(cirq.kraus(ReturnsUnitary()), (u,)) np.testing.assert_equal(cirq.kraus(ReturnsUnitary(), None), (u,)) np.testing.assert_equal(cirq.kraus(ReturnsUnitary(), NotImplemented), (u,)) np.testing.assert_equal(cirq.kraus(ReturnsUnitary(), (1,)), (u,)) np.testing.assert_equal(cirq.kraus(ReturnsUnitary(), LOCAL_DEFAULT), (u,)) assert cirq.has_kraus(ReturnsUnitary())
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_kraus(val) with pytest.warns(DeprecationWarning, match='_kraus_'): ks = cirq.kraus(val) assert len(ks) == 1 assert np.all(ks[0] == np.eye(2))
def test_explicit_kraus(): a0 = np.array([[0, 0], [1, 0]]) a1 = np.array([[1, 0], [0, 0]]) c = (a0, a1) class ReturnsKraus: def _kraus_(self) -> Sequence[np.ndarray]: return c assert cirq.kraus(ReturnsKraus()) is c assert cirq.kraus(ReturnsKraus(), None) is c assert cirq.kraus(ReturnsKraus(), NotImplemented) is c assert cirq.kraus(ReturnsKraus(), (1, )) is c assert cirq.kraus(ReturnsKraus(), LOCAL_DEFAULT) is c assert cirq.has_kraus(ReturnsKraus())
def test_multi_asymmetric_depolarizing_channel(): d = cirq.asymmetric_depolarize(error_probabilities={'II': 0.8, 'XX': 0.2}) np.testing.assert_almost_equal( cirq.kraus(d), (np.sqrt(0.8) * np.eye(4), np.sqrt(0.2) * np.kron(X, X))) assert cirq.has_kraus(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_channel_fallback_to_mixture(): m = ((0.3, cirq.unitary(cirq.X)), (0.4, cirq.unitary(cirq.Y)), (0.3, cirq.unitary(cirq.Z))) class ReturnsMixture: def _mixture_(self) -> Iterable[Tuple[float, np.ndarray]]: return m c = ( np.sqrt(0.3) * cirq.unitary(cirq.X), np.sqrt(0.4) * cirq.unitary(cirq.Y), np.sqrt(0.3) * cirq.unitary(cirq.Z), ) np.allclose(cirq.kraus(ReturnsMixture()), c) np.allclose(cirq.kraus(ReturnsMixture(), None), c) np.allclose(cirq.kraus(ReturnsMixture(), NotImplemented), c) np.allclose(cirq.kraus(ReturnsMixture(), (1,)), c) np.allclose(cirq.kraus(ReturnsMixture(), LOCAL_DEFAULT), c) assert cirq.has_kraus(ReturnsMixture())
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.kraus(h), cirq.kraus(tagged_h)).all() assert cirq.measurement_key_name( cirq.measure(q1, key='blah').with_tags(tag)) == 'blah' assert cirq.measurement_key_obj( cirq.measure(q1, key='blah').with_tags(tag)) == cirq.MeasurementKey('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) assert parameterized_op._unitary_() is NotImplemented assert parameterized_op._mixture_() is NotImplemented assert parameterized_op._kraus_() is NotImplemented 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_kraus(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 circuit_to_density_matrix_tensors( circuit: cirq.Circuit, qubits: Optional[Sequence[cirq.Qid]] = 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.kraus() protocols. qubits: The qubits in the circuit. The `positions` return argument will position qubits according to their index in this list. 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. Raises: ValueError: If an op is encountered that cannot be converted. """ if qubits is None: # coverage: ignore qubits = sorted(circuit.all_qubits()) qubits = tuple(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, _these_qubits): return _add_to_positions( positions, _mi, _these_qubits, all_qubits=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}', ), tags={'Q0', 'i0f', _qpos_tag(q)}), qtn.Tensor(data=quimb.up().squeeze(), inds=(f'nb0_q{q}', ), 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}' for q in op.qubits] start_inds_b = [f'nb{qubit_frontier[q]}_q{q}' for q in op.qubits] for q in op.qubits: qubit_frontier[q] += 1 end_inds_f = [f'nf{qubit_frontier[q]}_q{q}' for q in op.qubits] end_inds_b = [f'nb{qubit_frontier[q]}_q{q}' 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_kraus(op): K = np.asarray(cirq.kraus(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_has_kraus_when_decomposed(decomposed_cls): op = HasKrausWhenDecomposed(decomposed_cls).on(cirq.NamedQubit('test')) assert cirq.has_kraus(op) assert not cirq.has_kraus(op, allow_decompose=False)
def test_has_kraus(cls): assert cirq.has_kraus(cls())
def test_has_channel_protocol_is_deprecated(): with cirq.testing.assert_deprecated(deadline='v0.13'): assert cirq.has_channel(cirq.depolarize(0.1)) == cirq.has_kraus(cirq.depolarize(0.1))
def test_bit_flip_channel(): d = cirq.bit_flip(0.3) np.testing.assert_almost_equal( cirq.kraus(d), (np.sqrt(1.0 - 0.3) * np.eye(2), np.sqrt(0.3) * X)) assert cirq.has_kraus(d)
def translate_cirq_to_qtrajectory( self, qubit_order: cirq.QubitOrderOrList = cirq.QubitOrder.DEFAULT ) -> qsim.NoisyCircuit: """ Translates this noisy Cirq circuit to the qsim representation. :qubit_order: Ordering of qubits :return: a tuple of (C++ qsim NoisyCircuit object, moment boundary gate indices) """ qsim_ncircuit = qsim.NoisyCircuit() ordered_qubits = cirq.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 to_matrix(op: cirq.GateOperation): mat = cirq.unitary(op.gate, None) if mat is None: return NotImplemented return cirq.MatrixGate(mat).on(*op.qubits) qubit_to_index_dict = {q: i for i, q in enumerate(ordered_qubits)} time_offset = 0 gate_count = 0 moment_indices = [] 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_cirq_gate_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_kraus(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 add_op_to_circuit(qsim_op, time, qubit_to_index_dict, qsim_ncircuit) gate_count += 1 # 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) gate_count += 1 # Handle channel output. for channel in ops_by_channel: chdata = [] for i, mat in enumerate(cirq.kraus(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) gate_count += 1 time_offset += moment_length moment_indices.append(gate_count) return qsim_ncircuit, moment_indices