def Hamiltonian(Ej, Ec, freq_a, V): """ In the final Hamiltonian the levels should be interpreted (qubit, cavity): eigvals[0] : (0,0) eigvals[1] : (0,1) eigvals[2] : (1,0) eigvals[4] : (1,1) eigvals[5] : (2,0) """ plot_charge_matrix = False plot_transmon_levels = False # diagonalize transmon in charge basis n = tf.cast(diag(tf.range(-N_qb / 2, N_qb / 2)), c64) cos_phi = tf.cast( (diag(tf.ones(N_qb - 1), k=1) + diag(tf.ones(N_qb - 1), k=-1)) / 2, c64) H = -Ej * cos_phi + 4 * Ec * n**2 eigvals, U = tf.linalg.eigh(H) # H = U @ diag(eigvals) @ U_dag # project onto subspace of L transmon levels H_L = tf.linalg.diag(eigvals[:L]) n_L = (tf.linalg.adjoint(U) @ n @ U)[:L, :L] # operators in joint transmon-cavity Hilbert space H_cav = freq_a * tensor([ops.identity(L), ops.num(N_cav)]) H_qb = tensor([H_L, ops.identity(N_cav)]) H_coupling = V * tensor([n_L, ops.position(N_cav)]) if plot_transmon_levels: fig, ax = plt.subplots(1, 1) ax.set_ylabel('Transmon energy level (Hz)') ax.plot(range(25), eigvals[:25], marker='.', linestyle='none') if plot_charge_matrix: fig, ax = plt.subplots(1, 1) ax.set_title('Charge matrix in transmon basis') ax.pcolormesh(range(L), range(L), np.transpose(np.abs(n_L))) H0 = H_cav + H_qb H = H0 + H_coupling # free up that sweet memory del (n, cos_phi, H_L, n_L, H_cav, H_qb, H_coupling) return H0, H
def __call__(self, *args, **kwargs): this_op = self.compute(*args, **kwargs) if self.tensor_with is not None: ops = [T if T is not None else this_op for T in self.tensor_with] return tensor(ops) else: return this_op
def compute(self, theta, dangle=None): """Calculates SNAP(theta) using qubit rotation gates. Args: theta (Tensor([B1, ..., Bb, S], c64)): A batch of parameters. dangle (Tensor([B1, ..., Bb, S], c64)): A batch of offsets to add to qubit rotation angles to compenstate for possible angle offsets due to miscalibration. Returns: Tensor([B1, ..., Bb, 2N, 2N], c64): A batch of SNAP(theta) """ # this part is the same as for perfect SNAP: pad the angles with zeros S = theta.shape[-1] # SNAP truncation batch_shape = theta.shape[:-1] paddings = tf.constant([[0, 0]] * len(batch_shape) + [[0, self.N - S]]) dangle = tf.zeros_like(theta) if dangle is None else dangle theta = tf.pad(theta, paddings) # shape=[B,N] dangle = tf.pad(dangle, paddings) # inteded angle and phase of rotation angle, phase = tf.ones_like(theta) * pi + dangle, pi - theta # approximate angle and phase using partially selective pulses angle, phase = self.rotation_coeffs(angle, phase) angle, phase = tf.cast(angle, c64), tf.cast(phase, c64) # unitary corresponding to the first unselective qubit flip unselective_rotation = tensor([ self.rotate_qb(tf.constant(pi), tf.constant(0)), identity(self.N) ]) # construct a unitary corresponding to second "selective" qubit pulse R = self.rotate_qb(angle, phase) # shape=[B,N,2,2] projectors = tf.broadcast_to(self.projectors, batch_shape + self.projectors.shape) # shape=[B,N,N,N] selective_rotations = tensor([R, projectors]) # shape=[B,N,2N,2N] selective_rotations = tf.reduce_sum(selective_rotations, axis=-3) # shape=[B,2N,2N] snap = selective_rotations @ unselective_rotation return snap
x0=[Ej, Ec, freq_a, V], method='Nelder-Mead', options=dict(maxiter=300)) Ej, Ec, freq_a, V = res.x else: Ec = 188598981 Ej = 21608836632 freq_a = 4481053074 V = 23249563 # create operators in the joint Hilbert space H0, H = Hamiltonian(Ej, Ec, freq_a, V) U = ops.HamiltonianEvolutionOperator(H) U0 = ops.HamiltonianEvolutionOperator(H0) D = ops.DisplacementOperator(N_cav, tensor_with=[ops.identity(L), None]) q = tensor([ops.identity(L), ops.position(N_cav)]) p = tensor([ops.identity(L), ops.momentum(N_cav)]) SIMULATE_TIME_EVOLUTION = False # simulate rotation of the displaced state # Because H=const, this can be done with large steps in time if SIMULATE_TIME_EVOLUTION: dt = 10e-9 STEPS = 100 times = tf.range(STEPS, dtype=tf.float32) * dt alpha = 20 vac = Kronecker_product([basis(0, L), basis(0, N_cav)]) psi0 = tf.linalg.matvec(D(alpha), vac) psi, psi_int = psi0, psi0 U_dt, U0_dt = U(dt), U0(dt)
def _define_fixed_operators(self): N = self.N N_large = self._N_large self.I = tensor([ops.identity(2), ops.identity(N)]) self.a = tensor([ops.identity(2), ops.destroy(N)]) self.a_dag = tensor([ops.identity(2), ops.create(N)]) self.q = tensor([ops.identity(2), ops.position(N)]) self.p = tensor([ops.identity(2), ops.momentum(N)]) self.n = tensor([ops.identity(2), ops.num(N)]) self.parity = tensor([ops.identity(2), ops.parity(N)]) self.sx = tensor([ops.sigma_x(), ops.identity(N)]) self.sy = tensor([ops.sigma_y(), ops.identity(N)]) self.sz = tensor([ops.sigma_z(), ops.identity(N)]) self.sm = tensor([ops.sigma_m(), ops.identity(N)]) tensor_with = [ops.identity(2), None] self.phase = ops.Phase() self.translate = ops.TranslationOperator(N, tensor_with=tensor_with) self.displace = lambda a: self.translate(sqrt(2) * a) self.rotate = ops.RotationOperator(N, tensor_with=tensor_with) # displacement operators with larger intermediate hilbert space used for tomography self.translate_large = lambda a: tensor( [ops.identity(2), ops.TranslationOperator(N_large)(a)[:, :N, :N]] ) self.displace_large = lambda a: self.translate_large(sqrt(2) * a) self.displaced_parity_large = lambda a: tf.linalg.matmul( tf.linalg.matmul(self.displace_large(a), self.parity), self.displace_large(-a), ) tensor_with = [None, ops.identity(N)] self.rotate_qb_xy = ops.QubitRotationXY(tensor_with=tensor_with) self.rotate_qb_z = ops.QubitRotationZ(tensor_with=tensor_with) self.rxp = self.rotate_qb_xy(tf.constant(pi / 2), tf.constant(0)) self.rxm = self.rotate_qb_xy(tf.constant(-pi / 2), tf.constant(0)) # qubit sigma_z measurement projector self.P = {i: tensor([ops.projector(i, 2), ops.identity(N)]) for i in [0, 1]} self.sx_selective = tensor([ops.sigma_x(), ops.projector(0, N)]) + tensor( [ops.identity(2), ops.identity(N) - ops.projector(0, N)] )
def _define_fixed_operators(self): N = self.N self.I = tensor([ops.identity(2), ops.identity(N)]) self.a = tensor([ops.identity(2), ops.destroy(N)]) self.a_dag = tensor([ops.identity(2), ops.create(N)]) self.q = tensor([ops.identity(2), ops.position(N)]) self.p = tensor([ops.identity(2), ops.momentum(N)]) self.n = tensor([ops.identity(2), ops.num(N)]) self.parity = tensor([ops.identity(2), ops.parity(N)]) self.sx = tensor([ops.sigma_x(), ops.identity(N)]) self.sy = tensor([ops.sigma_y(), ops.identity(N)]) self.sz = tensor([ops.sigma_z(), ops.identity(N)]) self.sm = tensor([ops.sigma_m(), ops.identity(N)]) self.hadamard = tensor([ops.hadamard(), ops.identity(N)]) tensor_with = [ops.identity(2), None] self.phase = ops.Phase() self.translate = ops.TranslationOperator(N, tensor_with=tensor_with) self.displace = lambda a: self.translate(sqrt(2) * a) self.rotate = ops.RotationOperator(N, tensor_with=tensor_with) self.SNAP = ops.SNAP(N, tensor_with=tensor_with) self.SNAP_miscalibrated = ops.SNAPv3(N, chi=1e6, pulse_len=3.4e-6) tensor_with = [None, ops.identity(N)] self.rotate_qb_xy = ops.QubitRotationXY(tensor_with=tensor_with) self.rotate_qb_z = ops.QubitRotationZ(tensor_with=tensor_with) self.rxp = self.rotate_qb_xy(tf.constant(pi / 2), tf.constant(0)) self.rxm = self.rotate_qb_xy(tf.constant(-pi / 2), tf.constant(0)) # qubit sigma_z measurement projector self.P = { i: tensor([ops.projector(i, 2), ops.identity(N)]) for i in [0, 1] } self.sx_selective = tensor([ops.sigma_x(), ops.projector(0, N)]) + \ tensor([ops.identity(2), ops.identity(N)-ops.projector(0, N)])
def _define_fixed_operators(self): N1 = self.N1 N2 = self.N2 self.I = tensor([ops.identity(N1), ops.identity(N2)]) self.a1 = tensor([ops.destroy(N1), ops.identity(N2)]) self.a1_dag = tensor([ops.create(N1), ops.identity(N2)]) self.a2 = tensor([ops.identity(N1), ops.destroy(N2)]) self.a2_dag = tensor([ops.identity(N1), ops.create(N2)]) self.q1 = tensor([ops.position(N1), ops.identity(N2)]) self.p1 = tensor([ops.momentum(N1), ops.identity(N2)]) self.n1 = tensor([ops.num(N1), ops.identity(N2)]) self.q2 = tensor([ops.identity(N1), ops.position(N2)]) self.p2 = tensor([ops.identity(N1), ops.momentum(N2)]) self.n2 = tensor([ops.identity(N1), ops.num(N2)]) self.parity1 = tensor([ops.parity(N1), ops.identity(N2)]) self.parity2 = tensor([ops.identity(N1), ops.parity(N2)]) tensor_with = [None, ops.identity(N2)] self.translate1 = ops.TranslationOperator(N1, tensor_with=tensor_with) self.displace1 = lambda a: self.translate1(sqrt(2) * a) self.rotate1 = ops.RotationOperator(N1, tensor_with=tensor_with) tensor_with = [ops.identity(N1), None] self.translate2 = ops.TranslationOperator(N2, tensor_with=tensor_with) self.displace2 = lambda a: self.translate2(sqrt(2) * a) self.rotate2 = ops.RotationOperator(N2, tensor_with=tensor_with)