def _program_in_CJ_rep(prog, cutoff_dim: int): """Convert a Program object to Choi-Jamiolkowski representation. Doubles the number of modes of a Program object and prepends to its circuit the preparation of the maximally entangled ket state. The core idea is that when we apply any quantum channel (e.g. a unitary gate) to the density matrix of the maximally entangled state, we obtain the Choi matrix of the channel as the result. If the channel is unitary, applying it on the maximally entangled ket yields the corresponding unitary matrix, reshaped. Args: prog (Program): quantum program cutoff_dim (int): the Fock basis truncation Returns: Program: modified program """ prog = copy.deepcopy(prog) prog.locked = False # unlock the copy so we can modify it N = prog.init_num_subsystems prog._add_subsystems(N) # pylint: disable=protected-access prog.init_num_subsystems = 2 * N I = _interleaved_identities(N, cutoff_dim) # prepend the circuit with the I ket preparation prog.circuit.insert(0, Command(Ket(I), list(prog.reg_refs.values()))) return prog
def test_2mode_squeezed_vacuum_gradients(self, setup_eng, cutoff, tol, batch_size): """Tests whether the gradient for the probability of the states |0,0> and |1,1> created by an S2gate is correct.""" if batch_size is not None: pytest.skip( "Cannot calculate gradient in batch mode, as tape.gradient " "cannot differentiate non-scalar output.") R = 0.3 PHI = 0.2 eng, prog = setup_eng(2) _r, _phi = prog.params("r", "phi") vacuum = np.zeros((cutoff, cutoff), dtype=np.complex64) vacuum[0, 0] = 1.0 + 0.0j with prog.context as q: Ket(vacuum) | (q[0], q[1]) S2gate(_r, _phi) | (q[0], q[1]) r = tf.Variable(R) phi = tf.Variable(PHI) with tf.GradientTape(persistent=True) as tape: state = eng.run(prog, args={"r": r, "phi": phi}).state prob00 = tf.abs(state.ket()[0, 0])**2 prob11 = tf.abs(state.ket()[1, 1])**2 r_grad, phi_grad = tape.gradient(prob00, [r, phi]) assert np.allclose(r_grad, -2 * np.tanh(R) / np.cosh(R)**2, atol=tol, rtol=0) assert np.allclose(phi_grad, 0.0, atol=tol, rtol=0) r_grad, phi_grad = tape.gradient(prob11, [r, phi]) assert np.allclose(r_grad, 2 * (np.sinh(R) - np.sinh(R)**3) / np.cosh(R)**5, atol=tol, rtol=0) assert np.allclose(phi_grad, 0.0, atol=tol, rtol=0)
def test_MZ_state_gradients(self, setup_eng, cutoff, tol, batch_size): """Tests whether the gradient for the state created by interfering two single photons at a beam splitter is correct.""" if batch_size is not None: pytest.skip( "Cannot calculate gradient in batch mode, as tape.gradient " "cannot differentiate non-scalar output.") PHI_IN = 0.3 PHI_EX = 0.2 eng, prog = setup_eng(2) _phi_in, _phi_ex = prog.params("phi_in", "phi_ex") photon_11 = np.zeros((cutoff, cutoff), dtype=np.complex64) photon_11[1, 1] = 1.0 + 0.0j with prog.context as q: Ket(photon_11) | (q[0], q[1]) MZgate(_phi_in, _phi_ex) | (q[0], q[1]) phi_in = tf.Variable(PHI_IN) phi_ex = tf.Variable(PHI_EX) with tf.GradientTape(persistent=True) as tape: state = eng.run(prog, args={ "phi_in": phi_in, "phi_ex": phi_ex }).state prob11 = tf.abs(state.ket()[1, 1])**2 prob02 = tf.abs(state.ket()[0, 2])**2 phi_in_grad, phi_ex_grad = tape.gradient(prob11, [phi_in, phi_ex]) assert np.allclose(phi_in_grad, -np.sin(2 * PHI_IN), atol=tol, rtol=0) assert np.allclose(phi_ex_grad, 0, atol=tol, rtol=0) phi_in_grad, phi_ex_grad = tape.gradient(prob02, [phi_in, phi_ex]) assert np.allclose(phi_in_grad, np.sin(PHI_IN) * np.cos(PHI_IN), atol=tol, rtol=0) assert np.allclose(phi_ex_grad, 0, atol=tol, rtol=0)
def test_BS_state_gradients(self, setup_eng, cutoff, tol, batch_size): """Tests whether the gradient for the state created by interfering two single photons at a beam splitter is correct.""" if batch_size is not None: pytest.skip( "Cannot calculate gradient in batch mode, as tape.gradient " "cannot differentiate non-scalar output.") THETA = 0.3 PHI = 0.2 eng, prog = setup_eng(2) _theta, _phi = prog.params("theta", "phi") photon_11 = np.zeros((cutoff, cutoff), dtype=np.complex64) photon_11[1, 1] = 1.0 + 0.0j with prog.context as q: Ket(photon_11) | (q[0], q[1]) BSgate(_theta, _phi) | (q[0], q[1]) theta = tf.Variable(THETA) phi = tf.Variable(PHI) with tf.GradientTape(persistent=True) as tape: state = eng.run(prog, args={"theta": theta, "phi": phi}).state prob11 = tf.abs(state.ket()[1, 1])**2 prob02 = tf.abs(state.ket()[0, 2])**2 theta_grad, phi_grad = tape.gradient(prob11, [theta, phi]) assert np.allclose(theta_grad, -4 * np.sin(2 * THETA) * np.cos(2 * THETA), atol=tol, rtol=0) assert np.allclose(phi_grad, 0.0, atol=tol, rtol=0) theta_grad, phi_grad = tape.gradient(prob02, [theta, phi]) assert np.allclose(theta_grad, np.sin(4 * THETA), atol=tol, rtol=0) assert np.allclose(phi_grad, 0.0, atol=tol, rtol=0)