def _validate(self, qobj):
        """Semantic validations of the qobj which cannot be done via schemas.
        Some of these may later move to backend schemas.

        1. Set shots=1.
        2. Check number of qubits will fit in local memory.
        """
        n_qubits = qobj.config.n_qubits
        max_qubits = self.configuration().n_qubits
        if n_qubits > max_qubits:
            raise AerError('Number of qubits ({}) '.format(n_qubits) +
                           'is greater than maximum ({}) '.format(max_qubits) +
                           'for "{}" '.format(self.name()) +
                           'with {} GB system memory.'.format(int(local_hardware_info()['memory'])))
        if qobj.config.shots != 1:
            logger.info('"%s" only supports 1 shot. Setting shots=1.',
                        self.name())
            qobj.config.shots = 1
        for experiment in qobj.experiments:
            name = experiment.header.name
            if getattr(experiment.config, 'shots', 1) != 1:
                logger.info('"%s" only supports 1 shot. '
                            'Setting shots=1 for circuit "%s".',
                            self.name(), name)
                experiment.config.shots = 1
Ejemplo n.º 2
0
    def _validate(self, qobj, backend_options, noise_model):
        """Semantic validations of the qobj which cannot be done via schemas.
        Some of these may later move to backend schemas.
        1. Set shots=1
        2. No measurements or reset
        3. Check number of qubits will fit in local memory.
        """
        name = self.name()
        if noise_model is not None:
            logger.error("{} cannot be run with a noise.".format(name))
            raise AerError("{} does not support noise.".format(name))

        n_qubits = qobj.config.n_qubits
        max_qubits = self.configuration().n_qubits
        if n_qubits > max_qubits:
            raise AerError('Number of qubits ({}) '.format(n_qubits) +
                           'is greater than maximum ({}) '.format(max_qubits) +
                           'for "{}" '.format(name) +
                           'with {} GB system memory.'.format(
                               int(local_hardware_info()['memory'])))
        if qobj.config.shots != 1:
            logger.info('"%s" only supports 1 shot. Setting shots=1.', name)
            qobj.config.shots = 1
        for experiment in qobj.experiments:
            exp_name = experiment.header.name
            if getattr(experiment.config, 'shots', 1) != 1:
                logger.info(
                    '"%s" only supports 1 shot. '
                    'Setting shots=1 for circuit "%s".', name, exp_name)
                experiment.config.shots = 1
            for operation in experiment.instructions:
                if operation.name in ['measure', 'reset']:
                    raise AerError(
                        'Unsupported "%s" instruction "%s" ' +
                        'in circuit "%s" ', name, operation.name, exp_name)
Ejemplo n.º 3
0
    def _validate(self, qobj, backend_options, noise_model):
        """Semantic validations of the qobj which cannot be done via schemas.

        1. Check number of qubits will fit in local memory.
        2. warn if no classical registers or measurements in circuit.
        """
        clifford_instructions = [
            "id", "x", "y", "z", "h", "s", "sdg", "CX", "cx", "cz", "swap",
            "barrier", "reset", "measure"
        ]
        # Check if noise model is Clifford:
        method = "automatic"
        if backend_options and "method" in backend_options:
            method = backend_options["method"]

        clifford_noise = not (method == "statevector")

        if clifford_noise:
            if method != "stabilizer" and noise_model:
                for error in noise_model.as_dict()['errors']:
                    if error['type'] == 'qerror':
                        for circ in error["instructions"]:
                            for instr in circ:
                                if instr not in clifford_instructions:
                                    clifford_noise = False
                                    break
        # Check to see if experiments are clifford
        for experiment in qobj.experiments:
            name = experiment.header.name
            # Check for classical bits
            if experiment.config.memory_slots == 0:
                logger.warning(
                    'No classical registers in circuit "%s": '
                    'result data will not contain counts.', name)
            # Check if Clifford circuit or if measure opts missing
            no_measure = True
            clifford = False if method == "statevector" else clifford_noise
            for op in experiment.instructions:
                if not clifford and not no_measure:
                    break  # we don't need to check any more ops
                if clifford and op.name not in clifford_instructions:
                    clifford = False
                if no_measure and op.name == "measure":
                    no_measure = False
            # Print warning if clbits but no measure
            if no_measure:
                logger.warning(
                    'No measurements in circuit "%s": '
                    'count data will return all zeros.', name)
            # Check qubits for statevector simulation
            if not clifford:
                n_qubits = experiment.config.n_qubits
                max_qubits = self.configuration().n_qubits
                if n_qubits > max_qubits:
                    system_memory = int(local_hardware_info()['memory'])
                    raise AerError(
                        'Number of qubits ({}) '.format(n_qubits) +
                        'is greater than maximum ({}) '.format(max_qubits) +
                        'for "{}" (method=statevector) '.format(self.name()) +
                        'with {} GB system memory.'.format(system_memory))
Ejemplo n.º 4
0
    def _format_qobj_str(self, qobj, backend_options, noise_model):
        """Format qobj string for qiskit aer controller"""
        # Save original qobj config so we can revert our modification
        # after execution
        original_config = qobj.config
        # Convert to dictionary and add new parameters
        # from noise model and backend options
        config = original_config.as_dict()
        if backend_options is not None:
            for key, val in backend_options.items():
                config[key] = val
        if not "available_memory" in config:
            available_mb = int(local_hardware_info()['memory'] * 1024)
            config['available_memory'] = available_mb
        # Add noise model
        if noise_model is not None:
            config["noise_model"] = noise_model

        # Add runtime config
        config['library_dir'] = self.configuration().library_dir
        qobj.config = QobjConfig.from_dict(config)
        # Get the JSON serialized string
        output = json.dumps(qobj, cls=AerJSONEncoder).encode('UTF-8')
        # Revert original qobj
        qobj.config = original_config
        # Return output
        return output
Ejemplo n.º 5
0
class QiskitAquaGlobals(object):
    """Aqua class for global properties."""

    CPU_COUNT = local_hardware_info()['cpus']

    def __init__(self):
        self._random_seed = None
        self._num_processes = QiskitAquaGlobals.CPU_COUNT
        self._random = None

    @property
    def random_seed(self):
        """Return random seed."""
        return self._random_seed

    @random_seed.setter
    def random_seed(self, seed):
        """Set random seed."""
        self._random_seed = seed
        self._random = None

    @property
    def num_processes(self):
        """Return num processes."""
        return self._num_processes

    @num_processes.setter
    def num_processes(self, num_processes):
        """Set num processes."""
        if num_processes < 1:
            raise AquaError('Invalid Number of Processes {}.'.format(num_processes))
        if num_processes > QiskitAquaGlobals.CPU_COUNT:
            raise AquaError('Number of Processes {} cannot be greater than cpu count {}.'.format(num_processes, QiskitAquaGlobals._CPU_COUNT))
        self._num_processes = num_processes
        # TODO: change Terra CPU_COUNT until issue gets resolved: https://github.com/Qiskit/qiskit-terra/issues/1963
        try:
            qiskit.tools.parallel.CPU_COUNT = self.num_processes
        except Exception as e:
            logger.warning("Failed to set qiskit.tools.parallel.CPU_COUNT to value: '{}': Error: '{}'".format(self.num_processes, str(e)))

    @property
    def random(self):
        """Return a numpy random."""
        if self._random is None:
            if self._random_seed is None:
                self._random = np.random
            else:
                self._random = np.random.RandomState(self._random_seed)
        return self._random
Ejemplo n.º 6
0
    def _validate(self, qobj):
        """Semantic validations of the qobj which cannot be done via schemas.

        1. Check number of qubits will fit in local memory.
        2. warn if no classical registers or measurements in circuit.
        """
        n_qubits = qobj.config.n_qubits
        max_qubits = self.configuration().n_qubits
        if n_qubits > max_qubits:
            raise AerError('Number of qubits ({}) '.format(n_qubits) +
                           'is greater than maximum ({}) '.format(max_qubits) +
                           'for "{}" '.format(self.name()) +
                           'with {} GB system memory.'.format(int(local_hardware_info()['memory'])))
        for experiment in qobj.experiments:
            name = experiment.header.name
            if experiment.config.memory_slots == 0:
                logger.warning('No classical registers in circuit "%s": '
                               'result data will not contain counts.', name)
            elif 'measure' not in [op.name for op in experiment.instructions]:
                logger.warning('No measurements in circuit "%s": '
                               'count data will return all zeros.', name)
Ejemplo n.º 7
0
class StatevectorSimulatorPy(QasmSimulatorPy):
    """Python statevector simulator."""

    MAX_QUBITS_MEMORY = int(log2(local_hardware_info()['memory'] * (1024 ** 3) / 16))

    DEFAULT_CONFIGURATION = {
        'backend_name': 'statevector_simulator',
        'backend_version': '1.0.0',
        'n_qubits': min(24, MAX_QUBITS_MEMORY),
        'url': 'https://github.com/Qiskit/qiskit-terra',
        'simulator': True,
        'local': True,
        'conditional': True,
        'open_pulse': False,
        'memory': True,
        'max_shots': 65536,
        'description': 'A Python statevector simulator for qobj files',
        'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id', 'snapshot'],
        'gates': [
            {
                'name': 'u1',
                'parameters': ['lambda'],
                'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }'
            },
            {
                'name': 'u2',
                'parameters': ['phi', 'lambda'],
                'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }'
            },
            {
                'name': 'u3',
                'parameters': ['theta', 'phi', 'lambda'],
                'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }'
            },
            {
                'name': 'cx',
                'parameters': ['c', 't'],
                'qasm_def': 'gate cx c,t { CX c,t; }'
            },
            {
                'name': 'id',
                'parameters': ['a'],
                'qasm_def': 'gate id a { U(0,0,0) a; }'
            },
            {
                'name': 'snapshot',
                'parameters': ['slot'],
                'qasm_def': 'gate snapshot(slot) q { TODO }'
            }
        ]
    }

    # Override base class value to return the final state vector
    SHOW_FINAL_STATE = True

    def __init__(self, configuration=None, provider=None):
        super().__init__(configuration=(configuration or
                                        BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION)),
                         provider=provider)

    def run(self, qobj, backend_options=None):
        """Run qobj asynchronously.

        Args:
            qobj (Qobj): payload of the experiment
            backend_options (dict): backend options

        Returns:
            SimulatorsJob: derived from BaseJob

        Additional Information:
            backend_options: Is a dict of options for the backend. It may contain
                * "initial_statevector": vector_like
                * "chop_threshold": double

            The "initial_statevector" option specifies a custom initial
            initial statevector for the simulator to be used instead of the all
            zero state. This size of this vector must be correct for the number
            of qubits in all experiments in the qobj.

            The "chop_threshold" option specifies a trunctation value for
            setting small values to zero in the output statevector. The default
            value is 1e-15.

            Example:
            backend_options = {
                "initial_statevector": np.array([1, 0, 0, 1j]) / np.sqrt(2),
                "chop_threshold": 1e-15
            }
        """
        return super().run(qobj, backend_options=backend_options)

    def _validate(self, qobj):
        """Semantic validations of the qobj which cannot be done via schemas.
        Some of these may later move to backend schemas.

        1. No shots
        2. No measurements in the middle
        """
        n_qubits = qobj.config.n_qubits
        max_qubits = self.configuration().n_qubits
        if n_qubits > max_qubits:
            raise SimulatorError('Number of qubits {} '.format(n_qubits) +
                                 'is greater than maximum ({}) '.format(max_qubits) +
                                 'for "{}".'.format(self.name()))
        if qobj.config.shots != 1:
            logger.info('"%s" only supports 1 shot. Setting shots=1.',
                        self.name())
            qobj.config.shots = 1
        for experiment in qobj.experiments:
            name = experiment.header.name
            if getattr(experiment.config, 'shots', 1) != 1:
                logger.info('"{}" only supports 1 shot. ' +
                            'Setting shots=1 for circuit "{}".',
                            self.name(), name)
                experiment.config.shots = 1
Ejemplo n.º 8
0
class UnitarySimulator(AerBackend):
    """Unitary circuit simulator.

    Backend options:

        The following backend options may be used with in the
        `backend_options` kwarg diction for `UnitarySimulator.run` or
        `qiskit.execute`

        * "initial_unitary" (matrix_like): Sets a custom initial unitary
            matrix for the simulation instead of identity (Default: None).

        * "chop_threshold" (double): Sets the threshold for truncating small
            values to zero in the Result data (Default: 1e-15)

        * "max_parallel_threads" (int): Sets the maximum number of CPU
            cores used by OpenMP for parallelization. If set to 0 the
            maximum will be set to the number of CPU cores (Default: 0).

         * "max_parallel_experiments" (int): Sets the maximum number of
            qobj experiments that may be executed in parallel up to the
            max_parallel_threads value. If set to 1 parallel circuit
            execution will be disabled. If set to 0 the maximum will be
            automatically set to max_parallel_threads (Default: 1).

        * "unitary_parallel_threshold" (int): Sets the threshold that
            "n_qubits" must be greater than to enable OpenMP
            parallelization for matrix multiplication during execution of
            an experiment. If parallel circuit execution is enabled this
            will only use unallocated CPU cores up to max_parallel_threads.
            Note that setting this too low can reduce performance
            (Default: 6).
    """

    MAX_QUBITS_MEMORY = int(
        log2(sqrt(local_hardware_info()['memory'] * (1024**3) / 16)))

    DEFAULT_CONFIGURATION = {
        'backend_name':
        'unitary_simulator',
        'backend_version':
        __version__,
        'n_qubits':
        MAX_QUBITS_MEMORY,
        'url':
        'https://github.com/Qiskit/qiskit-aer',
        'simulator':
        True,
        'local':
        True,
        'conditional':
        False,
        'open_pulse':
        False,
        'memory':
        False,
        'max_shots':
        1,
        'description':
        'A Python simulator for computing the unitary' +
        'matrix for experiments in qobj files',
        'basis_gates': [
            'u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg',
            't', 'tdg', 'ccx', 'swap', 'snapshot', 'unitary'
        ],
        'gates': [{
            'name': 'TODO',
            'parameters': [],
            'qasm_def': 'TODO'
        }]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(unitary_controller_execute,
                         BackendConfiguration.from_dict(
                             self.DEFAULT_CONFIGURATION),
                         provider=provider)

    def run(self, qobj, backend_options=None):
        """Run a qobj on the backend.

        Args:
            qobj (Qobj): a Qobj.
            backend_options (dict): backend configuration options.

        Returns:
            AerJob: the simulation job.
        """
        return super().run(qobj, backend_options=backend_options)

    def _validate(self, qobj):
        """Semantic validations of the qobj which cannot be done via schemas.
        Some of these may later move to backend schemas.

        1. No shots
        2. No measurements or reset
        """
        if qobj.config.shots != 1:
            logger.info("UnitarySimulator only supports 1 shot. "
                        "Setting shots=1.")
            qobj.config.shots = 1
        for experiment in qobj.experiments:
            # Check for measure or reset operations
            for pos, instr in reversed(list(enumerate(
                    experiment.instructions))):
                if instr.name == "measure":
                    raise AerError(
                        "UnitarySimulator: circuit contains measure.")
                if instr.name == "reset":
                    raise AerError("UnitarySimulator: circuit contains reset.")
            # Set shots to 1
            if getattr(experiment.config, 'shots', 1) != 1:
                logger.info(
                    "UnitarySimulator only supports 1 shot. "
                    "Setting shots=1 for circuit %s.", experiment.header.name)
                experiment.config.shots = 1
            # Set memory slots to 0
            if getattr(experiment.config, 'memory_slots', 0) != 0:
                experiment.config.memory_slots = 0
Ejemplo n.º 9
0
class UnitarySimulator(AerBackend):
    """Unitary circuit simulator.

    Backend options:

        The following backend options may be used with in the
        `backend_options` kwarg diction for `UnitarySimulator.run` or
        `qiskit.execute`

        * "initial_unitary" (matrix_like): Sets a custom initial unitary
            matrix for the simulation instead of identity (Default: None).

        * "chop_threshold" (double): Sets the threshold for truncating small
            values to zero in the Result data (Default: 1e-15)

        * "max_parallel_threads" (int): Sets the maximum number of CPU
            cores used by OpenMP for parallelization. If set to 0 the
            maximum will be set to the number of CPU cores (Default: 0).

         * "max_parallel_experiments" (int): Sets the maximum number of
            qobj experiments that may be executed in parallel up to the
            max_parallel_threads value. If set to 1 parallel circuit
            execution will be disabled. If set to 0 the maximum will be
            automatically set to max_parallel_threads (Default: 1).

        * "unitary_parallel_threshold" (int): Sets the threshold that
            "n_qubits" must be greater than to enable OpenMP
            parallelization for matrix multiplication during execution of
            an experiment. If parallel circuit execution is enabled this
            will only use unallocated CPU cores up to max_parallel_threads.
            Note that setting this too low can reduce performance
            (Default: 6).
    """

    MAX_QUBIT_MEMORY = int(
        log2(sqrt(local_hardware_info()['memory'] * (1024**3) / 16)))

    DEFAULT_CONFIGURATION = {
        'backend_name':
        'unitary_simulator',
        'backend_version':
        __version__,
        'n_qubits':
        MAX_QUBIT_MEMORY,
        'url':
        'https://github.com/Qiskit/qiskit-aer',
        'simulator':
        True,
        'local':
        True,
        'conditional':
        False,
        'open_pulse':
        False,
        'memory':
        False,
        'max_shots':
        1,
        'description':
        'A Python simulator for computing the unitary' +
        'matrix for experiments in qobj files',
        'coupling_map':
        None,
        'basis_gates': [
            'u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg',
            't', 'tdg', 'ccx', 'swap', 'snapshot', 'unitary'
        ],
        'gates': [{
            'name': 'TODO',
            'parameters': [],
            'qasm_def': 'TODO'
        }],
        # Location where we put external libraries that will be loaded at runtime
        # by the simulator extension
        'library_dir':
        os.path.dirname(__file__)
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(unitary_controller_execute,
                         BackendConfiguration.from_dict(
                             self.DEFAULT_CONFIGURATION),
                         provider=provider)

    def _validate(self, qobj, backend_options, noise_model):
        """Semantic validations of the qobj which cannot be done via schemas.
        Some of these may later move to backend schemas.
        1. Set shots=1
        2. No measurements or reset
        3. Check number of qubits will fit in local memory.
        """
        name = self.name()
        if noise_model is not None:
            logger.error("{} cannot be run with a noise.".format(name))
            raise AerError("{} does not support noise.".format(name))

        n_qubits = qobj.config.n_qubits
        max_qubits = self.configuration().n_qubits
        if n_qubits > max_qubits:
            raise AerError('Number of qubits ({}) '.format(n_qubits) +
                           'is greater than maximum ({}) '.format(max_qubits) +
                           'for "{}" '.format(name) +
                           'with {} GB system memory.'.format(
                               int(local_hardware_info()['memory'])))
        if qobj.config.shots != 1:
            logger.info('"%s" only supports 1 shot. Setting shots=1.', name)
            qobj.config.shots = 1
        for experiment in qobj.experiments:
            exp_name = experiment.header.name
            if getattr(experiment.config, 'shots', 1) != 1:
                logger.info(
                    '"%s" only supports 1 shot. '
                    'Setting shots=1 for circuit "%s".', name, exp_name)
                experiment.config.shots = 1
            for operation in experiment.instructions:
                if operation.name in ['measure', 'reset']:
                    raise AerError(
                        'Unsupported "%s" instruction "%s" ' +
                        'in circuit "%s" ', name, operation.name, exp_name)
Ejemplo n.º 10
0
class QasmSimulator(BaseBackend):
    """C++ quantum circuit simulator with realistic noise"""

    DEFAULT_CONFIGURATION = {
        'backend_name':
        'qasm_simulator',
        'backend_version':
        '1.0.0',
        'n_qubits':
        int(log2(local_hardware_info()['memory'] * (1024**3) / 16)),
        'url':
        'https://github.com/Qiskit/qiskit-terra/src/qasm-simulator-cpp',
        'simulator':
        True,
        'local':
        True,
        'conditional':
        True,
        'open_pulse':
        False,
        'memory':
        True,
        'max_shots':
        65536,
        'description':
        'A C++ realistic noise simulator for qasm experiments',
        'basis_gates': [
            'u0', 'u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's',
            'sdg', 't', 'tdg', 'rzz', 'snapshot', 'wait', 'noise', 'save',
            'load'
        ],
        'gates': [{
            'name': 'TODO',
            'parameters': [],
            'qasm_def': 'TODO'
        }]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(configuration=(configuration
                                        or BackendConfiguration.from_dict(
                                            self.DEFAULT_CONFIGURATION)),
                         provider=provider)

        # Try to use the default executable if not specified.
        if 'exe' in self._configuration:
            paths = [self._configuration.exe]
        else:
            paths = DEFAULT_SIMULATOR_PATHS

        # Ensure that the executable is available.
        try:
            self._configuration.exe = next(
                path for path in paths
                if (os.path.exists(path) and os.path.getsize(path) > 100))
        except StopIteration:
            raise FileNotFoundError(
                'Simulator executable not found (using %s)' %
                getattr(self._configuration, 'exe', 'default locations'))

    def properties(self):
        """Return backend properties"""
        properties = {
            'backend_name':
            self.name(),
            'backend_version':
            self.configuration().backend_version,
            'last_update_date':
            '2000-01-01 00:00:00Z',
            'qubits': [[{
                'name': 'TODO',
                'date': '2000-01-01 00:00:00Z',
                'unit': 'TODO',
                'value': 0
            }]],
            'gates': [{
                'qubits': [0],
                'gate':
                'TODO',
                'parameters': [{
                    'name': 'TODO',
                    'date': '2000-01-01 00:00:00Z',
                    'unit': 'TODO',
                    'value': 0
                }]
            }],
            'general': []
        }

        return BackendProperties.from_dict(properties)

    def run(self, qobj):
        """Run a qobj on the backend."""
        job_id = str(uuid.uuid4())
        aer_job = AerJob(self, job_id, self._run_job, qobj)
        aer_job.submit()
        return aer_job

    def _run_job(self, job_id, qobj):
        self._validate(qobj)
        result = run(qobj, self._configuration.exe)
        result['job_id'] = job_id
        copy_qasm_from_qobj_into_result(qobj, result)

        return result_from_old_style_dict(result)

    def _validate(self, qobj):
        for experiment in qobj.experiments:
            if 'measure' not in [op.name for op in experiment.instructions]:
                logger.warning(
                    "no measurements in circuit '%s', "
                    "classical register will remain all zeros.",
                    experiment.header.name)
Ejemplo n.º 11
0
class QasmSimulator(AerBackend):
    """Aer quantum circuit simulator

    Backend options:

        The following backend options may be used with in the
        `backend_options` kwarg diction for `QasmSimulator.run` or
        `qiskit.execute`

        * "method" (str): Set the simulation method. Allowed values are:
            * "statevector": Uses a dense statevector simulation.
            * "stabilizer": uses a Clifford stabilizer state simulator that
            is only valid for Clifford circuits and noise models.
            * "ch": Uses an approximate simulator that decomposes circuits
            into stabilizer state terms, the number of which grows with the
            number of non-Clifford gates.
            * "automatic": automatically run on stabilizer simulator if
            the circuit and noise model supports it. If there is enough
            available memory, uses the statevector method. Otherwise, uses
            the ch method (Default: "automatic").

        * "available_memory" (int): Set the amount of memory (in MB)
        the simulator has access to (Default: Maximum available)

        * "initial_statevector" (vector_like): Sets a custom initial
            statevector for the simulation instead of the all zero
            initial state (Default: None).

        * "chop_threshold" (double): Sets the threshold for truncating small
            values to zero in the Result data (Default: 1e-15)

        * "max_parallel_threads" (int): Sets the maximum number of CPU
            cores used by OpenMP for parallelization. If set to 0 the
            maximum will be set to the number of CPU cores (Default: 0).

        * "max_parallel_experiments" (int): Sets the maximum number of
            qobj experiments that may be executed in parallel up to the
            max_parallel_threads value. If set to 1 parallel circuit
            execution will be disabled. If set to 0 the maximum will be
            automatically set to max_parallel_threads (Default: 1).

        * "max_parallel_shots" (int): Sets the maximum number of
            shots that may be executed in parallel during each experiment
            execution, up to the max_parallel_threads value. If set to 1
            parallel shot execution wil be disabled. If set to 0 the
            maximum will be automatically set to max_parallel_threads.
            Note that this cannot be enabled at the same time as parallel
            experiment execution (Default: 1).

        * "statevector_parallel_threshold" (int): Sets the threshold that
            "n_qubits" must be greater than to enable OpenMP
            parallelization for matrix multiplication during execution of
            an experiment. If parallel circuit or shot execution is enabled
            this will only use unallocated CPU cores up to
            max_parallel_threads. Note that setting this too low can reduce
            performance (Default: 12).

        * "statevector_sample_measure_opt" (int): Sets the threshold that
            the number of qubits must be greater than to enable a large
            qubit optimized implementation of measurement sampling. Note
            that setting this two low can reduce performance (Default: 10)

        * "statevector_hpc_gate_opt" (bool): If set to True this enables
            a different optimzied gate application routine that can
            increase performance on systems with a large number of CPU
            cores. For systems with a small number of cores it enabling
            can reduce performance (Default: False).

        * "ch_approximation_error" (double): Set the error in the 
            approximation for the ch method. A smaller error needs more
            memory and computational time. (Default: 0.05)

        * "ch_disable_measurement_opt" (bool): Force the simulator to
            re-run the monte-carlo step for every measurement. Enabling
            this will improve the sampling accuracy if the output
            distribution is strongly peaked, but requires more
            computational time. (Default: False)

        * "ch_mixing_time" (int): Set how long the monte-carlo method
            runs before performing measurements. If the output
            distribution is strongly peaked, this can be
            decreased alongside setting ch_disable_measurement_opt
            to True. (Default: 7000)

        * "ch_norm_estimation_samples" (int): Number of samples used to
            compute the correct normalisation for a statevector snapshot.
            (Default: 100)

        * "ch_parallel_threshold" (int): Set the minimum size of the ch
            decomposition before we enable OpenMP parallelisation. If
            parallel circuit or shot execution is enabled this will only
            use unallocated CPU cores up to max_parallel_threads. (Default: 100)
    """

    MAX_QUBIT_MEMORY = int(
        log2(local_hardware_info()['memory'] * (1024**3) / 16))

    DEFAULT_CONFIGURATION = {
        'backend_name':
        'qasm_simulator',
        'backend_version':
        __version__,
        'n_qubits':
        MAX_QUBIT_MEMORY,
        'url':
        'https://github.com/Qiskit/qiskit-aer',
        'simulator':
        True,
        'local':
        True,
        'conditional':
        True,
        'open_pulse':
        False,
        'memory':
        True,
        'max_shots':
        100000,
        'description':
        'A C++ simulator with realistic for qobj files',
        'basis_gates': [
            'u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg',
            't', 'tdg', 'ccx', 'swap', 'snapshot', 'unitary'
        ],
        'gates': [{
            'name': 'TODO',
            'parameters': [],
            'qasm_def': 'TODO'
        }],
        # Location where we put external libraries that will be loaded at runtime
        # by the simulator extension
        'library_dir':
        os.path.dirname(__file__)
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(qasm_controller_execute,
                         BackendConfiguration.from_dict(
                             self.DEFAULT_CONFIGURATION),
                         provider=provider)

    def _validate(self, qobj, backend_options, noise_model):
        """Semantic validations of the qobj which cannot be done via schemas.

        1. Check number of qubits will fit in local memory.
        2. warn if no classical registers or measurements in circuit.
        """
        clifford_instructions = [
            "id", "x", "y", "z", "h", "s", "sdg", "CX", "cx", "cz", "swap",
            "barrier", "reset", "measure"
        ]
        unsupported_ch_instructions = ["u2", "u3"]
        # Check if noise model is Clifford:
        method = "automatic"
        if backend_options and "method" in backend_options:
            method = backend_options["method"]

        clifford_noise = not (method == "statevector")

        if clifford_noise:
            if method != "stabilizer" and noise_model:
                for error in noise_model.as_dict()['errors']:
                    if error['type'] == 'qerror':
                        for circ in error["instructions"]:
                            for instr in circ:
                                if instr not in clifford_instructions:
                                    clifford_noise = False
                                    break
        # Check to see if experiments are clifford
        for experiment in qobj.experiments:
            name = experiment.header.name
            # Check for classical bits
            if experiment.config.memory_slots == 0:
                logger.warning(
                    'No classical registers in circuit "%s": '
                    'result data will not contain counts.', name)
            # Check if Clifford circuit or if measure opts missing
            no_measure = True
            ch_supported = False
            ch_supported = method in ["ch", "automatic"]
            clifford = False if method == "statevector" else clifford_noise
            for op in experiment.instructions:
                if not clifford and not no_measure:
                    break  # we don't need to check any more ops
                if clifford and op.name not in clifford_instructions:
                    clifford = False
                if no_measure and op.name == "measure":
                    no_measure = False
                if ch_supported and op.name in unsupported_ch_instructions:
                    ch_supported = False
            # Print warning if clbits but no measure
            if no_measure:
                logger.warning(
                    'No measurements in circuit "%s": '
                    'count data will return all zeros.', name)
            # Check qubits for statevector simulation
            if not clifford and method != "ch":
                n_qubits = experiment.config.n_qubits
                max_qubits = self.configuration().n_qubits
                if n_qubits > max_qubits:
                    system_memory = int(local_hardware_info()['memory'])
                    err_string = ('Number of qubits ({}) is greater than '
                                  'maximum ({}) for "{}" (method=statevector) '
                                  'with {} GB system memory')
                    err_string = err_string.format(n_qubits, max_qubits,
                                                   self.name(), system_memory)
                    if method != "automatic":
                        raise AerError(err_string + '.')
                    else:
                        if n_qubits > 63:
                            raise AerError(err_string + ', and has too many ' +
                                           'qubits to fall back to the ' +
                                           'CH simulator.')
                        if not ch_supported:
                            raise AerError(err_string + ', and contains ' +
                                           'instructions not supported by ' +
                                           'the CH simulator.')
                        logger.info('The QasmSimulator will automatically '
                                    'switch to the CH backend, based on '
                                    'the memory requirements.')
Ejemplo n.º 12
0
class StatevectorSimulatorPy(QasmSimulatorPy):
    """Python statevector simulator."""

    DEFAULT_CONFIGURATION = {
        'backend_name': 'statevector_simulator_py',
        'backend_version': '1.0.0',
        'n_qubits': int(log2(local_hardware_info()['memory'] * (1024**3)/16)),
        'url': 'https://github.com/Qiskit/qiskit-terra',
        'simulator': True,
        'local': True,
        'conditional': False,
        'open_pulse': False,
        'memory': False,
        'max_shots': 65536,
        'description': 'A Python statevector simulator for qobj files',
        'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id', 'snapshot'],
        'gates': [
            {
                'name': 'u1',
                'parameters': ['lambda'],
                'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }'
            },
            {
                'name': 'u2',
                'parameters': ['phi', 'lambda'],
                'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }'
            },
            {
                'name': 'u3',
                'parameters': ['theta', 'phi', 'lambda'],
                'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }'
            },
            {
                'name': 'cx',
                'parameters': ['c', 't'],
                'qasm_def': 'gate cx c,t { CX c,t; }'
            },
            {
                'name': 'id',
                'parameters': ['a'],
                'qasm_def': 'gate id a { U(0,0,0) a; }'
            },
            {
                'name': 'snapshot',
                'parameters': ['slot'],
                'qasm_def': 'gate snapshot(slot) q { TODO }'
            }
        ]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(configuration=(configuration or
                                        BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION)),
                         provider=provider)

    def run(self, qobj):
        """Run qobj asynchronously.

        Args:
            qobj (dict): job description

        Returns:
            AerJob: derived from BaseJob
        """
        job_id = str(uuid.uuid4())
        aer_job = AerJob(self, job_id, self._run_job, qobj)
        aer_job.submit()
        return aer_job

    def _run_job(self, job_id, qobj):
        """Run a Qobj on the backend."""
        self._validate(qobj)
        final_state_key = 32767  # Internal key for final state snapshot
        # Add final snapshots to circuits
        for experiment in qobj.experiments:
            experiment.instructions.append(
                QobjInstruction(name='snapshot', params=[final_state_key],
                                label='MISSING', type='MISSING')
            )
        result = super()._run_job(job_id, qobj)
        # Remove added snapshot from qobj
        for experiment in qobj.experiments:
            del experiment.instructions[-1]
        # Extract final state snapshot and move to 'statevector' data field
        for experiment_result in result.results:
            snapshots = experiment_result.data.snapshots.to_dict()
            if str(final_state_key) in snapshots:
                final_state_key = str(final_state_key)
            # Pop off final snapshot added above
            final_state = snapshots.pop(final_state_key, None)
            final_state = final_state['statevector'][0]
            # Add final state to results data
            experiment_result.data.statevector = final_state
            # Remove snapshot dict if empty
            if snapshots == {}:
                delattr(experiment_result.data, 'snapshots')
        return result

    def _validate(self, qobj):
        """Semantic validations of the qobj which cannot be done via schemas.
        Some of these may later move to backend schemas.

        1. No shots
        2. No measurements in the middle
        """
        if qobj.config.shots != 1:
            logger.info("statevector simulator only supports 1 shot. "
                        "Setting shots=1.")
            qobj.config.shots = 1
        for experiment in qobj.experiments:
            if getattr(experiment.config, 'shots', 1) != 1:
                logger.info("statevector simulator only supports 1 shot. "
                            "Setting shots=1 for circuit %s.", experiment.name)
                experiment.config.shots = 1
            for op in experiment.instructions:
                if op.name in ['measure', 'reset']:
                    raise SimulatorError(
                        "In circuit {}: statevector simulator does not support "
                        "measure or reset.".format(experiment.header.name))
Ejemplo n.º 13
0
class UnitarySimulatorPy(BaseBackend):
    """Python implementation of a unitary simulator."""

    MAX_QUBITS_MEMORY = int(
        log2(sqrt(local_hardware_info()['memory'] * (1024**3) / 16)))

    DEFAULT_CONFIGURATION = {
        'backend_name':
        'unitary_simulator',
        'backend_version':
        '1.0.0',
        'n_qubits':
        min(24, MAX_QUBITS_MEMORY),
        'url':
        'https://github.com/Qiskit/qiskit-terra',
        'simulator':
        True,
        'local':
        True,
        'conditional':
        False,
        'open_pulse':
        False,
        'memory':
        False,
        'max_shots':
        65536,
        'description':
        'A python simulator for unitary matrix corresponding to a circuit',
        'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id'],
        'gates': [{
            'name': 'u1',
            'parameters': ['lambda'],
            'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }'
        }, {
            'name': 'u2',
            'parameters': ['phi', 'lambda'],
            'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }'
        }, {
            'name':
            'u3',
            'parameters': ['theta', 'phi', 'lambda'],
            'qasm_def':
            'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }'
        }, {
            'name': 'cx',
            'parameters': ['c', 't'],
            'qasm_def': 'gate cx c,t { CX c,t; }'
        }, {
            'name': 'id',
            'parameters': ['a'],
            'qasm_def': 'gate id a { U(0,0,0) a; }'
        }]
    }

    DEFAULT_OPTIONS = {"initial_unitary": None, "chop_threshold": 1e-15}

    def __init__(self, configuration=None, provider=None):
        super().__init__(configuration=(configuration
                                        or BackendConfiguration.from_dict(
                                            self.DEFAULT_CONFIGURATION)),
                         provider=provider)

        # Define attributes inside __init__.
        self._unitary = None
        self._number_of_qubits = 0
        self._initial_unitary = None
        self._chop_threshold = 1e-15

    def _add_unitary_single(self, gate, qubit):
        """Apply an arbitrary 1-qubit unitary matrix.

        Args:
            gate (matrix_like): a single qubit gate matrix
            qubit (int): the qubit to apply gate to
        """
        # Convert to complex rank-2 tensor
        gate_tensor = np.array(gate, dtype=complex)
        # Compute einsum index string for 1-qubit matrix multiplication
        indexes = einsum_matmul_index([qubit], self._number_of_qubits)
        # Apply matrix multiplication
        self._unitary = np.einsum(indexes,
                                  gate_tensor,
                                  self._unitary,
                                  dtype=complex,
                                  casting='no')

    def _add_unitary_two(self, gate, qubit0, qubit1):
        """Apply a two-qubit unitary matrix.

        Args:
            gate (matrix_like): a the two-qubit gate matrix
            qubit0 (int): gate qubit-0
            qubit1 (int): gate qubit-1
        """

        # Convert to complex rank-4 tensor
        gate_tensor = np.reshape(np.array(gate, dtype=complex), 4 * [2])

        # Compute einsum index string for 2-qubit matrix multiplication
        indexes = einsum_matmul_index([qubit0, qubit1], self._number_of_qubits)

        # Apply matrix multiplication
        self._unitary = np.einsum(indexes,
                                  gate_tensor,
                                  self._unitary,
                                  dtype=complex,
                                  casting='no')

    def _validate_initial_unitary(self):
        """Validate an initial unitary matrix"""
        # If initial unitary isn't set we don't need to validate
        if self._initial_unitary is None:
            return
        # Check unitary is correct length for number of qubits
        shape = np.shape(self._initial_unitary)
        required_shape = (2**self._number_of_qubits, 2**self._number_of_qubits)
        if shape != required_shape:
            raise SimulatorError('initial unitary is incorrect shape: ' +
                                 '{} != 2 ** {}'.format(shape, required_shape))

    def _set_options(self, qobj_config=None, backend_options=None):
        """Set the backend options for all experiments in a qobj"""
        # Reset default options
        self._initial_unitary = self.DEFAULT_OPTIONS["initial_unitary"]
        self._chop_threshold = self.DEFAULT_OPTIONS["chop_threshold"]
        if backend_options is None:
            backend_options = {}

        # Check for custom initial statevector in backend_options first,
        # then config second
        if 'initial_unitary' in backend_options:
            self._initial_unitary = np.array(
                backend_options['initial_unitary'], dtype=complex)
        elif hasattr(qobj_config, 'initial_unitary'):
            self._initial_unitary = np.array(qobj_config.initial_unitary,
                                             dtype=complex)
        if self._initial_unitary is not None:
            # Check the initial unitary is actually unitary
            shape = np.shape(self._initial_unitary)
            if len(shape) != 2 or shape[0] != shape[1]:
                raise SimulatorError("initial unitary is not a square matrix")
            iden = np.eye(len(self._initial_unitary))
            u_dagger_u = np.dot(self._initial_unitary.T.conj(),
                                self._initial_unitary)
            norm = np.linalg.norm(u_dagger_u - iden)
            if round(norm, 10) != 0:
                raise SimulatorError("initial unitary is not unitary")
            # Check the initial statevector is normalized

        # Check for custom chop threshold
        # Replace with custom options
        if 'chop_threshold' in backend_options:
            self._chop_threshold = backend_options['chop_threshold']
        elif hasattr(qobj_config, 'chop_threshold'):
            self._chop_threshold = qobj_config.chop_threshold

    def _initialize_unitary(self):
        """Set the initial unitary for simulation"""
        self._validate_initial_unitary()
        if self._initial_unitary is None:
            # Set to identity matrix
            self._unitary = np.eye(2**self._number_of_qubits, dtype=complex)
        else:
            self._unitary = self._initial_unitary.copy()
        # Reshape to rank-N tensor
        self._unitary = np.reshape(self._unitary,
                                   self._number_of_qubits * [2, 2])

    def _get_unitary(self):
        """Return the current unitary in JSON Result spec format"""
        unitary = np.reshape(self._unitary, 2 * [2**self._number_of_qubits])
        # Expand complex numbers
        unitary = np.stack((unitary.real, unitary.imag), axis=-1)
        # Truncate small values
        unitary[abs(unitary) < self._chop_threshold] = 0.0
        return unitary

    def run(self, qobj, backend_options=None):
        """Run qobj asynchronously.

        Args:
            qobj (Qobj): payload of the experiment
            backend_options (dict): backend options

        Returns:
            SimulatorsJob: derived from BaseJob

        Additional Information::

            backend_options: Is a dict of options for the backend. It may contain
                * "initial_unitary": matrix_like
                * "chop_threshold": double

            The "initial_unitary" option specifies a custom initial unitary
            matrix for the simulator to be used instead of the identity
            matrix. This size of this matrix must be correct for the number
            of qubits inall experiments in the qobj.

            The "chop_threshold" option specifies a trunctation value for
            setting small values to zero in the output unitary. The default
            value is 1e-15.

            Example::

                backend_options = {
                    "initial_unitary": np.array([[1, 0, 0, 0],
                                                 [0, 0, 0, 1],
                                                 [0, 0, 1, 0],
                                                 [0, 1, 0, 0]])
                    "chop_threshold": 1e-15
                }
        """
        self._set_options(qobj_config=qobj.config,
                          backend_options=backend_options)
        job_id = str(uuid.uuid4())
        job = SimulatorsJob(self, job_id, self._run_job, qobj)
        job.submit()
        return job

    def _run_job(self, job_id, qobj):
        """Run experiments in qobj.

        Args:
            job_id (str): unique id for the job.
            qobj (Qobj): job description

        Returns:
            Result: Result object
        """
        self._validate(qobj)
        result_list = []
        start = time.time()
        for experiment in qobj.experiments:
            result_list.append(self.run_experiment(experiment))
        end = time.time()
        result = {
            'backend_name': self.name(),
            'backend_version': self._configuration.backend_version,
            'qobj_id': qobj.qobj_id,
            'job_id': job_id,
            'results': result_list,
            'status': 'COMPLETED',
            'success': True,
            'time_taken': (end - start),
            'header': qobj.header.as_dict()
        }

        return Result.from_dict(result)

    def run_experiment(self, experiment):
        """Run an experiment (circuit) and return a single experiment result.

        Args:
            experiment (QobjExperiment): experiment from qobj experiments list

        Returns:
            dict: A result dictionary which looks something like::

                {
                "name": name of this experiment (obtained from qobj.experiment header)
                "seed": random seed used for simulation
                "shots": number of shots used in the simulation
                "data":
                    {
                    "unitary": [[[0.0, 0.0], [1.0, 0.0]],
                                [[1.0, 0.0], [0.0, 0.0]]]
                    },
                "status": status string for the simulation
                "success": boolean
                "time taken": simulation time of this single experiment
                }

        Raises:
            SimulatorError: if the number of qubits in the circuit is greater than 24.
            Note that the practical qubit limit is much lower than 24.
        """
        start = time.time()
        self._number_of_qubits = experiment.header.n_qubits

        # Validate the dimension of initial unitary if set
        self._validate_initial_unitary()
        self._initialize_unitary()

        for operation in experiment.instructions:
            # Check if single  gate
            if operation.name in ('U', 'u1', 'u2', 'u3'):
                params = getattr(operation, 'params', None)
                qubit = operation.qubits[0]
                gate = single_gate_matrix(operation.name, params)
                self._add_unitary_single(gate, qubit)
            elif operation.name in ('id', 'u0'):
                pass
            # Check if CX gate
            elif operation.name in ('CX', 'cx'):
                qubit0 = operation.qubits[0]
                qubit1 = operation.qubits[1]
                gate = cx_gate_matrix()
                self._add_unitary_two(gate, qubit0, qubit1)
            # Check if barrier
            elif operation.name == 'barrier':
                pass
            else:
                backend = self.name()
                err_msg = '{0} encountered unrecognized operation "{1}"'
                raise SimulatorError(err_msg.format(backend, operation.name))
        # Add final state to data
        data = {'unitary': self._get_unitary()}
        end = time.time()
        return {
            'name': experiment.header.name,
            'shots': 1,
            'data': data,
            'status': 'DONE',
            'success': True,
            'time_taken': (end - start),
            'header': experiment.header.as_dict()
        }

    def _validate(self, qobj):
        """Semantic validations of the qobj which cannot be done via schemas.
        Some of these may later move to backend schemas.
        1. No shots
        2. No measurements in the middle
        """
        n_qubits = qobj.config.n_qubits
        max_qubits = self.configuration().n_qubits
        if n_qubits > max_qubits:
            raise SimulatorError(
                'Number of qubits {} '.format(n_qubits) +
                'is greater than maximum ({}) '.format(max_qubits) +
                'for "{}".'.format(self.name()))
        if qobj.config.shots != 1:
            logger.info('"%s" only supports 1 shot. Setting shots=1.',
                        self.name())
            qobj.config.shots = 1
        for experiment in qobj.experiments:
            name = experiment.header.name
            if getattr(experiment.config, 'shots', 1) != 1:
                logger.info(
                    '"%s" only supports 1 shot. ' +
                    'Setting shots=1 for circuit "%s".', self.name(), name)
                experiment.config.shots = 1
            for operation in experiment.instructions:
                if operation.name in ['measure', 'reset']:
                    raise SimulatorError(
                        'Unsupported "%s" instruction "%s" ' +
                        'in circuit "%s" ', self.name(), operation.name, name)
class UnitarySimulatorPy(BaseBackend):
    """Python implementation of a unitary simulator."""

    max_qubits = int(
        log2(sqrt(local_hardware_info()['memory'] * (1024**3)) / 16))

    DEFAULT_CONFIGURATION = {
        'backend_name':
        'unitary_simulator_py',
        'backend_version':
        '1.0.0',
        'n_qubits':
        max_qubits,
        'url':
        'https://github.com/Qiskit/qiskit-terra',
        'simulator':
        True,
        'local':
        True,
        'conditional':
        False,
        'open_pulse':
        False,
        'memory':
        False,
        'max_shots':
        65536,
        'description':
        'A python simulator for unitary matrix corresponding to a circuit',
        'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id'],
        'gates': [{
            'name': 'u1',
            'parameters': ['lambda'],
            'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }'
        }, {
            'name': 'u2',
            'parameters': ['phi', 'lambda'],
            'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }'
        }, {
            'name':
            'u3',
            'parameters': ['theta', 'phi', 'lambda'],
            'qasm_def':
            'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }'
        }, {
            'name': 'cx',
            'parameters': ['c', 't'],
            'qasm_def': 'gate cx c,t { CX c,t; }'
        }, {
            'name': 'id',
            'parameters': ['a'],
            'qasm_def': 'gate id a { U(0,0,0) a; }'
        }]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(configuration=(configuration
                                        or BackendConfiguration.from_dict(
                                            self.DEFAULT_CONFIGURATION)),
                         provider=provider)

        # Define attributes inside __init__.
        self._unitary_state = None
        self._number_of_qubits = 0

    def _add_unitary_single(self, gate, qubit):
        """Apply the single-qubit gate.

        gate is the single-qubit gate.
        qubit is the qubit to apply it on counts from 0 and order
            is q_{n-1} ... otimes q_1 otimes q_0.
        number_of_qubits is the number of qubits in the system.
        """
        # Convert to complex rank-2 tensor
        gate_tensor = np.array(gate, dtype=complex)
        # Compute einsum index string for 1-qubit matrix multiplication
        indexes = einsum_matmul_index([qubit], self._number_of_qubits)
        # Apply matrix multiplication
        self._unitary_state = np.einsum(indexes,
                                        gate_tensor,
                                        self._unitary_state,
                                        dtype=complex,
                                        casting='no')

    def _add_unitary_two(self, gate, qubit0, qubit1):
        """Apply the two-qubit gate.

        gate is the two-qubit gate
        qubit0 is the first qubit (control) counts from 0
        qubit1 is the second qubit (target)
        returns a complex numpy array
        """

        # Convert to complex rank-4 tensor
        gate_tensor = np.reshape(np.array(gate, dtype=complex), 4 * [2])

        # Compute einsum index string for 2-qubit matrix multiplication
        indexes = einsum_matmul_index([qubit0, qubit1], self._number_of_qubits)

        # Apply matrix multiplication
        self._unitary_state = np.einsum(indexes,
                                        gate_tensor,
                                        self._unitary_state,
                                        dtype=complex,
                                        casting='no')

    def run(self, qobj):
        """Run qobj asynchronously.

        Args:
            qobj (dict): job description

        Returns:
            AerJob: derived from BaseJob
        """
        job_id = str(uuid.uuid4())
        aer_job = AerJob(self, job_id, self._run_job, qobj)
        aer_job.submit()
        return aer_job

    def _run_job(self, job_id, qobj):
        """Run experiments in qobj.

        Args:
            job_id (str): unique id for the job.
            qobj (Qobj): job description

        Returns:
            Result: Result object
        """
        self._validate(qobj)
        result_list = []
        start = time.time()
        for experiment in qobj.experiments:
            result_list.append(self.run_experiment(experiment))
        end = time.time()
        result = {
            'backend_name': self.name(),
            'backend_version': self._configuration.backend_version,
            'qobj_id': qobj.qobj_id,
            'job_id': job_id,
            'results': result_list,
            'status': 'COMPLETED',
            'success': True,
            'time_taken': (end - start),
            'header': qobj.header.as_dict()
        }

        return Result.from_dict(result)

    def run_experiment(self, experiment):
        """Run an experiment (circuit) and return a single experiment result.

        Args:
            experiment (QobjExperiment): experiment from qobj experiments list

        Returns:
            dict: A dictionary of results.
            dict: A result dictionary which looks something like::
                {
                "name": name of this experiment (obtained from qobj.experiment header)
                "seed": random seed used for simulation
                "shots": number of shots used in the simulation
                "data":
                    {
                    "unitary": [[0.2, 0.6, j+0.1, 0.2j],
                                [0, 0.9+j, 0.5, 0.7],
                                [-j, -0.1, -3.14, 0],
                                [0, 0, 0.5j, j-0.5]]
                    },
                "status": status string for the simulation
                "success": boolean
                "time taken": simulation time of this single experiment
                }

        Raises:
            SimulatorError: if the number of qubits in the circuit is greater than 24.
            Note that the practical qubit limit is much lower than 24.
        """
        self._number_of_qubits = experiment.header.n_qubits
        if self._number_of_qubits > self.max_qubits:
            raise SimulatorError(
                "np.einsum implementation limits unitary_simulator_py" +
                " to 24 qubit circuits.")
        result = {
            'data': {},
            'name': experiment.header.name,
            'header': experiment.header.as_dict()
        }

        # Initialize unitary as rank 2*N tensor
        self._unitary_state = np.reshape(
            np.eye(2**self._number_of_qubits, dtype=complex),
            self._number_of_qubits * [2, 2])

        for operation in experiment.instructions:
            if operation.name in ('U', 'u1', 'u2', 'u3'):
                params = getattr(operation, 'params', None)
                qubit = operation.qubits[0]
                gate = single_gate_matrix(operation.name, params)
                self._add_unitary_single(gate, qubit)
            elif operation.name in ('id', 'u0'):
                pass
            elif operation.name in ('CX', 'cx'):
                qubit0 = operation.qubits[0]
                qubit1 = operation.qubits[1]
                gate = np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0],
                                 [0, 1, 0, 0]])
                self._add_unitary_two(gate, qubit0, qubit1)
            elif operation.name == 'barrier':
                pass
            else:
                result['status'] = 'ERROR'
                return result
        # Reshape unitary rank-2n tensor back to a matrix
        tmp = np.reshape(self._unitary_state, 2 * [2**self._number_of_qubits])
        # Convert complex numbers to pair of (real, imag)
        result['data']['unitary'] = np.stack((tmp.real, tmp.imag), axis=-1)
        result['status'] = 'DONE'
        result['success'] = True
        result['shots'] = 1
        return result

    def _validate(self, qobj):
        """Semantic validations of the qobj which cannot be done via schemas.
        Some of these may later move to backend schemas.
        1. No shots
        2. No measurements in the middle
        """
        if qobj.config.shots != 1:
            logger.info("unitary simulator only supports 1 shot. "
                        "Setting shots=1.")
            qobj.config.shots = 1
        for experiment in qobj.experiments:
            if getattr(experiment.config, 'shots', 1) != 1:
                logger.info(
                    "unitary simulator only supports 1 shot. "
                    "Setting shots=1 for circuit %s.", experiment.name)
                experiment.config.shots = 1
            for operation in experiment.instructions:
                if operation.name in ['measure', 'reset']:
                    raise SimulatorError(
                        "In circuit {}: unitary simulator does not support "
                        "measure or reset.".format(experiment.header.name))
Ejemplo n.º 15
0
 def test_local_hardware_none_cpu_count(self, cpu_count_mock, vmem_mock,
                                        platform_mock):
     """Test cpu count fallback to 1 when true value can't be determined"""
     # pylint: disable=unused-argument
     result = _util.local_hardware_info()
     self.assertEqual(1, result['cpus'])
Ejemplo n.º 16
0
class QasmSimulator(AerBackend):
    """Aer quantum circuit simulator

    Backend options:

        The following backend options may be used with in the
        `backend_options` kwarg diction for `QasmSimulator.run` or
        `qiskit.execute`

        * "method" (str): Set the simulation method. Allowed values are:
            * "statevector": Uses a dense statevector simulation.
            * "stabilizer": uses a Clifford stabilizer state simulator that
            is only valid for Clifford circuits and noise models.
            * "automatic": automatically run on stabilizer simulator if
            the circuit and noise model supports it, otherwise use the
            statevector method (Default: "automatic").

        * "initial_statevector" (vector_like): Sets a custom initial
            statevector for the simulation instead of the all zero
            initial state (Default: None).

        * "chop_threshold" (double): Sets the threshold for truncating small
            values to zero in the Result data (Default: 1e-15)

        * "max_parallel_threads" (int): Sets the maximum number of CPU
            cores used by OpenMP for parallelization. If set to 0 the
            maximum will be set to the number of CPU cores (Default: 0).

        * "max_parallel_experiments" (int): Sets the maximum number of
            qobj experiments that may be executed in parallel up to the
            max_parallel_threads value. If set to 1 parallel circuit
            execution will be disabled. If set to 0 the maximum will be
            automatically set to max_parallel_threads (Default: 1).

        * "max_parallel_shots" (int): Sets the maximum number of
            shots that may be executed in parallel during each experiment
            execution, up to the max_parallel_threads value. If set to 1
            parallel shot execution wil be disabled. If set to 0 the
            maximum will be automatically set to max_parallel_threads.
            Note that this cannot be enabled at the same time as parallel
            experiment execution (Default: 1).

        * "statevector_parallel_threshold" (int): Sets the threshold that
            "n_qubits" must be greater than to enable OpenMP
            parallelization for matrix multiplication during execution of
            an experiment. If parallel circuit or shot execution is enabled
            this will only use unallocated CPU cores up to
            max_parallel_threads. Note that setting this too low can reduce
            performance (Default: 12).

        * "statevector_sample_measure_opt" (int): Sets the threshold that
            the number of qubits must be greater than to enable a large
            qubit optimized implementation of measurement sampling. Note
            that setting this two low can reduce performance (Default: 10)

        * "statevector_hpc_gate_opt" (bool): If set to True this enables
            a different optimzied gate application routine that can
            increase performance on systems with a large number of CPU
            cores. For systems with a small number of cores it enabling
            can reduce performance (Default: False).
    """

    MAX_QUBIT_MEMORY = int(
        log2(local_hardware_info()['memory'] * (1024**3) / 16))

    DEFAULT_CONFIGURATION = {
        'backend_name':
        'qasm_simulator',
        'backend_version':
        __version__,
        'n_qubits':
        MAX_QUBIT_MEMORY,
        'url':
        'https://github.com/Qiskit/qiskit-aer',
        'simulator':
        True,
        'local':
        True,
        'conditional':
        True,
        'open_pulse':
        False,
        'memory':
        True,
        'max_shots':
        100000,
        'description':
        'A C++ simulator with realistic for qobj files',
        'basis_gates': [
            'u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg',
            't', 'tdg', 'ccx', 'swap', 'snapshot', 'unitary'
        ],
        'gates': [{
            'name': 'TODO',
            'parameters': [],
            'qasm_def': 'TODO'
        }]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(qasm_controller_execute,
                         BackendConfiguration.from_dict(
                             self.DEFAULT_CONFIGURATION),
                         provider=provider)

    def _validate(self, qobj, backend_options, noise_model):
        """Semantic validations of the qobj which cannot be done via schemas.

        1. Check number of qubits will fit in local memory.
        2. warn if no classical registers or measurements in circuit.
        """
        clifford_instructions = [
            "id", "x", "y", "z", "h", "s", "sdg", "CX", "cx", "cz", "swap",
            "barrier", "reset", "measure"
        ]
        # Check if noise model is Clifford:
        method = "automatic"
        if backend_options and "method" in backend_options:
            method = backend_options["method"]

        clifford_noise = not (method == "statevector")

        if clifford_noise:
            if method != "stabilizer" and noise_model:
                for error in noise_model.as_dict()['errors']:
                    if error['type'] == 'qerror':
                        for circ in error["instructions"]:
                            for instr in circ:
                                if instr not in clifford_instructions:
                                    clifford_noise = False
                                    break
        # Check to see if experiments are clifford
        for experiment in qobj.experiments:
            name = experiment.header.name
            # Check for classical bits
            if experiment.config.memory_slots == 0:
                logger.warning(
                    'No classical registers in circuit "%s": '
                    'result data will not contain counts.', name)
            # Check if Clifford circuit or if measure opts missing
            no_measure = True
            clifford = False if method == "statevector" else clifford_noise
            for op in experiment.instructions:
                if not clifford and not no_measure:
                    break  # we don't need to check any more ops
                if clifford and op.name not in clifford_instructions:
                    clifford = False
                if no_measure and op.name == "measure":
                    no_measure = False
            # Print warning if clbits but no measure
            if no_measure:
                logger.warning(
                    'No measurements in circuit "%s": '
                    'count data will return all zeros.', name)
            # Check qubits for statevector simulation
            if not clifford:
                n_qubits = experiment.config.n_qubits
                max_qubits = self.configuration().n_qubits
                if n_qubits > max_qubits:
                    system_memory = int(local_hardware_info()['memory'])
                    raise AerError(
                        'Number of qubits ({}) '.format(n_qubits) +
                        'is greater than maximum ({}) '.format(max_qubits) +
                        'for "{}" (method=statevector) '.format(self.name()) +
                        'with {} GB system memory.'.format(system_memory))
Ejemplo n.º 17
0
class UnitarySimulatorPy(BaseBackend):
    """Python implementation of a unitary simulator."""

    DEFAULT_CONFIGURATION = {
        'backend_name':
        'unitary_simulator_py',
        'backend_version':
        '1.0.0',
        'n_qubits':
        int(log2(sqrt(local_hardware_info()['memory'] * (1024**3)) / 16)),
        'url':
        'https://github.com/Qiskit/qiskit-terra',
        'simulator':
        True,
        'local':
        True,
        'conditional':
        False,
        'open_pulse':
        False,
        'memory':
        False,
        'max_shots':
        65536,
        'description':
        'A python simulator for unitary matrix corresponding to a circuit',
        'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id'],
        'gates': [{
            'name': 'TODO',
            'parameters': [],
            'qasm_def': 'TODO'
        }]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(configuration=(configuration
                                        or BackendConfiguration.from_dict(
                                            self.DEFAULT_CONFIGURATION)),
                         provider=provider)

        # Define attributes inside __init__.
        self._unitary_state = None
        self._number_of_qubits = 0

    def properties(self):
        """Return backend properties"""
        properties = {
            'backend_name':
            self.name(),
            'backend_version':
            self.configuration().backend_version,
            'last_update_date':
            '2000-01-01 00:00:00Z',
            'qubits': [[{
                'name': 'TODO',
                'date': '2000-01-01 00:00:00Z',
                'unit': 'TODO',
                'value': 0
            }]],
            'gates': [{
                'qubits': [0],
                'gate':
                'TODO',
                'parameters': [{
                    'name': 'TODO',
                    'date': '2000-01-01 00:00:00Z',
                    'unit': 'TODO',
                    'value': 0
                }]
            }],
            'general': []
        }

        return BackendProperties.from_dict(properties)

    def _add_unitary_single(self, gate, qubit):
        """Apply the single-qubit gate.

        gate is the single-qubit gate.
        qubit is the qubit to apply it on counts from 0 and order
            is q_{n-1} ... otimes q_1 otimes q_0.
        number_of_qubits is the number of qubits in the system.
        """
        # Convert to complex rank-2 tensor
        gate_tensor = np.array(gate, dtype=complex)
        # Compute einsum index string for 1-qubit matrix multiplication
        indexes = einsum_matmul_index([qubit], self._number_of_qubits)
        # Apply matrix multiplication
        self._unitary_state = np.einsum(indexes,
                                        gate_tensor,
                                        self._unitary_state,
                                        dtype=complex,
                                        casting='no')

    def _add_unitary_two(self, gate, qubit0, qubit1):
        """Apply the two-qubit gate.

        gate is the two-qubit gate
        qubit0 is the first qubit (control) counts from 0
        qubit1 is the second qubit (target)
        returns a complex numpy array
        """

        # Convert to complex rank-4 tensor
        gate_tensor = np.reshape(np.array(gate, dtype=complex), 4 * [2])

        # Compute einsum index string for 2-qubit matrix multiplication
        indexes = einsum_matmul_index([qubit0, qubit1], self._number_of_qubits)

        # Apply matrix multiplication
        self._unitary_state = np.einsum(indexes,
                                        gate_tensor,
                                        self._unitary_state,
                                        dtype=complex,
                                        casting='no')

    def run(self, qobj):
        """Run qobj asynchronously.

        Args:
            qobj (dict): job description

        Returns:
            AerJob: derived from BaseJob
        """
        job_id = str(uuid.uuid4())
        aer_job = AerJob(self, job_id, self._run_job, qobj)
        aer_job.submit()
        return aer_job

    def _run_job(self, job_id, qobj):
        """Run qobj. This is a blocking call.

        Args:
            job_id (str): unique id for the job.
            qobj (Qobj): job description
        Returns:
            Result: Result object
        """
        result_list = []
        start = time.time()
        for circuit in qobj.experiments:
            result_list.append(self.run_circuit(circuit))
        end = time.time()
        result = {
            'backend': self.name(),
            'id': qobj.qobj_id,
            'job_id': job_id,
            'result': result_list,
            'status': 'COMPLETED',
            'success': True,
            'time_taken': (end - start)
        }
        copy_qasm_from_qobj_into_result(qobj, result)

        return result_from_old_style_dict(result)

    def run_circuit(self, circuit):
        """Apply the single-qubit gate.

        Args:
            circuit (QobjExperiment): experiment from qobj experiments list

        Returns:
            dict: A dictionary of results.

        Raises:
            QiskitError: if the number of qubits in the circuit is greater than 24.
            Note that the practical qubit limit is much lower than 24.
        """
        self._number_of_qubits = circuit.header.number_of_qubits
        if self._number_of_qubits > 24:
            raise QiskitError(
                "np.einsum implementation limits unitary_simulator_py" +
                " to 24 qubit circuits.")
        result = {'data': {}, 'name': circuit.header.name}

        # Initialize unitary as rank 2*N tensor
        self._unitary_state = np.reshape(
            np.eye(2**self._number_of_qubits, dtype=complex),
            self._number_of_qubits * [2, 2])

        for operation in circuit.instructions:
            if operation.name in ('U', 'u1', 'u2', 'u3'):
                params = getattr(operation, 'params', None)
                qubit = operation.qubits[0]
                gate = single_gate_matrix(operation.name, params)
                self._add_unitary_single(gate, qubit)
            elif operation.name in ('id', 'u0'):
                pass
            elif operation.name in ('CX', 'cx'):
                qubit0 = operation.qubits[0]
                qubit1 = operation.qubits[1]
                gate = np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0],
                                 [0, 1, 0, 0]])
                self._add_unitary_two(gate, qubit0, qubit1)
            elif operation.name == 'measure':
                logger.info('Warning have dropped measure from unitary '
                            'simulator')
            elif operation.name == 'reset':
                logger.info('Warning have dropped reset from unitary '
                            'simulator')
            elif operation.name == 'barrier':
                pass
            else:
                result['status'] = 'ERROR'
                return result
        # Reshape unitary rank-2n tensor back to a matrix
        tmp = np.reshape(self._unitary_state, 2 * [2**self._number_of_qubits])
        # Convert complex numbers to pair of (real, imag)
        result['data']['unitary'] = np.stack((tmp.real, tmp.imag), axis=-1)
        result['status'] = 'DONE'
        result['success'] = True
        result['shots'] = 1
        return result
Ejemplo n.º 18
0
class CliffordSimulator(BaseBackend):
    """"C++ Clifford circuit simulator with realistic noise."""

    DEFAULT_CONFIGURATION = {
        'backend_name':
        'clifford_simulator',
        'backend_version':
        '1.0.0',
        'n_qubits':
        int(log2(local_hardware_info()['memory'] * (1024**3) / 16)),
        'url':
        'https://github.com/Qiskit/qiskit-terra/src/qasm-simulator-cpp',
        'simulator':
        True,
        'local':
        True,
        'conditional':
        True,
        'open_pulse':
        False,
        'memory':
        False,
        'max_shots':
        65536,
        'description':
        'A C++ Clifford simulator with approximate noise',
        'basis_gates': [
            'cx', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 'snapshot', 'wait',
            'noise', 'save', 'load'
        ],
        'gates': [{
            'name': 'cx',
            'parameters': ['c', 't'],
            'qasm_def': 'gate cx c,t { CX c,t; }'
        }, {
            'name': 'id',
            'parameters': ['a'],
            'qasm_def': 'gate id a { U(0,0,0) a; }'
        }, {
            'name': 'x',
            'parameters': ['a'],
            'qasm_def': 'gate x a { u3(pi,0,pi) a; }'
        }, {
            'name': 'y',
            'parameters': ['a'],
            'qasm_def': 'gate y a { u3(pi,pi/2,pi/2) a; }'
        }, {
            'name': 'z',
            'parameters': ['z'],
            'qasm_def': 'gate z a { u1(pi) a; }'
        }, {
            'name': 'h',
            'parameters': ['a'],
            'qasm_def': 'gate h a { u2(0,pi) a; }'
        }, {
            'name': 's',
            'parameters': ['a'],
            'qasm_def': 'gate s a { u1(pi/2) a; }'
        }, {
            'name': 'sdg',
            'parameters': ['a'],
            'qasm_def': 'gate sdg a { u1(-pi/2) a; }'
        }, {
            'name': 'snapshot',
            'parameters': ['slot'],
            'qasm_def': 'gate snapshot(slot) q { TODO }'
        }, {
            'name': 'wait',
            'parameters': ['t'],
            'qasm_def': 'gate wait(t) q { TODO }'
        }, {
            'name': 'noise',
            'parameters': ['switch'],
            'qasm_def': 'gate noise(switch) q { TODO }'
        }, {
            'name': 'save',
            'parameters': ['slot'],
            'qasm_def': 'gate save(slot) q { TODO }'
        }, {
            'name': 'load',
            'parameters': ['slot'],
            'qasm_def': 'gate load(slot) q { TODO }'
        }]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(configuration=(configuration
                                        or BackendConfiguration.from_dict(
                                            self.DEFAULT_CONFIGURATION)),
                         provider=provider)

        # Try to use the default executable if not specified.
        if 'exe' in self._configuration:
            paths = [self._configuration.exe]
        else:
            paths = DEFAULT_SIMULATOR_PATHS

        # Ensure that the executable is available.
        try:
            self._configuration.exe = next(
                path for path in paths
                if (os.path.exists(path) and os.path.getsize(path) > 100))
        except StopIteration:
            raise FileNotFoundError(
                'Simulator executable not found (using %s)' %
                getattr(self._configuration, 'exe', 'default locations'))

    def run(self, qobj):
        """Run a Qobj on the backend.

        Args:
            qobj (dict): job description

        Returns:
            AerJob: derived from BaseJob
        """
        job_id = str(uuid.uuid4())
        aer_job = AerJob(self, job_id, self._run_job, qobj)
        aer_job.submit()
        return aer_job

    def _run_job(self, job_id, qobj):
        qobj_dict = qobj.as_dict()
        self._validate()
        # set backend to Clifford simulator
        if 'config' in qobj_dict:
            qobj_dict['config']['simulator'] = 'clifford'
        else:
            qobj_dict['config'] = {'simulator': 'clifford'}
        result = run(qobj_dict, self._configuration.exe)
        result['job_id'] = job_id

        # Ensure that the required results fields are present, even if the
        # job failed.
        result['results'] = result.get('results', [])
        result['qobj_id'] = result.get('qobj_id', 'unavailable')
        result['backend_name'] = result.get('backend_name', self.name())
        result['backend_version'] = result.get(
            'backend_version',
            self.configuration().backend_version)

        return Result.from_dict(result)

    def _validate(self):
        return
Ejemplo n.º 19
0
class QasmSimulatorPy(BaseBackend):
    """Python implementation of a qasm simulator."""

    DEFAULT_CONFIGURATION = {
        'backend_name': 'qasm_simulator_py',
        'backend_version': '2.0.0',
        'n_qubits': int(log2(local_hardware_info()['memory'] * (1024**3)/16)),
        'url': 'https://github.com/Qiskit/qiskit-terra',
        'simulator': True,
        'local': True,
        'conditional': True,
        'open_pulse': False,
        'memory': True,
        'max_shots': 65536,
        'description': 'A python simulator for qasm experiments',
        'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id', 'snapshot'],
        'gates': [
            {
                'name': 'u1',
                'parameters': ['lambda'],
                'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }'
            },
            {
                'name': 'u2',
                'parameters': ['phi', 'lambda'],
                'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }'
            },
            {
                'name': 'u3',
                'parameters': ['theta', 'phi', 'lambda'],
                'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }'
            },
            {
                'name': 'cx',
                'parameters': ['c', 't'],
                'qasm_def': 'gate cx c,t { CX c,t; }'
            },
            {
                'name': 'id',
                'parameters': ['a'],
                'qasm_def': 'gate id a { U(0,0,0) a; }'
            },
            {
                'name': 'snapshot',
                'parameters': ['slot'],
                'qasm_def': 'gate snapshot(slot) q { TODO }'
            }
        ]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(configuration=(configuration or
                                        BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION)),
                         provider=provider)

        self._local_random = random.Random()

        # Define attributes in __init__.
        self._classical_state = 0
        self._statevector = 0
        self._snapshots = {}
        self._number_of_cbits = 0
        self._number_of_qubits = 0
        self._shots = 0
        self._memory = False
        self._qobj_config = None

    def _add_qasm_single(self, gate, qubit):
        """Apply an arbitrary 1-qubit operator to a qubit.

        Gate is the single qubit applied.
        qubit is the qubit the gate is applied to.
        """
        psi = self._statevector
        bit = 1 << qubit
        for k1 in range(0, 1 << self._number_of_qubits, 1 << (qubit+1)):
            for k2 in range(0, 1 << qubit, 1):
                k = k1 | k2
                cache0 = psi[k]
                cache1 = psi[k | bit]
                psi[k] = gate[0, 0] * cache0 + gate[0, 1] * cache1
                psi[k | bit] = gate[1, 0] * cache0 + gate[1, 1] * cache1

    def _add_qasm_cx(self, q0, q1):
        """Optimized ideal CX on two qubits.

        q0 is the first qubit (control) counts from 0.
        q1 is the second qubit (target).
        """
        psi = self._statevector
        for k in range(0, 1 << (self._number_of_qubits - 2)):
            # first bit is control, second is target
            ind1 = index2(1, q0, 0, q1, k)
            # swap target if control is 1
            ind3 = index2(1, q0, 1, q1, k)
            cache0 = psi[ind1]
            cache1 = psi[ind3]
            psi[ind3] = cache0
            psi[ind1] = cache1

    def _add_qasm_decision(self, qubit):
        """Apply the decision of measurement/reset qubit gate.

        qubit is the qubit that is measured/reset
        """
        probability_zero = 0
        random_number = self._local_random.random()
        for ii in range(1 << self._number_of_qubits):
            if ii & (1 << qubit) == 0:
                probability_zero += np.abs(self._statevector[ii])**2
        if random_number <= probability_zero:
            outcome = '0'
            norm = np.sqrt(probability_zero)
        else:
            outcome = '1'
            norm = np.sqrt(1-probability_zero)
        return (outcome, norm)

    def _add_qasm_measure(self, qubit, cbit):
        """Apply the measurement qubit gate.

        qubit is the qubit measured.
        cbit is the classical bit the measurement is assigned to.
        """
        outcome, norm = self._add_qasm_decision(qubit)
        for ii in range(1 << self._number_of_qubits):
            # update quantum state
            if (ii >> qubit) & 1 == int(outcome):
                self._statevector[ii] = self._statevector[ii]/norm
            else:
                self._statevector[ii] = 0
        # update classical state
        bit = 1 << cbit
        self._classical_state = (self._classical_state & (~bit)) | (int(outcome) << cbit)

    def _add_qasm_reset(self, qubit):
        """Apply the reset to the qubit.

        This is done by doing a measurement and if 0 do nothing and
        if 1 flip the qubit.

        qubit is the qubit that is reset.
        """
        # TODO: slow, refactor later
        outcome, norm = self._add_qasm_decision(qubit)
        temp = np.copy(self._statevector)
        self._statevector.fill(0.0)
        # measurement
        for ii in range(1 << self._number_of_qubits):
            if (ii >> qubit) & 1 == int(outcome):
                temp[ii] = temp[ii]/norm
            else:
                temp[ii] = 0
        # reset
        if outcome == '1':
            for ii in range(1 << self._number_of_qubits):
                iip = (~ (1 << qubit)) & ii  # bit number qubit set to zero
                self._statevector[iip] += temp[ii]
        else:
            self._statevector = temp

    def _add_qasm_snapshot(self, slot):
        """Snapshot instruction to record simulator's internal representation
        of quantum statevector.

        Args:
            slot (string): a label to identify the recorded snapshot.
        """
        self._snapshots.setdefault(str(slot),
                                   {}).setdefault("statevector",
                                                  []).append(np.copy(self._statevector))

    def run(self, qobj):
        """Run qobj asynchronously.

        Args:
            qobj (Qobj): payload of the experiment

        Returns:
            AerJob: derived from BaseJob
        """
        job_id = str(uuid.uuid4())
        aer_job = AerJob(self, job_id, self._run_job, qobj)
        aer_job.submit()
        return aer_job

    def _run_job(self, job_id, qobj):
        """Run experiments in qobj

        Args:
            job_id (str): unique id for the job.
            qobj (Qobj): job description

        Returns:
            Result: Result object
        """
        self._validate(qobj)
        result_list = []
        self._shots = qobj.config.shots
        self._memory = qobj.config.memory
        self._qobj_config = qobj.config
        start = time.time()

        for experiment in qobj.experiments:
            result_list.append(self.run_experiment(experiment))
        end = time.time()
        result = {'backend_name': self.name(),
                  'backend_version': self._configuration.backend_version,
                  'qobj_id': qobj.qobj_id,
                  'job_id': job_id,
                  'results': result_list,
                  'status': 'COMPLETED',
                  'success': True,
                  'time_taken': (end - start),
                  'header': qobj.header.as_dict()}

        return Result.from_dict(result)

    def run_experiment(self, experiment):
        """Run an experiment (circuit) and return a single experiment result.

        Args:
            experiment (QobjExperiment): experiment from qobj experiments list

        Returns:
             dict: A result dictionary which looks something like::

                {
                "name": name of this experiment (obtained from qobj.experiment header)
                "seed": random seed used for simulation
                "shots": number of shots used in the simulation
                "data":
                    {
                    "memory": ['0x9', '0xF', '0x1D', ..., '0x9']
                    "snapshots":
                            {
                            '1': [0.7, 0, 0, 0.7],
                            '2': [0.5, 0.5, 0.5, 0.5]
                            }
                    },
                "status": status string for the simulation
                "success": boolean
                "time_taken": simulation time of this single experiment
                }
        Raises:
            SimulatorError: if an error occurred.
        """
        self._number_of_qubits = experiment.config.n_qubits
        self._number_of_cbits = experiment.config.memory_slots
        self._statevector = 0
        self._classical_state = 0
        self._snapshots = {}

        # Get the seed looking in circuit, qobj, and then random.
        if hasattr(experiment, 'config') and hasattr(experiment.config, 'seed'):
            seed = experiment.config.seed
        elif hasattr(self._qobj_config, 'seed'):
            seed = self._qobj_config.seed
        else:
            seed = random.getrandbits(32)
        self._local_random.seed(seed)
        outcomes = []

        start = time.time()
        for _ in range(self._shots):
            self._statevector = np.zeros(1 << self._number_of_qubits,
                                         dtype=complex)
            self._statevector[0] = 1
            self._classical_state = 0
            for operation in experiment.instructions:
                if getattr(operation, 'conditional', None):
                    mask = int(operation.conditional.mask, 16)
                    if mask > 0:
                        value = self._classical_state & mask
                        while (mask & 0x1) == 0:
                            mask >>= 1
                            value >>= 1
                        if value != int(operation.conditional.val, 16):
                            continue
                # Check if single  gate
                if operation.name in ('U', 'u1', 'u2', 'u3'):
                    params = getattr(operation, 'params', None)
                    qubit = operation.qubits[0]
                    gate = single_gate_matrix(operation.name, params)
                    self._add_qasm_single(gate, qubit)
                # Check if CX gate
                elif operation.name in ('id', 'u0'):
                    pass
                elif operation.name in ('CX', 'cx'):
                    qubit0 = operation.qubits[0]
                    qubit1 = operation.qubits[1]
                    self._add_qasm_cx(qubit0, qubit1)
                # Check if measure
                elif operation.name == 'measure':
                    qubit = operation.qubits[0]
                    cbit = operation.memory[0]
                    self._add_qasm_measure(qubit, cbit)
                # Check if reset
                elif operation.name == 'reset':
                    qubit = operation.qubits[0]
                    self._add_qasm_reset(qubit)
                # Check if barrier
                elif operation.name == 'barrier':
                    pass
                # Check if snapshot command
                elif operation.name == 'snapshot':
                    params = operation.params
                    self._add_qasm_snapshot(params[0])
                else:
                    backend = self.name()
                    err_msg = '{0} encountered unrecognized operation "{1}"'
                    raise SimulatorError(err_msg.format(backend,
                                                        operation.name))
            # Turn classical_state (int) into bit string and pad zero for unused cbits
            outcome = bin(self._classical_state)[2:]
            # Return a compact hexadecimal
            outcomes.append(hex(int(outcome, 2)))

        data = {
            'counts': dict(Counter(outcomes)),
            'snapshots': self._snapshots
        }
        if self._memory:
            data['memory'] = outcomes

        end = time.time()
        return {'name': experiment.header.name,
                'seed': seed,
                'shots': self._shots,
                'data': data,
                'status': 'DONE',
                'success': True,
                'time_taken': (end-start),
                'header': experiment.header.as_dict()}

    def _validate(self, qobj):
        for experiment in qobj.experiments:
            if 'measure' not in [op.name for
                                 op in experiment.instructions]:
                logger.warning("no measurements in circuit '%s', "
                               "classical register will remain all zeros.",
                               experiment.header.name)
class StatevectorSimulator(AerBackend):
    """Aer statevector simulator

    Backend options:

        The following backend options may be used with in the
        `backend_options` kwarg diction for `StatevectorSimulator.run` or
        `qiskit.execute`

        * "initial_statevector" (vector_like): Sets a custom initial
            statevector for the simulation instead of the all zero
            initial state (Default: None).

        * "chop_threshold" (double): Sets the threshold for truncating small
            values to zero in the Result data (Default: 1e-15)

        * "max_parallel_threads" (int): Sets the maximum number of CPU
            cores used by OpenMP for parallelization. If set to 0 the
            maximum will be set to the number of CPU cores (Default: 0).

        * "max_parallel_experiments" (int): Sets the maximum number of
            qobj experiments that may be executed in parallel up to the
            max_parallel_threads value. If set to 1 parallel circuit
            execution will be disabled. If set to 0 the maximum will be
            automatically set to max_parallel_threads (Default: 1).

        * "statevector_parallel_threshold" (int): Sets the threshold that
            "n_qubits" must be greater than to enable OpenMP
            parallelization for matrix multiplication during execution of
            an experiment. If parallel circuit or shot execution is enabled
            this will only use unallocated CPU cores up to
            max_parallel_threads. Note that setting this too low can reduce
            performance (Default: 12).

        * "statevector_hpc_gate_opt" (bool): If set to True this enables
            a different optimzied gate application routine that can
            increase performance on systems with a large number of CPU
            cores. For systems with a small number of cores it enabling
            can reduce performance (Default: False).
    """

    MAX_QUBIT_MEMORY = int(log2(local_hardware_info()['memory'] * (1024 ** 3) / 16))

    DEFAULT_CONFIGURATION = {
        'backend_name': 'statevector_simulator',
        'backend_version': __version__,
        'n_qubits': MAX_QUBIT_MEMORY,
        'url': 'https://github.com/Qiskit/qiskit-aer',
        'simulator': True,
        'local': True,
        'conditional': True,
        'open_pulse': False,
        'memory': True,
        'max_shots': 1,
        'description': 'A C++ statevector simulator for qobj files',
        'basis_gates': ['u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z',
                        'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap',
                        'snapshot', 'unitary'],
        'gates': [
            {
                'name': 'TODO',
                'parameters': [],
                'qasm_def': 'TODO'
            }
        ]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(statevector_controller_execute,
                         BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION),
                         provider=provider)

    def run(self, qobj, backend_options=None):
        """Run a qobj on the backend.

        Args:
            qobj (Qobj): a Qobj.
            backend_options (dict): backend configuration options.

        Returns:
            AerJob: the simulation job.
        """
        return super().run(qobj, backend_options=backend_options)

    def _validate(self, qobj):
        """Semantic validations of the qobj which cannot be done via schemas.
        Some of these may later move to backend schemas.

        1. Set shots=1.
        2. Check number of qubits will fit in local memory.
        """
        n_qubits = qobj.config.n_qubits
        max_qubits = self.configuration().n_qubits
        if n_qubits > max_qubits:
            raise AerError('Number of qubits ({}) '.format(n_qubits) +
                           'is greater than maximum ({}) '.format(max_qubits) +
                           'for "{}" '.format(self.name()) +
                           'with {} GB system memory.'.format(int(local_hardware_info()['memory'])))
        if qobj.config.shots != 1:
            logger.info('"%s" only supports 1 shot. Setting shots=1.',
                        self.name())
            qobj.config.shots = 1
        for experiment in qobj.experiments:
            name = experiment.header.name
            if getattr(experiment.config, 'shots', 1) != 1:
                logger.info('"%s" only supports 1 shot. '
                            'Setting shots=1 for circuit "%s".',
                            self.name(), name)
                experiment.config.shots = 1
Ejemplo n.º 21
0
    def _validate(self, qobj, backend_options, noise_model):
        """Semantic validations of the qobj which cannot be done via schemas.

        1. Check number of qubits will fit in local memory.
        2. warn if no classical registers or measurements in circuit.
        """
        clifford_instructions = [
            "id", "x", "y", "z", "h", "s", "sdg", "CX", "cx", "cz", "swap",
            "barrier", "reset", "measure"
        ]
        unsupported_ch_instructions = ["u2", "u3"]
        # Check if noise model is Clifford:
        method = "automatic"
        if backend_options and "method" in backend_options:
            method = backend_options["method"]

        clifford_noise = not (method == "statevector")

        if clifford_noise:
            if method != "stabilizer" and noise_model:
                for error in noise_model.as_dict()['errors']:
                    if error['type'] == 'qerror':
                        for circ in error["instructions"]:
                            for instr in circ:
                                if instr not in clifford_instructions:
                                    clifford_noise = False
                                    break
        # Check to see if experiments are clifford
        for experiment in qobj.experiments:
            name = experiment.header.name
            # Check for classical bits
            if experiment.config.memory_slots == 0:
                logger.warning(
                    'No classical registers in circuit "%s": '
                    'result data will not contain counts.', name)
            # Check if Clifford circuit or if measure opts missing
            no_measure = True
            ch_supported = False
            ch_supported = method in ["ch", "automatic"]
            clifford = False if method == "statevector" else clifford_noise
            for op in experiment.instructions:
                if not clifford and not no_measure:
                    break  # we don't need to check any more ops
                if clifford and op.name not in clifford_instructions:
                    clifford = False
                if no_measure and op.name == "measure":
                    no_measure = False
                if ch_supported and op.name in unsupported_ch_instructions:
                    ch_supported = False
            # Print warning if clbits but no measure
            if no_measure:
                logger.warning(
                    'No measurements in circuit "%s": '
                    'count data will return all zeros.', name)
            # Check qubits for statevector simulation
            if not clifford and method != "ch":
                n_qubits = experiment.config.n_qubits
                max_qubits = self.configuration().n_qubits
                if n_qubits > max_qubits:
                    system_memory = int(local_hardware_info()['memory'])
                    err_string = ('Number of qubits ({}) is greater than '
                                  'maximum ({}) for "{}" (method=statevector) '
                                  'with {} GB system memory')
                    err_string = err_string.format(n_qubits, max_qubits,
                                                   self.name(), system_memory)
                    if method != "automatic":
                        raise AerError(err_string + '.')
                    else:
                        if n_qubits > 63:
                            raise AerError(err_string + ', and has too many ' +
                                           'qubits to fall back to the ' +
                                           'CH simulator.')
                        if not ch_supported:
                            raise AerError(err_string + ', and contains ' +
                                           'instructions not supported by ' +
                                           'the CH simulator.')
                        logger.info('The QasmSimulator will automatically '
                                    'switch to the CH backend, based on '
                                    'the memory requirements.')
Ejemplo n.º 22
0
"""
Routines for running Python functions in parallel using process pools
from the multiprocessing library.
"""

import os
import platform
from multiprocessing import Pool
from qiskit._qiskiterror import QISKitError
from qiskit._util import local_hardware_info
from ._receiver import receiver as rec
from ._progressbar import BaseProgressBar

# Number of local physical cpus
CPU_COUNT = local_hardware_info()['cpus']


def parallel_map(task, values, task_args=tuple(), task_kwargs={},  # pylint: disable=W0102
                 num_processes=CPU_COUNT):
    """
    Parallel execution of a mapping of `values` to the function `task`. This
    is functionally equivalent to::

        result = [task(value, *task_args, **task_kwargs) for value in values]

    On Windows this function defaults to a serial implimentation to avoid the
    overhead from spawning processes in Windows.

    Args:
        task (func): Function that is to be called for each value in ``task_vec``.
Ejemplo n.º 23
0
class QasmSimulator(BaseBackend):
    """C++ quantum circuit simulator with realistic noise"""

    DEFAULT_CONFIGURATION = {
        'backend_name':
        'qasm_simulator',
        'backend_version':
        '1.0.0',
        'n_qubits':
        int(log2(local_hardware_info()['memory'] * (1024**3) / 16)),
        'url':
        'https://github.com/Qiskit/qiskit-terra/src/qasm-simulator-cpp',
        'simulator':
        True,
        'local':
        True,
        'conditional':
        True,
        'open_pulse':
        False,
        'memory':
        True,
        'max_shots':
        65536,
        'description':
        'A C++ realistic noise simulator for qasm experiments',
        'basis_gates': [
            'u0', 'u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's',
            'sdg', 't', 'tdg', 'rzz', 'snapshot', 'wait', 'noise', 'save',
            'load'
        ],
        'gates': [{
            'name': 'u0',
            'parameters': ['gamma'],
            'qasm_def': 'gate u0(gamma) q { U(0,0,0) q; }'
        }, {
            'name': 'u1',
            'parameters': ['lambda'],
            'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }'
        }, {
            'name': 'u2',
            'parameters': ['phi', 'lambda'],
            'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }'
        }, {
            'name':
            'u3',
            'parameters': ['theta', 'phi', 'lambda'],
            'qasm_def':
            'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }'
        }, {
            'name': 'cx',
            'parameters': ['c', 't'],
            'qasm_def': 'gate cx c,t { CX c,t; }'
        }, {
            'name': 'cz',
            'parameters': ['a', 'b'],
            'qasm_def': 'gate cz a,b { h b; cx a,b; h b; }'
        }, {
            'name': 'id',
            'parameters': ['a'],
            'qasm_def': 'gate id a { U(0,0,0) a; }'
        }, {
            'name': 'x',
            'parameters': ['a'],
            'qasm_def': 'gate x a { u3(pi,0,pi) a; }'
        }, {
            'name': 'y',
            'parameters': ['a'],
            'qasm_def': 'gate y a { u3(pi,pi/2,pi/2) a; }'
        }, {
            'name': 'z',
            'parameters': ['z'],
            'qasm_def': 'gate z a { u1(pi) a; }'
        }, {
            'name': 'h',
            'parameters': ['a'],
            'qasm_def': 'gate h a { u2(0,pi) a; }'
        }, {
            'name': 's',
            'parameters': ['a'],
            'qasm_def': 'gate s a { u1(pi/2) a; }'
        }, {
            'name': 'sdg',
            'parameters': ['a'],
            'qasm_def': 'gate sdg a { u1(-pi/2) a; }'
        }, {
            'name': 't',
            'parameters': ['a'],
            'qasm_def': 'gate t a { u1(pi/4) a; }'
        }, {
            'name': 'tdg',
            'parameters': ['a'],
            'qasm_def': 'gate tdg a { u1(-pi/4) a; }'
        }, {
            'name':
            'rzz',
            'parameters': ['theta', 'a', 'b'],
            'qasm_def':
            'gate rzz(theta) a,b { cx a,b; u1(theta) b; cx a,b; }'
        }, {
            'name': 'snapshot',
            'parameters': ['slot'],
            'qasm_def': 'gate snapshot(slot) q { TODO }'
        }, {
            'name': 'wait',
            'parameters': ['t'],
            'qasm_def': 'gate wait(t) q { TODO }'
        }, {
            'name': 'noise',
            'parameters': ['switch'],
            'qasm_def': 'gate noise(switch) q { TODO }'
        }, {
            'name': 'save',
            'parameters': ['slot'],
            'qasm_def': 'gate save(slot) q { TODO }'
        }, {
            'name': 'load',
            'parameters': ['slot'],
            'qasm_def': 'gate load(slot) q { TODO }'
        }]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(configuration=(configuration
                                        or BackendConfiguration.from_dict(
                                            self.DEFAULT_CONFIGURATION)),
                         provider=provider)

        # Try to use the default executable if not specified.
        if 'exe' in self._configuration:
            paths = [self._configuration.exe]
        else:
            paths = DEFAULT_SIMULATOR_PATHS

        # Ensure that the executable is available.
        try:
            self._configuration.exe = next(
                path for path in paths
                if (os.path.exists(path) and os.path.getsize(path) > 100))
        except StopIteration:
            raise FileNotFoundError(
                'Simulator executable not found (using %s)' %
                getattr(self._configuration, 'exe', 'default locations'))

    def run(self, qobj):
        """Run a qobj on the backend."""
        job_id = str(uuid.uuid4())
        aer_job = AerJob(self, job_id, self._run_job, qobj)
        aer_job.submit()
        return aer_job

    def _run_job(self, job_id, qobj):
        """Run a Qobj on the backend."""
        self._validate(qobj)
        qobj_dict = qobj.as_dict()
        result = run(qobj_dict, self._configuration.exe)
        result['job_id'] = job_id

        # Ensure that the required results fields are present, even if the
        # job failed.
        result['results'] = result.get('results', [])
        result['qobj_id'] = result.get('qobj_id', 'unavailable')
        result['backend_name'] = result.get('backend_name', self.name())
        result['backend_version'] = result.get(
            'backend_version',
            self.configuration().backend_version)

        return Result.from_dict(result)

    def _validate(self, qobj):
        for experiment in qobj.experiments:
            if 'measure' not in [op.name for op in experiment.instructions]:
                logger.warning(
                    "no measurements in circuit '%s', "
                    "classical register will remain all zeros.",
                    experiment.header.name)
Ejemplo n.º 24
0
class QasmSimulatorPy(BaseBackend):
    """Python implementation of a qasm simulator."""

    DEFAULT_CONFIGURATION = {
        'backend_name': 'qasm_simulator_py',
        'backend_version': '2.0.0',
        'n_qubits':
        int(log2(local_hardware_info()['memory'] * (1024**3) / 16)),
        'url': 'https://github.com/Qiskit/qiskit-terra',
        'simulator': True,
        'local': True,
        'conditional': True,
        'open_pulse': False,
        'memory': True,
        'max_shots': 65536,
        'description': 'A python simulator for qasm experiments',
        'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id', 'snapshot'],
        'gates': [{
            'name': 'TODO',
            'parameters': [],
            'qasm_def': 'TODO'
        }]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(configuration=(configuration
                                        or BackendConfiguration.from_dict(
                                            self.DEFAULT_CONFIGURATION)),
                         provider=provider)

        self._local_random = random.Random()

        # Define attributes in __init__.
        self._classical_state = 0
        self._statevector = 0
        self._snapshots = {}
        self._number_of_cbits = 0
        self._number_of_qubits = 0
        self._shots = 0
        self._qobj_config = None

    def properties(self):
        """Return backend properties"""
        properties = {
            'backend_name':
            self.name(),
            'backend_version':
            self.configuration().backend_version,
            'last_update_date':
            '2000-01-01 00:00:00Z',
            'qubits': [[{
                'name': 'TODO',
                'date': '2000-01-01 00:00:00Z',
                'unit': 'TODO',
                'value': 0
            }]],
            'gates': [{
                'qubits': [0],
                'gate':
                'TODO',
                'parameters': [{
                    'name': 'TODO',
                    'date': '2000-01-01 00:00:00Z',
                    'unit': 'TODO',
                    'value': 0
                }]
            }],
            'general': []
        }

        return BackendProperties.from_dict(properties)

    def _add_qasm_single(self, gate, qubit):
        """Apply an arbitrary 1-qubit operator to a qubit.

        Gate is the single qubit applied.
        qubit is the qubit the gate is applied to.
        """
        psi = self._statevector
        bit = 1 << qubit
        for k1 in range(0, 1 << self._number_of_qubits, 1 << (qubit + 1)):
            for k2 in range(0, 1 << qubit, 1):
                k = k1 | k2
                cache0 = psi[k]
                cache1 = psi[k | bit]
                psi[k] = gate[0, 0] * cache0 + gate[0, 1] * cache1
                psi[k | bit] = gate[1, 0] * cache0 + gate[1, 1] * cache1

    def _add_qasm_cx(self, q0, q1):
        """Optimized ideal CX on two qubits.

        q0 is the first qubit (control) counts from 0.
        q1 is the second qubit (target).
        """
        psi = self._statevector
        for k in range(0, 1 << (self._number_of_qubits - 2)):
            # first bit is control, second is target
            ind1 = index2(1, q0, 0, q1, k)
            # swap target if control is 1
            ind3 = index2(1, q0, 1, q1, k)
            cache0 = psi[ind1]
            cache1 = psi[ind3]
            psi[ind3] = cache0
            psi[ind1] = cache1

    def _add_qasm_decision(self, qubit):
        """Apply the decision of measurement/reset qubit gate.

        qubit is the qubit that is measured/reset
        """
        probability_zero = 0
        random_number = self._local_random.random()
        for ii in range(1 << self._number_of_qubits):
            if ii & (1 << qubit) == 0:
                probability_zero += np.abs(self._statevector[ii])**2
        if random_number <= probability_zero:
            outcome = '0'
            norm = np.sqrt(probability_zero)
        else:
            outcome = '1'
            norm = np.sqrt(1 - probability_zero)
        return (outcome, norm)

    def _add_qasm_measure(self, qubit, cbit):
        """Apply the measurement qubit gate.

        qubit is the qubit measured.
        cbit is the classical bit the measurement is assigned to.
        """
        outcome, norm = self._add_qasm_decision(qubit)
        for ii in range(1 << self._number_of_qubits):
            # update quantum state
            if (ii >> qubit) & 1 == int(outcome):
                self._statevector[ii] = self._statevector[ii] / norm
            else:
                self._statevector[ii] = 0
        # update classical state
        bit = 1 << cbit
        self._classical_state = (self._classical_state &
                                 (~bit)) | (int(outcome) << cbit)

    def _add_qasm_reset(self, qubit):
        """Apply the reset to the qubit.

        This is done by doing a measruement and if 0 do nothing and
        if 1 flip the qubit.

        qubit is the qubit that is reset.
        """
        # TODO: slow, refactor later
        outcome, norm = self._add_qasm_decision(qubit)
        temp = np.copy(self._statevector)
        self._statevector.fill(0.0)
        # measurement
        for ii in range(1 << self._number_of_qubits):
            if (ii >> qubit) & 1 == int(outcome):
                temp[ii] = temp[ii] / norm
            else:
                temp[ii] = 0
        # reset
        if outcome == '1':
            for ii in range(1 << self._number_of_qubits):
                iip = (~(1 << qubit)) & ii  # bit number qubit set to zero
                self._statevector[iip] += temp[ii]
        else:
            self._statevector = temp

    def _add_qasm_snapshot(self, slot):
        """Snapshot instruction to record simulator's internal representation
        of quantum statevector.

        slot is a string indicating a snapshot slot label.
        """
        self._snapshots.setdefault(str(slot), {}).setdefault(
            "statevector", []).append(np.copy(self._statevector))

    def run(self, qobj):
        """Run qobj asynchronously.

        Args:
            qobj (dict): job description

        Returns:
            AerJob: derived from BaseJob
        """
        job_id = str(uuid.uuid4())
        aer_job = AerJob(self, job_id, self._run_job, qobj)
        aer_job.submit()
        return aer_job

    def _run_job(self, job_id, qobj):
        """Run circuits in qobj"""
        self._validate(qobj)
        result_list = []
        self._shots = qobj.config.shots
        self._qobj_config = qobj.config
        start = time.time()

        for circuit in qobj.experiments:
            result_list.append(self.run_circuit(circuit))
        end = time.time()
        result = {
            'backend': self.name(),
            'id': qobj.qobj_id,
            'job_id': job_id,
            'result': result_list,
            'status': 'COMPLETED',
            'success': True,
            'time_taken': (end - start)
        }

        copy_qasm_from_qobj_into_result(qobj, result)

        return result_from_old_style_dict(result)

    def run_circuit(self, circuit):
        """Run a circuit and return a single Result.

        Args:
            circuit (QobjExperiment): experiment from qobj experiments list

        Returns:
            dict: A dictionary of results which looks something like::

                {
                "data":
                    {  #### DATA CAN BE A DIFFERENT DICTIONARY FOR EACH BACKEND ####
                    "counts": {'00000': XXXX, '00001': XXXXX},
                    "time"  : xx.xxxxxxxx
                    },
                "status": --status (string)--
                }
        Raises:
            SimulatorError: if an error occurred.
        """
        self._number_of_qubits = circuit.header.number_of_qubits
        self._number_of_cbits = circuit.header.number_of_clbits
        self._statevector = 0
        self._classical_state = 0
        self._snapshots = {}
        cl_reg_index = []  # starting bit index of classical register
        cl_reg_nbits = []  # number of bits in classical register
        cbit_index = 0
        for cl_reg in circuit.header.clbit_labels:
            cl_reg_nbits.append(cl_reg[1])
            cl_reg_index.append(cbit_index)
            cbit_index += cl_reg[1]

        # Get the seed looking in circuit, qobj, and then random.
        if hasattr(circuit, 'config') and hasattr(circuit.config, 'seed'):
            seed = circuit.config.seed
        elif hasattr(self._qobj_config, 'seed'):
            seed = self._qobj_config.seed
        else:
            seed = random.getrandbits(32)
        self._local_random.seed(seed)
        outcomes = []

        start = time.time()
        for _ in range(self._shots):
            self._statevector = np.zeros(1 << self._number_of_qubits,
                                         dtype=complex)
            self._statevector[0] = 1
            self._classical_state = 0
            for operation in circuit.instructions:
                if getattr(operation, 'conditional', None):
                    mask = int(operation.conditional.mask, 16)
                    if mask > 0:
                        value = self._classical_state & mask
                        while (mask & 0x1) == 0:
                            mask >>= 1
                            value >>= 1
                        if value != int(operation.conditional.val, 16):
                            continue
                # Check if single  gate
                if operation.name in ('U', 'u1', 'u2', 'u3'):
                    params = getattr(operation, 'params', None)
                    qubit = operation.qubits[0]
                    gate = single_gate_matrix(operation.name, params)
                    self._add_qasm_single(gate, qubit)
                # Check if CX gate
                elif operation.name in ('id', 'u0'):
                    pass
                elif operation.name in ('CX', 'cx'):
                    qubit0 = operation.qubits[0]
                    qubit1 = operation.qubits[1]
                    self._add_qasm_cx(qubit0, qubit1)
                # Check if measure
                elif operation.name == 'measure':
                    qubit = operation.qubits[0]
                    cbit = operation.clbits[0]
                    self._add_qasm_measure(qubit, cbit)
                # Check if reset
                elif operation.name == 'reset':
                    qubit = operation.qubits[0]
                    self._add_qasm_reset(qubit)
                # Check if barrier
                elif operation.name == 'barrier':
                    pass
                # Check if snapshot command
                elif operation.name == 'snapshot':
                    params = operation.params
                    self._add_qasm_snapshot(params[0])
                else:
                    backend = self.name()
                    err_msg = '{0} encountered unrecognized operation "{1}"'
                    raise SimulatorError(
                        err_msg.format(backend, operation.name))
            # Turn classical_state (int) into bit string
            outcomes.append(
                bin(self._classical_state)[2:].zfill(self._number_of_cbits))
        # Return the results
        counts = dict(Counter(outcomes))
        data = {
            'counts': self._format_result(counts, cl_reg_index, cl_reg_nbits),
            'snapshots': self._snapshots
        }
        end = time.time()
        return {
            'name': circuit.header.name,
            'seed': seed,
            'shots': self._shots,
            'data': data,
            'status': 'DONE',
            'success': True,
            'time_taken': (end - start)
        }

    def _validate(self, qobj):
        for experiment in qobj.experiments:
            if 'measure' not in [op.name for op in experiment.instructions]:
                logger.warning(
                    "no measurements in circuit '%s', "
                    "classical register will remain all zeros.",
                    experiment.header.name)

    def _format_result(self, counts, cl_reg_index, cl_reg_nbits):
        """Format the result bit string.

        This formats the result bit strings such that spaces are inserted
        at register divisions.

        Args:
            counts (dict): dictionary of counts e.g. {'1111': 1000, '0000':5}
            cl_reg_index (list): starting bit index of classical register
            cl_reg_nbits (list): total amount of bits in classical register
        Returns:
            dict: spaces inserted into dictionary keys at register boundaries.
        """
        fcounts = {}
        for key, value in counts.items():
            if cl_reg_nbits:
                new_key = [key[-cl_reg_nbits[0]:]]
                for index, nbits in zip(cl_reg_index[1:], cl_reg_nbits[1:]):
                    new_key.insert(0, key[-(index + nbits):-index])
                fcounts[' '.join(new_key)] = value
        return fcounts
Ejemplo n.º 25
0
class QasmSimulatorPy(BaseBackend):
    """Python implementation of a qasm simulator."""

    MAX_QUBITS_MEMORY = int(
        log2(local_hardware_info()['memory'] * (1024**3) / 16))

    DEFAULT_CONFIGURATION = {
        'backend_name':
        'qasm_simulator',
        'backend_version':
        '2.0.0',
        'n_qubits':
        min(24, MAX_QUBITS_MEMORY),
        'url':
        'https://github.com/Qiskit/qiskit-terra',
        'simulator':
        True,
        'local':
        True,
        'conditional':
        True,
        'open_pulse':
        False,
        'memory':
        True,
        'max_shots':
        65536,
        'description':
        'A python simulator for qasm experiments',
        'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id', 'unitary'],
        'gates': [{
            'name': 'u1',
            'parameters': ['lambda'],
            'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }'
        }, {
            'name': 'u2',
            'parameters': ['phi', 'lambda'],
            'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }'
        }, {
            'name':
            'u3',
            'parameters': ['theta', 'phi', 'lambda'],
            'qasm_def':
            'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }'
        }, {
            'name': 'cx',
            'parameters': ['c', 't'],
            'qasm_def': 'gate cx c,t { CX c,t; }'
        }, {
            'name': 'id',
            'parameters': ['a'],
            'qasm_def': 'gate id a { U(0,0,0) a; }'
        }, {
            'name': 'unitary',
            'parameters': ['matrix'],
            'qasm_def': 'unitary(matrix) q1, q2,...'
        }]
    }

    DEFAULT_OPTIONS = {"initial_statevector": None, "chop_threshold": 1e-15}

    # Class level variable to return the final state at the end of simulation
    # This should be set to True for the statevector simulator
    SHOW_FINAL_STATE = False

    def __init__(self, configuration=None, provider=None):
        super().__init__(configuration=(configuration
                                        or BackendConfiguration.from_dict(
                                            self.DEFAULT_CONFIGURATION)),
                         provider=provider)

        # Define attributes in __init__.
        self._local_random = np.random.RandomState()
        self._classical_memory = 0
        self._classical_register = 0
        self._statevector = 0
        self._number_of_cmembits = 0
        self._number_of_qubits = 0
        self._shots = 0
        self._memory = False
        self._initial_statevector = self.DEFAULT_OPTIONS["initial_statevector"]
        self._chop_threshold = self.DEFAULT_OPTIONS["chop_threshold"]
        self._qobj_config = None
        # TEMP
        self._sample_measure = False

    def _add_unitary_single(self, gate, qubit):
        """Apply an arbitrary 1-qubit unitary matrix.

        Args:
            gate (matrix_like): a single qubit gate matrix
            qubit (int): the qubit to apply gate to
        """
        # Compute einsum index string for 1-qubit matrix multiplication
        indexes = einsum_vecmul_index([qubit], self._number_of_qubits)
        # Convert to complex rank-2 tensor
        gate_tensor = np.array(gate, dtype=complex)
        # Apply matrix multiplication
        self._statevector = np.einsum(indexes,
                                      gate_tensor,
                                      self._statevector,
                                      dtype=complex,
                                      casting='no')

    def _add_unitary_two(self, gate, qubit0, qubit1):
        """Apply a two-qubit unitary matrix.

        Args:
            gate (matrix_like): a the two-qubit gate matrix
            qubit0 (int): gate qubit-0
            qubit1 (int): gate qubit-1
        """
        # Compute einsum index string for 1-qubit matrix multiplication
        indexes = einsum_vecmul_index([qubit0, qubit1], self._number_of_qubits)
        # Convert to complex rank-4 tensor
        gate_tensor = np.reshape(np.array(gate, dtype=complex), 4 * [2])
        # Apply matrix multiplication
        self._statevector = np.einsum(indexes,
                                      gate_tensor,
                                      self._statevector,
                                      dtype=complex,
                                      casting='no')

    def _get_measure_outcome(self, qubit):
        """Simulate the outcome of measurement of a qubit.

        Args:
            qubit (int): the qubit to measure

        Return:
            tuple: pair (outcome, probability) where outcome is '0' or '1' and
            probability is the probability of the returned outcome.
        """
        # Axis for numpy.sum to compute probabilities
        axis = list(range(self._number_of_qubits))
        axis.remove(self._number_of_qubits - 1 - qubit)
        probabilities = np.sum(np.abs(self._statevector)**2, axis=tuple(axis))
        # Compute einsum index string for 1-qubit matrix multiplication
        random_number = self._local_random.rand()
        if random_number < probabilities[0]:
            return '0', probabilities[0]
        # Else outcome was '1'
        return '1', probabilities[1]

    def _add_sample_measure(self, measure_params, num_samples):
        """Generate memory samples from current statevector.

        Args:
            measure_params (list): List of (qubit, cmembit) values for
                                   measure instructions to sample.
            num_samples (int): The number of memory samples to generate.

        Returns:
            list: A list of memory values in hex format.
        """
        # Get unique qubits that are actually measured
        measured_qubits = list({qubit for qubit, cmembit in measure_params})
        num_measured = len(measured_qubits)
        # Axis for numpy.sum to compute probabilities
        axis = list(range(self._number_of_qubits))
        for qubit in reversed(measured_qubits):
            # Remove from largest qubit to smallest so list position is correct
            # with respect to position from end of the list
            axis.remove(self._number_of_qubits - 1 - qubit)
        probabilities = np.reshape(
            np.sum(np.abs(self._statevector)**2, axis=tuple(axis)),
            2**num_measured)
        # Generate samples on measured qubits
        samples = self._local_random.choice(range(2**num_measured),
                                            num_samples,
                                            p=probabilities)
        # Convert to bit-strings
        memory = []
        for sample in samples:
            classical_memory = self._classical_memory
            for count, (qubit, cmembit) in enumerate(sorted(measure_params)):
                qubit_outcome = int((sample & (1 << count)) >> count)
                membit = 1 << cmembit
                classical_memory = (classical_memory &
                                    (~membit)) | (qubit_outcome << cmembit)
            value = bin(classical_memory)[2:]
            memory.append(hex(int(value, 2)))
        return memory

    def _add_qasm_measure(self, qubit, cmembit, cregbit=None):
        """Apply a measure instruction to a qubit.

        Args:
            qubit (int): qubit is the qubit measured.
            cmembit (int): is the classical memory bit to store outcome in.
            cregbit (int, optional): is the classical register bit to store outcome in.
        """
        # get measure outcome
        outcome, probability = self._get_measure_outcome(qubit)
        # update classical state
        membit = 1 << cmembit
        self._classical_memory = (self._classical_memory &
                                  (~membit)) | (int(outcome) << cmembit)

        if cregbit is not None:
            regbit = 1 << cregbit
            self._classical_register = \
                (self._classical_register & (~regbit)) | (int(outcome) << cregbit)

        # update quantum state
        if outcome == '0':
            update_diag = [[1 / np.sqrt(probability), 0], [0, 0]]
        else:
            update_diag = [[0, 0], [0, 1 / np.sqrt(probability)]]
        # update classical state
        self._add_unitary_single(update_diag, qubit)

    def _add_qasm_reset(self, qubit):
        """Apply a reset instruction to a qubit.

        Args:
            qubit (int): the qubit being rest

        This is done by doing a simulating a measurement
        outcome and projecting onto the outcome state while
        renormalizing.
        """
        # get measure outcome
        outcome, probability = self._get_measure_outcome(qubit)
        # update quantum state
        if outcome == '0':
            update = [[1 / np.sqrt(probability), 0], [0, 0]]
            self._add_unitary_single(update, qubit)
        else:
            update = [[0, 1 / np.sqrt(probability)], [0, 0]]
            self._add_unitary_single(update, qubit)

    def _validate_initial_statevector(self):
        """Validate an initial statevector"""
        # If initial statevector isn't set we don't need to validate
        if self._initial_statevector is None:
            return
        # Check statevector is correct length for number of qubits
        length = len(self._initial_statevector)
        required_dim = 2**self._number_of_qubits
        if length != required_dim:
            raise BasicAerError('initial statevector is incorrect length: ' +
                                '{} != {}'.format(length, required_dim))

    def _set_options(self, qobj_config=None, backend_options=None):
        """Set the backend options for all experiments in a qobj"""
        # Reset default options
        self._initial_statevector = self.DEFAULT_OPTIONS["initial_statevector"]
        self._chop_threshold = self.DEFAULT_OPTIONS["chop_threshold"]
        if backend_options is None:
            backend_options = {}

        # Check for custom initial statevector in backend_options first,
        # then config second
        if 'initial_statevector' in backend_options:
            self._initial_statevector = np.array(
                backend_options['initial_statevector'], dtype=complex)
        elif hasattr(qobj_config, 'initial_statevector'):
            self._initial_statevector = np.array(
                qobj_config.initial_statevector, dtype=complex)
        if self._initial_statevector is not None:
            # Check the initial statevector is normalized
            norm = np.linalg.norm(self._initial_statevector)
            if round(norm, 12) != 1:
                raise BasicAerError('initial statevector is not normalized: ' +
                                    'norm {} != 1'.format(norm))
        # Check for custom chop threshold
        # Replace with custom options
        if 'chop_threshold' in backend_options:
            self._chop_threshold = backend_options['chop_threshold']
        elif hasattr(qobj_config, 'chop_threshold'):
            self._chop_threshold = qobj_config.chop_threshold

    def _initialize_statevector(self):
        """Set the initial statevector for simulation"""
        if self._initial_statevector is None:
            # Set to default state of all qubits in |0>
            self._statevector = np.zeros(2**self._number_of_qubits,
                                         dtype=complex)
            self._statevector[0] = 1
        else:
            self._statevector = self._initial_statevector.copy()
        # Reshape to rank-N tensor
        self._statevector = np.reshape(self._statevector,
                                       self._number_of_qubits * [2])

    def _get_statevector(self):
        """Return the current statevector in JSON Result spec format"""
        vec = np.reshape(self._statevector, 2**self._number_of_qubits)
        # Expand complex numbers
        vec = np.stack([vec.real, vec.imag], axis=1)
        # Truncate small values
        vec[abs(vec) < self._chop_threshold] = 0.0
        return vec

    def _validate_measure_sampling(self, experiment):
        """Determine if measure sampling is allowed for an experiment

        Args:
            experiment (QobjExperiment): a qobj experiment.
        """

        # Check for config flag
        if hasattr(experiment.config, 'allows_measure_sampling'):
            self._sample_measure = experiment.config.allows_measure_sampling
        # If flag isn't found do a simple test to see if a circuit contains
        # no reset instructions, and no gates instructions after
        # the first measure.
        else:
            measure_flag = False
            for instruction in experiment.instructions:
                # If circuit contains reset operations we cannot sample
                if instruction.name == "reset":
                    self._sample_measure = False
                    return
                # If circuit contains a measure option then we can
                # sample only if all following operations are measures
                if measure_flag:
                    # If we find a non-measure instruction
                    # we cannot do measure sampling
                    if instruction.name not in [
                            "measure", "barrier", "id", "u0"
                    ]:
                        self._sample_measure = False
                        return
                elif instruction.name == "measure":
                    measure_flag = True
            # If we made it to the end of the circuit without returning
            # measure sampling is allowed
            self._sample_measure = True

    def run(self, qobj, backend_options=None):
        """Run qobj asynchronously.

        Args:
            qobj (Qobj): payload of the experiment
            backend_options (dict): backend options

        Returns:
            BasicAerJob: derived from BaseJob

        Additional Information:
            backend_options: Is a dict of options for the backend. It may contain
                * "initial_statevector": vector_like

            The "initial_statevector" option specifies a custom initial
            initial statevector for the simulator to be used instead of the all
            zero state. This size of this vector must be correct for the number
            of qubits in all experiments in the qobj.

            Example::

                backend_options = {
                    "initial_statevector": np.array([1, 0, 0, 1j]) / np.sqrt(2),
                }
        """
        self._set_options(qobj_config=qobj.config,
                          backend_options=backend_options)
        job_id = str(uuid.uuid4())
        job = BasicAerJob(self, job_id, self._run_job, qobj)
        job.submit()
        return job

    def _run_job(self, job_id, qobj):
        """Run experiments in qobj

        Args:
            job_id (str): unique id for the job.
            qobj (Qobj): job description

        Returns:
            Result: Result object
        """
        self._validate(qobj)
        result_list = []
        self._shots = qobj.config.shots
        self._memory = getattr(qobj.config, 'memory', False)
        self._qobj_config = qobj.config
        start = time.time()
        for experiment in qobj.experiments:
            result_list.append(self.run_experiment(experiment))
        end = time.time()
        result = {
            'backend_name': self.name(),
            'backend_version': self._configuration.backend_version,
            'qobj_id': qobj.qobj_id,
            'job_id': job_id,
            'results': result_list,
            'status': 'COMPLETED',
            'success': True,
            'time_taken': (end - start),
            'header': qobj.header.as_dict()
        }

        return Result.from_dict(result)

    def run_experiment(self, experiment):
        """Run an experiment (circuit) and return a single experiment result.

        Args:
            experiment (QobjExperiment): experiment from qobj experiments list

        Returns:
             dict: A result dictionary which looks something like::

                {
                "name": name of this experiment (obtained from qobj.experiment header)
                "seed": random seed used for simulation
                "shots": number of shots used in the simulation
                "data":
                    {
                    "counts": {'0x9: 5, ...},
                    "memory": ['0x9', '0xF', '0x1D', ..., '0x9']
                    },
                "status": status string for the simulation
                "success": boolean
                "time_taken": simulation time of this single experiment
                }
        Raises:
            BasicAerError: if an error occurred.
        """
        start = time.time()
        self._number_of_qubits = experiment.config.n_qubits
        self._number_of_cmembits = experiment.config.memory_slots
        self._statevector = 0
        self._classical_memory = 0
        self._classical_register = 0
        self._sample_measure = False
        # Validate the dimension of initial statevector if set
        self._validate_initial_statevector()
        # Get the seed looking in circuit, qobj, and then random.
        if hasattr(experiment.config, 'seed'):
            seed = experiment.config.seed
        elif hasattr(self._qobj_config, 'seed'):
            seed = self._qobj_config.seed
        else:
            # For compatibility on Windows force dyte to be int32
            # and set the maximum value to be (2 ** 31) - 1
            seed = np.random.randint(2147483647, dtype='int32')

        self._local_random.seed(seed=seed)
        # Check if measure sampling is supported for current circuit
        self._validate_measure_sampling(experiment)

        # List of final counts for all shots
        memory = []
        # Check if we can sample measurements, if so we only perform 1 shot
        # and sample all outcomes from the final state vector
        if self._sample_measure:
            shots = 1
            # Store (qubit, cmembit) pairs for all measure ops in circuit to
            # be sampled
            measure_sample_ops = []
        else:
            shots = self._shots
        for _ in range(shots):
            self._initialize_statevector()
            # Initialize classical memory to all 0
            self._classical_memory = 0
            self._classical_register = 0
            for operation in experiment.instructions:
                conditional = getattr(operation, 'conditional', None)
                if isinstance(conditional, int):
                    if not self._classical_register[-conditional - 1]:
                        continue
                elif conditional is not None:
                    mask = int(operation.conditional.mask, 16)
                    if mask > 0:
                        value = self._classical_memory & mask
                        while (mask & 0x1) == 0:
                            mask >>= 1
                            value >>= 1
                        if value != int(operation.conditional.val, 16):
                            continue

                # Check if single  gate
                if operation.name in ('U', 'u1', 'u2', 'u3'):
                    params = getattr(operation, 'params', None)
                    qubit = operation.qubits[0]
                    gate = single_gate_matrix(operation.name, params)
                    self._add_unitary_single(gate, qubit)
                # Check if CX gate
                elif operation.name in ('id', 'u0'):
                    pass
                elif operation.name in ('CX', 'cx'):
                    qubit0 = operation.qubits[0]
                    qubit1 = operation.qubits[1]
                    gate = cx_gate_matrix()
                    self._add_unitary_two(gate, qubit0, qubit1)
                # Check if reset
                elif operation.name == 'reset':
                    qubit = operation.qubits[0]
                    self._add_qasm_reset(qubit)
                # Check if barrier
                elif operation.name == 'barrier':
                    pass
                # Check if measure
                elif operation.name == 'measure':
                    qubit = operation.qubits[0]
                    cmembit = operation.memory[0]
                    cregbit = operation.register[0] if hasattr(
                        operation, 'register') else None

                    if self._sample_measure:
                        # If sampling measurements record the qubit and cmembit
                        # for this measurement for later sampling
                        measure_sample_ops.append((qubit, cmembit))
                    else:
                        # If not sampling perform measurement as normal
                        self._add_qasm_measure(qubit, cmembit, cregbit)
                elif operation.name == 'bfunc':
                    mask = int(operation.mask, 16)
                    relation = operation.relation
                    val = int(operation.val, 16)

                    cregbit = operation.register
                    cmembit = operation.memory if hasattr(operation,
                                                          'memory') else None

                    compared = (self._classical_register & mask) - val

                    if relation == '==':
                        outcome = (compared == 0)
                    elif relation == '!=':
                        outcome = (compared != 0)
                    elif relation == '<':
                        outcome = (compared < 0)
                    elif relation == '<=':
                        outcome = (compared <= 0)
                    elif relation == '>':
                        outcome = (compared > 0)
                    elif relation == '>=':
                        outcome = (compared >= 0)
                    else:
                        raise BasicAerError(
                            'Invalid boolean function relation.')

                    # Store outcome in register and optionally memory slot
                    regbit = 1 << cregbit
                    self._classical_register = \
                        (self._classical_register & (~regbit)) | (int(outcome) << cregbit)
                    if cmembit is not None:
                        membit = 1 << cmembit
                        self._classical_memory = \
                            (self._classical_memory & (~membit)) | (int(outcome) << cmembit)
                else:
                    backend = self.name()
                    err_msg = '{0} encountered unrecognized operation "{1}"'
                    raise BasicAerError(err_msg.format(backend,
                                                       operation.name))

            # Add final creg data to memory list
            if self._number_of_cmembits > 0:
                if self._sample_measure:
                    # If sampling we generate all shot samples from the final statevector
                    memory = self._add_sample_measure(measure_sample_ops,
                                                      self._shots)
                else:
                    # Turn classical_memory (int) into bit string and pad zero for unused cmembits
                    outcome = bin(self._classical_memory)[2:]
                    memory.append(hex(int(outcome, 2)))

        # Add data
        data = {'counts': dict(Counter(memory))}
        # Optionally add memory list
        if self._memory:
            data['memory'] = memory
        # Optionally add final statevector
        if self.SHOW_FINAL_STATE:
            data['statevector'] = self._get_statevector()
            # Remove empty counts and memory for statevector simulator
            if not data['counts']:
                data.pop('counts')
            if 'memory' in data and not data['memory']:
                data.pop('memory')
        end = time.time()
        return {
            'name': experiment.header.name,
            'seed': seed,
            'shots': self._shots,
            'data': data,
            'status': 'DONE',
            'success': True,
            'time_taken': (end - start),
            'header': experiment.header.as_dict()
        }

    def _validate(self, qobj):
        """Semantic validations of the qobj which cannot be done via schemas."""
        n_qubits = qobj.config.n_qubits
        max_qubits = self.configuration().n_qubits
        if n_qubits > max_qubits:
            raise BasicAerError(
                'Number of qubits {} '.format(n_qubits) +
                'is greater than maximum ({}) '.format(max_qubits) +
                'for "{}".'.format(self.name()))
        for experiment in qobj.experiments:
            name = experiment.header.name
            if experiment.config.memory_slots == 0:
                logger.warning(
                    'No classical registers in circuit "%s", '
                    'counts will be empty.', name)
            elif 'measure' not in [op.name for op in experiment.instructions]:
                logger.warning(
                    'No measurements in circuit "%s", '
                    'classical register will remain all zeros.', name)
class StatevectorSimulator(QasmSimulator):
    """C++ statevector simulator"""

    DEFAULT_CONFIGURATION = {
        'backend_name': 'statevector_simulator',
        'backend_version': '1.0.0',
        'n_qubits': int(log2(local_hardware_info()['memory'] * (1024**3)/16)),
        'url': 'https://github.com/Qiskit/qiskit-terra/src/qasm-simulator-cpp',
        'simulator': True,
        'local': True,
        'conditional': False,
        'open_pulse': False,
        'memory': False,
        'max_shots': 65536,
        'description': 'A single-shot C++ statevector simulator for the |0> state evolution',
        'basis_gates': ['u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h',
                        's', 'sdg', 't', 'tdg', 'rzz', 'load', 'save',
                        'snapshot'],
        'gates': [
            {
                'name': 'u1',
                'parameters': ['lambda'],
                'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }'
            },
            {
                'name': 'u2',
                'parameters': ['phi', 'lambda'],
                'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }'
            },
            {
                'name': 'u3',
                'parameters': ['theta', 'phi', 'lambda'],
                'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }'
            },
            {
                'name': 'cx',
                'parameters': ['c', 't'],
                'qasm_def': 'gate cx c,t { CX c,t; }'
            },
            {
                'name': 'cz',
                'parameters': ['a', 'b'],
                'qasm_def': 'gate cz a,b { h b; cx a,b; h b; }'
            },
            {
                'name': 'id',
                'parameters': ['a'],
                'qasm_def': 'gate id a { U(0,0,0) a; }'
            },
            {
                'name': 'x',
                'parameters': ['a'],
                'qasm_def': 'gate x a { u3(pi,0,pi) a; }'
            },
            {
                'name': 'y',
                'parameters': ['a'],
                'qasm_def': 'gate y a { u3(pi,pi/2,pi/2) a; }'
            },
            {
                'name': 'z',
                'parameters': ['z'],
                'qasm_def': 'gate z a { u1(pi) a; }'
            },
            {
                'name': 'h',
                'parameters': ['a'],
                'qasm_def': 'gate h a { u2(0,pi) a; }'
            },
            {
                'name': 's',
                'parameters': ['a'],
                'qasm_def': 'gate s a { u1(pi/2) a; }'
            },
            {
                'name': 'sdg',
                'parameters': ['a'],
                'qasm_def': 'gate sdg a { u1(-pi/2) a; }'
            },
            {
                'name': 't',
                'parameters': ['a'],
                'qasm_def': 'gate t a { u1(pi/4) a; }'
            },
            {
                'name': 'tdg',
                'parameters': ['a'],
                'qasm_def': 'gate tdg a { u1(-pi/4) a; }'
            },
            {
                'name': 'rzz',
                'parameters': ['theta', 'a', 'b'],
                'qasm_def': 'gate rzz(theta) a,b { cx a,b; u1(theta) b; cx a,b; }'
            },
            {
                'name': 'load',
                'parameters': ['slot'],
                'qasm_def': 'gate load(slot) q { TODO }'
            },
            {
                'name': 'save',
                'parameters': ['slot'],
                'qasm_def': 'gate save(slot) q { TODO }'
            },
            {
                'name': 'snapshot',
                'parameters': ['slot'],
                'qasm_def': 'gate snapshot(slot) q { TODO }'
            }
        ]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(configuration=(configuration or
                                        BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION)),
                         provider=provider)

    def run(self, qobj):
        """Run a qobj on the backend."""
        job_id = str(uuid.uuid4())
        job = SimulatorsJob(self, job_id, self._run_job, qobj)
        job.submit()
        return job

    def _run_job(self, job_id, qobj):
        """Run a Qobj on the backend."""
        self._validate(qobj)
        final_state_key = 32767  # Internal key for final state snapshot
        # Add final snapshots to circuits
        for experiment in qobj.experiments:
            experiment.instructions.append(
                QobjInstruction(name='snapshot', params=[final_state_key],
                                label='MISSING', type='MISSING')
            )
        result = super()._run_job(job_id, qobj)
        # Remove added snapshot from qobj
        for experiment in qobj.experiments:
            del experiment.instructions[-1]
        # Extract final state snapshot and move to 'statevector' data field
        for experiment_result in result.results:
            snapshots = experiment_result.data.snapshots.to_dict()
            if str(final_state_key) in snapshots['statevector']:
                final_state_key = str(final_state_key)
            # Pop off final snapshot added above
            final_state = snapshots['statevector'].pop(final_state_key)[0]
            final_state = array([v[0] + 1j * v[1] for v in final_state], dtype=complex)
            # Add final state to results data
            experiment_result.data.statevector = final_state
            # Remove snapshot dict if empty
            if snapshots == {}:
                delattr(experiment_result.data, 'snapshots')
        return result

    def _validate(self, qobj):
        """Semantic validations of the qobj which cannot be done via schemas.
        Some of these may later move to backend schemas.

        1. No shots
        2. No measurements in the middle
        """
        if qobj.config.shots != 1:
            logger.info("statevector simulator only supports 1 shot. "
                        "Setting shots=1.")
            qobj.config.shots = 1
        for experiment in qobj.experiments:
            if getattr(experiment.config, 'shots', 1) != 1:
                logger.info("statevector simulator only supports 1 shot. "
                            "Setting shots=1 for circuit %s.", experiment.name)
                experiment.config.shots = 1
            for op in experiment.instructions:
                if op.name in ['measure', 'reset']:
                    raise SimulatorError(
                        "In circuit {}: statevector simulator does not support "
                        "measure or reset.".format(experiment.header.name))
class StatevectorSimulator(QasmSimulator):
    """C++ statevector simulator"""

    DEFAULT_CONFIGURATION = {
        'backend_name': 'statevector_simulator',
        'backend_version': '1.0.0',
        'n_qubits': int(log2(local_hardware_info()['memory'] * (1024**3)/16)),
        'url': 'https://github.com/Qiskit/qiskit-terra/src/qasm-simulator-cpp',
        'simulator': True,
        'local': True,
        'conditional': False,
        'open_pulse': False,
        'memory': False,
        'max_shots': 65536,
        'description': 'A single-shot C++ statevector simulator for the |0> state evolution',
        'basis_gates': ['u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h',
                        's', 'sdg', 't', 'tdg', 'rzz', 'load', 'save',
                        'snapshot'],
        'gates': [{'name': 'TODO', 'parameters': [], 'qasm_def': 'TODO'}]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(configuration=(configuration or
                                        BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION)),
                         provider=provider)

    def properties(self):
        """Return backend properties"""
        properties = {
            'backend_name': self.name(),
            'backend_version': self.configuration().backend_version,
            'last_update_date': '2000-01-01 00:00:00Z',
            'qubits': [[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
                         'unit': 'TODO', 'value': 0}]],
            'gates': [{'qubits': [0], 'gate': 'TODO',
                       'parameters':
                           [{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
                             'unit': 'TODO', 'value': 0}]}],
            'general': []
        }

        return BackendProperties.from_dict(properties)

    def run(self, qobj):
        """Run a qobj on the the backend."""
        job_id = str(uuid.uuid4())
        aer_job = AerJob(self, job_id, self._run_job, qobj)
        aer_job.submit()
        return aer_job

    def _run_job(self, job_id, qobj):
        """Run a Qobj on the backend."""
        self._validate(qobj)
        final_state_key = 32767  # Internal key for final state snapshot
        # Add final snapshots to circuits
        for experiment in qobj.experiments:
            experiment.instructions.append(
                QobjInstruction(name='snapshot', params=[final_state_key],
                                label='MISSING', type='MISSING')
            )
        result = super()._run_job(job_id, qobj)
        # Remove added snapshot from qobj
        for experiment in qobj.experiments:
            del experiment.instructions[-1]
        # Extract final state snapshot and move to 'statevector' data field
        for experiment_result in result.results:
            snapshots = experiment_result.data.snapshots.to_dict()
            if str(final_state_key) in snapshots:
                final_state_key = str(final_state_key)
            # Pop off final snapshot added above
            final_state = snapshots.pop(final_state_key, None)
            final_state = final_state['statevector'][0]
            # Add final state to results data
            experiment_result.data.statevector = final_state
            # Remove snapshot dict if empty
            if snapshots == {}:
                delattr(experiment_result.data, 'snapshots')
        return result

    def _validate(self, qobj):
        """Semantic validations of the qobj which cannot be done via schemas.
        Some of these may later move to backend schemas.

        1. No shots
        2. No measurements in the middle
        """
        if qobj.config.shots != 1:
            logger.info("statevector simulator only supports 1 shot. "
                        "Setting shots=1.")
            qobj.config.shots = 1
        for experiment in qobj.experiments:
            if getattr(experiment.config, 'shots', 1) != 1:
                logger.info("statevector simulator only supports 1 shot. "
                            "Setting shots=1 for circuit %s.", experiment.name)
                experiment.config.shots = 1
            for op in experiment.instructions:
                if op.name in ['measure', 'reset']:
                    raise SimulatorError(
                        "In circuit {}: statevector simulator does not support "
                        "measure or reset.".format(experiment.header.name))
Ejemplo n.º 28
0
class CliffordSimulator(BaseBackend):
    """"C++ Clifford circuit simulator with realistic noise."""

    DEFAULT_CONFIGURATION = {
        'backend_name':
        'clifford_simulator',
        'backend_version':
        '1.0.0',
        'n_qubits':
        int(log2(local_hardware_info()['memory'] * (1024**3) / 16)),
        'url':
        'https://github.com/Qiskit/qiskit-terra/src/qasm-simulator-cpp',
        'simulator':
        True,
        'local':
        True,
        'conditional':
        True,
        'open_pulse':
        False,
        'memory':
        False,
        'max_shots':
        65536,
        'description':
        'A C++ Clifford simulator with approximate noise',
        'basis_gates': [
            'cx', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 'snapshot', 'wait',
            'noise', 'save', 'load'
        ],
        'gates': [{
            'name': 'TODO',
            'parameters': [],
            'qasm_def': 'TODO'
        }]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(configuration=(configuration
                                        or BackendConfiguration.from_dict(
                                            self.DEFAULT_CONFIGURATION)),
                         provider=provider)

        # Try to use the default executable if not specified.
        if 'exe' in self._configuration:
            paths = [self._configuration.exe]
        else:
            paths = DEFAULT_SIMULATOR_PATHS

        # Ensure that the executable is available.
        try:
            self._configuration.exe = next(
                path for path in paths
                if (os.path.exists(path) and os.path.getsize(path) > 100))
        except StopIteration:
            raise FileNotFoundError(
                'Simulator executable not found (using %s)' %
                getattr(self._configuration, 'exe', 'default locations'))

    def properties(self):
        """Return backend properties"""
        properties = {
            'backend_name':
            self.name(),
            'backend_version':
            self.configuration().backend_version,
            'last_update_date':
            '2000-01-01 00:00:00Z',
            'qubits': [[{
                'name': 'TODO',
                'date': '2000-01-01 00:00:00Z',
                'unit': 'TODO',
                'value': 0
            }]],
            'gates': [{
                'qubits': [0],
                'gate':
                'TODO',
                'parameters': [{
                    'name': 'TODO',
                    'date': '2000-01-01 00:00:00Z',
                    'unit': 'TODO',
                    'value': 0
                }]
            }],
            'general': []
        }

        return BackendProperties.from_dict(properties)

    def run(self, qobj):
        """Run a Qobj on the backend.

        Args:
            qobj (dict): job description

        Returns:
            AerJob: derived from BaseJob
        """
        job_id = str(uuid.uuid4())
        aer_job = AerJob(self, job_id, self._run_job, qobj)
        aer_job.submit()
        return aer_job

    def _run_job(self, job_id, qobj):
        if isinstance(qobj, Qobj):
            qobj_dict = qobj.as_dict()
        else:
            qobj_dict = qobj
        self._validate()
        # set backend to Clifford simulator
        if 'config' in qobj_dict:
            qobj_dict['config']['simulator'] = 'clifford'
        else:
            qobj_dict['config'] = {'simulator': 'clifford'}

        qobj = Qobj.from_dict(qobj_dict)
        result = run(qobj, self._configuration.exe)
        result['job_id'] = job_id

        return result_from_old_style_dict(result)

    def _validate(self):
        return