def test_peak_on_ground_state(): sdm = SparseDM(1) sdm.ensure_dense(0) p0, p1 = sdm.peak_measurement(0) assert p0 == 1 assert p1 == 0
def SimulateRandomClifford(circ, gauge, noise): # Compute the entanglement fidelity between the noisy circuit and the perfect circuit. # 1. Compute the Choi state for the perfect circuit. # 2. Compute the Choi state for the imperfect circuit. # 3. Take the inner product. start = time.time() perfect = qc.Circuit(title='Perfect Circuit') RandomCliffordCircuit(perfect, circ, gauge, 0) pdm = SparseDM(perfect.get_qubit_names()) perfect.apply_to(pdm) #print("Perfect Choi state") #print(sdmc.full_dm.dm.ravel()) noisy = qc.Circuit(title='Noisy Circuit') RandomCliffordCircuit(noisy, circ, gauge, noise) ndm = SparseDM(noisy.get_qubit_names()) noisy.apply_to(ndm) fidelity = np.dot(pdm.full_dm.dm.ravel(), ndm.full_dm.dm.ravel()) # print("fidelity = %g"% (fidelity)) end = time.time() # print("gauge setting\n%s\ndone in %d seconds."% (np.array_str(gauge), end - start)) return fidelity
def test_ensure_dense_simple(): sdm = SparseDM(10) sdm.ensure_dense(0) sdm.ensure_dense(1) assert len(sdm.classical) == 8 assert len(sdm.idx_in_full_dm) == 2 assert sdm.full_dm.no_qubits == 2 assert np.allclose(sdm.trace(), 1)
def test_multiple_measurement_hadamard_on_classical(self): sdm = SparseDM(2) sdm.hadamard(0) meas = sdm.peak_multiple_measurements([0, 1]) assert len(meas) == 2 assert meas == [({0: 0, 1: 0}, 0.5), ({0: 1, 1: 0}, 0.5)]
def test_peak_on_hadamard(): sdm = SparseDM(1) hadamard = ptm.hadamard_ptm() sdm.apply_ptm(0, hadamard) p0, p1 = sdm.peak_measurement(0) assert np.allclose(p0, 0.5) assert np.allclose(p1, 0.5)
def test_majority_vote_on_excited_classical(self): bits = [1, 2, 3] sdm = SparseDM(bits) sdm.set_bit(1, 1) sdm.set_bit(3, 1) p = sdm.majority_vote(bits) assert np.allclose(p, 1) assert sdm._last_majority_vote_mask == 0
def test_renormalize(): sdm = SparseDM(2) sdm.hadamard(0) sdm.project_measurement(0, 1) assert np.allclose(sdm.trace(), 0.5) sdm.renormalize() assert np.allclose(sdm.trace(), 1)
def test_majority_vote_gs_classical(self): bits = [1, 2, 3] sdm = SparseDM(bits) assert sdm._last_majority_vote_array is None assert sdm._last_majority_vote_mask is None p = sdm.majority_vote(bits) assert sdm._last_majority_vote_mask == 0 assert np.allclose(p, 0)
def test_copy(): sdm = SparseDM(5) sdm.classical.update({1: 1, 3: 1}) sdm.ensure_dense(2) assert sdm.full_dm.no_qubits == 1 assert len(sdm.classical) == 4 sdm_copy = sdm.copy() assert len(sdm_copy.classical) == 4 assert sdm_copy.classical == {0: 0, 1: 1, 3: 1, 4: 0} assert sdm_copy.classical is not sdm.classical assert sdm.full_dm is not sdm_copy.full_dm assert np.allclose(sdm.full_dm.to_array(), sdm_copy.full_dm.to_array())
def test_multiple_does_not_change(self): sdm = SparseDM(3) sdm.hadamard(0) sdm.hadamard(1) sdm.ensure_dense(0) sdm.ensure_dense(1) sdm.ensure_dense(2) before = sdm.full_dm.to_array() assert before.shape == (8, 8) sdm.peak_multiple_measurements([0, 1, 2]) assert np.allclose(before, sdm.full_dm.to_array())
def test_majority_after_hadamard(self): bits = [1, 2, 3] sdm = SparseDM(bits) sdm.hadamard(1) sdm.hadamard(2) sdm.hadamard(3) p = sdm.majority_vote(bits) assert np.allclose(p, 0.5) sdm.hadamard(3) p = sdm.majority_vote(bits) assert np.allclose(p, 0.25)
def test_peak_then_measure(): sdm = SparseDM(1) assert np.allclose(sdm.trace(), 1) sdm.ensure_dense(0) assert np.allclose(sdm.trace(), 1) p0, p1 = sdm.peak_measurement(0) assert np.allclose(p0, 1) assert np.allclose(p1, 0) sdm.project_measurement(0, 0) assert len(sdm.classical) == 1 assert 0 in sdm.classical assert sdm.classical[0] == 0 assert len(sdm.idx_in_full_dm) == 0 assert sdm.full_dm.no_qubits == 0 assert np.allclose(sdm.trace(), 1)
def test_peak_on_decay(): sdm = SparseDM(1) sdm.classical[0] = 1 p0, p1 = sdm.peak_measurement(0) assert np.allclose(p0, 0) assert np.allclose(p1, 1) sdm.amp_ph_damping(0, 0.02, 0) p0, p1 = sdm.peak_measurement(0) assert np.allclose(p0, 0.02) assert np.allclose(p1, 0.98) sdm.amp_ph_damping(0, 0.02, 0) p0, p1 = sdm.peak_measurement(0) assert np.allclose(p0, 0.02 + 0.98 * 0.02)
def test_majority_vote_on_excited_quantum(self): bits = [1, 2, 3] sdm = SparseDM(bits) sdm.rotate_y(1, np.pi) sdm.rotate_y(2, np.pi) sdm.rotate_y(3, 2 * np.pi) p = sdm.majority_vote(bits) assert np.allclose(p, 1) assert sdm._last_majority_vote_mask == 7
def test_set_bit(): sdm = SparseDM(10) sdm.set_bit(0, 1) assert sdm.classical[0] == 1 sdm.hadamard(0) sdm.hadamard(0)
def test_multiple_measurement_hadamard_order2_regression(self): sdm = SparseDM(3) sdm.hadamard(0) sdm.hadamard(1) sdm.ensure_dense(0) sdm.ensure_dense(1) sdm.ensure_dense(2) meas = sdm.peak_multiple_measurements([0, 1, 2]) assert len(meas) == 8 for state, p in meas: print(meas) if state[2] == 0: assert np.allclose(p, 0.25) else: assert np.allclose(p, 0)
def test_majority_vote_invalid_results_error(self): bits = [1, 2, 3] sdm = SparseDM(bits) with pytest.raises(ValueError): sdm.majority_vote({1: "bla"}) with pytest.raises(ValueError): sdm.majority_vote({1: 2})
def test_multiple_measurement_classical_prob(self): sdm = SparseDM(3) sdm.hadamard(0) sdm.hadamard(1) sdm.classical_probability = 0.7 r = sdm.peak_multiple_measurements([0, 1, 2]) total_prob = sum(prob for outcome, prob in r) assert total_prob == sdm.trace()
def test_meas_on_ground_state(): sdm = SparseDM(1) sdm.ensure_dense(0) sdm.project_measurement(0, 0) assert len(sdm.classical) == 1 assert 0 in sdm.classical assert sdm.classical[0] == 0 assert len(sdm.idx_in_full_dm) == 0 assert sdm.full_dm.no_qubits == 0 assert np.allclose(sdm.trace(), 1)
def test_multiple_measurement_hadamard_order1(self): sdm = SparseDM(3) sdm.hadamard(0) sdm.hadamard(2) sdm.ensure_dense(0) sdm.ensure_dense(1) sdm.ensure_dense(2) meas = sdm.peak_multiple_measurements([0, 1, 2]) assert len(meas) == 8 for state, p in meas: for x in state.values(): assert x in [0, 1] if state[1] == 0: assert np.allclose(p, 0.25) else: assert np.allclose(p, 0)
def test_meas_on_hadamard(): sdm = SparseDM(1) sdm.hadamard(0) p0, p1 = sdm.peak_measurement(0) assert p0 == 0.5 assert p1 == 0.5 sdm.project_measurement(0, 1) print(sdm.full_dm.to_array()) assert len(sdm.classical) == 1 assert sdm.full_dm.no_qubits == 0 assert sdm.classical[0] == 1 assert np.allclose(sdm.trace(), 0.5)
def test_multiple_measurement_gs(self): sdm = SparseDM(3) sdm.ensure_dense(0) sdm.ensure_dense(1) sdm.ensure_dense(2) meas = sdm.peak_multiple_measurements([0, 1, 2]) assert len(meas) == 8 for state, p in meas: if state[0] == 0 and state[1] == 0 and state[2] == 0: assert np.allclose(p, 1) else: assert np.allclose(p, 0)
def test_override(self): qubit_list = ['swap', 'cp'] with pytest.warns(UserWarning): # We did not provide any seed setup = quick_setup(qubit_list, noise_flag=False) b = Builder(setup) b < ('RotateY', 'swap', np.pi/2) b < ('RotateY', 'cp', np.pi/2) b < ('CZ', 'cp', 'swap') b < ('RotateY', 'cp', -np.pi/2) b.finalize() bell_circuit = b.circuit bell_state = SparseDM(bell_circuit.get_qubit_names()) bell_circuit.apply_to(bell_state) diag = np.diag(bell_state.full_dm.to_array()) assert np.abs(diag[0]-0.5) < 1e-10 assert np.abs(diag[3]-0.5) < 1e-10 assert np.abs(diag[1]) < 1e-10 assert np.abs(diag[2]) < 1e-10
def test_make_imperfect_bell(self): qubit_list = ['swap', 'cp'] with pytest.warns(UserWarning): # We did not provide any seed setup = quick_setup(qubit_list) b = Builder(setup) b.add_gate('RotateY', ['swap'], angle=np.pi/2) b.add_gate('RotateY', ['cp'], angle=np.pi/2) b.add_gate('CZ', ['cp', 'swap']) b.add_gate('RotateY', ['cp'], angle=-np.pi/2) b.finalize() bell_circuit = b.circuit bell_state = SparseDM(bell_circuit.get_qubit_names()) bell_circuit.apply_to(bell_state) diag = np.diag(bell_state.full_dm.to_array()) assert np.abs(diag[0]-0.5) < 1e-2 assert np.abs(diag[3]-0.5) < 1e-2 assert np.abs(diag[1]) < 3e-2 assert np.abs(diag[2]) < 3e-2
def test_deferred_apply(self): sdm = SparseDM(1) assert 0 in sdm.classical p = ptm.hadamard_ptm() sdm.apply_ptm(0, p) assert 0 in sdm.classical assert 0 in sdm.single_ptms_to_do sdm.combine_and_apply_single_ptm(0) assert 0 not in sdm.classical assert 0 not in sdm.single_ptms_to_do
def test_qasm(self): qubit_list = ['swap', 'cp'] with pytest.warns(UserWarning): # We did not provide any seed setup = quick_setup(qubit_list, noise_flag=False) b = Builder(setup) qasm0 = 'Ry 1.57079632679 swap' qasm1 = 'Ry 1.57079632679 cp' qasm2 = 'CZ cp swap' qasm3 = 'Ry -1.57079632679 cp' qasm_list = [qasm0, qasm1, qasm2, qasm3] b.add_qasm(qasm_list, qubits_first=False) b.finalize() bell_circuit = b.circuit bell_state = SparseDM(bell_circuit.get_qubit_names()) bell_circuit.apply_to(bell_state) diag = np.diag(bell_state.full_dm.to_array()) assert np.abs(diag[0]-0.5) < 1e-10 assert np.abs(diag[3]-0.5) < 1e-10 assert np.abs(diag[1]) < 1e-10 assert np.abs(diag[2]) < 1e-10
class Controller: def __init__(self, filename=None, setup=None, random_state=None, seed=None, qubits=None, circuits=None, circuit_lists=None, adjust_gates=None, measurement_gates=None, angle_convert_matrices=None, mbits=None): """ qubits: list of qubits in the experiment mbits: the set of bits to receive measurements circuits: dictionary of circuits to apply to a state to perform an operation. circuit_lists: corresponding circuit lists (as output by qsoverlay builder for saving purposes) msmt_circuits: dictionary of circuits to measure a state. adjust_gates: a list of adjustable gates in a circuit that the user might pass parameters to when they run the circuit. measurement_gates: a set of Measurement type operators to extract extra details about the measurements made. """ self.circuits = circuits or {} self.circuit_lists = circuit_lists or {} if 'record' in self.circuits: raise ValueError('record is a protected keyword') if any([type(c) == int for c in self.circuits]): raise ValueError('Circuits must not use integers for labels') self.mbits = mbits or [] self.qubits = qubits or [] self.adjust_gates = adjust_gates or {} self.angle_convert_matrices = angle_convert_matrices or {} self.measurement_gates = measurement_gates or {} self.state = None if filename is not None: self.load(filename, setup, random_state, seed) self.make_state() def load(self, filename, setup, random_state=None, seed=None): with open(filename, 'r') as infile: data = json.load(infile) if type(setup) == str: setup = Setup(filename=setup, state=random_state, seed=seed) self.mbits = data['mbits'] self.qubits = data['qubits'] self.angle_convert_matrices = { key: np.array(val) for key, val in data['angle_convert_matrices'].items() } b = Builder(setup) for name, cl in data['circuit_lists'].items(): b.new_circuit() adjust_gates = b.add_circuit_list(cl) b.finalize() self.circuits[name] = b.circuit self.circuit_lists[name] = cl self.adjust_gates[name] = [ ag for ag in adjust_gates if type(ag) is not Measurement] self.measurement_gates[name] = [ ag for ag in adjust_gates if type(ag) is Measurement] if name in data['angle_convert_matrices']: self.angle_convert_matrices = \ data['angle_convert_matrices'][name] def save(self, filename): data = { 'mbits': self.mbits, 'qubits': self.qubits, 'circuit_lists': self.circuit_lists, 'angle_convert_matrices': { key: val.tolist() for key, val in self.angle_convert_matrices.items()} } with open(filename, 'w') as outfile: json.dump(data, outfile) def make_state(self, dense_qubits=None): self.state = SparseDM(self.qubits + self.mbits) if dense_qubits is not None: for qubit in dense_qubits: self.state.ensure_dense(qubit) def apply_circuit(self, circuit): """ Applies a circuit to the state. Each entry is either: a) a string corresponding to an entry in self.circuits b) a tuple or list with the first entry a string corresponding to an entry in self.circuits and the remaining entries angles to input for self.adjust_gates. c) a tuple or list with the first entry the reserved keyword 'record', and the remaining entries a list of mbits to store the current value of. d) a pair (n, circuit), where circuit is one of the above, and n is the number of times to repeat the circuit. """ # Record is a reserved keyword to copy the output # from a set of classical bits to return to the user. if type(circuit) is list or type(circuit) is tuple: if circuit[0] == 'record': output = [] for mbit in circuit[1:]: output.append(self.state.classical[mbit]) return output op_name = circuit[0] if type(op_name) == int: return_data = [] for _ in range(op_name): return_data.append(self.apply_circuit(circuit[1])) return return_data else: if op_name in self.angle_convert_matrices: angles = self.angle_convert_matrices[op_name] @ circuit[1:] else: angles = circuit[1:] for gate, param in zip( self.adjust_gates[op_name], angles): if not isinstance(param, tuple): param = (param,) gate.adjust(*param) self.circuits[op_name].apply_to(self.state, apply_all_pending=False) else: op_name = circuit self.circuits[op_name].apply_to(self.state, apply_all_pending=False) if op_name in self.measurement_gates: return_data = [{ 'probabilities': m.probabilities[-1], 'projects': m.projects[-1], 'measurements': m.measurements[-1]} for m in self.measurement_gates[op_name]] return return_data return None def __lt__(self, circuit): self.apply_circuit(circuit) def apply_circuit_list(self, circuit_list): """ Apply a set of operations to a state. circuit_list: a list of circuits to apply. """ output_list = [] for circuit in circuit_list: # Record is a reserved keyword to copy the output # from a set of classical bits to return to the user. output = self.apply_circuit(circuit) if output is not None: output_list.append(output) return output_list def simulate_tomo(self, circuit, tomo_circuits, measurement_model, num_measurements, output_format, data_type): """ Takes the current system state, copies it, applies multiple tomography circuits, and runs them through models for the measurement to return thresholded voltages. """ data = [] for tomo_circuit in tomo_circuits: self.make_state() self < circuit self < tomo_circuit self.state.renormalize() rho_dist = self.state.peak_multiple_measurements( measurement_model.qubits) data.append(measurement_model.sample( rho_dist, num_measurements, data_type=data_type, output_format=output_format)) return data def get_expectation_values(self, msmts, num_repetitions=None): """ Measures a set of Pauli strings on the current state. If num_repetitions is None this is performed perfectly. Otherwise, we treat the calculated value as a bernoulli random variable we are attempting to approximate, and sample an answer. input: msmts: list of measurement dictionaries, containing 'X', 'Y', or 'Z' for each non-trivial qubit label. """ results = [] self.state.apply_all_pending() self.state.renormalize() dm = self.state.full_dm.to_array() for msmt in msmts: mult = 1 # Make Pauli list pauli_list = [1] * len(self.state.idx_in_full_dm) for qubit in msmt: if qubit not in self.state.idx_in_full_dm: if msmt[qubit] == 'Z': mult *= (-1) ** self.state.classical[qubit] elif msmt[qubit] in ['X', 'Y']: mult = 0 else: raise ValueError('qubit measurements must be X, Y, Z') else: pauli_list[self.state.idx_in_full_dm[qubit]] = msmt[qubit] # Make measurement operator op = pauli_dic[pauli_list[0]] for label in pauli_list[1:]: op = np.kron(pauli_dic[label], op) result = mult * np.trace(op @ dm) assert np.imag(result) < 1e-9 result = float(np.real(result)) if num_repetitions is not None: bernoulli_rv = (1 - result) / 2 if -1e-6 < bernoulli_rv <= 0: noisy_result = 0 elif 1 <= bernoulli_rv < 1 + 1e-6: noisy_result = 1 else: try: noisy_result = np.random.beta(bernoulli_rv * num_repetitions, (1 - bernoulli_rv) * num_repetitions) except Exception: raise ValueError( 'My bernoulli random variable is weird: {}' .format(bernoulli_rv)) results.append(1 - 2 * noisy_result) else: results.append(result) return np.array(results) def get_prob_all_zero(self, qubits): """ Returns the probability that all qubits in qubits will return a 0 measurement (regardless of any other qubit measurement). """ self.state.apply_all_pending() self.state.renormalize() indices = [self.state.idx_in_full_dm[q] for q in qubits] diagonal = self.state.full_dm.get_diag() return sum([x for j, x in enumerate(diagonal) if all([(j // 2 ** i) % 2 == 0 for i in indices])])
def make_state(self, dense_qubits=None): self.state = SparseDM(self.qubits + self.mbits) if dense_qubits is not None: for qubit in dense_qubits: self.state.ensure_dense(qubit)
def test_init(self): sdm = SparseDM(10) assert sdm.no_qubits == 10 assert len(sdm.classical) == 10 assert sdm.classical[0] == 0
def test_cphase_simple(): sdm = SparseDM(2) sdm.cphase(0, 1) assert sdm.full_dm.no_qubits == 2