# Copyright (c) 2021, Qu & Co
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
This module contains all the public methods of the qubec-sdk API covering all most important use cases such as
authenticating to the QUBEC platform, launching quantum jobs and retrieving previously completed job information.
"""
# built-in
from binascii import Error as EncodingError
from dataclasses import asdict
from typing import Union
# local
from . import PROJECT_VERSION as VERSION
from .client import QubecClient, get_client
from .job import QubecJob
from .parameters import (
AVAILABLE_ALGORITHMS,
AVAILABLE_BACKEND_TYPES,
AVAILABLE_PROPERTIES,
DEFAULT_BACKEND_TYPE,
DEFAULT_N_SHOTS,
DEFAULT_PROVIDER,
DEFAULT_QPU_CHIP,
DEFAULT_QUANTUM_PARAMETERS,
DEFAULT_SCF_PARAMETERS,
DEFAULT_SIMULATION_TYPE,
QubecAlgorithm,
QubecChemistryProblem,
QubecSdkError,
)
from .tools import encode_geometry_xyz
[docs]def enable_account(username: str,
password: str = None,
token: str = None) -> dict:
"""
Login into a QUBEC account providing valid user credentials. This function
must be called before starting any QUBEC session otherwise the execution
of quantum jobs will not be allowed
Args:
username (str): The username as registered on the QUBEC platform
password (str): The password associated with the username. If no password
is provided, then a valid token must be supplied instead
token (str): An optional previously retrieved JWT token to use for login
Returns:
A dictionary with token information. Beware that this might contain a password
in clear text, do not store it on disk
Raises:
QubecSdkError if credentials are invalid or the backed call did not succeed
"""
client: QubecClient = get_client()
if password is not None:
client.login(username, password)
elif token is not None:
client.current_user(token)
else:
raise QubecSdkError("Provide either password or a valid QUBEC token")
return asdict(client.token)
[docs]def execute(
problem: dict = None,
algorithm_type: Union[str, QubecAlgorithm] = None,
backend_type: str = DEFAULT_BACKEND_TYPE,
provider: str = DEFAULT_PROVIDER,
simulation_type: str = DEFAULT_SIMULATION_TYPE,
n_shots: int = DEFAULT_N_SHOTS,
qpu_chip: str = DEFAULT_QPU_CHIP,
scf_parameters: dict = DEFAULT_SCF_PARAMETERS,
quantum_parameters: dict = DEFAULT_QUANTUM_PARAMETERS,
) -> QubecJob:
"""
Execute a new quantum job on the QUBEC platform. If the QUBEC token in the current session
has expired, this function will automatically refresh it
Args:
problem (dict): A dictionary containing the problem definition which must contain
at least 'geometry' and 'basis_set' keys. A full example of this dictionary is:
```
problem = {
"geometry": [
("H", (0.0, 0.0, 0.0)),
("H", (0.5, 0.5, 0.5))
],
"basis_set": "sto-3g",
"charge": 1
}
```
algorithm_type (Union[str, QubecAlgorithm]): The type of quantum algorithm to execute. Currently
only 'vqa' and 'qpe_re' are allowed values
backend_type (str): The backend type where the algorithm needs to be executed. Currently one
can choose among 'simulator', 'noisy_simulator' and 'qpu'
provider (str): The quantum computing provider to use
simulation_type (str): If the algorithm selected is 'vqa', this string contains the type of quantum
simulation to execute chosen between 'wavefunction' or 'tomography'
n_shots (int): The number of circuit repetitions, used only if 'tomography' is selected as simulation type
qpu_chip (str): If 'qpu' is selected as backend_type, this parameter specifies the actual quantum chip to use
scf_parameters (dict): the parameters of the self-consistent preprocessing calculation. See qb_sdk.parameters documentation
for information on the valid parameters
quantum_parameters (dict): the parameters of the quantum job. See qb_sdk.parameters documentation for information
on the valid parameters
Returns:
A QubecJob instance with a unique job identifier created by the QUBEC platform and all the relevant
information to retrieve job progress later
Raises:
QubecSdk if some parameters are wrong or the job has failed to be submitted correctly
"""
client: QubecClient = get_client()
client.refresh_session()
try:
problem = problem if problem is not None else {}
problem_model = QubecChemistryProblem(**problem)
problem_model.geometry = encode_geometry_xyz(problem_model.geometry)
properties = list(problem_model.properties.keys())
are_valid_props = all(
prop in AVAILABLE_PROPERTIES for prop in properties)
if len(properties) > 0 and not are_valid_props:
raise QubecSdkError(
f"Choose among the following physical properties: {AVAILABLE_PROPERTIES}"
)
except EncodingError:
raise QubecSdkError(f"Wrong problem definition provided: {problem}")
except QubecSdkError as e:
raise e
if isinstance(algorithm_type, QubecAlgorithm):
algorithm_type = algorithm_type.name
if algorithm_type is None or algorithm_type not in AVAILABLE_ALGORITHMS:
raise QubecSdkError(
f"Choose among the following quantum algorithms: {AVAILABLE_ALGORITHMS}"
)
if backend_type not in AVAILABLE_BACKEND_TYPES:
raise QubecSdkError(
f"Choose among the following backend types: {AVAILABLE_BACKEND_TYPES}"
)
data = {
"algorithm_type": algorithm_type,
"backend_type": backend_type,
"backend": {
"provider": provider,
"simulation_type": simulation_type,
"n_shots": n_shots,
"chip": qpu_chip,
},
"problem_definition": asdict(problem_model),
"scf_parameters": scf_parameters,
"quantum_parameters": quantum_parameters,
}
content = client.submit_job(data)
job = QubecJob(
content["data"]["job_id"],
experiment=data["algorithm_type"],
backend_type=data["backend_type"],
backend=data["backend"],
problem=data["problem_definition"],
**scf_parameters,
**quantum_parameters,
)
return job
[docs]def new_default_session() -> QubecClient:
"""
Logout from the current QUBEC session and begin a new one
"""
client: QubecClient = get_client()
client.logout()
return client
[docs]def version() -> str:
"""
Get the current package version
"""
return VERSION
# TODO
[docs]def get_job(job_id: str) -> QubecJob:
"""
Deserialize and existing QUBEC job into a new QubecJob instance
Args:
job_id (str): The unique identifier of the job
Returns:
A populated instance of QubecJob
"""
pass