コード例 #1
0
    def get_quantum_states_from_result(self, result_id: int) -> List[Any]:
        """ Gets the quantum states from the result of the executed cQASM code, given the result_id.

        Args:
            result_id: The identification number of the result.

        Raises:
            ApiError: If the quantum states url in result is invalid or the request for the quantum states using the
            url failed.

        Returns:
            The quantum states consists of a list of quantum state values. An empty list is returned when there is
            no data.
        """
        result = self.get_result(result_id)
        quantum_states_url = str(result.get('quantum_states_url'))
        try:
            token = quantum_states_url.split('/')[-2]
        except IndexError as err_msg:
            raise ApiError(f'Invalid quantum states url for result with id {result_id}!') from err_msg
        try:
            quantum_states: List[Any] = self._action(['results', 'quantum-states', 'read'], params={'id': result_id,
                                                                                                    'token': token})
        except ErrorMessage as err_msg:
            raise ApiError(f'Quantum states for result with id {result_id} does not exist!') from err_msg
        return quantum_states
コード例 #2
0
    def get_asset_from_job(self, job_id: int) -> Dict[str, Any]:
        """ Gets the asset data from the job, given the job_id.

        Args:
            job_id: The identification number of the job.

        Raises:
            ApiError: If the requested asset with identification from the job 'input' field does not exist.

        Returns:
            The assets with all of its properties.
            See `get_asset` for a description of the asset properties.
        """
        job = self.get_job(job_id)
        asset_url = str(job.get('input'))
        try:
            asset_id = int(asset_url.split('/')[-2])
        except (ValueError, IndexError) as err_msg:
            raise ApiError('Invalid input url for job with id {}!'.format(
                job_id)) from err_msg
        try:
            asset = self._action(['assets', 'read'], params={'id': asset_id})
        except ErrorMessage as err_msg:
            raise ApiError('Asset with id {} does not exist!'.format(
                asset_id)) from err_msg
        return OrderedDict(asset)
コード例 #3
0
    def get_measurement_register_from_result(self, result_id: int) -> List[Any]:
        """ Gets the measurement register from the result of the executed cQASM code, given the result_id.

        Args:
            result_id: The identification number of the result.

        Raises:
            ApiError: If the measurement register url in result is invalid or the request for the measurement register
            using the url failed.

        Returns:
            The measurement register consists of a list of measurement register values. An empty list is returned
            when there is no data.
        """
        result = self.get_result(result_id)
        measurement_register_url = str(result.get('measurement_register_url'))
        try:
            token = measurement_register_url.split('/')[-2]
        except IndexError as err_msg:
            raise ApiError(f'Invalid measurement register url for result with id {result_id}!') from err_msg
        try:
            measurement_register: List[Any] = self._action(['results', 'measurement-register', 'read'],
                                                           params={'id': result_id,
                                                                   'token': token})
        except ErrorMessage as err_msg:
            raise ApiError(f'Measurement register for result with id {result_id} does not exist!') from err_msg
        return measurement_register
コード例 #4
0
    def get_raw_data_from_result(self, result_id: int) -> List[int]:
        """ Gets the raw data from the result of the executed cQASM code, given the result_id. The raw data consists
            of a list with integer state values for each shot of the experiment (see job.number_of_shots).

        Args:
            result_id: The identification number of the result.

        Raises:
            ApiError: If the raw data url in result is invalid or the request for the raw data using the url failed.

        Returns:
            The raw data as a list of integer values. An empty list is returned when there is no raw data.
        """
        result = self.get_result(result_id)
        raw_data_url = str(result.get('raw_data_url'))
        try:
            token = raw_data_url.split('/')[-2]
        except IndexError as err_msg:
            raise ApiError(f'Invalid raw data url for result with id {result_id}!') from err_msg
        try:
            raw_data: List[int] = self._action(['results', 'raw-data', 'read'], params={'id': result_id,
                                                                                        'token': token})
        except ErrorMessage as err_msg:
            raise ApiError(f'Raw data for result with id {result_id} does not exist!') from err_msg
        return raw_data
コード例 #5
0
    def __init__(
            self,
            base_uri: str,
            authentication: Optional[coreapi.auth.AuthBase] = None,
            project_name: Optional[str] = None,
            coreapi_client_class: Type[coreapi.Client] = coreapi.Client
    ) -> None:
        """ Python interface to the Quantum Inspire API (Application Programmer Interface).

        The Quantum Inspire API supplies an interface for executing cQASM programs and can be used to access the
        different entities in the Quantum Inspire database needed for running the programs.
        The entities for which an interface is provided are:
            Backend types: Depending on the user account more qubits can be used and simulated on faster hardware.
            Projects: Executing programs is done from a project. Projects keep track of the other entities.
            Jobs: A job contains all the information and the parameters needed for executing the program.
            Assets: A container for a cQASM program. Is part of a job.
            Results: After the job is finished, the results are gathered in the result-entity.

        QuantumInspireAPI is a convenient interface (or wrapper) to the low level API and hides details for
        requesting data (via get) and performing operations on the different entities (via actions).

        For more documentation see the knowledge base on: https://www.quantum-inspire.com/kbase/low-level-api/
        The REST API can be found on: https://api.quantum-inspire.com/
        The Core API schema is published on: https://api.quantum-inspire.com/schema/

        Args:
            base_uri: The base uri of the Quantum Inspire API-location where the schema can be found (in path
                      'schema/').
            authentication: The authentication, can be one of the following coreapi authentications:
                            BasicAuthentication(email, password), HTTP authentication with valid email/password.
                            TokenAuthentication(token, scheme="token"), token authentication with a valid API-token.
                            When authentication is None, a token is read from the default resource.
            project_name: The project used for executing the jobs.
            coreapi_client_class: Coreapi client to interact with the API through a schema.
                                  Default set to coreapi.Client.

        Note: When no project name is given, a temporary project is created for the job and deleted after the job
              has finished. When a project name is given, a project is created if it does not exist, but re-used
              if a project with that name already exists. In either case, the project will not be deleted when a
              project name is supplied here.

        Raises:
            ApiError: An ApiError exception is raised when no authentication is given and the token could not be
                      loaded or the schema could not be loaded.
        """
        if authentication is None:
            token = load_token()
            if token is not None:
                authentication = TokenAuthentication(token, scheme="token")
            else:
                raise ApiError('No credentials have been provided')
        self.__client = coreapi_client_class(auth=authentication)
        self.project_name = project_name
        self.base_uri = base_uri
        try:
            self._load_schema()
        except (CoreAPIException, TypeError) as ex:
            raise ApiError('Could not connect to {}'.format(base_uri)) from ex
コード例 #6
0
 def test_retrieve_job_with_error(self):
     api = Mock(
         side_effect=ApiError(f'Project with id 404 does not exist!'))
     api.get_project.side_effect = ApiError(
         f'Project with id 404 does not exist!')
     backend = QuantumInspireBackend(api, QuantumInspireProvider())
     with self.assertRaises(QiskitBackendError) as error:
         backend.retrieve_job('wrong')
     self.assertEqual(("Could not retrieve job with job_id 'wrong' ", ),
                      error.exception.args)
コード例 #7
0
    def _gate_not_supported(_stream: StringIO, instruction: QasmQobjInstruction, _binary_control: Optional[str] = None)\
            -> None:
        """ Called when a gate is not supported with the backend. Throws an exception (ApiError)

        :param instruction: The Qiskit instruction to translate to cQASM.

        :raises ApiError: the gate is not supported by the circuit parser.

        """
        if hasattr(instruction, 'conditional'):
            raise ApiError(f'Conditional gate c-{instruction.name.lower()} not supported')

        raise ApiError(f'Gate {instruction.name.lower()} not supported')
コード例 #8
0
    def get_job(self, job_id: int) -> Dict[str, Any]:
        """ Gets the properties of a job, given the job id.
        Args:
            job_id: The job identification number.

        Raises:
            ApiError: If the requested job does not exist.

        Returns:
            The properties describing the job:
                | key                           | description
                |-------------------------------|----------------------------------------------------------------------
                | url (str)                     | The url for the job.
                | name (str)                    | Name of the circuit that is executed by this job.
                | id (int)                      | Unique id of the job.
                | status (str)                  | Execution status of the job: e.g. 'NEW', 'COMPLETE', 'CANCELLED',
                |                               | 'RUNNING'.
                | input (str)                   | Url to get the assets of the job.
                | backend (str)                 | Url to get the backend the job is executed on.
                | backend_type (str)            | Url to get the backend type of the backend the job is executed on.
                | results (str)                 | Url to get the results for the job.
                | queued_at (str)               | The date-time the job is queued at.
                |                               | The format is 'yyyy-MM-ddTHH:mm:ss.SSSSSSZ' Zulu Time.
                | number_of_shots (int)         | Number of executions for this job.
                | full_state_projection (bool)  | Indicates if the backend uses full state projection to determine
                |                               | the quantum state.
                |                               | Used for optimizing simulations. For more information see:
                |                               | https://www.quantum-inspire.com/kbase/optimization-of-simulations/
                | user_data (str)               | The user configuration data.
        """
        try:
            job = self._action(['jobs', 'read'], params={'id': job_id})
        except ErrorMessage as err_msg:
            raise ApiError(f'Job with id {job_id} does not exist!') from err_msg
        return OrderedDict(job)
コード例 #9
0
    def get_project(self, project_id: int) -> Dict[str, Any]:
        """ Gets the properties of a project, given the project id.
        Args:
            project_id: The project identification number.

        Raises:
            ApiError: If the requested project does not exist.

        Returns:
            The properties describing the project:
                | key                           | description
                |-------------------------------|----------------------------------------------------------------------
                | url (str)                     | The url for this project.
                | id (int)                      | Unique id of the project.
                | name (str)                    | Name of the project.
                | owner (int)                   | Url to get the owner of the project.
                | assets (str)                  | Url to get the assets of the project.
                | backend_type (str)            | Url to get the backend type of the project.
                | default_number_of_shots (int) | Default number of executions for this project.
        """
        try:
            project = self._action(['projects', 'read'], params={'id': project_id})
        except ErrorMessage as err_msg:
            raise ApiError(f'Project with id {project_id} does not exist!') from err_msg
        return OrderedDict(project)
コード例 #10
0
    def _create_job(self, name: str, asset: Dict[str, Any], project: Dict[str, Any], number_of_shots: int,
                    backend_type: Dict[str, Any], full_state_projection: bool = False,
                    user_data: str = '') -> Dict[str, Any]:
        """ Creates a new job for executing cQASM code. This method is used by execute_qasm_async and indirectly
            by execute_qasm.

        Args:
            name: The name for the job.
            asset:  The asset with the cQASM code.
            project: The project with backend type.
            number_of_shots: The number of executions before returning the result.
            full_state_projection: Used for optimizing simulations. For more information see:
                                   https://www.quantum-inspire.com/kbase/optimization-of-simulations/
            user_data: Data that the user wants to pass along with the job.

        Returns:
            The properties describing the new job.
            See `get_job` for a description of the job properties.
        """
        payload = {
            'status': 'NEW',
            'name': name,
            'input': asset['url'],
            'backend_type': project['backend_type'],
            'number_of_shots': number_of_shots,
            'full_state_projection': full_state_projection,
            'user_data': user_data
        }
        if not full_state_projection and self.enable_fsp_warning and not backend_type.get("is_hardware_backend", False):
            logger.warning("Your experiment can not be optimized and may take longer to execute, "
                           "see https://www.quantum-inspire.com/kbase/optimization-of-simulations/ for details.")
        try:
            return OrderedDict(self._action(['jobs', 'create'], params=payload))
        except (CoreAPIException, TypeError, ValueError) as err_msg:
            raise ApiError(f'Job with name {name} not created: {err_msg}') from err_msg
コード例 #11
0
    def get_result(self, result_id: int) -> Dict[str, Any]:
        """ Gets the histogram results of the executed cQASM code, given the result_id.
        Args:
            result_id: The result identification number.

        Raises:
            ApiError: If the requested result does not exist.

        Returns:
            The properties describing the result:
                | key                               | description
                |-----------------------------------|-------------------------------------------------------------------
                | id (int)                          | Unique id of the result.
                | url (str)                         | The url to get this result.
                | job (str)                         | The url to get the job that generated the result.
                | created_at (str)                  | The date-time the result is created at.
                |                                   | The format is 'yyyy-MM-ddTHH:mm:ss.SSSSSSZ' Zulu Time.
                | number_of_qubits (int)            | Number of qubits in the circuit for this experiment.
                | execution_time_in_seconds (float) | The execution time of the job.
                | raw_text (str)                    | Text string filled when an error occurred, else empty.
                | raw_data_url (str)                | Url to get the raw data of the result. The raw data exists of a
                |                                   | list of integer values depicting the state for each shot.
                | histogram (OrderedDict)           | The histogram as a list of tuples with state (str) and
                |                                   | its probability (float).
                | histogram_url (str)               | Url to get the histogram with probabilities. This results in the
                |                                   | OrderedDict as found in property histogram of result.
                | measurement_mask (int)            | (deprecated, unused) The measurement mask.
                | quantum_states_url (str)          | Url to get a list of quantum states.
                | measurement_register_url (str)    | Url to get a list of measurement register.
        """
        try:
            result = self._action(['results', 'read'], params={'id': result_id})
        except ErrorMessage as err_msg:
            raise ApiError('Result with id {} does not exist!'.format(result_id)) from err_msg
        return OrderedDict(result)
コード例 #12
0
    def get_asset(self, asset_id: int) -> Dict[str, Any]:
        """ Gets the properties of the asset, given the asset_id.
        Args:
            asset_id: The asset identification number.

        Raises:
            ApiError: If the requested asset does not exist.

        Returns:
            The properties describing the asset:
                | key                       | description
                |---------------------------|---------------------------------------------------------------------------
                | url (str)                 | The url to get this asset.
                | id (int)                  | Unique id of this asset.
                | name (str)                | The name to get the asset.
                | contentType (str)         | The description of the content e.g. 'application/qasm' or
                |                           | 'text/plain'.
                | content (str)             | The content itself. For example a cQASM program when linked to a job.
                | project (str)             | Url to get the project properties for which this asset was created.
                | project_id (int)          | The project id of the project for which this asset was created.
        """
        try:
            asset = self._action(['assets', 'read'], params={'id': asset_id})
        except ErrorMessage as err_msg:
            raise ApiError(f'Asset with id {asset_id} does not exist!') from err_msg
        return OrderedDict(asset)
コード例 #13
0
    def backends(self,
                 name: Optional[str] = None,
                 **kwargs: Any) -> List[QuantumInspireBackend]:
        """
        Provides a list of backends.

        Args:
            name: Name of the requested backend.
            **kwargs: Used for filtering, not implemented.

        Returns:
            List of backends that meet the filter requirements.
        """
        if self._api is None:
            raise ApiError('Authentication details have not been set.')

        available_backends = self._api.get_backend_types()
        if name is not None:
            available_backends = list(
                filter(lambda b: b['name'] == name, available_backends))
        backends = []
        for backend in available_backends:
            if backend['is_allowed']:
                config = copy(QuantumInspireBackend.DEFAULT_CONFIGURATION)
                config.backend_name = backend['name']
                backends.append(
                    QuantumInspireBackend(self._api,
                                          provider=self,
                                          configuration=config))

        return backends
コード例 #14
0
    def delete_project(self, project_id: int) -> None:
        """ Deletes the project identified by project_id together with all its assets, jobs and results.
            Only projects can be deleted that are registered for the user the API is currently authenticated for.

        Args:
            project_id: The project identification number.

        Raises:
            ApiError: If the project identified by project_id does not exist.
        """
        payload = {'id': project_id}
        try:
            self._action(['projects', 'delete'], params=payload)
        except ErrorMessage as err_msg:
            raise ApiError('Project with id {} does not exist!'.format(
                project_id)) from err_msg
コード例 #15
0
    def get_backend_type_by_id(self, backend_type_id: int) -> Dict[str, Any]:
        """ Gets the properties of a specific backend type, given the backend type id.

        Args:
            backend_type_id: The backend identification number.

        Raises:
            ApiError: An ApiError exception is raised when the backend type indicated by backend_type_id does not exist.

        Returns:
            The requested backend type indicated by backend_type_id with all of its properties.
            See `get_default_backend_type` for a description of the backend type properties.
        """
        try:
            backend_type = self._action(['backendtypes', 'read'], params={'id': backend_type_id})
        except ErrorMessage as err_msg:
            raise ApiError(f'Backend type with id {backend_type_id} does not exist!') from err_msg
        return OrderedDict(backend_type)
コード例 #16
0
    def get_backend_type_by_name(self, backend_name: str) -> Dict[str, Any]:
        """ Gets the properties of a backend type, given the backend name (case insensitive).

        Args:
            backend_name: The backend name.

        Raises:
            ApiError: An ApiError exception is thrown when the backend name does not exist.

        Returns:
            The properties of the backend type of the specific backend.
            See `get_default_backend_type` for a description of the backend type properties.
        """
        backend_type = next((backend for backend in self.get_backend_types()
                            if backend['name'].lower() == backend_name.lower()), None)
        if backend_type is None:
            raise ApiError(f'Backend type with name {backend_name} does not exist!')
        return OrderedDict(backend_type)
コード例 #17
0
    def get_assets_from_project(self, project_id: int) -> List[Dict[str, Any]]:
        """ Gets the assets with its properties for a single project, given the project id.

        Args:
            project_id: The project identification number.

        Returns:
            List of assets with its properties for the project with identification project_id.
            An empty list is returned when the project has no assets.

        Raises:
            ApiError: If the project identified by project_id does not exist.
        """
        try:
            assets: List[Dict[str, Any]] = self._action(['projects', 'assets', 'list'], params={'id': project_id})
        except ErrorMessage as err_msg:
            raise ApiError(f'Project with id {project_id} does not exist!') from err_msg
        return assets
コード例 #18
0
    def delete_job(self, job_id: int) -> Dict[str, Any]:
        """ Deletes the job identified by job_id.
            Only jobs can be deleted that are registered for the user the API is currently authenticated for.

        Args:
            job_id: The job identification number.

        Returns:
            The deleted job indicated by the job identification number.
            See `get_job` for a description of the job properties.

        Raises:
            ApiError: If the job identified by job_id does not exist.
        """
        try:
            return OrderedDict(self._action(['jobs', 'delete'], params={'id': job_id}))
        except ErrorMessage as err_msg:
            raise ApiError(f'Job with id {job_id} does not exist!') from err_msg
コード例 #19
0
    def get_result_from_job(self, job_id: int) -> Dict[str, Any]:
        """ Gets the result with its properties for a single job, given the job id.

        Args:
            job_id: The job identification number.

        Returns:
            The result with its properties for the job with identification job_id.
            See `get_result` for a description of the result properties.

        Raises:
            ApiError: If the job identified by job_id does not exist.
        """
        try:
            result = self._action(['jobs', 'result', 'list'], params={'id': job_id})
        except ErrorMessage as err_msg:
            raise ApiError(f'Job with id {job_id} does not exist!') from err_msg
        return OrderedDict(result)
コード例 #20
0
    def get_jobs_from_asset(self, asset_id: int) -> List[Dict[str, Any]]:
        """ Gets the jobs with its properties for an asset, given the asset id.

        Args:
            asset_id: The asset identification number.

        Returns:
            List of jobs with its properties for the asset with identification asset_id.
            An empty list is returned when the asset has no jobs.

        Raises:
            ApiError: If the asset identified by asset_id does not exist.
        """
        try:
            jobs = self._action(['assets', 'jobs', 'list'], params={'id': asset_id})
        except ErrorMessage as err_msg:
            raise ApiError(f'Asset with id {asset_id} does not exist!') from err_msg
        ret: List[Dict[str, Any]] = jobs
        return ret
コード例 #21
0
    def _parse_bin_ctrl_gate(self, stream: StringIO, instruction: QasmQobjInstruction) -> None:
        """ Parses a binary controlled gate.

        A binary controlled gate name is preceded by 'c-'.
        The gate is executed when a specific measurement is true. Multiple measurement outcomes are used
        to control the quantum operation. This measurement is a combination of classical bits being 1 and others
        being 0. Because cQASM only supports measurement outcomes of 1, any other bits in the
        masked bit pattern first have to be inverted with the not-operator. The same inversion also has to
        take place after the binary controlled quantum operation.
        The mask can be one or more bits and start at any bit depending on the instruction and the declaration
        of classical bits.
        The resulting stream will be expanded with something like:
        not b[the 0-bits in the value relative to the mask changed to 1]
        c-gate [classical bits in the mask], other arguments
        not b[the 0-bits reset to 0 again]
        When the c-gate results in an empty string (e.g. binary controlled u(0, 0, 0) or barrier gate),
        nothing is added to the stream.

        :param stream: The string-io stream to where the resulting cQASM is written.
        :param instruction: The Qiskit instruction to translate to cQASM.

        """
        conditional_reg_idx = instruction.conditional
        conditional = next((x for x in self.bfunc_instructions if x.register == conditional_reg_idx), None)
        if conditional is None:
            raise ApiError(f'Conditional not found: reg_idx = {conditional_reg_idx}')
        self.bfunc_instructions.remove(conditional)

        conditional_type = conditional.relation
        if conditional_type != '==':
            raise ApiError(f'Conditional statement with relation {conditional_type} not supported')
        mask = int(conditional.mask, 16)
        if mask == 0:
            raise ApiError(f'Conditional statement {instruction.name.lower()} without a mask')
        lowest_mask_bit, mask_length = self.get_mask_data(mask)
        val = int(conditional.val, 16)
        masked_val = mask & val

        # form the negation to the 0-values of the measurement registers, when value == mask no bits are negated
        negate_zeroes_line = ''
        if masked_val != mask:
            negate_zeroes_line = 'not b[' + ','.join(
                str(i) for i in range(lowest_mask_bit, lowest_mask_bit + mask_length)
                if not (masked_val & (1 << i))) + ']\n'

        if mask_length == 1:
            binary_control = f'b[{lowest_mask_bit}], '
        else:
            # form multi bits control - qasm-single-gate-multiple-qubits
            binary_control = f'b[{lowest_mask_bit}:{lowest_mask_bit + mask_length - 1}], '

        with StringIO() as gate_stream:
            # add the gate
            gate_name = f'_c_{instruction.name.lower()}'
            gate_function = getattr(self, gate_name, getattr(self, "_gate_not_supported"))
            gate_function(gate_stream, instruction, binary_control)
            line = gate_stream.getvalue()
            if len(line) != 0:
                # negate the measurement registers that has to be 0
                stream.write(negate_zeroes_line)
                stream.write(line)
                # reverse the measurement registers that had to be 0
                stream.write(negate_zeroes_line)