diff --git a/catalyst b/catalyst index 0b8213d..7c5b828 160000 --- a/catalyst +++ b/catalyst @@ -1 +1 @@ -Subproject commit 0b8213d58c8bfbd53dc57842b360fcb5767f3dfa +Subproject commit 7c5b828d5173cdaa52073d30a5f3a7df660b37d6 diff --git a/doc/conf.py b/doc/conf.py index 994ecd8..71c6dc5 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -20,7 +20,7 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath("..")) sys.path.insert(0, os.path.abspath("_ext")) -sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath('.')), 'doc')) +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(".")), "doc")) # -- General configuration ------------------------------------------------ @@ -39,8 +39,8 @@ "sphinx.ext.napoleon", "sphinx.ext.inheritance_diagram", "sphinx.ext.intersphinx", - 'sphinx.ext.viewcode', - "sphinx_automodapi.automodapi" + "sphinx.ext.viewcode", + "sphinx_automodapi.automodapi", ] autosummary_generate = True @@ -50,6 +50,7 @@ # Add any paths that contain templates here, relative to this directory. from pennylane_sphinx_theme import templates_dir + templates_path = [templates_dir()] # The suffix(es) of source filenames. @@ -111,7 +112,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -#html_theme = "sphinx_rtd_theme" +# html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -135,12 +136,12 @@ # The name of an image file (relative to this directory) to use as a favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -html_favicon = '_static/favicon.ico' +html_favicon = "_static/favicon.ico" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -220,13 +221,13 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('..')) -sys.path.insert(0, os.path.abspath('_ext')) -sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath('.')), 'doc')) +sys.path.insert(0, os.path.abspath("..")) +sys.path.insert(0, os.path.abspath("_ext")) +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(".")), "doc")) class Mock(MagicMock): - __name__ = 'foo' + __name__ = "foo" @classmethod def __getattr__(cls, name): @@ -241,7 +242,7 @@ def __getattr__(cls, name): htmlhelp_basename = "PennyLane-Qrackdoc" # # -- Xanadu theme --------------------------------------------------------- -html_theme = 'pennylane' +html_theme = "pennylane" # Register the theme as an extension to generate a sitemap.xml # extensions.append("guzzle_sphinx_theme") @@ -250,16 +251,15 @@ def __getattr__(cls, name): html_theme_options = { "navbar_name": "PennyLane-Qrack", "extra_copyrights": [ - "TensorFlow, the TensorFlow logo, and any related marks are trademarks " - "of Google Inc." + "TensorFlow, the TensorFlow logo, and any related marks are trademarks " "of Google Inc." ], "toc_overview": True, "navbar_active_link": 3, - "google_analytics_tracking_id": "G-C480Z9JL0D" + "google_analytics_tracking_id": "G-C480Z9JL0D", } -edit_on_github_project = 'PennyLaneAI/pennylane-qrack' -edit_on_github_branch = 'master/doc' +edit_on_github_project = "PennyLaneAI/pennylane-qrack" +edit_on_github_branch = "master/doc" # -- Options for LaTeX output --------------------------------------------- @@ -316,10 +316,10 @@ def __getattr__(cls, name): # ============================================================ # the order in which autodoc lists the documented members -autodoc_member_order = 'bysource' +autodoc_member_order = "bysource" # inheritance_diagram graphviz attributes -inheritance_node_attrs = dict(color='lightskyblue1', style='filled') +inheritance_node_attrs = dict(color="lightskyblue1", style="filled") -#autodoc_default_flags = ['members'] +# autodoc_default_flags = ['members'] autosummary_generate = True diff --git a/pennylane_qrack/qrack_device.py b/pennylane_qrack/qrack_device.py index 59fbc55..9406a08 100644 --- a/pennylane_qrack/qrack_device.py +++ b/pennylane_qrack/qrack_device.py @@ -189,6 +189,8 @@ def __init__(self, wires=0, shots=None, **kwargs): self.isHostPointer = options["isHostPointer"] if "noise" in options: self.noise = options["noise"] + if self.noise != 0 and shots is None: + raise ValueError("Shots must be finite for noisy simulation (not analytical mode).") super().__init__(wires=wires, shots=shots) self._state = QrackSimulator( self.num_wires, @@ -200,6 +202,7 @@ def __init__(self, wires=0, shots=None, **kwargs): isHostPointer=self.isHostPointer, noise=self.noise, ) + self._circuit = [] def _reverse_state(self): end = self.num_wires - 1 @@ -216,7 +219,14 @@ def apply(self, operations, **kwargs): operations (List[pennylane.Operation]): operations to be applied """ - for i, op in enumerate(operations): + self._circuit = self._circuit + operations + if self.noise == 0: + self._apply() + self._circuit = [] + # else: Defer application until shots or expectation values are requested + + def _apply(self): + for op in self._circuit: if isinstance(op, QubitStateVector): self._apply_qubit_state_vector(op) elif isinstance(op, BasisState): @@ -647,6 +657,14 @@ def expval(self, observable, **kwargs): # estimate the ev return np.mean(self.sample(observable)) + def _generate_sample(self): + rev_sample = self._state.m_all() + sample = 0 + for i in range(self.num_wires): + if (rev_sample & (1 << i)) > 0: + sample |= 1 << (self.num_wires - (i + 1)) + return sample + def generate_samples(self): if self.shots is None: raise QuantumFunctionError( @@ -654,13 +672,23 @@ def generate_samples(self): "when using sample-based measurements." ) + if self.noise != 0: + self._samples = [] + for _ in range(self.shots): + self._state.reset_all() + self._apply() + self._samples.append(self._generate_sample()) + self._samples = QubitDevice.states_to_binary( + np.array([self._generate_sample()]), self.num_wires + ) + self._circuit = [] + + return self._samples + if self.shots == 1: - rev_sample = self._state.m_all() - sample = 0 - for i in range(self.num_wires): - if (rev_sample & (1 << i)) > 0: - sample |= 1 << (self.num_wires - (i + 1)) - self._samples = QubitDevice.states_to_binary(np.array([sample]), self.num_wires) + self._samples = QubitDevice.states_to_binary( + np.array([self._generate_sample()]), self.num_wires + ) return self._samples diff --git a/setup.py b/setup.py index 1424f1a..f84c48a 100644 --- a/setup.py +++ b/setup.py @@ -19,10 +19,11 @@ from setuptools import setup from setuptools.command.build_py import build_py + class Build(build_py): def run(self): protoc_command = ["make", "build-deps"] - if os.name != 'nt': + if os.name != "nt": if subprocess.call(protoc_command) != 0: sys.exit(-1) super().run() @@ -31,11 +32,7 @@ def run(self): with open("./pennylane_qrack/_version.py") as f: (version,) = re.findall('__version__ = "(.*)"', f.read()) -requirements = [ - "pennylane>=0.32", - "pyqrack>=1.30.0", - "numpy>=1.16" -] +requirements = ["pennylane>=0.32", "pyqrack>=1.30.0", "numpy>=1.16"] info = { "name": "pennylane-qrack", @@ -45,11 +42,9 @@ def run(self): "url": "http://github.com/vm6502q", "license": "Apache License 2.0", "packages": ["pennylane_qrack"], - "cmdclass": { 'build_py': Build }, + "cmdclass": {"build_py": Build}, "entry_points": { - "pennylane.plugins": [ - "qrack.simulator = pennylane_qrack.qrack_device:QrackDevice" - ] + "pennylane.plugins": ["qrack.simulator = pennylane_qrack.qrack_device:QrackDevice"] }, "description": "PennyLane plugin for Qrack.", "long_description": open("README.rst").read(), diff --git a/tests/conftest.py b/tests/conftest.py index ca7960a..864278e 100755 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -36,9 +36,7 @@ U2 = np.array([[0, 1, 1, 1], [1, 0, 1, -1], [1, -1, 0, 1], [1, 1, -1, 0]]) / np.sqrt(3) # single qubit Hermitian observable -A = np.array( - [[1.02789352, 1.61296440 - 0.3498192j], [1.61296440 + 0.3498192j, 1.23920938 + 0j]] -) +A = np.array([[1.02789352, 1.61296440 - 0.3498192j], [1.61296440 + 0.3498192j, 1.23920938 + 0j]]) # ========================================================== diff --git a/tests/test_apply.py b/tests/test_apply.py index 37bfc5a..ed61a02 100755 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -260,9 +260,7 @@ def test_qubit_state_vector(self, init_state, tol): @pytest.mark.parametrize("device_wires", [3, 4, 5]) @pytest.mark.parametrize("op_wires", [[0], [2], [0, 1], [1, 0], [2, 0]]) - def test_qubit_state_vector_on_wires_subset( - self, init_state, device_wires, op_wires, tol - ): + def test_qubit_state_vector_on_wires_subset(self, init_state, device_wires, op_wires, tol): """Test QubitStateVector application on a subset of device wires""" dev = QrackDevice(device_wires, isOpenCL=False) state = init_state(len(op_wires)) @@ -314,7 +312,7 @@ def circuit(): qml.StatePrep(state, wires=[0]) qml.apply(op) return qml.probs() - + dev._obs_queue = [] res = circuit() @@ -352,7 +350,7 @@ def circuit(): qml.StatePrep(state, wires=[0]) qml.apply(op) return qml.probs() - + dev._obs_queue = [] res = circuit() @@ -364,9 +362,7 @@ def circuit(): @pytest.mark.parametrize("theta", [0.5432, -0.232]) @pytest.mark.parametrize("omega", [1.213, -0.221]) @pytest.mark.parametrize("op,func", single_qubit_three_param) - def test_single_qubit_three_parameters( - self, init_state, op, func, phi, theta, omega, tol - ): + def test_single_qubit_three_parameters(self, init_state, op, func, phi, theta, omega, tol): """Test PauliX application""" dev = QrackDevice(1, isOpenCL=False) state = init_state(1) @@ -383,14 +379,13 @@ def test_single_qubit_three_parameters( @pytest.mark.parametrize("theta", [0.5432, -0.232]) @pytest.mark.parametrize("omega", [1.213, -0.221]) @pytest.mark.parametrize("op,func", single_qubit_three_param) - def test_single_qubit_three_parameters_qjit( - self, init_state, op, func, phi, theta, omega, tol - ): + def test_single_qubit_three_parameters_qjit(self, init_state, op, func, phi, theta, omega, tol): """Test PauliX application""" dev = QrackDevice(1, isOpenCL=False) state = init_state(1) op.data = [phi, theta, omega] + @qjit @qml.qnode(dev) def circuit(): @@ -430,6 +425,7 @@ def circuit(): qml.StatePrep(state, wires=[0, 1]) qml.apply(op) return qml.probs() + dev._obs_queue = [] res = circuit() @@ -462,12 +458,14 @@ def test_qubit_unitary_qjit(self, init_state, mat, tol): state = init_state(N) op = qml.QubitUnitary(mat, wires=list(range(N))) + @qjit @qml.qnode(dev) def circuit(): qml.StatePrep(state, wires=list(range(N))) qml.apply(op) return qml.probs() + dev._obs_queue = [] res = circuit() @@ -506,6 +504,7 @@ def circuit(): qml.StatePrep(state, wires=[0, 1, 2]) qml.apply(op) return qml.probs() + dev._obs_queue = [] res = circuit() @@ -549,6 +548,7 @@ def test_two_qubit_parameters_qjit(self, init_state, op, func, theta, tol): state = init_state(2) op.data = [theta] + @qjit @qml.qnode(dev) def circuit(): @@ -567,9 +567,7 @@ def circuit(): @pytest.mark.parametrize("theta", [0.5432, -0.232]) @pytest.mark.parametrize("omega", [1.213, -0.221]) @pytest.mark.parametrize("op,func", two_qubit_three_param) - def test_two_qubit_three_parameters( - self, init_state, op, func, phi, theta, omega, tol - ): + def test_two_qubit_three_parameters(self, init_state, op, func, phi, theta, omega, tol): """Test parametrized two qubit gates application""" dev = QrackDevice(2, isOpenCL=False) state = init_state(2) @@ -588,9 +586,7 @@ def test_apply_errors_qubit_state_vector(self): """Test that apply fails for incorrect state preparation.""" dev = QrackDevice(2, isOpenCL=False) - with pytest.raises( - ValueError, match="Sum of amplitudes-squared does not equal one." - ): + with pytest.raises(ValueError, match="Sum of amplitudes-squared does not equal one."): dev.apply([qml.QubitStateVector(np.array([1, -1]), wires=[0])]) with pytest.raises( diff --git a/tests/test_integration.py b/tests/test_integration.py index 0ffadf8..5f9c81c 100755 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -17,6 +17,7 @@ from pennylane_qrack.qrack_device import QrackDevice from catalyst import qjit + class TestIntegration: """Some basic integration tests."""