diff --git a/.github/workflows/boilerplate.yml b/.github/workflows/boilerplate.yml index 13a2f111a..862b33f87 100644 --- a/.github/workflows/boilerplate.yml +++ b/.github/workflows/boilerplate.yml @@ -85,9 +85,26 @@ jobs: # See the License for the specific language governing permissions and # limitations under the License. + boilerplate2024: |- + # Copyright 2024 Agnostiq Inc. + # + # This file is part of Covalent. + # + # Licensed under the Apache License 2.0 (the "License"). A copy of the + # License may be obtained with this software package or at + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Use of this file is prohibited except in compliance with the License. + # 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. + run: | for file in ${{ steps.changed-files.outputs.all_changed_files }}; do - if [[ ! $( cat $file | tr -d '\r' ) =~ "$boilerplate2021" && ! $( cat $file | tr -d '\r' ) =~ "$boilerplate2022" && ! $( cat $file | tr -d '\r' ) =~ "$boilerplate2023" ]] ; then + if [[ ! $( cat $file | tr -d '\r' ) =~ "$boilerplate2021" && ! $( cat $file | tr -d '\r' ) =~ "$boilerplate2022" && ! $( cat $file | tr -d '\r' ) =~ "$boilerplate2023" && ! $( cat $file | tr -d '\r' ) =~ "$boilerplate2024" ]] ; then printf "Boilerplate is missing from $file.\n" printf "The first 15 lines of $file are\n\n" cat $file | tr -d '\r' | cat -ET | head -n 15 diff --git a/.github/workflows/requirements.yml b/.github/workflows/requirements.yml index 30198b6be..4502b183f 100644 --- a/.github/workflows/requirements.yml +++ b/.github/workflows/requirements.yml @@ -50,10 +50,14 @@ jobs: --ignore-module=pkg_resources --ignore-module=covalent/_dispatcher_plugins --ignore-module=covalent/_shared_files + --ignore-file=covalent/quantum/** + --ignore-file=covalent/_workflow/q* + --ignore-file=covalent/_shared_files/q* + --ignore-file=covalent/_results_manager/q* + --ignore-file=covalent/_shared_files/pickling.py --ignore-file=covalent/executor/** --ignore-file=covalent/triggers/** --ignore-file=covalent/cloud_resource_manager/** - --ignore-file=covalent/quantum/qserver/** --ignore-file=covalent/_programmatic/** covalent diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 946e343f5..710655301 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -90,6 +90,11 @@ jobs: sdk: - 'covalent/**' - 'tests/covalent_tests/**' + qelectron: + - 'covalent/executor/quantum_plugins/**' + - 'covalent/executor/qbase.py' + - 'covalent/quantum/**' + - 'tests/qelectron_tests/**' dispatcher: - 'covalent_dispatcher/**' - 'tests/covalent_dispatcher_tests/**' @@ -134,6 +139,7 @@ jobs: echo "NEED_PYTHON=$NEED_PYTHON" >> $GITHUB_ENV echo "NEED_FRONTEND=$NEED_FRONTEND" >> $GITHUB_ENV echo "BUILD_AND_RUN_ALL=$BUILD_AND_RUN_ALL" >> $GITHUB_ENV + echo "COVALENT_DISABLE_QELECTRON_TESTS=true" >> $GITHUB_ENV - name: Set up Python if: > @@ -159,6 +165,7 @@ jobs: run: | pip install --no-cache-dir -r ./requirements.txt pip install --no-cache-dir -r ./tests/requirements.txt + pip install --no-cache-dir -r ./requirements-qelectron.txt - name: Set up Node if: env.NEED_FRONTEND || env.BUILD_AND_RUN_ALL @@ -252,6 +259,18 @@ jobs: if: steps.sdk-tests.outcome == 'success' run: coverage xml -o sdk_coverage.xml + - name: Run Qelectron tests and measure coverage + id: qelectron-tests + if: > + (steps.modified-files.outputs.qelectron == 'true' + || env.BUILD_AND_RUN_ALL) && env.COVALENT_DISABLE_QELECTRON_TESTS != 'true' + run: PYTHONPATH=$PWD/ pytest -vvs --reruns=5 tests/qelectron_tests/core_tests --cov=covalent_qelectron --cov-config=.coveragerc + + - name: Generate Qelectron coverage report + id: qelectron-coverage + if: steps.qelectron-tests.outcome == 'success' && env.COVALENT_DISABLE_QELECTRON_TESTS != 'true' + run: coverage xml -o qelectron_coverage.xml + - name: Run dispatcher tests and measure coverage id: dispatcher-tests if: > diff --git a/.gitignore b/.gitignore index 4e5d125f3..9e53722e0 100644 --- a/.gitignore +++ b/.gitignore @@ -59,6 +59,7 @@ !pyproject.toml !requirements.txt !requirements-client.txt +!requirements-qelectron.txt !setup.py # Allow markdown etc diff --git a/CHANGELOG.md b/CHANGELOG.md index 0df4c8bf1..474352138 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [UNRELEASED] +### Added + +- Added `pennylane` as a requirement in tests due to the tutorials using it + ### Changed - Updated RTD notebooks to fix their behavior - Changed the error being shown when drawing the transport graph of a lattice to a debug message instead - Revamped README +- Reorganized `qelectron` tests +- Made qelectron an opt-in feature using `covalent[quantum]` extra ### Removed @@ -26,8 +32,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed the scenario where any deploy commands would fail if the user had a non deploy compatible plugin installed +- Fixed the SQLAlchemy warning that used to show up at every fresh server start - Fixed deploy commands' default value of plugins not being propagated to the tfvars file +### Operations + +- Added qelectron tests to the `tests` workflow + ## [0.233.0-rc.0] - 2024-01-07 ### Authors diff --git a/MANIFEST.in b/MANIFEST.in index 8d2699c53..71deeab82 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,7 @@ include VERSION include requirements.txt include requirements-client.txt +include requirements-qelectron.txt include covalent/py.typed recursive-include covalent/executor/ * recursive-include covalent_dispatcher/_service/ * diff --git a/covalent/__init__.py b/covalent/__init__.py index 6d88af00d..0cfc7bbe7 100644 --- a/covalent/__init__.py +++ b/covalent/__init__.py @@ -16,6 +16,7 @@ """Main Covalent public functionality.""" +import contextlib from importlib import metadata from . import _file_transfer as fs # nopycln: import @@ -48,9 +49,11 @@ lattice, ) from ._workflow.electron import wait # nopycln: import -from ._workflow.qelectron import qelectron # nopycln: import from .executor.utils import get_context # nopycln: import -from .quantum import QCluster # nopycln: import + +with contextlib.suppress(ImportError): + from ._workflow.qelectron import qelectron # nopycln: import + from .quantum import QCluster # nopycln: import __all__ = [s for s in dir() if not s.startswith("_")] diff --git a/covalent/_shared_files/qelectron_utils.py b/covalent/_shared_files/qelectron_utils.py index 1c0a8c6f3..387722a2a 100644 --- a/covalent/_shared_files/qelectron_utils.py +++ b/covalent/_shared_files/qelectron_utils.py @@ -14,10 +14,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +import importlib +import inspect +from typing import Any, Tuple -from covalent.quantum.qserver.database import Database +import cloudpickle +from pennylane._device import Device from .logger import app_log +from .pickling import _qml_mods_pickle + +_IMPORT_PATH_SEPARATOR = ":" def get_qelectron_db_path(dispatch_id: str, task_id: int): @@ -28,6 +35,8 @@ def get_qelectron_db_path(dispatch_id: str, task_id: int): AS WHERE THE USER'S TASK FUNCTION IS BEING RUN. """ + from covalent.quantum.qserver.database import Database + database = Database() db_path = database.get_db_path(dispatch_id=dispatch_id, node_id=task_id) @@ -38,3 +47,50 @@ def get_qelectron_db_path(dispatch_id: str, task_id: int): else: app_log.debug(f"Qelectron database not found for task {task_id}") return None + + +@_qml_mods_pickle +def cloudpickle_serialize(obj): + return cloudpickle.dumps(obj) + + +def cloudpickle_deserialize(obj): + return cloudpickle.loads(obj) + + +def select_first_executor(qnode, executors): + """Selects the first executor to run the qnode""" + return executors[0] + + +def get_import_path(obj) -> Tuple[str, str]: + """ + Determine the import path of an object. + """ + if module := inspect.getmodule(obj): + module_path = module.__name__ + class_name = obj.__name__ + return f"{module_path}{_IMPORT_PATH_SEPARATOR}{class_name}" + raise RuntimeError(f"Unable to determine import path for {obj}.") + + +def import_from_path(path: str) -> Any: + """ + Import a class from a path. + """ + module_path, class_name = path.split(_IMPORT_PATH_SEPARATOR) + module = importlib.import_module(module_path) + return getattr(module, class_name) + + +def get_original_shots(dev: Device): + """ + Recreate vector of shots if device has a shot vector. + """ + if not dev.shot_vector: + return dev.shots + + shot_sequence = [] + for shots in dev.shot_vector: + shot_sequence.extend([shots.shots] * shots.copies) + return type(dev.shot_vector)(shot_sequence) diff --git a/covalent/_shared_files/qresult_utils.py b/covalent/_shared_files/qresult_utils.py index 9ffe3c182..efad64797 100644 --- a/covalent/_shared_files/qresult_utils.py +++ b/covalent/_shared_files/qresult_utils.py @@ -23,7 +23,7 @@ from pennylane.tape import QuantumTape from .._workflow.qdevice import QEDevice -from .utils import get_original_shots +from .qelectron_utils import get_original_shots def re_execute( diff --git a/covalent/_shared_files/utils.py b/covalent/_shared_files/utils.py index e7bd60368..f41899f24 100644 --- a/covalent/_shared_files/utils.py +++ b/covalent/_shared_files/utils.py @@ -16,19 +16,14 @@ """General utils for Covalent.""" -import importlib import inspect import shutil import socket from datetime import timedelta -from typing import Any, Callable, Dict, List, Tuple - -import cloudpickle -from pennylane._device import Device +from typing import Callable, Dict, List, Tuple from . import logger from .config import get_config -from .pickling import _qml_mods_pickle app_log = logger.app_log log_stack_info = logger.log_stack_info @@ -37,9 +32,6 @@ DEFAULT_UI_PORT = get_config("user_interface.port") -_IMPORT_PATH_SEPARATOR = ":" - - def get_ui_url(path): baseUrl = f"http://{DEFAULT_UI_ADDRESS}:{DEFAULT_UI_PORT}" return f"{baseUrl}{path}" @@ -264,49 +256,16 @@ def copy_file_locally(src_uri, dest_uri): shutil.copyfile(src_path, dest_path) -@_qml_mods_pickle -def cloudpickle_serialize(obj): - return cloudpickle.dumps(obj) - - -def cloudpickle_deserialize(obj): - return cloudpickle.loads(obj) - - -def select_first_executor(qnode, executors): - """Selects the first executor to run the qnode""" - return executors[0] - - -def get_import_path(obj) -> Tuple[str, str]: - """ - Determine the import path of an object. +def get_qelectron_db_path(dispatch_id: str, task_id: int): """ - module = inspect.getmodule(obj) - if module: - module_path = module.__name__ - class_name = obj.__name__ - return f"{module_path}{_IMPORT_PATH_SEPARATOR}{class_name}" - raise RuntimeError(f"Unable to determine import path for {obj}.") + Return the path to the Qelectron database for a given dispatch_id and task_id. - -def import_from_path(path: str) -> Any: - """ - Import a class from a path. + This is a proxy to qelectron_utils.get_qelectron_db_path() for removing qelectron dependency. """ - module_path, class_name = path.split(_IMPORT_PATH_SEPARATOR) - module = importlib.import_module(module_path) - return getattr(module, class_name) + try: + from .qelectron_utils import get_qelectron_db_path -def get_original_shots(dev: Device): - """ - Recreate vector of shots if device has a shot vector. - """ - if not dev.shot_vector: - return dev.shots - - shot_sequence = [] - for shots in dev.shot_vector: - shot_sequence.extend([shots.shots] * shots.copies) - return type(dev.shot_vector)(shot_sequence) + return get_qelectron_db_path(dispatch_id, task_id) + except ImportError: + return None diff --git a/covalent/_workflow/qelectron.py b/covalent/_workflow/qelectron.py index e0356723a..641e4a78e 100644 --- a/covalent/_workflow/qelectron.py +++ b/covalent/_workflow/qelectron.py @@ -19,7 +19,7 @@ import pennylane as qml -from .._shared_files.utils import get_import_path, get_original_shots +from .._shared_files.qelectron_utils import get_import_path, get_original_shots from ..quantum.qcluster import QCluster from ..quantum.qcluster.base import AsyncBaseQCluster, BaseQExecutor from ..quantum.qcluster.simulator import Simulator diff --git a/covalent/_workflow/qnode.py b/covalent/_workflow/qnode.py index 7e97fee34..f2019aaa0 100644 --- a/covalent/_workflow/qnode.py +++ b/covalent/_workflow/qnode.py @@ -25,9 +25,9 @@ from .._results_manager.qresult import QNodeFutureResult from .._shared_files import logger +from .._shared_files.qelectron_utils import get_original_shots from .._shared_files.qinfo import QElectronInfo, QNodeSpecs from .._shared_files.qresult_utils import re_execute -from .._shared_files.utils import get_original_shots from ..executor.qbase import BaseQExecutor from .qdevice import QEDevice diff --git a/covalent/executor/__init__.py b/covalent/executor/__init__.py index ffc139f96..be68b1182 100644 --- a/covalent/executor/__init__.py +++ b/covalent/executor/__init__.py @@ -31,7 +31,6 @@ from .._shared_files import logger from .._shared_files.config import get_config, update_config -from ..quantum import QCluster, Simulator from .base import BaseExecutor app_log = logger.app_log @@ -284,6 +283,8 @@ class _QExecutorManager: """ def __init__(self): + from ..quantum import QCluster, Simulator + # Dictionary mapping executor name to executor class self.executor_plugins_map: Dict[str, Any] = { "QCluster": QCluster, @@ -370,11 +371,12 @@ def validate_module(self, module_obj) -> None: _executor_manager = _ExecutorManager() -_qexecutor_manager = _QExecutorManager() - for name in _executor_manager.executor_plugins_map: plugin_class = _executor_manager.executor_plugins_map[name] globals()[plugin_class.__name__] = plugin_class -for qexecutor_cls in _qexecutor_manager.executor_plugins_map.values(): - globals()[qexecutor_cls.__name__] = qexecutor_cls +# Only creating the qexecutor manager if its requirements are installed +with contextlib.suppress(ImportError): + _qexecutor_manager = _QExecutorManager() + for qexecutor_cls in _qexecutor_manager.executor_plugins_map.values(): + globals()[qexecutor_cls.__name__] = qexecutor_cls diff --git a/covalent/executor/quantum_plugins/qiskit_plugin/qiskit_plugin.py b/covalent/executor/quantum_plugins/qiskit_plugin/qiskit_plugin.py index 43531ddef..a959122e3 100644 --- a/covalent/executor/quantum_plugins/qiskit_plugin/qiskit_plugin.py +++ b/covalent/executor/quantum_plugins/qiskit_plugin/qiskit_plugin.py @@ -25,7 +25,7 @@ from runtime_sampler import QiskitRuntimeSampler from covalent._shared_files.config import get_config -from covalent._shared_files.utils import import_from_path +from covalent._shared_files.qelectron_utils import import_from_path from covalent.executor.qbase import ( AsyncBaseQExecutor, BaseThreadPoolQExecutor, diff --git a/covalent/executor/utils/wrappers.py b/covalent/executor/utils/wrappers.py index 4cbd7fccb..8f7fd8256 100644 --- a/covalent/executor/utils/wrappers.py +++ b/covalent/executor/utils/wrappers.py @@ -29,7 +29,7 @@ import requests -from covalent._shared_files.qelectron_utils import get_qelectron_db_path +from covalent._shared_files.utils import get_qelectron_db_path from covalent._workflow.depsbash import DepsBash from covalent._workflow.depscall import RESERVED_RETVAL_KEY__FILES, DepsCall from covalent._workflow.depspip import DepsPip diff --git a/covalent/quantum/qclient/local_client.py b/covalent/quantum/qclient/local_client.py index 89bff7382..8ca46c99d 100644 --- a/covalent/quantum/qclient/local_client.py +++ b/covalent/quantum/qclient/local_client.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ..._shared_files.utils import cloudpickle_deserialize, cloudpickle_serialize +from ..._shared_files.qelectron_utils import cloudpickle_deserialize, cloudpickle_serialize from ..qserver import LocalQServer from .base_client import BaseQClient diff --git a/covalent/quantum/qcluster/clusters.py b/covalent/quantum/qcluster/clusters.py index fe56aecbf..7aa1de314 100644 --- a/covalent/quantum/qcluster/clusters.py +++ b/covalent/quantum/qcluster/clusters.py @@ -17,7 +17,7 @@ import base64 from typing import Callable, Union -from ..._shared_files.utils import cloudpickle_deserialize, cloudpickle_serialize +from ..._shared_files.qelectron_utils import cloudpickle_deserialize, cloudpickle_serialize from .base import AsyncBaseQCluster, BaseQExecutor from .default_selectors import selector_map diff --git a/covalent/quantum/qserver/core.py b/covalent/quantum/qserver/core.py index f8efe09d8..2bc31c913 100644 --- a/covalent/quantum/qserver/core.py +++ b/covalent/quantum/qserver/core.py @@ -25,12 +25,12 @@ from pennylane.tape import QuantumScript -from ..._shared_files.qinfo import QElectronInfo, QNodeSpecs -from ..._shared_files.utils import ( +from ..._shared_files.qelectron_utils import ( cloudpickle_deserialize, cloudpickle_serialize, select_first_executor, ) +from ..._shared_files.qinfo import QElectronInfo, QNodeSpecs from ...executor.utils import get_context from ..qcluster.base import AsyncBaseQCluster, BaseQExecutor from .database import Database diff --git a/covalent_dispatcher/_cli/service.py b/covalent_dispatcher/_cli/service.py index faa14617d..df2299a8f 100644 --- a/covalent_dispatcher/_cli/service.py +++ b/covalent_dispatcher/_cli/service.py @@ -27,6 +27,7 @@ import sys import time import traceback +import warnings from pathlib import Path from subprocess import DEVNULL, Popen from typing import Optional @@ -50,6 +51,7 @@ from rich.syntax import Syntax from rich.table import Table from rich.text import Text +from sqlalchemy import exc as sa_exc from covalent._shared_files.config import ConfigManager, get_config, reload_config, set_config @@ -67,6 +69,9 @@ ZOMBIE_PROCESS_STATUS_MSG = "Covalent server is unhealthy: Process is in zombie status" STOPPED_PROCESS_STATUS_MSG = "Covalent server is unhealthy: Process is in stopped status" +# Ignore SQLAlchemy warnings +warnings.simplefilter("ignore", category=sa_exc.SAWarning) + def print_header(console): branding_title = Text("Covalent", style="bold blue") diff --git a/covalent_ui/api/v1/data_layer/electron_dal.py b/covalent_ui/api/v1/data_layer/electron_dal.py index 5f85c965a..9e10ee154 100644 --- a/covalent_ui/api/v1/data_layer/electron_dal.py +++ b/covalent_ui/api/v1/data_layer/electron_dal.py @@ -28,7 +28,6 @@ from covalent._results_manager.results_manager import get_result from covalent._shared_files import logger from covalent._shared_files.config import get_config -from covalent.quantum.qserver.database import Database from covalent_dispatcher._core.execution import _get_task_inputs as get_task_inputs from covalent_ui.api.v1.data_layer.lattice_dal import Lattices from covalent_ui.api.v1.database.schema.electron import Electron @@ -322,11 +321,17 @@ def get_electron_inputs(self, dispatch_id: uuid.UUID, electron_id: int) -> str: def _get_qelectron_db_dict(self, dispatch_id: str, node_id: int) -> dict: """Return the QElectron DB for a given node.""" - electron = self.get_electrons_id(dispatch_id, node_id) + try: + from covalent.quantum.qserver.database import Database - database = Database(electron.storage_path) - qelectron_db_dict = database.get_db_dict( - dispatch_id=dispatch_id, node_id=node_id, direct_path=True - ) + electron = self.get_electrons_id(dispatch_id, node_id) + + database = Database(electron.storage_path) + qelectron_db_dict = database.get_db_dict( + dispatch_id=dispatch_id, node_id=node_id, direct_path=True + ) - return qelectron_db_dict + return qelectron_db_dict + except ImportError: + app_log.debug("QElectron not installed.") + return {} diff --git a/requirements-qelectron.txt b/requirements-qelectron.txt new file mode 100644 index 000000000..98b537b50 --- /dev/null +++ b/requirements-qelectron.txt @@ -0,0 +1,4 @@ +lmdbm>=0.0.5 +mpire>=2.7.1 +orjson>=3.8.10 +pennylane>=0.31.1,<0.33.0 diff --git a/requirements.txt b/requirements.txt index 886884819..f9d0fa34c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,12 +8,8 @@ dask[distributed]>=2022.6.0 fastapi>=0.100.0 filelock>=3.12.2 furl>=2.1.3 -lmdbm>=0.0.5 -mpire>=2.7.1 natsort>=8.4.0 networkx>=2.8.6 -orjson>=3.8.10 -pennylane>=0.31.1,<0.33.0 psutil>=5.9.0 pydantic>=2.1.1 python-multipart>=0.0.6 diff --git a/setup.py b/setup.py index 50e3d6b63..317a469c7 100644 --- a/setup.py +++ b/setup.py @@ -55,6 +55,11 @@ with open(requirements_file) as f: required = f.read().splitlines() +# By default we don't install qelectron requirements +# and only install them as an extra +with open("requirements-qelectron.txt") as f: + qelectron_reqs = f.read().splitlines() + def recursively_append_files(directory: str): """ @@ -220,6 +225,7 @@ def find_sources(self): "qiskit-ibm-provider==0.6.1", "qiskit-ibm-runtime==0.10.0", ], + "quantum": qelectron_reqs, }, "classifiers": [ "Development Status :: 4 - Beta", diff --git a/tests/covalent_tests/shared_files/qelectron_utils_test.py b/tests/covalent_tests/shared_files/qelectron_utils_test.py index 6c213e217..2cb66dbb6 100644 --- a/tests/covalent_tests/shared_files/qelectron_utils_test.py +++ b/tests/covalent_tests/shared_files/qelectron_utils_test.py @@ -30,7 +30,7 @@ def test_get_qelectron_db_path(mocker, db_exists): """Test the get_qelectron_db_path function.""" - mock_database = mocker.patch("covalent._shared_files.qelectron_utils.Database") + mock_database = mocker.patch("covalent.quantum.qserver.database.Database") mock_database.return_value.get_db_path.return_value.exists.return_value = db_exists dispatch_id = "mock_dispatch_id" diff --git a/tests/qelectron_tests/core_tests/__init__.py b/tests/qelectron_tests/core_tests/__init__.py new file mode 100644 index 000000000..6b2415372 --- /dev/null +++ b/tests/qelectron_tests/core_tests/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2024 Agnostiq Inc. +# +# This file is part of Covalent. +# +# Licensed under the Apache License 2.0 (the "License"). A copy of the +# License may be obtained with this software package or at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Use of this file is prohibited except in compliance with the License. +# 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. diff --git a/tests/qelectron_tests/test_decorator.py b/tests/qelectron_tests/core_tests/test_decorator.py similarity index 98% rename from tests/qelectron_tests/test_decorator.py rename to tests/qelectron_tests/core_tests/test_decorator.py index f376d0099..3bd11da15 100644 --- a/tests/qelectron_tests/test_decorator.py +++ b/tests/qelectron_tests/core_tests/test_decorator.py @@ -25,7 +25,7 @@ import covalent as ct EXECUTORS = [ - ct.executor.QiskitExecutor(device="local_sampler", shots=10_000), + ct.executor.Simulator(), ] diff --git a/tests/qelectron_tests/test_qelectron_db.py b/tests/qelectron_tests/core_tests/test_qelectron_db.py similarity index 96% rename from tests/qelectron_tests/test_qelectron_db.py rename to tests/qelectron_tests/core_tests/test_qelectron_db.py index 4d75f296e..7bc771003 100644 --- a/tests/qelectron_tests/test_qelectron_db.py +++ b/tests/qelectron_tests/core_tests/test_qelectron_db.py @@ -25,7 +25,7 @@ def test_db_exposed_in_result(): """ # Define a QElectron circuit. - qexecutor = ct.executor.QiskitExecutor(device="local_sampler") # pylint: disable=no-member + qexecutor = ct.executor.Simulator() # pylint: disable=no-member @ct.qelectron(executors=qexecutor) @qml.qnode(qml.device("default.qubit", wires=1)) diff --git a/tests/qelectron_tests/test_run_later.py b/tests/qelectron_tests/core_tests/test_run_later.py similarity index 97% rename from tests/qelectron_tests/test_run_later.py rename to tests/qelectron_tests/core_tests/test_run_later.py index 169e96662..aec023182 100644 --- a/tests/qelectron_tests/test_run_later.py +++ b/tests/qelectron_tests/core_tests/test_run_later.py @@ -23,7 +23,6 @@ import covalent as ct EXECUTORS = [ - ct.executor.QiskitExecutor(device="local_sampler", shots=10_000), ct.executor.Simulator(), ] diff --git a/tests/qelectron_tests/pennylane_tests/conftest.py b/tests/qelectron_tests/pennylane_tests/conftest.py index 30b774570..773ba2edf 100644 --- a/tests/qelectron_tests/pennylane_tests/conftest.py +++ b/tests/qelectron_tests/pennylane_tests/conftest.py @@ -33,7 +33,7 @@ import pytest import covalent as ct -from covalent._shared_files.utils import get_original_shots +from covalent._shared_files.qelectron_utils import get_original_shots from covalent.quantum.qcluster.simulator import SIMULATOR_DEVICES SKIP_RETURN_TYPES = ["qml.apply", "qml.vn_entropy", "qml.mutual_info"] diff --git a/tests/qelectron_tests/plugin_tests/__init__.py b/tests/qelectron_tests/plugin_tests/__init__.py new file mode 100644 index 000000000..6b2415372 --- /dev/null +++ b/tests/qelectron_tests/plugin_tests/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2024 Agnostiq Inc. +# +# This file is part of Covalent. +# +# Licensed under the Apache License 2.0 (the "License"). A copy of the +# License may be obtained with this software package or at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Use of this file is prohibited except in compliance with the License. +# 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. diff --git a/tests/qelectron_tests/test_braket_plugin.py b/tests/qelectron_tests/plugin_tests/test_braket_plugin.py similarity index 100% rename from tests/qelectron_tests/test_braket_plugin.py rename to tests/qelectron_tests/plugin_tests/test_braket_plugin.py diff --git a/tests/qelectron_tests/test_qiskit_plugin.py b/tests/qelectron_tests/plugin_tests/test_qiskit_plugin.py similarity index 98% rename from tests/qelectron_tests/test_qiskit_plugin.py rename to tests/qelectron_tests/plugin_tests/test_qiskit_plugin.py index 438532424..b6046005f 100644 --- a/tests/qelectron_tests/test_qiskit_plugin.py +++ b/tests/qelectron_tests/plugin_tests/test_qiskit_plugin.py @@ -23,7 +23,7 @@ import covalent as ct from covalent._shared_files.config import get_config -from .utils import arg_vector, simple_circuit, weight_vector +from ..utils import arg_vector, simple_circuit, weight_vector EXECUTOR_CLASSES = [ ct.executor.QiskitExecutor, diff --git a/tests/qelectron_tests/test_qiskit_plugin_runtime.py b/tests/qelectron_tests/plugin_tests/test_qiskit_plugin_runtime.py similarity index 98% rename from tests/qelectron_tests/test_qiskit_plugin_runtime.py rename to tests/qelectron_tests/plugin_tests/test_qiskit_plugin_runtime.py index 913d54388..e21724d23 100644 --- a/tests/qelectron_tests/test_qiskit_plugin_runtime.py +++ b/tests/qelectron_tests/plugin_tests/test_qiskit_plugin_runtime.py @@ -25,7 +25,7 @@ import covalent as ct from covalent._shared_files.config import get_config -from .utils import arg_vector, cyclic_selector, get_hamiltonian_circuit, weight_vector +from ..utils import arg_vector, cyclic_selector, get_hamiltonian_circuit, weight_vector EXECUTOR_CLASSES = [ ct.executor.QiskitExecutor, diff --git a/tests/requirements.txt b/tests/requirements.txt index 99f936c73..38c17dc3c 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -5,6 +5,7 @@ isort>=5.10.1 locust>=2.11.0 mock>=4.0.3 nbconvert>=6.5.1 +pennylane>=0.31.1 pre-commit>=2.20.0 pytest>=7.1.3 pytest-asyncio>=0.21.0