def test_eng_run_state_reduced_dm(self, setup_eng, cutoff, tol): """Tests whether the reduced_density matrix of the returned state is a Tensor object when executed with `eng.run`.""" eng, prog = setup_eng(1) with prog.context as q: Dgate(ALPHA) | q state = eng.run(prog).state rho = state.reduced_dm([0]) assert isinstance(rho, tf.Tensor) ket = coherent_state(ALPHA, cutoff) coh_dm = np.einsum("i,j->ij", ket, ket.conj()) assert np.allclose(rho, coh_dm, atol=tol, rtol=0.0)
def test_eval_true_state_all_fock_probs(self, setup_eng, cutoff, batch_size, tol): """Tests whether the Fock-basis probabilities of the state return a Tensor with the correct value.""" eng, prog = setup_eng(2) with prog.context as q: Dgate(ALPHA) | q[0] Dgate(-ALPHA) | q[1] state = eng.run(prog).state probs = state.all_fock_probs() assert isinstance(probs, tf.Tensor) probs = probs.numpy().flatten() ref_probs = (np.abs( np.outer(coherent_state(ALPHA, cutoff), coherent_state(-ALPHA, cutoff))).flatten()**2) if batch_size is not None: ref_probs = np.tile(ref_probs, batch_size) assert np.allclose(probs, ref_probs, atol=tol, rtol=0.0)
def circuit(): # Create a parameter with an initial value of 0.1 params = [make_param(name='alpha', constant=0.1)] eng, q = sf.Engine(1) with eng: Dgate(params[0]) | q[0] state = eng.run('tf', cutoff_dim=7, eval=False) # As the output we take the probability of measuring one photon in the mode prob = state.fock_prob([1]) circuit_output = tf.identity(prob, name="prob") return circuit_output
def test_eng_run_state_ket(self, setup_eng, cutoff, pure, tol): """Tests whether the ket of the returned state is a Tensor object when executed with `eng.run`.""" if not pure: pytest.skip("Tested only for pure states") eng, prog = setup_eng(1) with prog.context as q: Dgate(ALPHA) | q state = eng.run(prog).state ket = state.ket() assert isinstance(ket, tf.Tensor) coh_ket = coherent_state(ALPHA, cutoff) assert np.allclose(ket, coh_ket, atol=tol, rtol=0.0)
def test_eng_run_state_mean_photon(self, setup_eng, tol): """Tests whether the local mean photon number of the state is Tensor object when executed with `eng.run`.""" eng, prog = setup_eng(1) with prog.context as q: Dgate(ALPHA) | q state = eng.run(prog).state nbar, var = state.mean_photon(0) assert isinstance(nbar, tf.Tensor) assert isinstance(var, tf.Tensor) ref_nbar = np.abs(ALPHA) ** 2 ref_var = np.abs(ALPHA) ** 2 assert np.allclose(nbar, ref_nbar, atol=tol, rtol=0.0) assert np.allclose(var, ref_var, atol=tol, rtol=0.0)
def test_eng_run_state_quad_expectation(self, setup_eng, tol, hbar): """Tests whether the local quadrature expectation of the state is Tensor object when executed with `eng.run`.""" eng, prog = setup_eng(1) with prog.context as q: Dgate(ALPHA) | q state = eng.run(prog).state e, v = state.quad_expectation(0, 0) assert isinstance(e, tf.Tensor) assert isinstance(v, tf.Tensor) true_exp = np.sqrt(hbar / 2.0) * (ALPHA + np.conj(ALPHA)) true_var = hbar / 2.0 assert np.allclose(e, true_exp, atol=tol, rtol=0.0) assert np.allclose(v, true_var, atol=tol, rtol=0.0)
def test_eng_run_state_dm(self, pure, cutoff, setup_eng, tol): """Tests whether the density matrix of the returned state is an Tensor object when executed with `eng.run`.""" if not pure: pytest.skip("Tested only for pure states") eng, prog = setup_eng(1) with prog.context as q: Dgate(ALPHA) | q state = eng.run(prog).state dm = state.dm() assert isinstance(dm, tf.Tensor) ket = coherent_state(ALPHA, cutoff) coh_dm = np.einsum("i,j->ij", ket, ket.conj()) assert np.allclose(dm, coh_dm, atol=tol, rtol=0.0)
def circuit(params): eng, q = sf.Engine(1) with eng: Dgate(params[0]) | q[0] state = eng.run('fock', cutoff_dim=7) circuit_output = state.fock_prob([1]) trace = state.trace() # Log the trace of the state to check if it is 1 log = {'Prob': circuit_output, 'Trace': trace} # The second return value can be an optional log dictionary # of one or more values return circuit_output, log
def circuit(): # This time we want to keep the parameter small via regularization and visualize its evolution in tensorboard params = [ make_param(name='alpha', constant=0.1, regularize=True, monitor=True) ] eng, q = sf.Engine(1) with eng: Dgate(params[0]) | q[0] state = eng.run('tf', cutoff_dim=7, eval=False) circuit_output = state.fock_prob([1]) # The identity() function allows us to give this tensor a name # which we can refer to below circuit_output = tf.identity(circuit_output, name="prob") trace = tf.identity(state.trace(), name='trace') return circuit_output
def test_displaced_squeezed_mean_photon_gradient(self, setup_eng, cutoff, tol, batch_size): """Test whether the gradient of the mean photon number of a displaced squeezed state is correct. .. note:: As this test contains multiple gates being applied to the program, this test will fail in TensorFlow 2.1 due to the bug discussed in https://github.com/tensorflow/tensorflow/issues/37307, if `tf.einsum` is being used in ``tfbackend/ops.py`` rather than _einsum_v1. """ if batch_size is not None: pytest.skip( "Cannot calculate gradient in batch mode, as tape.gradient " "cannot differentiate non-scalar output." ) eng, prog = setup_eng(1) with prog.context as q: Sgate(prog.params("r"), prog.params("phi")) | q Dgate(prog.params("a")) | q a = tf.Variable(ALPHA) r = tf.Variable(0.105) phi = tf.Variable(0.123) with tf.GradientTape() as tape: state = eng.run(prog, args={"a": a, "r": r, "phi": phi}).state mean, _ = state.mean_photon(0) # test the mean and variance of the photon number is correct mean_ex = a ** 2 + tf.sinh(r) ** 2 assert np.allclose(mean, mean_ex, atol=tol, rtol=0) # test the gradient of the mean is correct grad = tape.gradient(mean, [a, r, phi]) grad_ex = [2 * a, 2 * tf.sinh(r) * tf.cosh(r), 0] assert np.allclose(grad, grad_ex, atol=tol, rtol=0)
def test_displaced_thermal_mean_photon_gradient(self, setup_eng, tol, batch_size): """Tests whether the gradient for the mean photon variance on a displaced thermal state is correct: E(n)=|a|^2+nbar and var(n)=var_th+|a|^2(1+2nbar) """ if batch_size is not None: pytest.skip( "Cannot calculate gradient in batch mode, as tape.gradient " "cannot differentiate non-scalar output." ) eng, prog = setup_eng(1) alpha = prog.params("alpha") nbar = prog.params("nbar") with prog.context as q: Thermal(nbar) | q Dgate(alpha) | q a = tf.Variable(0.2) n = tf.Variable(0.052) with tf.GradientTape() as tape: state = eng.run(prog, args={"nbar": n, "alpha": a}).state mean, var = state.mean_photon(0) # test the mean and variance of the photon number is correct mean_ex = a ** 2 + n var_ex = n ** 2 + n + a ** 2 * (1 + 2 * n) assert np.allclose(mean, mean_ex, atol=tol, rtol=0) assert np.allclose(var, var_ex, atol=tol, rtol=0) # test the gradient of the variance is correct grad = tape.gradient(var, [a, n]) grad_ex = [2 * a * (1 + 2 * n), 2 * n + 1 + 2 * a ** 2] assert np.allclose(grad, grad_ex, atol=tol, rtol=0)
def qnn_layer(layer_number): with tf.name_scope('layer_{}'.format(layer_number)): BSgate(bs_variables[layer_number, 0, 0, 0], bs_variables[layer_number, 0, 0, 1]) \ | (q[0], q[1]) for i in range(mode_number): Rgate(phase_variables[layer_number, i, 0]) | q[i] for i in range(mode_number): Sgate(tf.clip_by_value(sq_magnitude_variables[layer_number, i], -sq_clip, sq_clip), sq_phase_variables[layer_number, i]) | q[i] BSgate(bs_variables[layer_number, 0, 1, 0], bs_variables[layer_number, 0, 1, 1]) \ | (q[0], q[1]) for i in range(mode_number): Rgate(phase_variables[layer_number, i, 1]) | q[i] for i in range(mode_number): Dgate(tf.clip_by_value(disp_magnitude_variables[layer_number, i], -disp_clip, disp_clip), disp_phase_variables[layer_number, i]) | q[i] for i in range(mode_number): Kgate(tf.clip_by_value(kerr_variables[layer_number, i], -kerr_clip, kerr_clip)) | q[i]
def circuit(params, a, m1, m2, cutoff): """Runs the constrained variational circuit with specified parameters, returning the output fidelity to the requested ON state, as well as the post-selection probability. Args: params (list): list of gate parameters for the constrained variational quantum circuit. This should contain the following 15 values in the following order: * ``sq_r0, sq_r1, sq_r2``: the squeezing magnitudes applied to the first three modes * ``sq_phi0, sq_phi1, sq_phi2``: the squeezing phase applied to the first three modes * ``d_r0, d_r1, d_r2``: the displacement magnitudes applied to the first three modes * ``bs_theta1, bs_theta2, bs_theta3``: the 3-mode interferometer beamsplitter angles theta * ``bs_phi1, bs_phi2, bs_phi3``: the 3-mode interferometer beamsplitter phases phi a (float): the ON state parameter m1 (int): the Fock state measurement of mode 0 to be post-selected m2 (int): the Fock state measurement of mode 1 to be post-selected cutoff (int): the Fock basis truncation Returns: tuple: a tuple containing the output fidelity to the target ON state, the probability of post-selection, the state norm before entering the beamsplitter, the state norm after exiting the beamsplitter, and the density matrix of the output state. """ # define target state ONdm = on_state(a, cutoff) # unpack circuit parameters # squeezing magnitudes sq_r = params[:3] # squeezing phase sq_phi = params[3:6] # displacement magnitudes (assume displacement is real for now) d_r = params[6:9] # beamsplitter theta bs_theta1, bs_theta2, bs_theta3 = params[9:12] # beamsplitter phi bs_phi1, bs_phi2, bs_phi3 = params[12:] # quantum circuit prior to entering the beamsplitter prog = sf.Program(3) with prog.context as q: for k in range(3): Sgate(sq_r[k], sq_phi[k]) | q[k] Dgate(d_r[k]) | q[k] eng = sf.Engine("fock", backend_options={"cutoff_dim": cutoff}) stateIn = eng.run(prog).state normIn = np.abs(stateIn.trace()) # norm of output state and probability prog_BS = sf.Program(3) with prog_BS.context as q: BSgate(bs_theta1, bs_phi1) | (q[0], q[1]) BSgate(bs_theta2, bs_phi2) | (q[1], q[2]) BSgate(bs_theta3, bs_phi3) | (q[0], q[1]) stateOut = eng.run(prog_BS).state normOut = np.abs(stateOut.trace()) rho = stateOut.dm() # probability of meausring m1 and m2 prob = np.abs(np.trace(rho[m1, m1, m2, m2])) # output state rhoC = rho[m1, m1, m2, m2]/prob #fidelity with the target fidelity = np.abs(np.trace(np.einsum('ij,jk->ik', rhoC, ONdm))) return (fidelity, prob, normIn, normOut, rhoC)
Sgate(tf.clip_by_value(sqr2[l], -sq_clip, sq_clip), sqphi2[l]) | q[1] BSgate(theta2[l], phi2[l]) | (q[0], q[1]) Rgate(r2[l]) | q[0] Dgate(tf.clip_by_value(dr1[l], -disp_clip, disp_clip), dphi1[l]) | q[0] Dgate(tf.clip_by_value(dr2[l], -disp_clip, disp_clip), dphi2[l]) | q[1] Kgate(tf.clip_by_value(kappa1[l], -kerr_clip, kerr_clip)) | q[0] Kgate(tf.clip_by_value(kappa2[l], -kerr_clip, kerr_clip)) | q[1] # StrawberryFields quantum simulator of 2 optical modes engine, q = sf.Engine(num_subsystems=2) # Definition of the CV quantum network with engine: # State preparation Dgate(disps_alpha) | q[0] Dgate(disps_beta) | q[1] # Sequence of variational layers for i in range(depth): layer(i) # Symbolic evaluation of the output state state = engine.run('tf', cutoff_dim=cutoff, eval=False, batch_size=num_images) ket = state.ket() trace = tf.abs(state.trace()) # Projection on the subspace of up to im_dim-1 photons for each mode. ket_reduced = ket[:, :im_dim, :im_dim] norm = tf.sqrt(tf.abs(tf.reduce_sum(tf.conj(ket_reduced) * ket_reduced, axis=[1, 2]))) # Since norm has shape [num_images] while ket_reduced has shape [num_images,im_dim,im_dim] # we need to add 2 extra dimensions to the norm tensor.
def circuit(X): num_qubits = X.get_shape().as_list()[1] phi_1 = make_param(name='phi_1', stdev=np.sqrt(2) / num_qubits, shape=[depth, num_qubits], regularize=False) theta_1 = make_param(name='theta_1', stdev=np.sqrt(2) / num_qubits, shape=[depth, num_qubits], regularize=False) a = make_param(name='a', stdev=np.sqrt(2) / num_qubits, shape=[depth, num_qubits], regularize=False, monitor=True) rtheta_1 = make_param(name='rtheta_1', stdev=np.sqrt(2) / num_qubits, shape=[depth, num_qubits], regularize=False, monitor=True) r = make_param(name='r', stdev=np.sqrt(2) / num_qubits, shape=[depth, num_qubits], regularize=False, monitor=True) kappa = make_param(name='kappa', stdev=np.sqrt(2) / num_qubits, shape=[depth, num_qubits], regularize=False, monitor=True) phi_2 = make_param(name='phi_2', stdev=np.sqrt(2) / num_qubits, shape=[depth, num_qubits], regularize=False) theta_2 = make_param(name='theta_2', stdev=np.sqrt(2) / num_qubits, shape=[depth, num_qubits], regularize=False) rtheta_2 = make_param(name='rtheta_2', stdev=np.sqrt(2) / num_qubits, shape=[depth, num_qubits], regularize=False, monitor=True) def layer(i, size): Rgate(rtheta_1[i, 0]) | (q[0]) BSgate(phi_1[i, 0], 0) | (q[0], q[1]) Rgate(rtheta_1[i, 2]) | (q[1]) for j in range(size): Sgate(r[i, j]) | q[j] Rgate(rtheta_2[i, 0]) | (q[0]) BSgate(phi_2[i, 0], 0) | (q[0], q[1]) Rgate(rtheta_2[i, 2]) | (q[2]) BSgate(phi_2[i, 2], theta_2[i, 3]) | (q[2], q[3]) Rgate(rtheta_2[i, 1]) | (q[1]) BSgate(phi_2[i, 1], 0) | (q[1], q[2]) Rgate(rtheta_2[i, 0]) | (q[0]) BSgate(phi_2[i, 0], 0) | (q[0], q[1]) Rgate(rtheta_2[i, 0]) | (q[0]) Rgate(rtheta_2[i, 1]) | (q[1]) Rgate(rtheta_2[i, 2]) | (q[2]) Rgate(rtheta_2[i, 3]) | (q[3]) BSgate(phi_2[i, 2], 0) | (q[2], q[3]) Rgate(rtheta_2[i, 2]) | (q[2]) BSgate(phi_2[i, 1], 0) | (q[1], q[2]) Rgate(rtheta_2[i, 1]) | (q[1]) for j in range(size): Kgate(kappa[i, j]) | q[j] eng, q = sf.Engine(num_qubits) with eng: for i in range(num_qubits): Dgate(X[:, i], 0.) | q[i] for d in range(depth): layer(d, num_qubits) num_inputs = X.get_shape().as_list()[0] state = eng.run('tf', cutoff_dim=10, eval=False, batch_size=num_inputs) circuit_output, var0 = state.quad_expectation(0) return circuit_output
import strawberryfields as sf from strawberryfields.ops import Dgate, BSgate import tensorflow as tf from qmlt.tf.helpers import make_param from qmlt.tf import CircuitLearner # Define the variational circuit and its output. X = tf.placeholder(tf.float32, shape=[2]) y = tf.placeholder(tf.float32) phi = tf.Variable(2.) eng, q = sf.Engine(2) with eng: # Note that we are feeding 1-d tensors into gates, not scalars! Dgate(X[0], 0.) | q[0] Dgate(X[1], 0.) | q[1] BSgate(phi) | (q[0], q[1]) BSgate() | (q[0], q[1]) # We have to tell the engine how big the batches (first dim of X) are # which we feed into gates num_inputs = X.get_shape().as_list()[0] state = eng.run('tf', cutoff_dim=10, eval=False) # Define the output as the probability of measuring |0,2> as opposed to |2,0> p0 = state.fock_prob([0, 2]) p1 = state.fock_prob([2, 0]) normalization = p0 + p1 + 1e-10 circuit_output = p1 / normalization
# We have to tell the engine how big the batches (first dim of X) are # which we feed into gates num_inputs = X.get_shape().as_list()[0] state = eng.run('tf', cutoff_dim=10, eval=False, batch_size=num_inputs) # Define the output as the probability of measuring |0,2> as opposed to |2,0> p0 = state.fock_prob([0, 2]) p1 = state.fock_prob([2, 0]) normalization = p0 + p1 + 1e-10 output1 = p1 / normalization with eng: # Note that we are feeding 1-d tensors into gates, not scalars! X1 = output1 Dgate(X1, 0.) | q[0] Dgate(0., X1) | q[1] BSgate(phi=params[1]) | (q[0], q[1]) BSgate() | (q[0], q[1]) Vgate(params[5]) | q[0] Vgate(params[6]) | q[1] # We have to tell the engine how big the batches (first dim of X) are # which we feed into gates num_inputs1 = X1.get_shape().as_list()[0] state2 = eng.run('tf', cutoff_dim=10, eval=False, batch_size=num_inputs1) # Define the output as the probability of measuring |0,2> as opposed to |2,0> p00 = state2.fock_prob([0, 2]) p01 = state2.fock_prob([2, 0]) normalization1 = p00 + p01 + 1e-10