def lindbladian_average_infid(ideal: np.ndarray, actual: tf.constant, index=[0], dims=[2]) -> tf.constant: """ Average fidelity uses the Pauli basis to compare. Thus, perfect gates are always 2x2 (per qubit) and the actual unitary needs to be projected down. Parameters ---------- ideal: np.ndarray Contains ideal unitary representations of the gate actual: tf.Tensor Contains actual unitary representations of the gate index : int Index of the qubit(s) in the Hilbert space to be evaluated dims : list List of dimensions of qubits """ U_ideal = tf_super(ideal) actual_comp = tf_project_to_comp(actual, dims=dims, index=index, to_super=True) infid = 1 - tf_superoper_average_fidelity(actual_comp, U_ideal, lvls=dims) return infid
def lindbladian_average_infid( U_dict: dict, gate: str, index, dims, proj=True ): """ Average fidelity uses the Pauli basis to compare. Thus, perfect gates are always 2x2 (per qubit) and the actual unitary needs to be projected down. Parameters ---------- U_dict : dict Contains unitary representations of the gates, identified by a key. index : int Index of the qubit(s) in the Hilbert space to be evaluated dims : list List of dimensions of qubits proj : boolean Project to computational subspace """ U = U_dict[gate] ideal = tf.constant( perfect_gate(gate, index, dims=[2]*len(dims)), dtype=tf.complex128 ) U_ideal = tf_super(ideal) infid = 1 - tf_superoper_average_fidelity(U, U_ideal, lvls=dims) return infid
def lindbladian_unitary_infid(U_dict: dict, gate: str, index, dims, proj: bool): """ Variant of the unitary fidelity for the Lindbladian propagator. Parameters ---------- U_dict : dict Contains unitary representations of the gates, identified by a key. index : int Index of the qubit(s) in the Hilbert space to be evaluated gate : str One of the keys of U_dict, selects the gate to be evaluated dims : list List of dimensions of qubits proj : boolean Project to computational subspace Returns ------- tf.float Overlap fidelity for the Lindblad propagator. """ # Here we deal with the projected case differently because it's not easy # to select the right section of the superoper U = U_dict[gate] projection = "fulluni" fid_lvls = np.prod([dims[i] for i in index]) if proj: projection = "wzeros" fid_lvls = 2 ** len(index) U_ideal = tf_super( tf.Variable(perfect_gate(gate, index, dims, projection), dtype=tf.complex128) ) infid = 1 - tf_superoper_unitary_overlap(U, U_ideal, lvls=fid_lvls) return infid
def lindbladian_unitary_infid(ideal: np.ndarray, actual: tf.constant, index=[0], dims=[2]) -> tf.constant: """ Variant of the unitary fidelity for the Lindbladian propagator. Parameters ---------- ideal: np.ndarray Contains ideal unitary representations of the gate actual: tf.Tensor Contains actual unitary representations of the gate index : List[int] Index of the qubit(s) in the Hilbert space to be evaluated dims : list List of dimensions of qubits Returns ------- tf.float Overlap fidelity for the Lindblad propagator. """ U_ideal = tf_super(ideal) actual_comp = tf_project_to_comp(actual, dims=dims, index=index, to_super=True) fid_lvls = 2**len(index) infid = 1 - tf_superoper_unitary_overlap( actual_comp, U_ideal, lvls=fid_lvls) return infid
def get_average_fidelitiy(get_error_process): lvls = [3, 3] Lambda = get_error_process d = 4 err = tf_super(Lambda) choi = super_to_choi(err) chi = tf_choi_to_chi(choi, dims=lvls) fid = tf_abs((chi[0, 0] / d + 1) / (d + 1)) return fid
def get_dephasing_channel(self, t_final, amps): """ Compute the matrix of the dephasing channel to be applied on the operation. Parameters ---------- t_final : tf.float64 Duration of the operation. amps : dict of tf.float64 Dictionary of average amplitude on each drive line. Returns ------- tf.tensor Matrix representation of the dephasing channel. """ tot_dim = self.tot_dim ones = tf.ones(tot_dim, dtype=tf.complex128) Id = tf_utils.tf_super(tf.linalg.diag(ones)) deph_ch = Id for line in amps.keys(): amp = amps[line] qubit = self.couplings[line].connected[0] # TODO extend this to multiple qubits ann_oper = self.ann_opers[self.names.index(qubit)] num_oper = tf.constant( np.matmul(ann_oper.T.conj(), ann_oper), dtype=tf.complex128 ) Z = tf_utils.tf_super( tf.linalg.expm( 1.0j * num_oper * tf.constant(np.pi, dtype=tf.complex128) ) ) p = t_final * amp * self.dephasing_strength print("dephasing stength: ", p) if p.numpy() > 1 or p.numpy() < 0: raise ValueError("strengh of dephasing channels outside [0,1]") print("dephasing stength: ", p) # TODO: check that this is right (or do you put the Zs together?) deph_ch = deph_ch * ((1 - p) * Id + p * Z) return deph_ch
def lindbladian_epc_analytical(U_dict: dict, proj: bool): real_cliffords = evaluate_sequences(U_dict, cliffords_decomp) lvls = int(np.sqrt(real_cliffords[0].shape[0])) projection = "fulluni" fid_lvls = lvls if proj: projection = "wzeros" fid_lvls = 2 ideal_cliffords = perfect_cliffords(str(lvls), projection) fids = [] for C_indx in range(24): C_real = real_cliffords[C_indx] C_ideal = tf_super(tf.Variable(ideal_cliffords[C_indx], dtype=tf.complex128)) ave_fid = tf_superoper_average_fidelity(C_real, C_ideal, lvls=fid_lvls) fids.append(ave_fid) infid = 1 - tf_ave(fids) return infid
def lindbladian_epc_analytical(U_dict: dict, index, dims, proj: bool, cliffords=False): num_gates = len(dims) if cliffords: real_cliffords = evaluate_sequences(U_dict, [[C] for C in cliffords_string]) elif num_gates == 1: real_cliffords = evaluate_sequences(U_dict, cliffords_decomp) elif num_gates == 2: real_cliffords = evaluate_sequences(U_dict, cliffords_decomp_xId) ideal_cliffords = perfect_cliffords(lvls=[2] * num_gates, num_gates=num_gates) fids = [] for C_indx in range(24): C_real = real_cliffords[C_indx] C_ideal = tf_super( tf.constant(ideal_cliffords[C_indx], dtype=tf.complex128)) ave_fid = tf_superoper_average_fidelity(C_real, C_ideal, lvls=dims) fids.append(ave_fid) infid = 1 - tf_ave(fids) return infid
def get_gates(self): """ Compute the unitary representation of operations. If no operations are specified in self.opt_gates the complete gateset is computed. Returns ------- dict A dictionary of gate names and their unitary representation. """ model = self.pmap.model generator = self.pmap.generator instructions = self.pmap.instructions gates = {} gate_keys = self.opt_gates if gate_keys is None: gate_keys = instructions.keys() for gate in gate_keys: try: instr = instructions[gate] except KeyError: raise Exception( f"C3:Error: Gate '{gate}' is not defined." f" Available gates are:\n {list(instructions.keys())}.") signal = generator.generate_signals(instr) U = self.propagation(signal, gate) if model.use_FR: # TODO change LO freq to at the level of a line freqs = {} framechanges = {} for line, ctrls in instr.comps.items(): # TODO calculate properly the average frequency that each qubit sees offset = 0.0 for ctrl in ctrls.values(): if "freq_offset" in ctrl.params.keys(): if ctrl.params["amp"] != 0.0: offset = ctrl.params["freq_offset"].get_value() freqs[line] = tf.cast( ctrls["carrier"].params["freq"].get_value() + offset, tf.complex128, ) framechanges[line] = tf.cast( ctrls["carrier"].params["framechange"].get_value(), tf.complex128, ) t_final = tf.constant(instr.t_end - instr.t_start, dtype=tf.complex128) FR = model.get_Frame_Rotation(t_final, freqs, framechanges) if model.lindbladian: SFR = tf_utils.tf_super(FR) U = tf.matmul(SFR, U) self.FR = SFR else: U = tf.matmul(FR, U) self.FR = FR if model.dephasing_strength != 0.0: if not model.lindbladian: raise ValueError( "Dephasing can only be added when lindblad is on.") else: amps = {} for line, ctrls in instr.comps.items(): amp, sum = generator.devices["awg"].get_average_amp() amps[line] = tf.cast(amp, tf.complex128) t_final = tf.constant(instr.t_end - instr.t_start, dtype=tf.complex128) dephasing_channel = model.get_dephasing_channel( t_final, amps) U = tf.matmul(dephasing_channel, U) gates[gate] = U self.unitaries = gates return gates
def plot_dynamics(self, psi_init, seq, goal=-1, debug=False): # TODO double check if it works well """ Plotting code for time-resolved populations. Parameters ---------- psi_init: tf.Tensor Initial state or density matrix. seq: list List of operations to apply to the initial state. goal: tf.float64 Value of the goal function, if used. debug: boolean If true, return a matplotlib figure instead of saving. """ dUs = self.dUs psi_t = psi_init.numpy() pop_t = self.populations(psi_t, self.model.lindbladian) for gate in seq: for du in dUs[gate]: psi_t = np.matmul(du.numpy(), psi_t) pops = self.populations(psi_t, self.model.lindbladian) pop_t = np.append(pop_t, pops, axis=1) if self.model.use_FR: instr = self.gateset.instructions[gate] signal, ts = self.generator.generate_signals(instr) # TODO change LO freq to at the level of a line freqs = {} framechanges = {} for line, ctrls in instr.comps.items(): offset = 0.0 if "gauss" in ctrls: if ctrls['gauss'].params["amp"] != 0.0: offset = ctrls['gauss'].params[ 'freq_offset'].get_value() freqs[line] = tf.cast( ctrls['carrier'].params['freq'].get_value() + offset, tf.complex128) framechanges[line] = tf.cast( ctrls['carrier'].params['framechange'].get_value(), tf.complex128) t_final = tf.constant(instr.t_end - instr.t_start, dtype=tf.complex128) FR = self.model.get_Frame_Rotation(t_final, freqs, framechanges) if self.model.lindbladian: FR = tf_utils.tf_super(FR) psi_t = tf.matmul(FR, psi_t) # TODO added framchanged psi to list fig, axs = plt.subplots(1, 1) ts = self.ts dt = ts[1] - ts[0] ts = np.linspace(0.0, dt * pop_t.shape[1], pop_t.shape[1]) axs.plot(ts / 1e-9, pop_t.T) axs.grid(linestyle="--") axs.tick_params(direction="in", left=True, right=True, top=True, bottom=True) axs.set_xlabel('Time [ns]') axs.set_ylabel('Population') plt.legend(self.model.state_labels) if debug: plt.show() else: plt.savefig( self.logdir + f"dynamics/eval_{self.dynamics_plot_counter}_{seq[0]}_{goal}.png", dpi=300)
def get_gates(self): """ Compute the unitary representation of operations. If no operations are specified in self.opt_gates the complete gateset is computed. Returns ------- dict A dictionary of gate names and their unitary representation. """ gates = {} if "opt_gates" in self.__dict__: gate_keys = self.opt_gates else: gate_keys = self.gateset.instructions.keys() for gate in gate_keys: try: instr = self.gateset.instructions[gate] except KeyError: raise Exception( f"C3:Error: Gate \'{gate}\' is not defined." f" Available gates are:\n {list(self.gateset.instructions.keys())}." ) signal, ts = self.generator.generate_signals(instr) U = self.propagation(signal, ts, gate) if self.model.use_FR: # TODO change LO freq to at the level of a line freqs = {} framechanges = {} for line, ctrls in instr.comps.items(): # TODO calculate properly the average frequency that each qubit sees offset = 0.0 if "gauss" in ctrls: if ctrls['gauss'].params["amp"] != 0.0: offset = ctrls['gauss'].params[ 'freq_offset'].get_value() if "flux" in ctrls: if ctrls['flux'].params["amp"] != 0.0: offset = ctrls['flux'].params[ 'freq_offset'].get_value() if "pwc" in ctrls: offset = ctrls['pwc'].params['freq_offset'].get_value() # print("gate: ", gate, "; line: ", line, "; offset: ", offset) freqs[line] = tf.cast( ctrls['carrier'].params['freq'].get_value() + offset, tf.complex128) framechanges[line] = tf.cast( ctrls['carrier'].params['framechange'].get_value(), tf.complex128) t_final = tf.constant(instr.t_end - instr.t_start, dtype=tf.complex128) FR = self.model.get_Frame_Rotation(t_final, freqs, framechanges) if self.model.lindbladian: SFR = tf_utils.tf_super(FR) U = tf.matmul(SFR, U) self.FR = SFR else: U = tf.matmul(FR, U) self.FR = FR if self.model.dephasing_strength != 0.0: if not self.model.lindbladian: raise ValueError( 'Dephasing can only be added when lindblad is on.') else: amps = {} for line, ctrls in instr.comps.items(): amp, sum = self.generator.devices[ 'awg'].get_average_amp() amps[line] = tf.cast(amp, tf.complex128) t_final = tf.constant(instr.t_end - instr.t_start, dtype=tf.complex128) dephasing_channel = self.model.get_dephasing_channel( t_final, amps) U = tf.matmul(dephasing_channel, U) gates[gate] = U self.unitaries = gates return gates
def test_tf_super(): for el in data["tf_super"]: np.testing.assert_allclose(actual=tf_super(el["in"]), desired=el["desired"])
def compute_propagators(self): """ Compute the unitary representation of operations. If no operations are specified in self.opt_gates the complete gateset is computed. Returns ------- dict A dictionary of gate names and their unitary representation. """ model = self.pmap.model generator = self.pmap.generator instructions = self.pmap.instructions propagators = {} partial_propagators = {} gate_ids = self.opt_gates if gate_ids is None: gate_ids = instructions.keys() for gate in gate_ids: try: instr = instructions[gate] except KeyError: raise Exception( f"C3:Error: Gate '{gate}' is not defined." f" Available gates are:\n {list(instructions.keys())}.") model.controllability = self.use_control_fields result = self.propagation(model, generator, instr) U = result["U"] dUs = result["dUs"] self.ts = result["ts"] if model.use_FR: # TODO change LO freq to at the level of a line freqs = {} framechanges = {} for line, ctrls in instr.comps.items(): # TODO calculate properly the average frequency that each qubit sees offset = 0.0 for ctrl in ctrls.values(): if "freq_offset" in ctrl.params.keys(): if ctrl.params["amp"] != 0.0: offset = ctrl.params["freq_offset"].get_value() freqs[line] = tf.cast( ctrls["carrier"].params["freq"].get_value() + offset, tf.complex128, ) framechanges[line] = tf.cast( ctrls["carrier"].params["framechange"].get_value(), tf.complex128, ) t_final = tf.constant(instr.t_end - instr.t_start, dtype=tf.complex128) FR = model.get_Frame_Rotation(t_final, freqs, framechanges) if model.lindbladian: SFR = tf_super(FR) U = tf.matmul(SFR, U) self.FR = SFR else: U = tf.matmul(FR, U) self.FR = FR if model.dephasing_strength != 0.0: if not model.lindbladian: raise ValueError( "Dephasing can only be added when lindblad is on.") else: amps = {} for line, ctrls in instr.comps.items(): amp, sum = generator.devices["awg"].get_average_amp() amps[line] = tf.cast(amp, tf.complex128) t_final = tf.constant(instr.t_end - instr.t_start, dtype=tf.complex128) dephasing_channel = model.get_dephasing_channel( t_final, amps) U = tf.matmul(dephasing_channel, U) propagators[gate] = U partial_propagators[gate] = dUs # TODO we might want to move storing of the propagators to the instruction object if self.overwrite_propagators: self.propagators = propagators self.partial_propagators = partial_propagators else: self.propagators.update(propagators) self.partial_propagators.update(partial_propagators) self.compute_propagators_timestamp = time.time() return propagators