def test_decoherence_noise(): prog = Program(RX(np.pi / 2, 0), CZ(0, 1), RZ(np.pi, 0)) gates = _get_program_gates(prog) m1 = _decoherence_noise_model(gates, T1=INFINITY, T2=INFINITY, ro_fidelity=1.) # with no readout error, assignment_probs = identity matrix assert np.allclose(m1.assignment_probs[0], np.eye(2)) assert np.allclose(m1.assignment_probs[1], np.eye(2)) for g in m1.gates: # with infinite coherence time all kraus maps should only have a single, unitary kraus op assert len(g.kraus_ops) == 1 k0, = g.kraus_ops # check unitarity k0dk0 = k0.dot(k0.conjugate().transpose()) assert np.allclose(k0dk0, np.eye(k0dk0.shape[0])) # verify that selective (by qubit) dephasing and readout infidelity is working m2 = _decoherence_noise_model(gates, T1=INFINITY, T2={0: 30e-6}, ro_fidelity={ 0: .95, 1: 1.0 }) assert np.allclose(m2.assignment_probs[0], [[.95, 0.05], [.05, .95]]) assert np.allclose(m2.assignment_probs[1], np.eye(2)) for g in m2.gates: if 0 in g.targets: # single dephasing (no damping) channel on qc 0, no noise on qc1 -> 2 Kraus ops assert len(g.kraus_ops) == 2 else: assert len(g.kraus_ops) == 1 # verify that combined T1 and T2 will lead to 4 outcome Kraus map. m3 = _decoherence_noise_model(gates, T1={0: 30e-6}, T2={0: 30e-6}) for g in m3.gates: if 0 in g.targets: # damping (implies dephasing) channel on qc 0, no noise on qc1 -> 4 Kraus ops assert len(g.kraus_ops) == 4 else: assert len(g.kraus_ops) == 1 # verify that gate names are translated new_prog = apply_noise_model(prog, m3) new_gates = _get_program_gates(new_prog) # check that headers have been embedded headers = _noise_model_program_header(m3) assert all( (isinstance(i, Pragma) and i.command in ["ADD-KRAUS", "READOUT-POVM"]) or isinstance(i, DefGate) for i in headers) assert headers.out() in new_prog.out() # verify that high-level add_decoherence_noise reproduces new_prog new_prog2 = add_decoherence_noise(prog, T1={0: 30e-6}, T2={0: 30e-6}) assert new_prog == new_prog2
def test_noise_helpers(): rx90_0, rxm90_1, i_1, cz_01 = gates = RX(np.pi / 2)(0), RX(-np.pi / 2)(1), I(1), CZ(0, 1) prog = Program(*gates) inferred_gates = _get_program_gates(prog) assert set(inferred_gates) == set(gates) noisy_names = _get_noisy_names(gates) assert noisy_names[rx90_0] == "NOISY-RX-PLUS-90" assert noisy_names[rxm90_1] == "NOISY-RX-MINUS-90" assert noisy_names[i_1] == "NOISY-I" assert noisy_names[cz_01] == "NOISY-CZ"
def add_modified_decoherence_noise( prog: "Program", T1: Union[Dict[int, float], float] = 30e-6, T2: Union[Dict[int, float], float] = 30e-6, gate_time_1q: float = 50e-9, gate_time_2q: float = 150e-09, ro_fidelity: Union[Dict[int, float], float] = 0.95, ) -> "Program": """ Add generic damping and dephasing noise to a program. This high-level function is provided as a convenience to investigate the effects of a generic noise model on a program. For more fine-grained control, please investigate the other methods available in the ``pyquil.noise`` module. In an attempt to closely model the QPU, noisy versions of RX(+-pi/2) and CZ are provided; I and parametric RZ are noiseless, and other gates are not allowed. To use this function, you need to compile your program to this native gate set. The default noise parameters - T1 = 30 us - T2 = 30 us - 1q gate time = 50 ns - 2q gate time = 150 ns are currently typical for near-term devices. This function will define new gates and add Kraus noise to these gates. It will translate the input program to use the noisy version of the gates. :param prog: A pyquil program consisting of I, RZ, CZ, and RX(+-pi/2) instructions :param T1: The T1 amplitude damping time either globally or in a dictionary indexed by qubit id. By default, this is 30 us. :param T2: The T2 dephasing time either globally or in a dictionary indexed by qubit id. By default, this is also 30 us. :param gate_time_1q: The duration of the one-qubit gates, namely RX(+pi/2) and RX(-pi/2). By default, this is 50 ns. :param gate_time_2q: The duration of the two-qubit gates, namely CZ. By default, this is 150 ns. :param ro_fidelity: The readout assignment fidelity :math:`F = (p(0|0) + p(1|1))/2` either globally or in a dictionary indexed by qubit id. :return: A new program with noisy operators. """ gates = _get_program_gates(prog) #definitions = { # def_gate.name: def_gate.matrix for def_gate in prog.defined_gates #figure out what is it for #} noise_model = _modified_decoherence_noise_model(gates, T1=T1, T2=T2, gate_time_1q=gate_time_1q, gate_time_2q=gate_time_2q, ro_fidelity=ro_fidelity) return apply_modified_noise_model(prog, noise_model)
def test_apply_noise_model(): p = Program(RX(np.pi / 2, 0), RX(np.pi / 2, 1), CZ(0, 1), RX(np.pi / 2, 1)) noise_model = _decoherence_noise_model(_get_program_gates(p)) pnoisy = apply_noise_model(p, noise_model) for i in pnoisy: if isinstance(i, DefGate): pass elif isinstance(i, Pragma): assert i.command in ['ADD-KRAUS', 'READOUT-POVM'] elif isinstance(i, Gate): assert i.name in NO_NOISE or not i.params
def test_apply_noise_model_perturbed_angles(): eps = 1e-15 p = Program(RX(np.pi / 2 + eps, 0), RX(np.pi / 2 - eps, 1), CZ(0, 1), RX(np.pi / 2 + eps, 1)) noise_model = _decoherence_noise_model(_get_program_gates(p)) pnoisy = apply_noise_model(p, noise_model) for i in pnoisy: if isinstance(i, DefGate): pass elif isinstance(i, Pragma): assert i.command in ["ADD-KRAUS", "READOUT-POVM"] elif isinstance(i, Gate): assert i.name in NO_NOISE or not i.params
def test_noise_helpers(): gates = RX(np.pi / 2, 0), RX(-np.pi / 2, 1), I(1), CZ(0, 1) prog = Program(*gates) inferred_gates = _get_program_gates(prog) assert set(inferred_gates) == set(gates)