def run(self, psi, steps): """ Simulate a batch of trajectories for a number of steps. Args: psi (Tensor([B1,...Bb,N], c64)): batch of quantum states. steps (int): number of steps to run the trajectory """ psi, _ = normalize(psi) j = tf.constant(0) _, psi_new, steps = tf.while_loop(self._cond, self._step, loop_vars=[j, psi, steps]) # Check for NaN mask = tf.math.is_nan(tf.math.real(psi_new)) psi_new = tf.where(mask, psi, psi_new) return psi_new
def _step(self, j, psi, steps): """ One step in the Markov chain. """ traj, p, norm = {}, {}, {} cumulant = tf.zeros([psi.shape[0], 1]) prob = tf.random.uniform([psi.shape[0], 1]) state = psi for i, Kraus in self.Kraus_operators.items(): # Compute a trajectory for this Kraus operator traj[i] = tf.linalg.matvec(Kraus, psi) # shape = [b,N] traj[i], norm[i] = normalize(traj[i]) p[i] = tf.math.real(norm[i])**2 # Select this trajectory depending on sampled 'prob' mask = tf.math.logical_and(prob > cumulant, prob < cumulant + p[i]) state = tf.where(mask, traj[i], state) # Update cumulant cumulant += p[i] return [j + 1, state, steps]
def _control_circuit(self, psi, action): """ Args: psi (Tensor([batch_size,N], c64)): batch of states action (dict, 'alpha' : Tensor([batch_size,2], tf.float32), 'theta' : Tensor([batch_size,10], tf.float32)) Returns: see parent class docs """ # Extract parameters alpha = hf.vec_to_complex(action['alpha']) theta = action['theta'] # Build gates displace = self.displace(alpha) snap = self.SNAP_miscalibrated(theta) # Apply gates psi = tf.linalg.matvec(displace, psi) psi = tf.linalg.matvec(snap, psi) psi = tf.linalg.matvec(tf.linalg.adjoint(displace), psi) # Either implement a measurement with a random qubit projection, or # project on the specified 'self.bit_string' sequence of qubit states. if self.bit_string is not None: s = int(self.bit_string[self._elapsed_steps]) psi = tf.linalg.matvec(self.P[s], psi) if s == 1: psi = tf.linalg.matvec(self.sx, psi) psi, norm = normalize(psi) self.norms.append(norm) msmt = tf.ones((self.batch_size, 1)) * (1 if s == 0 else -1) else: # Readout with feedback to flip the qubit psi, msmt = measurement(psi, self.P) psi = tf.where(msmt == 1, psi, tf.linalg.matvec(self.sx, psi)) return psi, psi, msmt
def reward_overlap(self, target_projector, postselect_0): """ Reward only on last time step using the overlap of the cached state with the target state. The cached state is measured prior to computing the overlap, to make sure that the oscillator and qubit are disentangled. The agent can learn to arrange things so that this measurement doesn't matter (i.e. if they are already disentangled before the measurement). """ if self._elapsed_steps < self.episode_length: z = tf.zeros(self.batch_size, dtype=tf.float32) else: if self.tensorstate: if postselect_0: # project qubit to |0> psi = tf.linalg.matvec(self.P[0], self._state) psi, _ = normalize(psi) else: # randomly project qubit with a measurement psi, _ = measurement(self._state, self.P, sample=True) else: psi = self._state overlap = expectation(psi, target_projector, reduce_batch=False) z = tf.reshape(tf.math.real(overlap), shape=[self.batch_size]) self.info['psi_cached'] = psi return z
import numpy as np """ Benchmarking of the overlap fidelity estimator. Wigner / characteristic function are computed with Monte Carlo method by sampling points in phase space. The sampling distribution here is ~ Wigner**2 according to these two papers: https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.107.210404 https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.106.230501 """ N = 40 # Hilbert space truncation state = basis(2, N) target_state = normalize(basis(2, N) + 1 / 4 * basis(1, N))[0] window_size = 12 alpha_samples = 100 msmt_samples = 1 BUFFER_SIZE = 20000 T = ops.TranslationOperator(N) parity = ops.parity(N) F_true = float(tf.math.real(batch_dot(state, target_state))**2) print('True overlap fidelity: %.5f' % F_true) def phase_estimation(psi, U, angle, sample=False):