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
Example #5
0
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):