From e174fa365a5f3189cc1bf53a9c1fc6285f054aa6 Mon Sep 17 00:00:00 2001 From: Fran Cabrera Date: Tue, 10 Jan 2017 14:39:53 +0000 Subject: [PATCH 1/6] Preparing the IBM format for QASM 2.0 API (it still works with 1.1 API) --- projectq/backends/_ibm/_ibm.py | 5 ++++- projectq/backends/_ibm/_ibm_http_client.py | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/projectq/backends/_ibm/_ibm.py b/projectq/backends/_ibm/_ibm.py index 769fae828..c283e2173 100755 --- a/projectq/backends/_ibm/_ibm.py +++ b/projectq/backends/_ibm/_ibm.py @@ -67,7 +67,7 @@ def __init__(self, use_hardware=False, num_runs=1024, verbose=False, if use_hardware: self._device = 'real' else: - self._device = 'sim_trivial' + self._device = 'sim_trivial_2' self._num_runs = num_runs self._verbose = verbose self._user = user @@ -166,6 +166,7 @@ def _run(self): gate['position'] = j try: gate['name'] = cmd.gate + gate['qasm'] = cmd.gate if not cmd.ctrl is None: gate['to'] = cmd.ctrl cnot_qubit_id = i @@ -209,8 +210,10 @@ def _run(self): for j in range(len(lines[i]['gates'])): try: name = lines[i]['gates'][j]['name'] + qasm = lines[i]['gates'][j]['qasm'] gates += '{"position":' + str(j) gates += ',"name":"' + name + '"' + gates += ',"qasm":"' + qasm + '"' if name == "cx": gates += ',"to":' + str(lines[i]['gates'][j]['to']) gates += '},' diff --git a/projectq/backends/_ibm/_ibm_http_client.py b/projectq/backends/_ibm/_ibm_http_client.py index fa1d8d937..10e3b6c09 100755 --- a/projectq/backends/_ibm/_ibm_http_client.py +++ b/projectq/backends/_ibm/_ibm_http_client.py @@ -27,7 +27,7 @@ class DeviceOfflineError(Exception): pass -def send(json_qasm, name, device='sim_trivial', user=None, password=None, +def send(json_qasm, name, device='sim_trivial_2', user=None, password=None, shots=1, verbose=False): """ Sends json QASM through the IBM API and runs the quantum circuit represented @@ -36,7 +36,7 @@ def send(json_qasm, name, device='sim_trivial', user=None, password=None, Args: json_qasm: JSON QASM representation of the circuit to run. name (str): Name of the experiment. - device (str): 'sim_trivial' or 'real' to run on simulator or on the real + device (str): 'sim_trivial_2' or 'real' to run on simulator or on the real chip, respectively. user (str): IBM quantum experience user. password (str): IBM quantum experience user password. From ff1aa9233f6a4377de1ecd72fc7cd8a571c5085a Mon Sep 17 00:00:00 2001 From: Fran Cabrera Date: Thu, 19 Jan 2017 09:51:08 +0000 Subject: [PATCH 2/6] Fixing test vor v2.0 --- projectq/backends/_ibm/_ibm_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projectq/backends/_ibm/_ibm_test.py b/projectq/backends/_ibm/_ibm_test.py index 1fc5b755c..687934e6d 100755 --- a/projectq/backends/_ibm/_ibm_test.py +++ b/projectq/backends/_ibm/_ibm_test.py @@ -71,7 +71,7 @@ def test_ibm_backend_functional_test(monkeypatch): toffoli2cnotandtgate, entangle, qft2crandhadamard) - correct_info = '{"playground":[{"line":0,"name":"q","gates":[{"position":0,"name":"h"},{"position":3,"name":"h"},{"position":4,"name":"measure"}]},{"line":1,"name":"q","gates":[{"position":0,"name":"h"},{"position":2,"name":"h"},{"position":3,"name":"measure"}]},{"line":2,"name":"q","gates":[{"position":1,"name":"cx","to":1},{"position":2,"name":"cx","to":0},{"position":3,"name":"h"},{"position":4,"name":"measure"}]},{"line":3,"name":"q","gates":[]},{"line":4,"name":"q","gates":[]}],"numberColumns":40,"numberLines":5,"numberGates":200,"hasMeasures":true,"topology":"250e969c6b9e68aa2a045ffbceb3ac33"}' + correct_info = '{"playground":[{"line":0,"name":"q","gates":[{"position":0,"name":"h","qasm":"h"},{"position":3,"name":"h","qasm":"h"},{"position":4,"name":"measure","qasm":"measure"}]},{"line":1,"name":"q","gates":[{"position":0,"name":"h","qasm":"h"},{"position":2,"name":"h","qasm":"h"},{"position":3,"name":"measure","qasm":"measure"}]},{"line":2,"name":"q","gates":[{"position":1,"name":"cx","qasm":"cx","to":1},{"position":2,"name":"cx","qasm":"cx","to":0},{"position":3,"name":"h","qasm":"h"},{"position":4,"name":"measure","qasm":"measure"}]},{"line":3,"name":"q","gates":[]},{"line":4,"name":"q","gates":[]}],"numberColumns":40,"numberLines":5,"numberGates":200,"hasMeasures":true,"topology":"250e969c6b9e68aa2a045ffbceb3ac33"}' # patch send def mock_send(*args, **kwargs): assert args[0] == correct_info From c73992c06a62eb95e0c26c7298adc13aaaf22ade Mon Sep 17 00:00:00 2001 From: Thomas Haener Date: Thu, 19 Jan 2017 11:03:31 +0100 Subject: [PATCH 3/6] Bumped version number. --- projectq/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projectq/_version.py b/projectq/_version.py index 8460a4b98..531d7887b 100755 --- a/projectq/_version.py +++ b/projectq/_version.py @@ -11,4 +11,4 @@ # limitations under the License. """Define version number here and read it from setup.py automatically""" -__version__ = "0.1.2" +__version__ = "0.1.3" From fb687a415e33f9f99fcbba7197b574561fc93169 Mon Sep 17 00:00:00 2001 From: Thomas Haener Date: Thu, 19 Jan 2017 11:29:25 +0100 Subject: [PATCH 4/6] Order of returned measurement results. --- projectq/backends/_ibm/_ibm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projectq/backends/_ibm/_ibm.py b/projectq/backends/_ibm/_ibm.py index c283e2173..71bb3e3e2 100755 --- a/projectq/backends/_ibm/_ibm.py +++ b/projectq/backends/_ibm/_ibm.py @@ -259,7 +259,7 @@ def __init__(self, ID): ID = self._cnot_qubit_id elif ID == self._cnot_qubit_id: # same here. ID = 2 - self.main_engine.set_measurement_result(QB(ID), int(measured[i])) + self.main_engine.set_measurement_result(QB(ID), int(measured[-1-i])) self._reset() except TypeError: raise Exception("Failed to run the circuit. Aborting.") From 3c4ecf7e8ac4ded28ccbc5b2a38b771535ba3797 Mon Sep 17 00:00:00 2001 From: Thomas Haener Date: Thu, 19 Jan 2017 14:55:16 +0100 Subject: [PATCH 5/6] IBMBackend: get_probabilities and facilitate accessing the back-end from the MainEngine. TODO: Fix test. --- examples/ibm.py | 13 ++- projectq/backends/_ibm/_ibm.py | 124 ++++++++++++++++++++-------- projectq/backends/_ibm/_ibm_test.py | 2 +- projectq/cengines/_main.py | 4 +- 4 files changed, 104 insertions(+), 39 deletions(-) diff --git a/examples/ibm.py b/examples/ibm.py index 3e2c077dd..0853097ae 100755 --- a/examples/ibm.py +++ b/examples/ibm.py @@ -3,7 +3,7 @@ from projectq import MainEngine -def run_entangle(eng, num_qubits=3): +def run_entangle(eng, num_qubits=5): """ Runs an entangling operation on the provided compiler engine. @@ -16,7 +16,7 @@ def run_entangle(eng, num_qubits=3): """ # allocate the quantum register to entangle qureg = eng.allocate_qureg(num_qubits) - + # entangle the qureg Entangle | qureg @@ -26,12 +26,17 @@ def run_entangle(eng, num_qubits=3): # run the circuit eng.flush() - # return the list of measurements + # access the probabilities via the back-end: + results = eng.backend.get_probabilities(qureg) + for state in results: + print("Measured {} with p = {}.".format(state, results[state])) + + # return one (random) measurement outcome. return [int(q) for q in qureg] if __name__ == "__main__": # create main compiler engine for the IBM back-end - eng = MainEngine(IBMBackend(use_hardware=True, num_runs=1024, verbose=True)) + eng = MainEngine(IBMBackend(use_hardware=True, num_runs=1024, verbose=False)) # run the circuit and print the result print(run_entangle(eng)) diff --git a/projectq/backends/_ibm/_ibm.py b/projectq/backends/_ibm/_ibm.py index 71bb3e3e2..8f3594adc 100755 --- a/projectq/backends/_ibm/_ibm.py +++ b/projectq/backends/_ibm/_ibm.py @@ -72,6 +72,10 @@ def __init__(self, use_hardware=False, num_runs=1024, verbose=False, self._verbose = verbose self._user = user self._password = password + self._mapping = dict() + self._inverse_mapping = dict() + self._mapped_qubits = 0 + self._probabilities = dict() def is_available(self, cmd): """ @@ -101,6 +105,7 @@ def _reset(self): for _ in range(self._num_qubits): self._cmds.append([""] * self._num_cols) self._positions = [0] * self._num_qubits + self._mapped_qubits = 0 def _store(self, cmd): """ @@ -113,14 +118,32 @@ def _store(self, cmd): """ gate = cmd.gate if gate == Allocate or gate == Deallocate: - pass - elif gate == Measure: + return + + if self._mapped_qubits == 0: + self._mapping = dict() + self._inverse_mapping = dict() + self._probabilities = dict() + + for qr in cmd.qubits: + for qb in qr: + if not qb.id in self._mapping: + self._mapping[qb.id] = self._mapped_qubits + self._inverse_mapping[self._mapped_qubits] = qb.id + self._mapped_qubits += 1 + for qb in cmd.control_qubits: + if not qb.id in self._mapping: + self._mapping[qb.id] = self._mapped_qubits + self._inverse_mapping[self._mapped_qubits] = qb.id + self._mapped_qubits += 1 + + if gate == Measure: for qr in cmd.qubits: for qb in qr: - qb_id = qb.id - meas = _IBMGateCommand("measure", qb_id) - self._cmds[qb_id][self._positions[qb_id]] = meas - self._positions[qb_id] += 1 + qb_pos = self._mapping[qb.id] + meas = _IBMGateCommand("measure", qb_pos) + self._cmds[qb_pos][self._positions[qb_pos]] = meas + self._positions[qb_pos] += 1 elif not (gate == NOT and get_control_count(cmd) == 1): cls = gate.__class__.__name__ @@ -129,20 +152,56 @@ def _store(self, cmd): else: gate_str = str(gate).lower() - qb_id = cmd.qubits[0][0].id - ibm_cmd = _IBMGateCommand(gate_str, qb_id) - self._cmds[qb_id][self._positions[qb_id]] = ibm_cmd - self._positions[qb_id] += 1 + qb_pos = self._mapping[cmd.qubits[0][0].id] + ibm_cmd = _IBMGateCommand(gate_str, qb_pos) + self._cmds[qb_pos][self._positions[qb_pos]] = ibm_cmd + self._positions[qb_pos] += 1 else: - ctrl_id = cmd.control_qubits[0].id - qb_id = cmd.qubits[0][0].id - pos = max(self._positions[qb_id], self._positions[ctrl_id]) - self._positions[qb_id] = pos - self._positions[ctrl_id] = pos - ibm_cmd = _IBMGateCommand("cx", qb_id, ctrl_id) - self._cmds[qb_id][self._positions[qb_id]] = ibm_cmd - self._positions[qb_id] += 1 - self._positions[ctrl_id] += 1 + ctrl_pos = self._mapping[cmd.control_qubits[0].id] + qb_pos = self._mapping[cmd.qubits[0][0].id] + pos = max(self._positions[qb_pos], self._positions[ctrl_pos]) + self._positions[qb_pos] = pos + self._positions[ctrl_pos] = pos + ibm_cmd = _IBMGateCommand("cx", qb_pos, ctrl_pos) + self._cmds[qb_pos][self._positions[qb_pos]] = ibm_cmd + self._positions[qb_pos] += 1 + self._positions[ctrl_pos] += 1 + + def get_probabilities(self, qureg): + """ + Return the list of basis states with corresponding probabilities. + + The measured bits are ordered according to the supplied quantum register, + i.e., the left-most bit in the state-string corresponds to the first qubit + in the supplied quantum register. + + Warning: + Only call this function after the circuit has been executed! + + Args: + qureg (list): Quantum register determining the order of the + qubits. + + Returns: + probability_dict (dict): Dictionary mapping n-bit strings to + probabilities. + + Raises: + Exception: If no data is available (i.e., if the circuit has not been + executed). + """ + if len(self._probabilities) == 0: + raise RuntimeError("Please, run the circuit first!") + + probability_dict = dict() + + for state in self._probabilities: + mapped_state = ['0'] * len(qureg) + for i in range(len(qureg)): + mapped_state[i] = state[self._mapping[qureg[i].id]] + probability_dict["".join(mapped_state)] = self._probabilities[state] + + return probability_dict def _run(self): """ @@ -239,27 +298,26 @@ def _run(self): p_sum = 0. measured = "" for state, probability in zip(data['labels'], data['values']): - if self._verbose and probability > 0: - print(str(state) + " with p = " + str(probability)) + state = list(reversed(state)) + state[2], state[self._cnot_qubit_id] = state[self._cnot_qubit_id], state[2] + state = "".join(state) p_sum += probability + star = "" if p_sum >= P and measured == "": measured = state - + star = "*" + self._probabilities[state] = probability + if self._verbose and probability > 0: + print(str(state) + " with p = " + str(probability) + star) + class QB(): def __init__(self, ID): self.id = ID - + # register measurement result - for i in range(len(data['qubits'])): - ID = int(data['qubits'][i]) - if ID == 2: - # we may have swapped these two qubits - # (cnot qubit is always #2 on the device) - # --> undo this transformation - ID = self._cnot_qubit_id - elif ID == self._cnot_qubit_id: # same here. - ID = 2 - self.main_engine.set_measurement_result(QB(ID), int(measured[-1-i])) + for ID in self._mapping: + location = self._mapping[ID] + self.main_engine.set_measurement_result(QB(ID), int(measured[location])) self._reset() except TypeError: raise Exception("Failed to run the circuit. Aborting.") diff --git a/projectq/backends/_ibm/_ibm_test.py b/projectq/backends/_ibm/_ibm_test.py index 687934e6d..8e8c59c5c 100755 --- a/projectq/backends/_ibm/_ibm_test.py +++ b/projectq/backends/_ibm/_ibm_test.py @@ -32,7 +32,7 @@ def no_requests(monkeypatch): monkeypatch.delattr("requests.sessions.Session.request") -_api_url = 'https://qcwi-staging.mybluemix.net/api/' +_api_url = 'https://quantumexperience.ng.bluemix.net/api/' _api_url_status = 'https://quantumexperience.ng.bluemix.net/api/' diff --git a/projectq/cengines/_main.py b/projectq/cengines/_main.py index f39a6c2c7..b9bdc2809 100755 --- a/projectq/cengines/_main.py +++ b/projectq/cengines/_main.py @@ -44,6 +44,7 @@ class MainEngine(BasicEngine): main_engine (MainEngine): Self. active_qubits (WeakSet): WeakSet containing all active qubits dirty_qubits (Set): Containing all dirty qubit ids + backend (BasicEngine): Access the back-end. """ def __init__(self, backend=None, engine_list=None): @@ -107,7 +108,8 @@ def __init__(self, backend=None, engine_list=None): " MainEngine(engine_list=[AutoReplacer()])") engine_list.append(backend) - + self.backend = backend + # Test that user did not supply twice the same engine instance num_different_engines = len(set([id(item) for item in engine_list])) if len(engine_list) != num_different_engines: From a5b0228b649595bc3b637ae7b847dc1def48fd33 Mon Sep 17 00:00:00 2001 From: "Damian S. Steiger" Date: Thu, 19 Jan 2017 15:45:13 +0100 Subject: [PATCH 6/6] updated ibm functional test --- projectq/backends/_ibm/_ibm_test.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/projectq/backends/_ibm/_ibm_test.py b/projectq/backends/_ibm/_ibm_test.py index 8e8c59c5c..af860301c 100755 --- a/projectq/backends/_ibm/_ibm_test.py +++ b/projectq/backends/_ibm/_ibm_test.py @@ -54,11 +54,9 @@ def test_ibm_backend_is_available_control_not(num_ctrl_qubits, is_available): eng = MainEngine(backend=DummyEngine(), engine_list=[DummyEngine()]) qubit1 = eng.allocate_qubit() qureg = eng.allocate_qureg(num_ctrl_qubits) - print(len(qureg)) ibm_backend = _ibm.IBMBackend() cmd = Command(eng, NOT , (qubit1,)) cmd.add_control_qubits(qureg) - print(cmd) assert ibm_backend.is_available(cmd) == is_available @@ -71,17 +69,18 @@ def test_ibm_backend_functional_test(monkeypatch): toffoli2cnotandtgate, entangle, qft2crandhadamard) - correct_info = '{"playground":[{"line":0,"name":"q","gates":[{"position":0,"name":"h","qasm":"h"},{"position":3,"name":"h","qasm":"h"},{"position":4,"name":"measure","qasm":"measure"}]},{"line":1,"name":"q","gates":[{"position":0,"name":"h","qasm":"h"},{"position":2,"name":"h","qasm":"h"},{"position":3,"name":"measure","qasm":"measure"}]},{"line":2,"name":"q","gates":[{"position":1,"name":"cx","qasm":"cx","to":1},{"position":2,"name":"cx","qasm":"cx","to":0},{"position":3,"name":"h","qasm":"h"},{"position":4,"name":"measure","qasm":"measure"}]},{"line":3,"name":"q","gates":[]},{"line":4,"name":"q","gates":[]}],"numberColumns":40,"numberLines":5,"numberGates":200,"hasMeasures":true,"topology":"250e969c6b9e68aa2a045ffbceb3ac33"}' + correct_info = '{"playground":[{"line":0,"name":"q","gates":[{"position":0,"name":"h","qasm":"h"},{"position":2,"name":"h","qasm":"h"},{"position":3,"name":"measure","qasm":"measure"}]},{"line":1,"name":"q","gates":[{"position":0,"name":"h","qasm":"h"},{"position":3,"name":"h","qasm":"h"},{"position":4,"name":"measure","qasm":"measure"}]},{"line":2,"name":"q","gates":[{"position":1,"name":"cx","qasm":"cx","to":0},{"position":2,"name":"cx","qasm":"cx","to":1},{"position":3,"name":"h","qasm":"h"},{"position":4,"name":"measure","qasm":"measure"}]},{"line":3,"name":"q","gates":[]},{"line":4,"name":"q","gates":[]}],"numberColumns":40,"numberLines":5,"numberGates":200,"hasMeasures":true,"topology":"250e969c6b9e68aa2a045ffbceb3ac33"}' # patch send def mock_send(*args, **kwargs): assert args[0] == correct_info - return {'data': {'qasm': 'qreg q,5;gate h, [[0.7071067811865476,0.7071067811865476],[0.7071067811865476,-0.7071067811865476]];gate measure, [[1,0],[0,0.7071067811865476+0.7071067811865476i]];gate cx, [[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]];\nh q[0];h q[1];cx q[1], q[2];h q[1];cx q[0], q[2];h q[0];measure q[1];h q[2];measure q[0];measure q[2];', 'p': {'values': [0.4580078125, 0.0068359375, 0.013671875, 0.064453125, 0.048828125, 0.0234375, 0.013671875, 0.37109375], 'qubits': [0, 1, 2], 'labels': ['000', '001', '010', '011', '100', '101', '110', '111']}, 'time': 16.12812304496765, 'serialNumberDevice': 'Real5Qv1'}, 'date': '2016-12-27T01:04:04.395Z'} + return {'date': '2017-01-19T14:28:47.622Z', 'data': {'time': 14.429004907608032, 'serialNumberDevice': 'Real5Qv1', 'p': {'labels': ['00000', '00001', '00010', '00011', '00100', '00101', '00110', '00111'], 'values': [0.4521484375, 0.0419921875, 0.0185546875, 0.0146484375, 0.005859375, 0.0263671875, 0.0537109375, 0.38671875], 'qubits': [0, 1, 2]}, 'qasm': 'IBMQASM 2.0;\n\ninclude "qelib1.inc";\nqreg q[5];\ncreg c[5];\n\nh q[0];\nh q[1];\nCX q[0],q[2];\nh q[0];\nCX q[1],q[2];\nmeasure q[0] -> c[0];\nh q[1];\nh q[2];\nmeasure q[1] -> c[1];\nmeasure q[2] -> c[2];\n'}} monkeypatch.setattr(_ibm, "send", mock_send) backend = _ibm.IBMBackend() engine_list = [TagRemover(), LocalOptimizer(10), AutoReplacer(), TagRemover(), IBMCNOTMapper(), LocalOptimizer(10)] eng = MainEngine(backend=backend, engine_list=engine_list) + unused_qubit = eng.allocate_qubit() qureg = eng.allocate_qureg(3) # entangle the qureg Entangle | qureg @@ -89,4 +88,7 @@ def mock_send(*args, **kwargs): Measure | qureg # run the circuit eng.flush() + prob_dict = eng.backend.get_probabilities([qureg[0],qureg[2],qureg[1]]) + assert prob_dict['111'] == pytest.approx(0.38671875) + assert prob_dict['101'] == pytest.approx(0.0263671875)