From 8d57cf6a90f3b270cc0defe655f66c17f24a01f2 Mon Sep 17 00:00:00 2001 From: Ari Jordan Date: Tue, 10 Mar 2020 17:36:23 +0100 Subject: [PATCH 01/19] Add histogram plotting function for Simulator --- projectq/libs/__init__.py | 2 + projectq/libs/hist/__init__.py | 14 +++++ projectq/libs/hist/_histogram.py | 36 ++++++++++++ projectq/libs/hist/_histogram_test.py | 82 +++++++++++++++++++++++++++ pytest.ini | 1 + 5 files changed, 135 insertions(+) create mode 100644 projectq/libs/hist/__init__.py create mode 100644 projectq/libs/hist/_histogram.py create mode 100644 projectq/libs/hist/_histogram_test.py diff --git a/projectq/libs/__init__.py b/projectq/libs/__init__.py index ee1451dcd..9c1d76507 100755 --- a/projectq/libs/__init__.py +++ b/projectq/libs/__init__.py @@ -11,3 +11,5 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + +from projectq.libs.hist import histogram diff --git a/projectq/libs/hist/__init__.py b/projectq/libs/hist/__init__.py new file mode 100644 index 000000000..de0c3d4ec --- /dev/null +++ b/projectq/libs/hist/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2017 ProjectQ-Framework (www.projectq.ch) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from ._histogram import histogram diff --git a/projectq/libs/hist/_histogram.py b/projectq/libs/hist/_histogram.py new file mode 100644 index 000000000..41f94f618 --- /dev/null +++ b/projectq/libs/hist/_histogram.py @@ -0,0 +1,36 @@ +import matplotlib.pyplot as plt +from projectq.backends import Simulator + +def histogram(sim, qureg): + #qubit_ids = set() + #for qb in qubit_list: + #qubit_ids.add(qb.id) + qubit_list = [] + for q in qureg: + if(isinstance(q, list)): + for qb in q: + qubit_list.append(qb) + else: + qubit_list.append(q) + if len(qubit_list) > 5: + print(f'Warning: For {len(qubit_list)} qubits there are 2^{len(qubit_list)} different outcomes') + print("The resulting histogram may look bad and/or take too long") + print("Consider calling histogram() with a sublist of the qubits", flush=True) + outcome = [0] * len(qubit_list) + n_outcomes = (1 << len(qubit_list)) + probabilities = {} + for i in range (n_outcomes): + for pos in range (len(qubit_list)): + if((1 << pos) & i): + outcome[pos] = 1 + else: + outcome[pos] = 0 + str1 = "" + probabilities[''.join([str(bit) for bit in outcome])] = sim.get_probability(outcome, qubit_list) + fig, axes = plt.subplots(figsize = (min(21.2, 2 + 0.6 * (1 << len(qubit_list))), 7)) + names = list(probabilities.keys()) + values = list(probabilities.values()) + axes.bar(names, values) + fig.suptitle('Measurement Probabilities') + plt.show() + return(fig, axes, probabilities) diff --git a/projectq/libs/hist/_histogram_test.py b/projectq/libs/hist/_histogram_test.py new file mode 100644 index 000000000..c097e1e45 --- /dev/null +++ b/projectq/libs/hist/_histogram_test.py @@ -0,0 +1,82 @@ +from projectq import MainEngine +from projectq.ops import H, C, X, Measure, All +from projectq.backends import Simulator +from projectq.libs import histogram +import pytest +from pytest import approx +import matplotlib + +@pytest.fixture(scope="module") +def matplotlib_setup(): + old_backend = matplotlib.get_backend() + matplotlib.use('agg') # avoid showing the histogram plots + yield + matplotlib.use(old_backend) + +def test_qubit(matplotlib_setup): + sim = Simulator() + eng = MainEngine(sim) + qb = eng.allocate_qubit() + eng.flush() + fig, axes, prob = histogram(sim, qb) + assert prob["0"] == approx(1) + assert prob["1"] == approx(0) + H | qb + eng.flush() + fig, axes, prob = histogram(sim, qb) + assert prob["0"] == approx(0.5) + Measure | qb + eng.flush() + fig, axes, prob = histogram(sim, qb) + assert prob["0"] == approx(1) or prob["1"] == approx(1) + + +def test_qureg(matplotlib_setup): + sim = Simulator() + eng = MainEngine(sim) + qr = eng.allocate_qureg(3) + eng.flush() + fig, axes, prob = histogram(sim, qr) + assert prob["000"] == approx(1) + assert prob ["110"] == approx(0) + H | qr[0] + C(X, 1) | (qr[0], qr[1]) + H | qr[2] + eng.flush() + fig, axes, prob = histogram(sim, qr) + assert prob["110"] == approx(0.25) + assert prob["100"] == approx(0) + All(Measure) | qr + eng.flush() + fig, axes, prob = histogram(sim, qr) + assert prob["000"] == approx(1) or prob["001"] == approx(1) or prob["110"] == approx(1) or prob["111"] == approx(1) + assert prob["000"] + prob["001"] + prob["110"] + prob["111"] == approx(1) + + +def test_combination(matplotlib_setup): + sim = Simulator() + eng = MainEngine(sim) + qr = eng.allocate_qureg(2) + qb = eng.allocate_qubit() + eng.flush() + fig, axes, prob = histogram(sim, [qr, qb]) + assert prob["000"] == approx(1) + H | qr[0] + C(X, 1)| (qr[0], qr[1]) + H | qb + Measure | qr[0] + eng.flush() + fig, axes, prob = histogram(sim, [qr, qb]) + assert (prob["000"] == approx(0.5) and prob["001"] == approx(0.5)) or (prob["110"] == approx(0.5) and prob["111"] == approx(0.5)) + assert prob["100"] == approx(0) + Measure | qb + + +def test_too_many_qubits(): + sim = Simulator() + eng = MainEngine(sim) + qr = eng.allocate_qureg(6) + eng.flush() + fig, axes, prob = histogram(sim, qr) + assert prob["000000"] == approx(1) + All(Measure) diff --git a/pytest.ini b/pytest.ini index fab634b12..d17d4ce2e 100755 --- a/pytest.ini +++ b/pytest.ini @@ -4,3 +4,4 @@ testpaths = projectq filterwarnings = error ignore:the matrix subclass is not the recommended way:PendingDeprecationWarning + ignore:Matplotlib is currently using agg, which is a non-GUI backend, so cannot show the figure. From 29989359dbaee89c285ba23503a008503373750d Mon Sep 17 00:00:00 2001 From: Ari Jordan 80055203 Date: Sat, 14 Mar 2020 11:20:39 +0000 Subject: [PATCH 02/19] Apply suggestion to projectq/libs/__init__.py --- projectq/libs/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/projectq/libs/__init__.py b/projectq/libs/__init__.py index 9c1d76507..a153103d4 100755 --- a/projectq/libs/__init__.py +++ b/projectq/libs/__init__.py @@ -12,4 +12,3 @@ # See the License for the specific language governing permissions and # limitations under the License. -from projectq.libs.hist import histogram From 24accda2516541dfb9fd11e1cb57654b4f1d589a Mon Sep 17 00:00:00 2001 From: Ari Jordan 80055203 Date: Sat, 14 Mar 2020 11:21:03 +0000 Subject: [PATCH 03/19] Apply suggestion to projectq/libs/hist/__init__.py --- projectq/libs/hist/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projectq/libs/hist/__init__.py b/projectq/libs/hist/__init__.py index de0c3d4ec..219348fc0 100644 --- a/projectq/libs/hist/__init__.py +++ b/projectq/libs/hist/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2017 ProjectQ-Framework (www.projectq.ch) +# Copyright 2020 ProjectQ-Framework (www.projectq.ch) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 0aae42c3158dc48dd21a70eaa44dfdfa224593d9 Mon Sep 17 00:00:00 2001 From: Ari Jordan 80055203 Date: Sat, 14 Mar 2020 11:26:03 +0000 Subject: [PATCH 04/19] Update _histogram.py --- projectq/libs/hist/_histogram.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/projectq/libs/hist/_histogram.py b/projectq/libs/hist/_histogram.py index 41f94f618..8ad689479 100644 --- a/projectq/libs/hist/_histogram.py +++ b/projectq/libs/hist/_histogram.py @@ -1,3 +1,17 @@ +# Copyright 2020 ProjectQ-Framework (www.projectq.ch) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import matplotlib.pyplot as plt from projectq.backends import Simulator From 0e870de10dcc37adfbde0c6426cab8cb1bb2c128 Mon Sep 17 00:00:00 2001 From: Ari Jordan 80055203 Date: Sat, 14 Mar 2020 11:28:08 +0000 Subject: [PATCH 05/19] Apply suggestion to projectq/libs/hist/_histogram.py --- projectq/libs/hist/_histogram.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projectq/libs/hist/_histogram.py b/projectq/libs/hist/_histogram.py index 8ad689479..837eea61c 100644 --- a/projectq/libs/hist/_histogram.py +++ b/projectq/libs/hist/_histogram.py @@ -22,8 +22,7 @@ def histogram(sim, qureg): qubit_list = [] for q in qureg: if(isinstance(q, list)): - for qb in q: - qubit_list.append(qb) + qubit_list.extend(q) else: qubit_list.append(q) if len(qubit_list) > 5: From 360efb62dc8e664d1e8ac483958105010b72f888 Mon Sep 17 00:00:00 2001 From: Ari Jordan 80055203 Date: Sat, 14 Mar 2020 11:28:20 +0000 Subject: [PATCH 06/19] Apply suggestion to projectq/libs/hist/_histogram.py --- projectq/libs/hist/_histogram.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/projectq/libs/hist/_histogram.py b/projectq/libs/hist/_histogram.py index 837eea61c..7da3fa091 100644 --- a/projectq/libs/hist/_histogram.py +++ b/projectq/libs/hist/_histogram.py @@ -16,9 +16,6 @@ from projectq.backends import Simulator def histogram(sim, qureg): - #qubit_ids = set() - #for qb in qubit_list: - #qubit_ids.add(qb.id) qubit_list = [] for q in qureg: if(isinstance(q, list)): From a537eb856e16736b14b48d6b73080f44f7f018fe Mon Sep 17 00:00:00 2001 From: Ari Jordan Date: Mon, 16 Mar 2020 15:10:38 +0100 Subject: [PATCH 07/19] Add docstrings --- projectq/libs/hist/__init__.py | 6 ++++ projectq/libs/hist/_histogram.py | 19 +++++++++- projectq/libs/hist/_histogram_test.py | 52 ++++++++++++++++++--------- 3 files changed, 60 insertions(+), 17 deletions(-) diff --git a/projectq/libs/hist/__init__.py b/projectq/libs/hist/__init__.py index 219348fc0..088766263 100644 --- a/projectq/libs/hist/__init__.py +++ b/projectq/libs/hist/__init__.py @@ -11,4 +11,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + +""" +contains a function to plot measurement outcome probabilities +as a histogram for the simulator +""" + from ._histogram import histogram diff --git a/projectq/libs/hist/_histogram.py b/projectq/libs/hist/_histogram.py index 7da3fa091..0ffb1aab4 100644 --- a/projectq/libs/hist/_histogram.py +++ b/projectq/libs/hist/_histogram.py @@ -16,6 +16,24 @@ from projectq.backends import Simulator def histogram(sim, qureg): + """ + Make a measurement outcome probability histogram for the given qubits + + Args: + sim (Simulator): The simulator to call get_probability + qureg (list of qubits and/or quregs): The qubits, + for which to make the histogram + + Returns: + A tuple (fig, axes, probabilities), where: + fig: The histogram as figure + axes: The axes of the histogram + probabilities (dict): A dictionary mapping outcomes as string + to their probabilities + + Note: + Don't forget to flush() before using this function. + """ qubit_list = [] for q in qureg: if(isinstance(q, list)): @@ -42,5 +60,4 @@ def histogram(sim, qureg): values = list(probabilities.values()) axes.bar(names, values) fig.suptitle('Measurement Probabilities') - plt.show() return(fig, axes, probabilities) diff --git a/projectq/libs/hist/_histogram_test.py b/projectq/libs/hist/_histogram_test.py index c097e1e45..703d488a6 100644 --- a/projectq/libs/hist/_histogram_test.py +++ b/projectq/libs/hist/_histogram_test.py @@ -1,10 +1,24 @@ +# Copyright 2020 ProjectQ-Framework (www.projectq.ch) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + from projectq import MainEngine from projectq.ops import H, C, X, Measure, All from projectq.backends import Simulator -from projectq.libs import histogram +from projectq.libs.hist import histogram import pytest -from pytest import approx import matplotlib +import matplotlib.pyplot as plt @pytest.fixture(scope="module") def matplotlib_setup(): @@ -19,16 +33,17 @@ def test_qubit(matplotlib_setup): qb = eng.allocate_qubit() eng.flush() fig, axes, prob = histogram(sim, qb) - assert prob["0"] == approx(1) - assert prob["1"] == approx(0) + assert prob["0"] == pytest.approx(1) + assert prob["1"] == pytest.approx(0) H | qb eng.flush() fig, axes, prob = histogram(sim, qb) - assert prob["0"] == approx(0.5) + assert prob["0"] == pytest.approx(0.5) Measure | qb eng.flush() fig, axes, prob = histogram(sim, qb) - assert prob["0"] == approx(1) or prob["1"] == approx(1) + assert prob["0"] == pytest.approx(1) or prob["1"] == pytest.approx(1) + plt.show() def test_qureg(matplotlib_setup): @@ -37,20 +52,22 @@ def test_qureg(matplotlib_setup): qr = eng.allocate_qureg(3) eng.flush() fig, axes, prob = histogram(sim, qr) - assert prob["000"] == approx(1) - assert prob ["110"] == approx(0) + assert prob["000"] == pytest.approx(1) + assert prob ["110"] == pytest.approx(0) H | qr[0] C(X, 1) | (qr[0], qr[1]) H | qr[2] eng.flush() fig, axes, prob = histogram(sim, qr) - assert prob["110"] == approx(0.25) - assert prob["100"] == approx(0) + assert prob["110"] == pytest.approx(0.25) + assert prob["100"] == pytest.approx(0) All(Measure) | qr eng.flush() fig, axes, prob = histogram(sim, qr) - assert prob["000"] == approx(1) or prob["001"] == approx(1) or prob["110"] == approx(1) or prob["111"] == approx(1) - assert prob["000"] + prob["001"] + prob["110"] + prob["111"] == approx(1) + assert prob["000"] == pytest.approx(1) or prob["001"] == pytest.approx(1) \ + or prob["110"] == pytest.approx(1) or prob["111"] == pytest.approx(1) + assert prob["000"] + prob["001"] + prob["110"] + prob["111"] == pytest.approx(1) + plt.show() def test_combination(matplotlib_setup): @@ -60,16 +77,18 @@ def test_combination(matplotlib_setup): qb = eng.allocate_qubit() eng.flush() fig, axes, prob = histogram(sim, [qr, qb]) - assert prob["000"] == approx(1) + assert prob["000"] == pytest.approx(1) H | qr[0] C(X, 1)| (qr[0], qr[1]) H | qb Measure | qr[0] eng.flush() fig, axes, prob = histogram(sim, [qr, qb]) - assert (prob["000"] == approx(0.5) and prob["001"] == approx(0.5)) or (prob["110"] == approx(0.5) and prob["111"] == approx(0.5)) - assert prob["100"] == approx(0) + assert (prob["000"] == pytest.approx(0.5) and prob["001"] == pytest.approx(0.5)) \ + or (prob["110"] == pytest.approx(0.5) and prob["111"] == pytest.approx(0.5)) + assert prob["100"] == pytest.approx(0) Measure | qb + plt.show() def test_too_many_qubits(): @@ -78,5 +97,6 @@ def test_too_many_qubits(): qr = eng.allocate_qureg(6) eng.flush() fig, axes, prob = histogram(sim, qr) - assert prob["000000"] == approx(1) + assert prob["000000"] == pytest.approx(1) All(Measure) + plt.show() From baf739dc56b3e102c919a18ca4c3d720fde30dc2 Mon Sep 17 00:00:00 2001 From: Ari Jordan Date: Mon, 16 Mar 2020 15:30:17 +0100 Subject: [PATCH 08/19] All suggestions applied. --- projectq/libs/hist/_histogram.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/projectq/libs/hist/_histogram.py b/projectq/libs/hist/_histogram.py index 0ffb1aab4..2ae87a7a7 100644 --- a/projectq/libs/hist/_histogram.py +++ b/projectq/libs/hist/_histogram.py @@ -17,7 +17,7 @@ def histogram(sim, qureg): """ - Make a measurement outcome probability histogram for the given qubits + Make a measurement outcome probability histogram for the given qubits. Args: sim (Simulator): The simulator to call get_probability @@ -41,9 +41,9 @@ def histogram(sim, qureg): else: qubit_list.append(q) if len(qubit_list) > 5: - print(f'Warning: For {len(qubit_list)} qubits there are 2^{len(qubit_list)} different outcomes') - print("The resulting histogram may look bad and/or take too long") - print("Consider calling histogram() with a sublist of the qubits", flush=True) + print('Warning: For ', len(qubit_list), ' qubits there are 2^', len(qubit_list), ' different outcomes.', sep='') + print("The resulting histogram may look bad and/or take too long.") + print("Consider calling histogram() with a sublist of the qubits.", flush=True) outcome = [0] * len(qubit_list) n_outcomes = (1 << len(qubit_list)) probabilities = {} From e575cc2ce204f07fed7641a00dc491b136aac322 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Tue, 17 Mar 2020 08:16:47 +0000 Subject: [PATCH 09/19] Minor fixes for _histogram.py --- projectq/libs/hist/_histogram.py | 33 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/projectq/libs/hist/_histogram.py b/projectq/libs/hist/_histogram.py index 2ae87a7a7..56b6cc2f0 100644 --- a/projectq/libs/hist/_histogram.py +++ b/projectq/libs/hist/_histogram.py @@ -13,7 +13,7 @@ # limitations under the License. import matplotlib.pyplot as plt -from projectq.backends import Simulator + def histogram(sim, qureg): """ @@ -22,7 +22,7 @@ def histogram(sim, qureg): Args: sim (Simulator): The simulator to call get_probability qureg (list of qubits and/or quregs): The qubits, - for which to make the histogram + for which to make the histogram Returns: A tuple (fig, axes, probabilities), where: @@ -32,32 +32,39 @@ def histogram(sim, qureg): to their probabilities Note: - Don't forget to flush() before using this function. + Don't forget to call eng.flush() before using this function. """ qubit_list = [] for q in qureg: - if(isinstance(q, list)): + if isinstance(q, list): qubit_list.extend(q) else: qubit_list.append(q) + if len(qubit_list) > 5: - print('Warning: For ', len(qubit_list), ' qubits there are 2^', len(qubit_list), ' different outcomes.', sep='') + print('Warning: For {0} qubits there are 2^{0} different outcomes'. + format(len(qubit_list))) print("The resulting histogram may look bad and/or take too long.") - print("Consider calling histogram() with a sublist of the qubits.", flush=True) + print("Consider calling histogram() with a sublist of the qubits.", + flush=True) + outcome = [0] * len(qubit_list) n_outcomes = (1 << len(qubit_list)) probabilities = {} - for i in range (n_outcomes): - for pos in range (len(qubit_list)): - if((1 << pos) & i): + for i in range(n_outcomes): + for pos in range(len(qubit_list)): + if (1 << pos) & i: outcome[pos] = 1 else: outcome[pos] = 0 - str1 = "" - probabilities[''.join([str(bit) for bit in outcome])] = sim.get_probability(outcome, qubit_list) - fig, axes = plt.subplots(figsize = (min(21.2, 2 + 0.6 * (1 << len(qubit_list))), 7)) + probabilities[''.join([str(bit) for bit in outcome + ])] = sim.get_probability(outcome, qubit_list) + + # Empirical figure size for up to 5 qubits + fig, axes = plt.subplots(figsize=(min(21.2, 2 + + 0.6 * (1 << len(qubit_list))), 7)) names = list(probabilities.keys()) values = list(probabilities.values()) axes.bar(names, values) fig.suptitle('Measurement Probabilities') - return(fig, axes, probabilities) + return (fig, axes, probabilities) From 00468a776704722e8a88c3589a025a1745be9764 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Tue, 17 Mar 2020 08:24:13 +0000 Subject: [PATCH 10/19] Minor modifications to _histogram_test.py --- projectq/libs/hist/_histogram_test.py | 70 +++++++++++++-------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/projectq/libs/hist/_histogram_test.py b/projectq/libs/hist/_histogram_test.py index 703d488a6..8038009b1 100644 --- a/projectq/libs/hist/_histogram_test.py +++ b/projectq/libs/hist/_histogram_test.py @@ -12,91 +12,91 @@ # See the License for the specific language governing permissions and # limitations under the License. +import pytest +import matplotlib +import matplotlib.pyplot as plt + from projectq import MainEngine from projectq.ops import H, C, X, Measure, All from projectq.backends import Simulator from projectq.libs.hist import histogram -import pytest -import matplotlib -import matplotlib.pyplot as plt + @pytest.fixture(scope="module") def matplotlib_setup(): old_backend = matplotlib.get_backend() - matplotlib.use('agg') # avoid showing the histogram plots + matplotlib.use('agg') # avoid showing the histogram plots yield matplotlib.use(old_backend) + def test_qubit(matplotlib_setup): sim = Simulator() eng = MainEngine(sim) - qb = eng.allocate_qubit() + qubit = eng.allocate_qubit() eng.flush() - fig, axes, prob = histogram(sim, qb) + _, _, prob = histogram(sim, qubit) assert prob["0"] == pytest.approx(1) assert prob["1"] == pytest.approx(0) - H | qb + H | qubit eng.flush() - fig, axes, prob = histogram(sim, qb) + _, _, prob = histogram(sim, qubit) assert prob["0"] == pytest.approx(0.5) - Measure | qb + Measure | qubit eng.flush() - fig, axes, prob = histogram(sim, qb) + _, _, prob = histogram(sim, qubit) assert prob["0"] == pytest.approx(1) or prob["1"] == pytest.approx(1) - plt.show() def test_qureg(matplotlib_setup): sim = Simulator() eng = MainEngine(sim) - qr = eng.allocate_qureg(3) + qureg = eng.allocate_qureg(3) eng.flush() - fig, axes, prob = histogram(sim, qr) + _, _, prob = histogram(sim, qureg) assert prob["000"] == pytest.approx(1) - assert prob ["110"] == pytest.approx(0) - H | qr[0] - C(X, 1) | (qr[0], qr[1]) - H | qr[2] + assert prob["110"] == pytest.approx(0) + H | qureg[0] + C(X, 1) | (qureg[0], qureg[1]) + H | qureg[2] eng.flush() - fig, axes, prob = histogram(sim, qr) + _, _, prob = histogram(sim, qureg) assert prob["110"] == pytest.approx(0.25) assert prob["100"] == pytest.approx(0) - All(Measure) | qr + All(Measure) | qureg eng.flush() - fig, axes, prob = histogram(sim, qr) + _, _, prob = histogram(sim, qureg) assert prob["000"] == pytest.approx(1) or prob["001"] == pytest.approx(1) \ or prob["110"] == pytest.approx(1) or prob["111"] == pytest.approx(1) - assert prob["000"] + prob["001"] + prob["110"] + prob["111"] == pytest.approx(1) - plt.show() + assert prob["000"] + prob["001"] + prob["110"] + prob[ + "111"] == pytest.approx(1) def test_combination(matplotlib_setup): sim = Simulator() eng = MainEngine(sim) - qr = eng.allocate_qureg(2) - qb = eng.allocate_qubit() + qureg = eng.allocate_qureg(2) + qubit = eng.allocate_qubit() eng.flush() - fig, axes, prob = histogram(sim, [qr, qb]) + _, _, prob = histogram(sim, [qureg, qubit]) assert prob["000"] == pytest.approx(1) - H | qr[0] - C(X, 1)| (qr[0], qr[1]) - H | qb - Measure | qr[0] + H | qureg[0] + C(X, 1) | (qureg[0], qureg[1]) + H | qubit + Measure | qureg[0] eng.flush() - fig, axes, prob = histogram(sim, [qr, qb]) + _, _, prob = histogram(sim, [qureg, qubit]) assert (prob["000"] == pytest.approx(0.5) and prob["001"] == pytest.approx(0.5)) \ or (prob["110"] == pytest.approx(0.5) and prob["111"] == pytest.approx(0.5)) assert prob["100"] == pytest.approx(0) - Measure | qb - plt.show() + Measure | qubit def test_too_many_qubits(): sim = Simulator() eng = MainEngine(sim) - qr = eng.allocate_qureg(6) + qureg = eng.allocate_qureg(6) eng.flush() - fig, axes, prob = histogram(sim, qr) + _, _, prob = histogram(sim, qureg) assert prob["000000"] == pytest.approx(1) All(Measure) - plt.show() From 2503c2a7d56c9d09dba3398f6366b3f6f4accc63 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Tue, 17 Mar 2020 08:51:51 +0000 Subject: [PATCH 11/19] Check that some message was printed in case of too many qubits --- projectq/libs/hist/_histogram_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/projectq/libs/hist/_histogram_test.py b/projectq/libs/hist/_histogram_test.py index 8038009b1..f4a0ab4ef 100644 --- a/projectq/libs/hist/_histogram_test.py +++ b/projectq/libs/hist/_histogram_test.py @@ -92,11 +92,13 @@ def test_combination(matplotlib_setup): Measure | qubit -def test_too_many_qubits(): +def test_too_many_qubits(matplotlib_setup, capsys): sim = Simulator() eng = MainEngine(sim) qureg = eng.allocate_qureg(6) eng.flush() + l_ref = len(capsys.readouterr().out) _, _, prob = histogram(sim, qureg) + assert len(capsys.readouterr().out) > l_ref assert prob["000000"] == pytest.approx(1) All(Measure) From f95a54f9a93354b60153fb8655d6df6e0f9c9a64 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Wed, 25 Mar 2020 16:25:48 +0100 Subject: [PATCH 12/19] Update __init__.py Trigger Travis-CI --- projectq/libs/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/projectq/libs/__init__.py b/projectq/libs/__init__.py index a153103d4..ee1451dcd 100755 --- a/projectq/libs/__init__.py +++ b/projectq/libs/__init__.py @@ -11,4 +11,3 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - From edf4bdd447157a3951e35524bae77b1d7dfbc04f Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Wed, 25 Mar 2020 17:06:51 +0100 Subject: [PATCH 13/19] Add print_function for Python 2.7 --- projectq/libs/hist/_histogram.py | 1 + 1 file changed, 1 insertion(+) diff --git a/projectq/libs/hist/_histogram.py b/projectq/libs/hist/_histogram.py index 56b6cc2f0..570379ad7 100644 --- a/projectq/libs/hist/_histogram.py +++ b/projectq/libs/hist/_histogram.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import print_function import matplotlib.pyplot as plt From b46705e60ec1e958ccaabbf61fff36d59504ca03 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Wed, 25 Mar 2020 17:11:54 +0100 Subject: [PATCH 14/19] Remove unsupported 'flush' keyword for Python 2.7 --- projectq/libs/hist/_histogram.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projectq/libs/hist/_histogram.py b/projectq/libs/hist/_histogram.py index 570379ad7..15ac62454 100644 --- a/projectq/libs/hist/_histogram.py +++ b/projectq/libs/hist/_histogram.py @@ -46,8 +46,7 @@ def histogram(sim, qureg): print('Warning: For {0} qubits there are 2^{0} different outcomes'. format(len(qubit_list))) print("The resulting histogram may look bad and/or take too long.") - print("Consider calling histogram() with a sublist of the qubits.", - flush=True) + print("Consider calling histogram() with a sublist of the qubits.") outcome = [0] * len(qubit_list) n_outcomes = (1 << len(qubit_list)) From ad566703e80a18f75b26e9c8fb8c3b038615fb96 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Mon, 20 Apr 2020 13:11:06 +0200 Subject: [PATCH 15/19] Update bellpair example with histogram plot of probabilities --- examples/bellpair_circuit.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/bellpair_circuit.py b/examples/bellpair_circuit.py index 108e224fa..96c001ffe 100755 --- a/examples/bellpair_circuit.py +++ b/examples/bellpair_circuit.py @@ -1,13 +1,20 @@ +import matplotlib.pyplot as plt + from projectq import MainEngine from projectq.backends import CircuitDrawer +from projectq.setups.default import get_engine_list +from projectq.libs.hist import histogram from teleport import create_bell_pair # create a main compiler engine drawing_engine = CircuitDrawer() -eng = MainEngine(drawing_engine) +eng = MainEngine(engine_list = get_engine_list() + [drawing_engine]) -create_bell_pair(eng) +qb0, qb1 = create_bell_pair(eng) eng.flush() print(drawing_engine.get_latex()) + +histogram(eng.backend, [qb0, qb1]) +plt.show() From fcbc9bb7edeb744f97167c931a6c08876ef4f265 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Mon, 20 Apr 2020 13:43:09 +0200 Subject: [PATCH 16/19] Add support for other backends --- projectq/libs/hist/_histogram.py | 34 ++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/projectq/libs/hist/_histogram.py b/projectq/libs/hist/_histogram.py index 15ac62454..0ad980892 100644 --- a/projectq/libs/hist/_histogram.py +++ b/projectq/libs/hist/_histogram.py @@ -15,13 +15,15 @@ from __future__ import print_function import matplotlib.pyplot as plt +from projectq.backends import Simulator -def histogram(sim, qureg): + +def histogram(backend, qureg): """ Make a measurement outcome probability histogram for the given qubits. Args: - sim (Simulator): The simulator to call get_probability + backend (BasicEngine): A ProjectQ backend qureg (list of qubits and/or quregs): The qubits, for which to make the histogram @@ -48,17 +50,23 @@ def histogram(sim, qureg): print("The resulting histogram may look bad and/or take too long.") print("Consider calling histogram() with a sublist of the qubits.") - outcome = [0] * len(qubit_list) - n_outcomes = (1 << len(qubit_list)) - probabilities = {} - for i in range(n_outcomes): - for pos in range(len(qubit_list)): - if (1 << pos) & i: - outcome[pos] = 1 - else: - outcome[pos] = 0 - probabilities[''.join([str(bit) for bit in outcome - ])] = sim.get_probability(outcome, qubit_list) + if hasattr(backend, 'get_probabilities'): + probabilities = backend.get_probabilities(qureg) + elif isinstance(backend, Simulator): + outcome = [0] * len(qubit_list) + n_outcomes = (1 << len(qubit_list)) + probabilities = {} + for i in range(n_outcomes): + for pos in range(len(qubit_list)): + if (1 << pos) & i: + outcome[pos] = 1 + else: + outcome[pos] = 0 + probabilities[''.join([str(bit) for bit in outcome + ])] = backend.get_probability( + outcome, qubit_list) + else: + raise RuntimeError('Unable to retrieve probabilities from backend') # Empirical figure size for up to 5 qubits fig, axes = plt.subplots(figsize=(min(21.2, 2 From e694f3affdbea032c6e26781b5b349692291bf98 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Mon, 20 Apr 2020 13:44:35 +0200 Subject: [PATCH 17/19] Update IBM examples with histogram plots --- examples/ibm.py | 28 ++++++----- examples/ibmq_tutorial.ipynb | 90 ++++++++++++++++++++++++++++-------- 2 files changed, 89 insertions(+), 29 deletions(-) diff --git a/examples/ibm.py b/examples/ibm.py index 11a81a832..eafd50b80 100755 --- a/examples/ibm.py +++ b/examples/ibm.py @@ -1,8 +1,11 @@ -import projectq.setups.ibm +import matplotlib.pyplot as plt +import getpass + +from projectq import MainEngine from projectq.backends import IBMBackend +from projectq.libs.hist import histogram from projectq.ops import Measure, Entangle, All -from projectq import MainEngine -import getpass +import projectq.setups.ibm def run_entangle(eng, num_qubits=3): @@ -29,9 +32,12 @@ def run_entangle(eng, num_qubits=3): eng.flush() # 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])) + # results = eng.backend.get_probabilities(qureg) + # for state in results: + # print("Measured {} with p = {}.".format(state, results[state])) + # or plot them directly: + histogram(eng.backend, qureg) + plt.show() # return one (random) measurement outcome. return [int(q) for q in qureg] @@ -39,11 +45,11 @@ def run_entangle(eng, num_qubits=3): if __name__ == "__main__": #devices commonly available : - #ibmq_16_melbourne (15 qubit) - #ibmq_essex (5 qubit) - #ibmq_qasm_simulator (32 qubits) - device = None #replace by the IBM device name you want to use - token = None #replace by the token given by IBMQ + # ibmq_16_melbourne (15 qubit) + # ibmq_essex (5 qubit) + # ibmq_qasm_simulator (32 qubits) + device = None # replace by the IBM device name you want to use + token = None # replace by the token given by IBMQ if token is None: token = getpass.getpass(prompt='IBM Q token > ') if device is None: diff --git a/examples/ibmq_tutorial.ipynb b/examples/ibmq_tutorial.ipynb index 464612ca5..358bdcf9e 100644 --- a/examples/ibmq_tutorial.ipynb +++ b/examples/ibmq_tutorial.ipynb @@ -2,14 +2,18 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "collapsed": false + }, "source": [ "# Running ProjectQ code on IBM Q devices" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "collapsed": false + }, "source": [ "In this tutorial, we will see how to run code on IBM Q devices directly from within ProjectQ. All that is needed is an IBM Q Experience user account. To sign up, visit https://quantumexperience.ng.bluemix.net/.\n", "\n", @@ -19,10 +23,24 @@ "First, we import all necessary operations (`Entangle`, measurement), the back-end (`IBMBackend`), and the main compiler engine (`MainEngine`). The Entangle operation is defined as a Hadamard gate on the first qubit (creates an equal superposition of |0> and |1>), followed by controlled NOT gates acting on all other qubits controlled on the first." ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import matplotlib.pyplot as plt" + ] + }, { "cell_type": "code", "execution_count": 1, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "import projectq.setups.ibm\n", @@ -33,7 +51,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "collapsed": false + }, "source": [ "Next, we instantiate a main compiler engine using the IBM Q back-end and the predefined compiler engines which take care of the qubit placement, translation of operations, etc.:" ] @@ -41,7 +61,9 @@ { "cell_type": "code", "execution_count": 2, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "eng = MainEngine(IBMBackend(use_hardware=True, num_runs=1024,\n", @@ -51,7 +73,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "collapsed": false + }, "source": [ "If `use_hardware` is set to `False`, it will use the IBM Q simulator instead. `num_runs` specifies the number of samples to collect for statistics, `verbose=True` would output additional information which may be helpful for debugging, and the device parameter lets users choose between the two devices (\"ibmqx4\" and \"ibmqx5\").\n", "\n", @@ -61,7 +85,9 @@ { "cell_type": "code", "execution_count": 3, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "name": "stdout", @@ -105,9 +131,12 @@ " eng.flush()\n", "\n", " # access the probabilities via the back-end:\n", - " results = eng.backend.get_probabilities(qureg)\n", - " for state in results:\n", - " print(\"Measured {} with p = {}.\".format(state, results[state]))\n", + " # results = eng.backend.get_probabilities(qureg)\n", + " # for state in results:\n", + " # print(\"Measured {} with p = {}.\".format(state, results[state]))\n", + " # or plot them directly:\n", + " histogram(eng.backend, qureg)\n", + " plt.show()\n", "\n", " # return one (random) measurement outcome.\n", " return [int(q) for q in qureg]\n", @@ -117,7 +146,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "collapsed": false + }, "source": [ "## Retrieving a timed-out execution\n", "Sometimes, the queue is very long and the waiting times may exceed the limit of 5 minutes. In this case, ProjectQ will raise an exception which contains the job ID, as could be seen above, where the job ID was `5b557df2306393003b746da2`." @@ -125,7 +156,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "collapsed": false + }, "source": [ "In order to still retrieve all results at a later point in time, one can simply re-run the entire program using a slightly modified back-end:" ] @@ -133,7 +166,9 @@ { "cell_type": "code", "execution_count": 4, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "name": "stdout", @@ -193,7 +228,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "collapsed": false + }, "source": [ "## Entangling more qubits: Using ibmqx5\n", "\n", @@ -209,7 +246,9 @@ { "cell_type": "code", "execution_count": 5, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [], "source": [ "import projectq.setups.ibm16 # import setup which contains the grid mapper\n", @@ -220,7 +259,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "collapsed": false + }, "source": [ "and then re-run the example from before via `run_entangle(eng, num_qubits)`. If an execution times out, it can also be retrieved at a later point by providing the additional `retrieve_execution=\"execution_id\"` parameter to the IBMBackend (but this time with `device='ibmqx5'`)." ] @@ -228,7 +269,9 @@ { "cell_type": "code", "execution_count": 6, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "name": "stdout", @@ -488,8 +531,18 @@ ], "metadata": { "kernelspec": { + "argv": [ + "python", + "-m", + "ipykernel_launcher", + "-f", + "{connection_file}" + ], "display_name": "Python 3", + "env": null, + "interrupt_mode": "signal", "language": "python", + "metadata": null, "name": "python3" }, "language_info": { @@ -503,7 +556,8 @@ "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.5" - } + }, + "name": "ibmq_tutorial.ipynb" }, "nbformat": 4, "nbformat_minor": 2 From a7497adc150812fe5db20026f74efc3fb7ae8847 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Mon, 20 Apr 2020 13:44:46 +0200 Subject: [PATCH 18/19] Fix error in conf.py --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 169414e6c..4083653fa 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,3 +1,4 @@ + #!/usr/bin/env python3 # -*- coding: utf-8 -*- # @@ -28,7 +29,6 @@ import projectq.setups.default import projectq.setups.grid import projectq.setups.ibm -import projectq.setups.ibm16 import projectq.setups.linear import projectq.setups.restrictedgateset import projectq.setups.decompositions From c787e1ade4b2a6e0861ee867aba78abc231235c5 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Mon, 20 Apr 2020 13:58:49 +0200 Subject: [PATCH 19/19] Increase test coverage --- projectq/libs/hist/_histogram.py | 2 +- projectq/libs/hist/_histogram_test.py | 33 ++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/projectq/libs/hist/_histogram.py b/projectq/libs/hist/_histogram.py index 0ad980892..77b0d2c20 100644 --- a/projectq/libs/hist/_histogram.py +++ b/projectq/libs/hist/_histogram.py @@ -32,7 +32,7 @@ def histogram(backend, qureg): fig: The histogram as figure axes: The axes of the histogram probabilities (dict): A dictionary mapping outcomes as string - to their probabilities + to their probabilities Note: Don't forget to call eng.flush() before using this function. diff --git a/projectq/libs/hist/_histogram_test.py b/projectq/libs/hist/_histogram_test.py index f4a0ab4ef..1f38573b0 100644 --- a/projectq/libs/hist/_histogram_test.py +++ b/projectq/libs/hist/_histogram_test.py @@ -17,7 +17,8 @@ import matplotlib.pyplot as plt from projectq import MainEngine -from projectq.ops import H, C, X, Measure, All +from projectq.ops import H, C, X, Measure, All, AllocateQubitGate, FlushGate +from projectq.cengines import DummyEngine, BasicEngine from projectq.backends import Simulator from projectq.libs.hist import histogram @@ -30,6 +31,36 @@ def matplotlib_setup(): matplotlib.use(old_backend) +def test_invalid_backend(matplotlib_setup): + eng = MainEngine(backend=DummyEngine()) + qubit = eng.allocate_qubit() + eng.flush() + + with pytest.raises(RuntimeError): + histogram(eng.backend, qubit) + + +def test_backend_get_probabilities_method(matplotlib_setup): + class MyBackend(BasicEngine): + def get_probabilities(self, qureg): + return {'000': 0.5, '111': 0.5} + + def is_available(self, cmd): + return True + + def receive(self, command_list): + for cmd in command_list: + if not isinstance(cmd.gate, FlushGate): + assert isinstance(cmd.gate, AllocateQubitGate) + + eng = MainEngine(backend=MyBackend(), verbose=True) + qureg = eng.allocate_qureg(3) + eng.flush() + _, _, prob = histogram(eng.backend, qureg) + assert prob['000'] == 0.5 + assert prob['111'] == 0.5 + + def test_qubit(matplotlib_setup): sim = Simulator() eng = MainEngine(sim)