diff --git a/qsdk/backendbuddy/circuit.py b/qsdk/backendbuddy/circuit.py index d3efc88c2..da41c7261 100644 --- a/qsdk/backendbuddy/circuit.py +++ b/qsdk/backendbuddy/circuit.py @@ -122,11 +122,13 @@ def check_index_valid(index): f"Gate = {gate}") # Track qubit indices - self._qubit_indices.add(gate.target) - check_index_valid(gate.target) - if isinstance(gate.control, int): - self._qubit_indices.add(gate.control) - check_index_valid(gate.control) + for t in gate.target: + self._qubit_indices.add(t) + check_index_valid(t) + if isinstance(gate.control, list): + for c in gate.control: + self._qubit_indices.add(c) + check_index_valid(c) # Keep track of the total gate count self._gate_counts[gate.name] = self._gate_counts.get(gate.name, 0) + 1 diff --git a/qsdk/backendbuddy/gate.py b/qsdk/backendbuddy/gate.py index 64a4ad638..ae394b4dc 100644 --- a/qsdk/backendbuddy/gate.py +++ b/qsdk/backendbuddy/gate.py @@ -17,8 +17,11 @@ mathematical operation. """ -ONE_QUBIT_GATES = {"H", "X", "Y", "Z", "S", "T", "RX", "RY", "RZ"} -TWO_QUBIT_GATES = {"CNOT"} +from typing import Union + +ONE_QUBIT_GATES = {"H", "X", "Y", "Z", "S", "T", "RX", "RY", "RZ", "PHASE"} +TWO_QUBIT_GATES = {"CNOT", "CX", "CY", "CZ", "CRX", "CRY", "CRZ", "CPHASE", "XX", "SWAP"} +THREE_QUBIT_GATES = {"CSWAP"} class Gate(dict): @@ -37,15 +40,27 @@ class Gate(dict): variational or not. """ - # TODO: extend control to a list to support gates such as the Toffoli gate etc in the future - # TODO: extend target to a list to support gates such as U2, U3 etc in the future - def __init__(self, name: str, target: int, control: int=None, parameter="", is_variational: bool=False): + def __init__(self, name: str, target: Union[int, list], control: Union[int, list] = None, parameter="", is_variational: bool = False): """ This gate class is basically a dictionary with extra methods. """ - if not (isinstance(target, int) and target >= 0): - raise ValueError("Qubit index must be a positive integer.") - if control and (not (isinstance(control, int) and control >= 0)): - raise ValueError("Qubit index must be a positive integer.") + if not isinstance(target, (int, list)): + raise ValueError("Qubit index must be int or list of ints.") + else: + if isinstance(target, int): + target = [target] + for t in target: + if not isinstance(t, int) or t < 0: + raise ValueError(f"Target {t} of input {target} is not a positive integer") + + if control is not None: + if not isinstance(control, (int, list)): + raise ValueError("Qubit index must be int or list of ints.") + else: + if isinstance(control, int): + control = [control] + for c in control: + if not isinstance(c, int) or c < 0: + raise ValueError(f"Target {c} of input {control} is not a positive integer") self.__dict__ = {"name": name, "target": target, "control": control, "parameter": parameter, "is_variational": is_variational} diff --git a/qsdk/backendbuddy/simulator.py b/qsdk/backendbuddy/simulator.py index a0338076d..044cfe878 100644 --- a/qsdk/backendbuddy/simulator.py +++ b/qsdk/backendbuddy/simulator.py @@ -198,9 +198,8 @@ def simulate(self, source_circuit, return_statevector=False, initial_statevector # Noiseless simulation using the statevector simulator otherwise else: backend = qiskit.Aer.get_backend("aer_simulator", method='statevector') - save_state_circuit = qiskit.QuantumCircuit(source_circuit.width, source_circuit.width) - save_state_circuit.save_statevector() - translated_circuit = translated_circuit.compose(save_state_circuit) + translated_circuit = qiskit.transpile(translated_circuit, backend) + translated_circuit.save_statevector() sim_results = backend.run(translated_circuit).result() self._current_state = sim_results.get_statevector(translated_circuit) frequencies = self._statevector_to_frequencies(self._current_state) diff --git a/qsdk/backendbuddy/tests/test_translator.py b/qsdk/backendbuddy/tests/test_translator.py index fd34e02f7..ae4f04863 100644 --- a/qsdk/backendbuddy/tests/test_translator.py +++ b/qsdk/backendbuddy/tests/test_translator.py @@ -28,8 +28,11 @@ path_data = os.path.dirname(os.path.realpath(__file__)) + '/data' gates = [Gate("H", 2), Gate("CNOT", 1, control=0), Gate("CNOT", 2, control=1), Gate("Y", 0), Gate("S", 0)] +multi_controlled_gates = [Gate("X", 0), Gate("X", 1), Gate("CX", target=2, control=[0,1])] abs_circ = Circuit(gates) + Circuit([Gate("RX", 1, parameter=2.)]) +abs_multi_circ = Circuit(multi_controlled_gates) references = [0., 0.38205142 ** 2, 0., 0.59500984 ** 2, 0., 0.38205142 ** 2, 0., 0.59500984 ** 2] +references_multi = [0., 0., 0., 0., 0., 0., 0., 1.] abs_circ_mixed = Circuit(gates) + Circuit([Gate("RX", 1, parameter=1.5), Gate("MEASURE", 0)]) @@ -68,6 +71,30 @@ def test_qulacs(self): # Assert that both simulations returned the same state vector np.testing.assert_array_equal(state1.get_vector(), state2.get_vector()) + # Generates the qulacs circuit by translating from the abstract one + translated_circuit = translator.translate_qulacs(abs_multi_circ) + + # Run the simulation + state1 = qulacs.QuantumState(abs_multi_circ.width) + translated_circuit.update_quantum_state(state1) + + # Directly define the same circuit through qulacs + # NB: this includes convention fixes for some parametrized rotation gates (-theta instead of theta) + qulacs_circuit = qulacs.QuantumCircuit(3) + qulacs_circuit.add_X_gate(0) + qulacs_circuit.add_X_gate(1) + mat_gate = qulacs.gate.to_matrix_gate(qulacs.gate.X(2)) + mat_gate.add_control_qubit(0, 1) + mat_gate.add_control_qubit(1, 1) + qulacs_circuit.add_gate(mat_gate) + + # Run the simulation + state2 = qulacs.QuantumState(abs_multi_circ.width) + qulacs_circuit.update_quantum_state(state2) + + # Assert that both simulations returned the same state vector + np.testing.assert_array_equal(state1.get_vector(), state2.get_vector()) + @unittest.skipIf("qiskit" not in installed_backends, "Test Skipped: Backend not available \n") def test_qiskit(self): """ @@ -104,6 +131,9 @@ def test_qiskit(self): np.testing.assert_array_equal(v1, v2) + #Return error when attempting to use qiskit with multiple controls + self.assertRaises(ValueError, translator.translate_qiskit, abs_multi_circ) + @unittest.skipIf("cirq" not in installed_backends, "Test Skipped: Backend not available \n") def test_cirq(self): """ @@ -138,6 +168,21 @@ def test_cirq(self): np.testing.assert_array_equal(v1, v2) + translated_circuit = translator.translate_cirq(abs_multi_circ) + circ = cirq.Circuit() + circ.append(cirq.X(qubit_labels[0])) + circ.append(cirq.X(qubit_labels[1])) + next_gate = cirq.X.controlled(num_controls=2) + circ.append(next_gate(qubit_labels[0], qubit_labels[1], qubit_labels[2])) + + job_sim = cirq_simulator.simulate(circ) + v1 = job_sim.final_state_vector + + job_sim = cirq_simulator.simulate(translated_circuit) + v2 = job_sim.final_state_vector + + np.testing.assert_array_equal(v1, v2) + @unittest.skipIf("qdk" not in installed_backends, "Test Skipped: Backend not available \n") def test_qdk(self): """ Compares the frequencies computed by the QDK/Q# shot-based simulator to the theoretical ones """ @@ -163,6 +208,27 @@ def test_qdk(self): # Compares with theoretical probabilities obtained through a statevector simulator np.testing.assert_almost_equal(np.array(probabilities), np.array(references), 2) + # Generate the qdk circuit by translating from the abstract one and print it + translated_circuit = translator.translate_qsharp(abs_multi_circ) + print(translated_circuit) + + # Write to file + with open('tmp_circuit.qs', 'w+') as f_out: + f_out.write(translated_circuit) + + # Compile all qsharp files found in directory and import the qsharp operation + import qsharp + qsharp.reload() + from MyNamespace import EstimateFrequencies + + # Simulate, return frequencies + n_shots = 10**4 + probabilities = EstimateFrequencies.simulate(nQubits=abs_multi_circ.width, nShots=n_shots) + print("Q# frequency estimation with {0} samples: \n {1}".format(n_shots, probabilities)) + + # Compares with theoretical probabilities obtained through a statevector simulator + np.testing.assert_almost_equal(np.array(probabilities), np.array(references_multi), 2) + @unittest.skipIf("projectq" not in installed_backends, "Test Skipped: Backend not available \n") def test_projectq(self): """ Compares state vector of native ProjectQ circuit against translated one """ @@ -300,6 +366,9 @@ def test_braket(self): np.testing.assert_array_equal(circ_result.values[0], translated_result.values[0]) + #Return error when attempting to use braket with multiple controls + self.assertRaises(ValueError, translator.translate_braket, abs_multi_circ) + @unittest.skipIf("qiskit" not in installed_backends, "Test Skipped: Backend not available \n") def test_unsupported_gate(self): """ Must return an error if a gate is not supported for the target backend """ diff --git a/qsdk/backendbuddy/translator/translate_braket.py b/qsdk/backendbuddy/translator/translate_braket.py index c186b0f93..8ed89fbce 100644 --- a/qsdk/backendbuddy/translator/translate_braket.py +++ b/qsdk/backendbuddy/translator/translate_braket.py @@ -36,12 +36,21 @@ def get_braket_gates(): GATE_BRAKET["X"] = BraketCircuit.x GATE_BRAKET["Y"] = BraketCircuit.y GATE_BRAKET["Z"] = BraketCircuit.z + GATE_BRAKET["CX"] = BraketCircuit.cnot + GATE_BRAKET["CY"] = BraketCircuit.cy + GATE_BRAKET["CZ"] = BraketCircuit.cz GATE_BRAKET["S"] = BraketCircuit.s GATE_BRAKET["T"] = BraketCircuit.t GATE_BRAKET["RX"] = BraketCircuit.rx GATE_BRAKET["RY"] = BraketCircuit.ry GATE_BRAKET["RZ"] = BraketCircuit.rz + GATE_BRAKET["XX"] = BraketCircuit.xx + GATE_BRAKET["CRZ"] = [BraketCircuit.cphaseshift, BraketCircuit.cphaseshift10] + GATE_BRAKET["PHASE"] = BraketCircuit.phaseshift + GATE_BRAKET["CPHASE"] = BraketCircuit.cphaseshift GATE_BRAKET["CNOT"] = BraketCircuit.cnot + GATE_BRAKET["SWAP"] = BraketCircuit.swap + GATE_BRAKET["CSWAP"] = BraketCircuit.cswap # GATE_BRAKET["MEASURE"] = ? (mid-circuit measurement currently unsupported?) return GATE_BRAKET @@ -65,12 +74,26 @@ def translate_braket(source_circuit): # Map the gate information properly. Different for each backend (order, values) for gate in source_circuit._gates: + if gate.control is not None: + if len(gate.control) > 1: + raise ValueError('braket does not support multi controlled gates: Gate {gate.name} with controls {gate.control} is invalid') if gate.name in {"H", "X", "Y", "Z", "S", "T"}: - (GATE_BRAKET[gate.name])(target_circuit, gate.target) - elif gate.name in {"RX", "RY", "RZ"}: - (GATE_BRAKET[gate.name])(target_circuit, gate.target, gate.parameter) - elif gate.name in {"CNOT"}: - (GATE_BRAKET[gate.name])(target_circuit, control=gate.control, target=gate.target) + (GATE_BRAKET[gate.name])(target_circuit, gate.target[0]) + elif gate.name in {"RX", "RY", "RZ", "PHASE"}: + (GATE_BRAKET[gate.name])(target_circuit, gate.target[0], gate.parameter) + elif gate.name in {"CNOT", "CX", "CY", "CZ"}: + (GATE_BRAKET[gate.name])(target_circuit, control=gate.control, target=gate.target[0]) + elif gate.name in {"XX"}: + (GATE_BRAKET[gate.name])(target_circuit, gate.target[0], gate.target[1], gate.parameter) + elif gate.name in {"CRZ"}: + (GATE_BRAKET[gate.name][0])(target_circuit, gate.control[0], gate.target[0], gate.parameter/2.) + (GATE_BRAKET[gate.name][1])(target_circuit, gate.control[0], gate.target[0], -gate.parameter/2.) + elif gate.name in {"SWAP"}: + (GATE_BRAKET[gate.name])(target_circuit, gate.target[0], gate.target[1]) + elif gate.name in {"CSWAP"}: + (GATE_BRAKET[gate.name])(target_circuit, gate.control[0], gate.target[0], gate.target[1]) + elif gate.name in {"CPHASE"}: + (GATE_BRAKET[gate.name])(target_circuit, gate.control[0], gate.target[0], gate.parameter) # elif gate.name in {"MEASURE"}: # implement if mid-circuit measurement available through Braket later on else: diff --git a/qsdk/backendbuddy/translator/translate_cirq.py b/qsdk/backendbuddy/translator/translate_cirq.py index fd5f20a88..36a15735f 100644 --- a/qsdk/backendbuddy/translator/translate_cirq.py +++ b/qsdk/backendbuddy/translator/translate_cirq.py @@ -21,6 +21,7 @@ - how the order and conventions for some of the inputs to the gate operations may also differ. """ +from math import pi def get_cirq_gates(): @@ -29,17 +30,29 @@ def get_cirq_gates(): """ import cirq + GATE_CIRQ = dict() GATE_CIRQ = dict() GATE_CIRQ["H"] = cirq.H GATE_CIRQ["X"] = cirq.X GATE_CIRQ["Y"] = cirq.Y GATE_CIRQ["Z"] = cirq.Z + GATE_CIRQ["CX"] = cirq.X + GATE_CIRQ["CY"] = cirq.Y + GATE_CIRQ["CZ"] = cirq.Z GATE_CIRQ["S"] = cirq.S GATE_CIRQ["T"] = cirq.T GATE_CIRQ["RX"] = cirq.rx GATE_CIRQ["RY"] = cirq.ry GATE_CIRQ["RZ"] = cirq.rz GATE_CIRQ["CNOT"] = cirq.CNOT + GATE_CIRQ["CRZ"] = cirq.rz + GATE_CIRQ["CRX"] = cirq.rx + GATE_CIRQ["CRY"] = cirq.ry + GATE_CIRQ["PHASE"] = cirq.ZPowGate + GATE_CIRQ["CPHASE"] = cirq.ZPowGate + GATE_CIRQ["XX"] = cirq.XXPowGate + GATE_CIRQ["SWAP"] = cirq.SWAP + GATE_CIRQ["CSWAP"] = cirq.SWAP GATE_CIRQ["MEASURE"] = cirq.measure return GATE_CIRQ @@ -69,15 +82,40 @@ def translate_cirq(source_circuit, noise_model=None): # Maps the gate information properly. Different for each backend (order, values) for gate in source_circuit._gates: + if gate.control is not None and gate.name is not 'CNOT': + control_list = [] + num_controls = len(gate.control) + for c in gate.control: + control_list.append(qubit_list[c]) if gate.name in {"H", "X", "Y", "Z", "S", "T"}: - target_circuit.append(GATE_CIRQ[gate.name](qubit_list[gate.target])) + target_circuit.append(GATE_CIRQ[gate.name](qubit_list[gate.target[0]])) + elif gate.name in {"CX", "CY", "CZ"}: + next_gate = GATE_CIRQ[gate.name].controlled(num_controls) + target_circuit.append(next_gate(*control_list, qubit_list[gate.target[0]])) elif gate.name in {"RX", "RY", "RZ"}: next_gate = GATE_CIRQ[gate.name](gate.parameter) - target_circuit.append(next_gate(qubit_list[gate.target])) + target_circuit.append(next_gate(qubit_list[gate.target[0]])) elif gate.name in {"CNOT"}: - target_circuit.append(GATE_CIRQ[gate.name](qubit_list[gate.control], qubit_list[gate.target])) + target_circuit.append(GATE_CIRQ[gate.name](qubit_list[gate.control[0]], qubit_list[gate.target[0]])) elif gate.name in {"MEASURE"}: - target_circuit.append(GATE_CIRQ[gate.name](qubit_list[gate.target])) + target_circuit.append(GATE_CIRQ[gate.name](qubit_list[gate.target[0]])) + elif gate.name in {"CRZ", "CRX", "CRY"}: + next_gate = GATE_CIRQ[gate.name](gate.parameter).controlled(num_controls) + target_circuit.append(next_gate(*control_list, qubit_list[gate.target[0]])) + elif gate.name in {"XX"}: + next_gate = GATE_CIRQ[gate.name](exponent=gate.parameter/pi) + target_circuit.append(next_gate(qubit_list[gate.target[0]], qubit_list[gate.target1[0]])) + elif gate.name in {"PHASE"}: + next_gate = GATE_CIRQ[gate.name](exponent=gate.parameter/pi) + target_circuit.append(next_gate(qubit_list[gate.target[0]])) + elif gate.name in {"CPHASE"}: + next_gate = GATE_CIRQ[gate.name](exponent=gate.parameter/pi).controlled(num_controls) + target_circuit.append(next_gate(*control_list, qubit_list[gate.target[0]])) + elif gate.name in {"SWAP"}: + target_circuit.append(GATE_CIRQ[gate.name](qubit_list[gate.target[0]], qubit_list[gate.target[1]])) + elif gate.name in {"CSWAP"}: + next_gate = GATE_CIRQ[gate.name].controlled(num_controls) + target_circuit.append(next_gate(*control_list, qubit_list[gate.target[0]], qubit_list[gate.target[1]])) else: raise ValueError(f"Gate '{gate.name}' not supported on backend cirq") @@ -87,17 +125,21 @@ def translate_cirq(source_circuit, noise_model=None): if nt == 'pauli': # Define pauli gate in cirq language depo = cirq.asymmetric_depolarize(np[0], np[1], np[2]) - target_circuit.append(depo(qubit_list[gate.target])) - if gate.control or gate.control == 0: - target_circuit.append(depo(qubit_list[gate.control])) + for t in gate.target: + target_circuit.append(depo(qubit_list[t])) + if gate.control is not None: + for c in gate.control: + target_circuit.append(depo(qubit_list[c])) elif nt == 'depol': - if gate.control or gate.control == 0: - # define 2-qubit depolarization gate - depo = cirq.depolarize(np*15/16, 2) # sparam, num_qubits - target_circuit.append(depo(qubit_list[gate.control], qubit_list[gate.target])) # gates targetted - else: - # sdefine 1-qubit depolarization gate - depo = cirq.depolarize(np*3/4, 1) - target_circuit.append(depo(qubit_list[gate.target])) + depo_list = [] + if gate.control is not None: + for c in gate.control: + depo_list.append(qubit_list[c]) + for t in gate.target: + depo_list.append(qubit_list[t]) + depo_size = len(depo_list) + # define depo_size-qubit depolarization gate + depo = cirq.depolarize(np*(4**depo_size-1)/4**depo_size, depo_size) # sparam, num_qubits + target_circuit.append(depo(*depo_list)) # gates targetted return target_circuit diff --git a/qsdk/backendbuddy/translator/translate_json_ionq.py b/qsdk/backendbuddy/translator/translate_json_ionq.py index b35162dea..e70a6bac7 100644 --- a/qsdk/backendbuddy/translator/translate_json_ionq.py +++ b/qsdk/backendbuddy/translator/translate_json_ionq.py @@ -54,11 +54,11 @@ def translate_json_ionq(source_circuit): json_gates = [] for gate in source_circuit._gates: if gate.name in {"H", "X", "Y", "Z", "S", "T"}: - json_gates.append({'gate': GATE_JSON_IONQ[gate.name], 'target': gate.target}) + json_gates.append({'gate': GATE_JSON_IONQ[gate.name], 'target': gate.target[0]}) elif gate.name in {"RX", "RY", "RZ"}: - json_gates.append({'gate': GATE_JSON_IONQ[gate.name], 'target': gate.target, 'rotation': gate.parameter}) + json_gates.append({'gate': GATE_JSON_IONQ[gate.name], 'target': gate.target[0], 'rotation': gate.parameter}) elif gate.name in {"CNOT"}: - json_gates.append({'gate': GATE_JSON_IONQ[gate.name], 'target': gate.target, 'control': gate.control}) + json_gates.append({'gate': GATE_JSON_IONQ[gate.name], 'target': gate.target[0], 'control': gate.control[0]}) else: raise ValueError(f"Gate '{gate.name}' not supported with JSON IonQ translation") diff --git a/qsdk/backendbuddy/translator/translate_projectq.py b/qsdk/backendbuddy/translator/translate_projectq.py index e1de3d08e..dca73d20a 100644 --- a/qsdk/backendbuddy/translator/translate_projectq.py +++ b/qsdk/backendbuddy/translator/translate_projectq.py @@ -62,11 +62,11 @@ def translate_projectq(source_circuit): for gate in source_circuit._gates: if gate.name in {"H", "X", "Y", "Z", "S", "T", "MEASURE"}: - projectq_circuit += f"{GATE_PROJECTQ[gate.name]} | Qureg[{gate.target}]\n" + projectq_circuit += f"{GATE_PROJECTQ[gate.name]} | Qureg[{gate.target[0]}]\n" elif gate.name in {"RX", "RY", "RZ"}: - projectq_circuit += f"{GATE_PROJECTQ[gate.name]}({gate.parameter}) | Qureg[{gate.target}]\n" + projectq_circuit += f"{GATE_PROJECTQ[gate.name]}({gate.parameter}) | Qureg[{gate.target[0]}]\n" elif gate.name in {"CNOT"}: - projectq_circuit += f"{GATE_PROJECTQ[gate.name]} | ( Qureg[{gate.control}], Qureg[{gate.target}] )\n" + projectq_circuit += f"{GATE_PROJECTQ[gate.name]} | ( Qureg[{gate.control[0]}], Qureg[{gate.target[0]}] )\n" else: raise ValueError(f"Gate '{gate.name}' not supported on backend projectQ") diff --git a/qsdk/backendbuddy/translator/translate_qdk.py b/qsdk/backendbuddy/translator/translate_qdk.py index 5846624bf..8995b270e 100644 --- a/qsdk/backendbuddy/translator/translate_qdk.py +++ b/qsdk/backendbuddy/translator/translate_qdk.py @@ -34,6 +34,13 @@ def get_qdk_gates(): GATE_QDK[name] = name for name in {"RX", "RY", "RZ"}: GATE_QDK[name] = name[0] + name[1:].lower() + GATE_QDK["PHASE"] = "R1" + GATE_QDK["CPHASE"] = "R1" + for name in {"CRX", "CRY", "CRZ"}: + GATE_QDK[name] = name[1] + name[2:].lower() + for name in {"CX", "CY", "CZ", "CSWAP"}: + GATE_QDK[name] = name[1:] + GATE_QDK["SWAP"] = "SWAP" GATE_QDK["MEASURE"] = "M" return GATE_QDK @@ -66,14 +73,28 @@ def translate_qsharp(source_circuit, operation="MyQsharpOperation"): # Generate Q# strings with the right syntax, order and values for the gate inputs body_str = "" for gate in source_circuit._gates: + if gate.control is not None and gate.name is not "CNOT": + control_string = '[' + num_controls = len(gate.control) + for i, c in enumerate(gate.control): + control_string += f'qreg[{c}]]' if i == num_controls - 1 else f'qreg[{c}], ' + if gate.name in {"H", "X", "Y", "Z", "S", "T"}: - body_str += f"\t\t{GATE_QDK[gate.name]}(qreg[{gate.target}]);\n" - elif gate.name in {"RX", "RY", "RZ"}: - body_str += f"\t\t{GATE_QDK[gate.name]}({gate.parameter}, qreg[{gate.target}]);\n" + body_str += f"\t\t{GATE_QDK[gate.name]}(qreg[{gate.target[0]}]);\n" + elif gate.name in {"RX", "RY", "RZ", "PHASE"}: + body_str += f"\t\t{GATE_QDK[gate.name]}({gate.parameter}, qreg[{gate.target[0]}]);\n" elif gate.name in {"CNOT"}: - body_str += f"\t\t{GATE_QDK[gate.name]}(qreg[{gate.control}], qreg[{gate.target}]);\n" + body_str += f"\t\t{GATE_QDK[gate.name]}(qreg[{gate.control[0]}], qreg[{gate.target[0]}]);\n" + elif gate.name in {"CRX", "CRY", "CRZ", "CPHASE"}: + body_str += f"\t\tControlled {GATE_QDK[gate.name]}({control_string}, ({gate.parameter}, qreg[{gate.target[0]}]));\n" + elif gate.name in {"CX", "CY", "CZ"}: + body_str += f"\t\tControlled {GATE_QDK[gate.name]}({control_string}, (qreg[{gate.target[0]}]));\n" + elif gate.name in {"SWAP"}: + body_str += f"\t\t{GATE_QDK[gate.name]}(qreg[{gate.target[0]}], qreg[{gate.target[1]}]);\n" + elif gate.name in {"CSWAP"}: + body_str += f"\t\tControlled {GATE_QDK[gate.name]}({control_string}, (qreg[{gate.target[0]}], qreg[{gate.target[1]}]));\n" elif gate.name in {"MEASURE"}: - body_str += f"\t\tset c w/= {gate.target} <- {GATE_QDK[gate.name]}(qreg[{gate.target}]);\n" + body_str += f"\t\tset c w/= {gate.target[0]} <- {GATE_QDK[gate.name]}(qreg[{gate.target[0]}]);\n" else: raise ValueError(f"Gate '{gate.name}' not supported on backend qdk") qsharp_string += body_str + "\n\t\treturn ForEach(MResetZ, qreg);\n" diff --git a/qsdk/backendbuddy/translator/translate_qiskit.py b/qsdk/backendbuddy/translator/translate_qiskit.py index efb304ef1..4a688b88d 100644 --- a/qsdk/backendbuddy/translator/translate_qiskit.py +++ b/qsdk/backendbuddy/translator/translate_qiskit.py @@ -36,12 +36,23 @@ def get_qiskit_gates(): GATE_QISKIT["X"] = qiskit.QuantumCircuit.x GATE_QISKIT["Y"] = qiskit.QuantumCircuit.y GATE_QISKIT["Z"] = qiskit.QuantumCircuit.z + GATE_QISKIT["CX"] = qiskit.QuantumCircuit.cx + GATE_QISKIT["CY"] = qiskit.QuantumCircuit.cy + GATE_QISKIT["CZ"] = qiskit.QuantumCircuit.cz GATE_QISKIT["S"] = qiskit.QuantumCircuit.s GATE_QISKIT["T"] = qiskit.QuantumCircuit.t GATE_QISKIT["RX"] = qiskit.QuantumCircuit.rx GATE_QISKIT["RY"] = qiskit.QuantumCircuit.ry GATE_QISKIT["RZ"] = qiskit.QuantumCircuit.rz + GATE_QISKIT["CRX"] = qiskit.QuantumCircuit.crx + GATE_QISKIT["CRY"] = qiskit.QuantumCircuit.cry + GATE_QISKIT["CRZ"] = qiskit.QuantumCircuit.crz GATE_QISKIT["CNOT"] = qiskit.QuantumCircuit.cx + GATE_QISKIT["SWAP"] = qiskit.QuantumCircuit.swap + GATE_QISKIT["XX"] = qiskit.QuantumCircuit.rxx + GATE_QISKIT["CSWAP"] = qiskit.QuantumCircuit.cswap + GATE_QISKIT["PHASE"] = qiskit.QuantumCircuit.p + GATE_QISKIT["CPHASE"] = qiskit.QuantumCircuit.cp GATE_QISKIT["MEASURE"] = qiskit.QuantumCircuit.measure return GATE_QISKIT @@ -64,14 +75,25 @@ def translate_qiskit(source_circuit): # Maps the gate information properly. Different for each backend (order, values) for gate in source_circuit._gates: + if gate.control is not None: + if len(gate.control) > 1: + raise ValueError('qiskit does not support arbitrary number of controls. Gate {gate.name} with controls {gate.control} is not allowed') if gate.name in {"H", "X", "Y", "Z", "S", "T"}: - (GATE_QISKIT[gate.name])(target_circuit, gate.target) - elif gate.name in {"RX", "RY", "RZ"}: - (GATE_QISKIT[gate.name])(target_circuit, gate.parameter, gate.target) - elif gate.name in {"CNOT"}: - (GATE_QISKIT[gate.name])(target_circuit, gate.control, gate.target) + (GATE_QISKIT[gate.name])(target_circuit, gate.target[0]) + elif gate.name in {"RX", "RY", "RZ", "PHASE"}: + (GATE_QISKIT[gate.name])(target_circuit, gate.parameter, gate.target[0]) + elif gate.name in {"CRX", "CRY", "CRZ", "CPHASE"}: + (GATE_QISKIT[gate.name])(target_circuit, gate.parameter, gate.control[0], gate.target[0]) + elif gate.name in {"CNOT", "CX", "CY", "CZ"}: + (GATE_QISKIT[gate.name])(target_circuit, gate.control[0], gate.target[0]) + elif gate.name in {"SWAP"}: + (GATE_QISKIT[gate.name])(target_circuit, gate.target[0], gate.target[1]) + elif gate.name in {"CSWAP"}: + (GATE_QISKIT[gate.name])(target_circuit, gate.control[0], gate.target[0], gate.target[1]) + elif gate.name in {"XX"}: + (GATE_QISKIT[gate.name])(target_circuit, gate.parameter, gate.target[0], gate.target[1]) elif gate.name in {"MEASURE"}: - (GATE_QISKIT[gate.name])(target_circuit, gate.target, gate.target) + (GATE_QISKIT[gate.name])(target_circuit, gate.target[0], gate.target[0]) else: raise ValueError(f"Gate '{gate.name}' not supported on backend qiskit") return target_circuit diff --git a/qsdk/backendbuddy/translator/translate_qulacs.py b/qsdk/backendbuddy/translator/translate_qulacs.py index c0982ef09..011b1c93c 100644 --- a/qsdk/backendbuddy/translator/translate_qulacs.py +++ b/qsdk/backendbuddy/translator/translate_qulacs.py @@ -21,6 +21,7 @@ - how the order and conventions for some of the inputs to the gate operations may also differ. """ +from numpy import exp def get_qulacs_gates(): @@ -35,12 +36,22 @@ def get_qulacs_gates(): GATE_QULACS["X"] = qulacs.QuantumCircuit.add_X_gate GATE_QULACS["Y"] = qulacs.QuantumCircuit.add_Y_gate GATE_QULACS["Z"] = qulacs.QuantumCircuit.add_Z_gate + GATE_QULACS["CX"] = qulacs.gate.X + GATE_QULACS["CY"] = qulacs.gate.Y + GATE_QULACS["CZ"] = qulacs.gate.Z GATE_QULACS["S"] = qulacs.QuantumCircuit.add_S_gate GATE_QULACS["T"] = qulacs.QuantumCircuit.add_T_gate GATE_QULACS["RX"] = qulacs.QuantumCircuit.add_RX_gate GATE_QULACS["RY"] = qulacs.QuantumCircuit.add_RY_gate GATE_QULACS["RZ"] = qulacs.QuantumCircuit.add_RZ_gate GATE_QULACS["CNOT"] = qulacs.QuantumCircuit.add_CNOT_gate + GATE_QULACS["CRX"] = qulacs.gate.RX + GATE_QULACS["CRY"] = qulacs.gate.RY + GATE_QULACS["CRZ"] = qulacs.gate.RZ + GATE_QULACS["PHASE"] = qulacs.gate.DenseMatrix + GATE_QULACS["CPHASE"] = qulacs.gate.DenseMatrix + GATE_QULACS["SWAP"] = qulacs.QuantumCircuit.add_SWAP_gate + GATE_QULACS["CSWAP"] = qulacs.gate.SWAP GATE_QULACS["MEASURE"] = qulacs.gate.Measurement return GATE_QULACS @@ -69,13 +80,38 @@ def translate_qulacs(source_circuit, noise_model=None): # Maps the gate information properly. Different for each backend (order, values) for gate in source_circuit._gates: if gate.name in {"H", "X", "Y", "Z", "S", "T"}: - (GATE_QULACS[gate.name])(target_circuit, gate.target) + (GATE_QULACS[gate.name])(target_circuit, gate.target[0]) + elif gate.name in {"CX", "CY", "CZ"}: + mat_gate = qulacs.gate.to_matrix_gate(GATE_QULACS[gate.name](gate.target[0])) + for c in gate.control: + mat_gate.add_control_qubit(c, 1) + target_circuit.add_gate(mat_gate) elif gate.name in {"RX", "RY", "RZ"}: - (GATE_QULACS[gate.name])(target_circuit, gate.target, -1. * gate.parameter) + (GATE_QULACS[gate.name])(target_circuit, gate.target[0], -1. * gate.parameter) + elif gate.name in {"CRX", "CRY", "CRZ"}: + mat_gate = qulacs.gate.to_matrix_gate(GATE_QULACS[gate.name](gate.target[0], -1. * gate.parameter)) + for c in gate.control: + mat_gate.add_control_qubit(c, 1) + target_circuit.add_gate(mat_gate) + elif gate.name in {"SWAP"}: + (GATE_QULACS[gate.name])(target_circuit, gate.target[0], gate.target[1]) + elif gate.name in {"CSWAP"}: + mat_gate = qulacs.gate.to_matrix_gate(GATE_QULACS[gate.name](gate.target[0], gate.target[1])) + for c in gate.control: + mat_gate.add_control_qubit(c, 1) + target_circuit.add_gate(mat_gate) + elif gate.name in {"PHASE"}: + mat_gate = GATE_QULACS[gate.name](gate.target[0], [[1, 0], [0, exp(1j * gate.parameter)]]) + target_circuit.add_gate(mat_gate) + elif gate.name in {"CPHASE"}: + mat_gate = GATE_QULACS[gate.name](gate.target[0], [[1, 0], [0, exp(1j * gate.parameter)]]) + for c in gate.control: + mat_gate.add_control_qubit(c, 1) + target_circuit.add_gate(mat_gate) elif gate.name in {"CNOT"}: - (GATE_QULACS[gate.name])(target_circuit, gate.control, gate.target) + (GATE_QULACS[gate.name])(target_circuit, gate.control[0], gate.target[0]) elif gate.name in {"MEASURE"}: - gate = (GATE_QULACS[gate.name])(gate.target, gate.target) + gate = (GATE_QULACS[gate.name])(gate.target[0], gate.target[0]) target_circuit.add_gate(gate) else: raise ValueError(f"Gate '{gate.name}' not supported on backend qulacs") @@ -84,13 +120,24 @@ def translate_qulacs(source_circuit, noise_model=None): if noise_model and (gate.name in noise_model.noisy_gates): for nt, np in noise_model._quantum_errors[gate.name]: if nt == 'pauli': - target_circuit.add_gate(Probabilistic(np, [X(gate.target), Y(gate.target), Z(gate.target)])) - if gate.control or gate.control == 0: - target_circuit.add_gate(Probabilistic(np, [X(gate.control), Y(gate.control), Z(gate.control)])) + for t in gate.target: + target_circuit.add_gate(Probabilistic(np, [X(t), Y(t), Z(t)])) + if gate.control is not None: + for c in gate.control: + target_circuit.add_gate(Probabilistic(np, [X(c), Y(c), Z(c)])) elif nt == 'depol': - if gate.control or gate.control == 0: - target_circuit.add_gate(TwoQubitDepolarizingNoise(gate.control, gate.target, (15/16)*np)) + depol_list = [] + for t in gate.target: + depol_list.append(t) + if gate.control is not None: + for c in gate.control: + depol_list.append(c) + n_depol = len(depol_list) + if n_depol == 2: + target_circuit.add_gate(TwoQubitDepolarizingNoise(*depol_list, (15/16)*np)) + elif n_depol == 1: + target_circuit.add_gate(DepolarizingNoise(depol_list[0], (3/4) * np)) else: - target_circuit.add_gate(DepolarizingNoise(gate.target, (3/4) * np)) + raise ValueError(f'{gate.name} has more than 2 qubits, Qulacs DepolarizingNoise only supports 1- and 2-qubits') return target_circuit diff --git a/setup.py b/setup.py index 9858df848..f1876a3b4 100755 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ def install(package): install('wheel') install('h5py==3.2.0') -install('pyscf==1.7.6.post1') +install('pyscf==1.7.6') setuptools.setup( name="qSDK",