コード例 #1
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).

        * "zero_threshold" (double): Sets the threshold for truncating
            small values to zero in the result data (Default: 1e-10).

        * "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_memory_mb" (int): Sets the maximum size of memory
            to store a state vector. If a state vector needs more, an error
            is thrown. In general, a state vector of n-qubits uses 2^n complex
            values (16 Bytes). If set to 0, the maximum will be automatically
            set to half the system memory size (Default: 0).

        * "statevector_parallel_threshold" (int): Sets the threshold that
            2 * "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: 14).
    """

    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', 'multiplexer', '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:
            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 ({}) is greater than max ({}) for "{}" with {} GB system memory.'
                .format(n_qubits, max_qubits, name,
                        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 {} instruction {} in circuit {}'.format(
                            name, operation.name, exp_name))
コード例 #2
0
class QasmSimulator(AerBackend):
    """
    Noisy quantum circuit simulator backend.

    The `QasmSimulator` supports multiple simulation methods and
    configurable options for each simulation method. These options are
    specified in a dictionary which may be passed to the simulator using
    the ``backend_options`` kwarg for :meth:`QasmSimulator.run` or
    ``qiskit.execute``.

    The default behavior chooses a simulation method automatically based on
    the input circuit and noise model. A custom method can be specified using the
    ``"method"`` field in ``backend_options`` as illustrated in the following
    example. Available simulation methods and additional backend options are
    listed below.

    **Example**

    .. code-block:: python

        backend = QasmSimulator()
        backend_options = {"method": "statevector"}

        # Circuit execution
        job = execute(circuits, backend, backend_options=backend_options)

        # Qobj execution
        job = backend.run(qobj, backend_options=backend_options)

    **Simulation method**

    Available simulation methods are:

    * ``"statevector"``: A dense statevector simulation that can sample
      measurement outcomes from *ideal* circuits with all measurements at
      end of the circuit. For noisy simulations each shot samples a
      randomly sampled noisy circuit from the noise model.
      ``"statevector_cpu"`` is an alias of ``"statevector"``.

    * ``"statevector_gpu"``: A dense statevector simulation that provides
      the same functionalities with ``"statevector"``. GPU performs the computation
      to calculate probability amplitudes as CPU does. If no GPU is available,
      a runtime error is raised.

    * ``"density_matrix"``: A dense density matrix simulation that may
      sample measurement outcomes from *noisy* circuits with all
      measurements at end of the circuit. It can only simulate half the
      number of qubits as the statevector method.

    * ``"density_matrix_gpu"``: A dense density matrix simulation that provides
      the same functionalities with ``"density_matrix"``. GPU performs the computation
      to calculate probability amplitudes as CPU does. If no GPU is available,
      a runtime error is raised.

    * ``"stabilizer"``: An efficient Clifford stabilizer state simulator
      that can simulate noisy Clifford circuits if all errors in the noise model are also
      Clifford errors.

    * ``"extended_stabilizer"``: An approximate simulated based on a
      ranked-stabilizer decomposition that decomposes circuits into stabilizer
      state terms. The number of terms grows with the number of
      non-Clifford gates.

    * ``"matrix_product_state"``: A tensor-network statevector simulator that
      uses a Matrix Product State (MPS) representation for the state.

    * ``"automatic"``: The default behavior where the method is chosen
      automatically for each circuit based on the circuit instructions,
      number of qubits, and noise model.

    **Backend options**

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

    * ``"method"`` (str): Set the simulation method. See backend methods
      for additional information (Default: "automatic").

    * ``"precision"`` (str): Set the floating point precision for
      certain simulation methods to either "single" or "double"
      precision (default: "double").

    * ``"zero_threshold"`` (double): Sets the threshold for truncating
      small values to zero in the result data (Default: 1e-10).

    * ``"validation_threshold"`` (double): Sets the threshold for checking
      if initial states are valid (Default: 1e-8).

    * ``"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 will 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: 0).

    * ``"max_memory_mb"`` (int): Sets the maximum size of memory
      to store a state vector. If a state vector needs more, an error
      is thrown. In general, a state vector of n-qubits uses 2^n complex
      values (16 Bytes). If set to 0, the maximum will be automatically
      set to half the system memory size (Default: 0).

    * ``"optimize_ideal_threshold"`` (int): Sets the qubit threshold for
      applying circuit optimization passes on ideal circuits.
      Passes include gate fusion and truncation of unused qubits
      (Default: 5).

    * ``"optimize_noise_threshold"`` (int): Sets the qubit threshold for
      applying circuit optimization passes on ideal circuits.
      Passes include gate fusion and truncation of unused qubits
      (Default: 12).

    These backend options only apply when using the ``"statevector"``
    simulation method:

    * ``"statevector_parallel_threshold"`` (int): Sets the threshold that
      the number of 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: 14).

    * ``"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)

    These backend options only apply when using the ``"stabilizer"``
    simulation method:

    * ``"stabilizer_max_snapshot_probabilities"`` (int): set the maximum
      qubit number for the
      `~qiskit.providers.aer.extensions.SnapshotProbabilities`
      instruction (Default: 32).

    These backend options only apply when using the ``"extended_stabilizer"``
    simulation method:

    * ``"extended_stabilizer_measure_sampling"`` (bool): Enable measure
      sampling optimization on supported circuits. This prevents the
      simulator from re-running the measure monte-carlo step for each
      shot. Enabling measure sampling may reduce accuracy of the
      measurement counts if the output distribution is strongly
      peaked (Default: False).

    * ``"extended_stabilizer_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 extended_stabilizer_disable_measurement_opt
      to True (Default: 5000).

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

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

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

    These backend options apply in circuit optimization passes:

    * ``"fusion_enable"`` (bool): Enable fusion optimization in circuit
      optimization passes [Default: True]
    * ``"fusion_verbose"`` (bool): Output gates generated in fusion optimization
      into metadata [Default: False]
    * ``"fusion_max_qubit"`` (int): Maximum number of qubits for a operation generated
      in a fusion optimization [Default: 5]
    * ``"fusion_threshold"`` (int): Threshold that number of qubits must be greater
      than or equal to enable fusion optimization [Default: 20]

    These backend options only apply when using the ``"matrix_product_state"``
    simulation method:

    * ``"matrix_product_state_max_bond_dimension"`` (int): Sets a limit
      on the number of Schmidt coefficients retained at the end of
      the svd algorithm. Coefficients beyond this limit will be discarded.
      (Default: None, i.e., no limit on the bond dimension).

    * ``"matrix_product_state_truncation_threshold"`` (double):
      Discard the smallest coefficients for which the sum of
      their squares is smaller than this threshold.
      (Default: 1e-16).

    """

    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':
        int(1e6),
        'description':
        'A C++ simulator with realistic noise for QASM Qobj files',
        'coupling_map':
        None,
        'basis_gates': [
            'u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg',
            't', 'tdg', 'swap', 'ccx', 'unitary', 'diagonal', 'initialize',
            'cu1', 'cu2', 'cu3', 'cswap', 'mcx', 'mcy', 'mcz', 'mcu1', 'mcu2',
            'mcu3', 'mcswap', 'multiplexer', 'kraus', 'roerror'
        ],
        'gates': [{
            'name': 'u1',
            'parameters': ['lam'],
            'conditional': True,
            'description': 'Single-qubit gate [[1, 0], [0, exp(1j*lam)]]',
            'qasm_def': 'gate u1(lam) q { U(0,0,lam) q; }'
        }, {
            'name': 'u2',
            'parameters': ['phi', 'lam'],
            'conditional': True,
            'description':
            'Single-qubit gate [[1, -exp(1j*lam)], [exp(1j*phi), exp(1j*(phi+lam))]]/sqrt(2)',
            'qasm_def': 'gate u2(phi,lam) q { U(pi/2,phi,lam) q; }'
        }, {
            'name':
            'u3',
            'parameters': ['theta', 'phi', 'lam'],
            'conditional':
            True,
            'description':
            'Single-qubit gate with three rotation angles',
            'qasm_def':
            'gate u3(theta,phi,lam) q { U(theta,phi,lam) q; }'
        }, {
            'name': 'cx',
            'parameters': [],
            'conditional': True,
            'description': 'Two-qubit Controlled-NOT gate',
            'qasm_def': 'gate cx c,t { CX c,t; }'
        }, {
            'name': 'cz',
            'parameters': [],
            'conditional': True,
            'description': 'Two-qubit Controlled-Z gate',
            'qasm_def': 'gate cz a,b { h b; cx a,b; h b; }'
        }, {
            'name': 'id',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit identity gate',
            'qasm_def': 'gate id a { U(0,0,0) a; }'
        }, {
            'name': 'x',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit Pauli-X gate',
            'qasm_def': 'gate x a { U(pi,0,pi) a; }'
        }, {
            'name': 'y',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit Pauli-Y gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'z',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit Pauli-Z gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'h',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit Hadamard gate',
            'qasm_def': 'TODO'
        }, {
            'name': 's',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit phase gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'sdg',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit adjoint phase gate',
            'qasm_def': 'TODO'
        }, {
            'name': 't',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit T gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'tdg',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit adjoint T gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'swap',
            'parameters': [],
            'conditional': True,
            'description': 'Two-qubit SWAP gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'ccx',
            'parameters': [],
            'conditional': True,
            'description': 'Three-qubit Toffoli gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'cswap',
            'parameters': [],
            'conditional': True,
            'description': 'Three-qubit Fredkin (controlled-SWAP) gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'unitary',
            'parameters': ['matrix'],
            'conditional': True,
            'description': 'N-qubit unitary gate. '
            'The parameter is the N-qubit matrix to apply.',
            'qasm_def': 'unitary(matrix) q1, q2,...'
        }, {
            'name':
            'diagonal',
            'parameters': ['diag_elements'],
            'conditional':
            True,
            'description':
            'N-qubit diagonal unitary gate. The parameters are the'
            ' diagonal entries of the N-qubit matrix to apply.',
            'qasm_def':
            'TODO'
        }, {
            'name': 'initialize',
            'parameters': ['vector'],
            'conditional': False,
            'description': 'N-qubit state initialize. '
            'Resets qubits then sets statevector to the parameter vector.',
            'qasm_def': 'initialize(vector) q1, q2,...'
        }, {
            'name': 'cu1',
            'parameters': ['lam'],
            'conditional': True,
            'description': 'Two-qubit Controlled-u1 gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'cu2',
            'parameters': ['phi', 'lam'],
            'conditional': True,
            'description': 'Two-qubit Controlled-u2 gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'cu3',
            'parameters': ['theta', 'phi', 'lam'],
            'conditional': True,
            'description': 'Two-qubit Controlled-u3 gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'mcx',
            'parameters': [],
            'conditional': True,
            'description': 'N-qubit multi-controlled-X gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'mcy',
            'parameters': [],
            'conditional': True,
            'description': 'N-qubit multi-controlled-Y gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'mcz',
            'parameters': [],
            'conditional': True,
            'description': 'N-qubit multi-controlled-Z gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'mcu1',
            'parameters': ['lam'],
            'conditional': True,
            'description': 'N-qubit multi-controlled-u1 gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'mcu2',
            'parameters': ['phi', 'lam'],
            'conditional': True,
            'description': 'N-qubit multi-controlled-u2 gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'mcu3',
            'parameters': ['theta', 'phi', 'lam'],
            'conditional': True,
            'description': 'N-qubit multi-controlled-u3 gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'mcswap',
            'parameters': [],
            'conditional': True,
            'description': 'N-qubit multi-controlled-SWAP gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'multiplexer',
            'parameters': ['mat1', 'mat2', '...'],
            'conditional': True,
            'description': 'N-qubit multi-plexer gate. '
            'The input parameters are the gates for each value.',
            'qasm_def': 'TODO'
        }, {
            'name': 'kraus',
            'parameters': ['mat1', 'mat2', '...'],
            'conditional': True,
            'description': 'N-qubit Kraus error instruction. '
            'The input parameters are the Kraus matrices.',
            'qasm_def': 'TODO'
        }, {
            'name': 'roerror',
            'parameters': ['matrix'],
            'conditional': False,
            'description': 'N-bit classical readout error instruction. '
            'The input parameter is the readout error probability matrix.',
            'qasm_def': 'TODO'
        }]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(qasm_controller_execute,
                         QasmBackendConfiguration.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.

        Warn if no measurements in circuit with classical registers.
        """
        for experiment in qobj.experiments:
            # If circuit contains classical registers but not
            # measurements raise a warning
            if experiment.config.memory_slots > 0:
                # Check if measure opts missing
                no_measure = True
                for op in experiment.instructions:
                    if not no_measure:
                        break  # we don't need to check any more ops
                    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.',
                        experiment.header.name)
コード例 #3
0
class UnitarySimulator(AerBackend):
    """Ideal quantum circuit unitary simulator.

    **Backend options**

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

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

    * ``"validation_threshold"`` (double): Sets the threshold for checking
      if initial unitary and target unitary are unitary matrices.
      (Default: 1e-8).

    * ``"zero_threshold"`` (double): Sets the threshold for truncating
      small values to zero in the result data (Default: 1e-10).

    * ``"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_memory_mb"`` (int): Sets the maximum size of memory
      to store a state vector. If a state vector needs more, an error
      is thrown. In general, a state vector of n-qubits uses 2^n complex
      values (16 Bytes). If set to 0, the maximum will be automatically
      set to half the system memory size (Default: 0).

    * ``"statevector_parallel_threshold"`` (int): Sets the threshold that
      2 * "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: 14).
    """

    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':
        int(1e6),  # Note that this backend will only ever
        # perform a single shot. This value is just
        # so that the default shot value for execute
        # will not raise an error when trying to run
        # a simulation
        'description':
        'A C++ unitary simulator for QASM Qobj files',
        'coupling_map':
        None,
        'basis_gates': [
            'u1', 'u2', 'u3', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', 'y', 'z',
            'h', 's', 'sdg', 'sx', 't', 'tdg', 'swap', 'cx', 'cy', 'cz', 'csx',
            'cp', 'cu1', 'cu2', 'cu3', 'rxx', 'ryy', 'rzz', 'rzx', 'ccx',
            'cswap', 'mcx', 'mcy', 'mcz', 'mcsx', 'mcp', 'mcu1', 'mcu2',
            'mcu3', 'mcrx', 'mcry', 'mcrz', 'mcr', 'mcswap', 'unitary',
            'diagonal', 'multiplexer', 'delay'
        ],
        'gates': []
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(unitary_controller_execute(),
                         QasmBackendConfiguration.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:
            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 ({}) is greater than max ({}) for "{}" with {} GB system memory.'
                .format(n_qubits, max_qubits, name,
                        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 {} instruction {} in circuit {}'.format(
                            name, operation.name, exp_name))
コード例 #4
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,
        'coupling_map': None,
        '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 QasmBackendConfiguration.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(self, gate, qubits):
        """Apply an N-qubit unitary matrix.

        Args:
            gate (matrix_like): an N-qubit unitary matrix
            qubits (list): the list of N-qubits.
        """
        # Get the number of qubits
        num_qubits = len(qubits)
        # Compute einsum index string for 1-qubit matrix multiplication
        indexes = einsum_vecmul_index(qubits, self._number_of_qubits)
        # Convert to complex rank-2N tensor
        gate_tensor = np.reshape(np.array(gate, dtype=complex),
                                 num_qubits * [2, 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 and sort in
        # ascending order
        measured_qubits = sorted(list({qubit for qubit, cmembit in measure_params}))
        num_measured = len(measured_qubits)
        # We use the axis kwarg for numpy.sum to compute probabilities
        # this sums over all non-measured qubits to return a vector
        # of measure probabilities for the measured qubits
        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 as ints with qubit
        # position in the bit-string for each int given by the qubit
        # position in the sorted measured_qubits list
        samples = self._local_random.choice(range(2 ** num_measured),
                                            num_samples, p=probabilities)
        # Convert the ints to bitstrings
        memory = []
        for sample in samples:
            classical_memory = self._classical_memory
            for qubit, cmembit in measure_params:
                pos = measured_qubits.index(qubit)
                qubit_outcome = int((sample & (1 << pos)) >> pos)
                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(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(update, [qubit])
        else:
            update = [[0, 1 / np.sqrt(probability)], [0, 0]]
            self._add_unitary(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"""
        vec = np.reshape(self._statevector, 2 ** self._number_of_qubits)
        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.
        """
        # If shots=1 we should disable measure sampling.
        # This is also required for statevector simulator to return the
        # correct final statevector without silently dropping final measurements.
        if self._shots <= 1:
            self._sample_measure = False
            return

        # 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.to_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_simulator'):
            seed_simulator = experiment.config.seed_simulator
        elif hasattr(self._qobj_config, 'seed_simulator'):
            seed_simulator = self._qobj_config.seed_simulator
        else:
            # For compatibility on Windows force dyte to be int32
            # and set the maximum value to be (2 ** 31) - 1
            seed_simulator = np.random.randint(2147483647, dtype='int32')

        self._local_random.seed(seed=seed_simulator)
        # 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):
                    conditional_bit_set = (self._classical_register >> conditional) & 1
                    if not conditional_bit_set:
                        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 == 'unitary':
                    qubits = operation.qubits
                    gate = operation.params[0]
                    self._add_unitary(gate, qubits)
                elif operation.name == "h":
                    qubit = operation.qubits[0]
                    gate = h_gate_matrix()
                    self._add_unitary(gate, [qubit])
                elif 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(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(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_simulator': seed_simulator,
                'shots': self._shots,
                'data': data,
                'status': 'DONE',
                'success': True,
                'time_taken': (end - start),
                'header': experiment.header.to_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)
コード例 #5
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,
        'coupling_map':
        None,
        '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:
            BasicAerJob: 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 BasicAerError(
                '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
コード例 #6
0
class QiskitAquaGlobals:
    """Aqua class for global properties."""

    CPU_COUNT = local_hardware_info()['cpus']

    def __init__(self) -> None:
        self._random_seed = None  # type: Optional[int]
        self._num_processes = QiskitAquaGlobals.CPU_COUNT
        self._random = None
        self._massive = False

    @property
    def random_seed(self) -> Optional[int]:
        """Return random seed."""
        warn_variable('aqua.aqua_globals',
                      'qiskit.utils.algorithm_globals',
                      'qiskit-terra', 3)
        return self._random_seed

    @random_seed.setter
    def random_seed(self, seed: Optional[int]) -> None:
        """Set random seed."""
        warn_variable('aqua.aqua_globals',
                      'qiskit.utils.algorithm_globals',
                      'qiskit-terra', 3)
        self._random_seed = seed
        self._random = None

    @property
    def num_processes(self) -> int:
        """Return num processes."""
        warn_variable('aqua.aqua_globals',
                      'qiskit.utils.algorithm_globals',
                      'qiskit-terra', 3)
        return self._num_processes

    @num_processes.setter
    def num_processes(self, num_processes: Optional[int]) -> None:
        """Set num processes.
           If 'None' is passed, it resets to QiskitAquaGlobals.CPU_COUNT
        """
        warn_variable('aqua.aqua_globals',
                      'qiskit.utils.algorithm_globals',
                      'qiskit-terra', 3)
        if num_processes is None:
            num_processes = QiskitAquaGlobals.CPU_COUNT
        elif num_processes < 1:
            raise AquaError('Invalid Number of Processes {}.'.format(num_processes))
        elif 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 ex:  # pylint: disable=broad-except
            logger.warning("Failed to set qiskit.tools.parallel.CPU_COUNT "
                           "to value: '%s': Error: '%s'", self.num_processes, str(ex))

    @property
    def random(self) -> np.random.Generator:
        """Return a numpy np.random.Generator (default_rng)."""
        warn_variable('aqua.aqua_globals',
                      'qiskit.utils.algorithm_globals',
                      'qiskit-terra', 3)
        if self._random is None:
            self._random = np.random.default_rng(self._random_seed)  # type: ignore
        return self._random

    @property
    def massive(self) -> bool:
        """Return massive to allow processing of large matrices or vectors."""
        warn_variable('aqua.aqua_globals',
                      'qiskit.utils.algorithm_globals',
                      'qiskit-terra', 3)
        return self._massive

    @massive.setter
    def massive(self, massive: bool) -> None:
        """Set massive to allow processing of large matrices or  vectors."""
        warn_variable('aqua.aqua_globals',
                      'qiskit.utils.algorithm_globals',
                      'qiskit-terra', 3)
        self._massive = massive
コード例 #7
0
ファイル: test_util.py プロジェクト: yanhongliu/qiskit-terra
 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'])
コード例 #8
0
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',
        '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__(statevector_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. 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
コード例 #9
0
class UnitarySimulator(AerBackend):
    """Ideal quantum circuit unitary simulator.

    **Backend options**

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

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

    * ``"validation_threshold"`` (double): Sets the threshold for checking
      if initial unitary and target unitary are unitary matrices.
      (Default: 1e-8).

    * ``"zero_threshold"`` (double): Sets the threshold for truncating
      small values to zero in the result data (Default: 1e-10).

    * ``"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_memory_mb"`` (int): Sets the maximum size of memory
      to store a state vector. If a state vector needs more, an error
      is thrown. In general, a state vector of n-qubits uses 2^n complex
      values (16 Bytes). If set to 0, the maximum will be automatically
      set to half the system memory size (Default: 0).

    * ``"statevector_parallel_threshold"`` (int): Sets the threshold that
      2 * "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: 14).
    """

    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':
        int(1e6),  # Note that this backend will only ever
        # perform a single shot. This value is just
        # so that the default shot value for execute
        # will not raise an error when trying to run
        # a simulation
        'description':
        'A C++ unitary simulator for QASM Qobj files',
        'coupling_map':
        None,
        'basis_gates': [
            'u1',
            'u2',
            'u3',
            'cx',
            'cz',
            'id',
            'x',
            'y',
            'z',
            'h',
            's',
            'sdg',
            't',
            'tdg',
            'swap',
            'ccx',
            'unitary',
            'cu1',
            'cu2',
            'cu3',
            'cswap',
            'mcx',
            'mcy',
            'mcz',
            'mcu1',
            'mcu2',
            'mcu3',
            'mcswap',
            'multiplexer',
        ],
        'gates': [{
            'name': 'u1',
            'parameters': ['lam'],
            'conditional': True,
            'description': 'Single-qubit gate [[1, 0], [0, exp(1j*lam)]]',
            'qasm_def': 'gate u1(lam) q { U(0,0,lam) q; }'
        }, {
            'name': 'u2',
            'parameters': ['phi', 'lam'],
            'conditional': True,
            'description':
            'Single-qubit gate [[1, -exp(1j*lam)], [exp(1j*phi), exp(1j*(phi+lam))]]/sqrt(2)',
            'qasm_def': 'gate u2(phi,lam) q { U(pi/2,phi,lam) q; }'
        }, {
            'name':
            'u3',
            'parameters': ['theta', 'phi', 'lam'],
            'conditional':
            True,
            'description':
            'Single-qubit gate with three rotation angles',
            'qasm_def':
            'gate u3(theta,phi,lam) q { U(theta,phi,lam) q; }'
        }, {
            'name': 'cx',
            'parameters': [],
            'conditional': True,
            'description': 'Two-qubit Controlled-NOT gate',
            'qasm_def': 'gate cx c,t { CX c,t; }'
        }, {
            'name': 'cz',
            'parameters': [],
            'conditional': True,
            'description': 'Two-qubit Controlled-Z gate',
            'qasm_def': 'gate cz a,b { h b; cx a,b; h b; }'
        }, {
            'name': 'id',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit identity gate',
            'qasm_def': 'gate id a { U(0,0,0) a; }'
        }, {
            'name': 'x',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit Pauli-X gate',
            'qasm_def': 'gate x a { U(pi,0,pi) a; }'
        }, {
            'name': 'y',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit Pauli-Y gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'z',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit Pauli-Z gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'h',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit Hadamard gate',
            'qasm_def': 'TODO'
        }, {
            'name': 's',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit phase gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'sdg',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit adjoint phase gate',
            'qasm_def': 'TODO'
        }, {
            'name': 't',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit T gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'tdg',
            'parameters': [],
            'conditional': True,
            'description': 'Single-qubit adjoint T gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'swap',
            'parameters': [],
            'conditional': True,
            'description': 'Two-qubit SWAP gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'ccx',
            'parameters': [],
            'conditional': True,
            'description': 'Three-qubit Toffoli gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'cswap',
            'parameters': [],
            'conditional': True,
            'description': 'Three-qubit Fredkin (controlled-SWAP) gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'unitary',
            'parameters': ['matrix'],
            'conditional': True,
            'description': 'N-qubit arbitrary unitary gate. '
            'The parameter is the N-qubit matrix to apply.',
            'qasm_def': 'unitary(matrix) q1, q2,...'
        }, {
            'name': 'cu1',
            'parameters': ['lam'],
            'conditional': True,
            'description': 'Two-qubit Controlled-u1 gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'cu2',
            'parameters': ['phi', 'lam'],
            'conditional': True,
            'description': 'Two-qubit Controlled-u2 gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'cu3',
            'parameters': ['theta', 'phi', 'lam'],
            'conditional': True,
            'description': 'Two-qubit Controlled-u3 gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'mcx',
            'parameters': [],
            'conditional': True,
            'description': 'N-qubit multi-controlled-X gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'mcy',
            'parameters': [],
            'conditional': True,
            'description': 'N-qubit multi-controlled-Y gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'mcz',
            'parameters': [],
            'conditional': True,
            'description': 'N-qubit multi-controlled-Z gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'mcu1',
            'parameters': ['lam'],
            'conditional': True,
            'description': 'N-qubit multi-controlled-u1 gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'mcu2',
            'parameters': ['phi', 'lam'],
            'conditional': True,
            'description': 'N-qubit multi-controlled-u2 gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'mcu3',
            'parameters': ['theta', 'phi', 'lam'],
            'conditional': True,
            'description': 'N-qubit multi-controlled-u3 gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'mcswap',
            'parameters': [],
            'conditional': True,
            'description': 'N-qubit multi-controlled-SWAP gate',
            'qasm_def': 'TODO'
        }, {
            'name': 'multiplexer',
            'parameters': ['mat1', 'mat2', '...'],
            'conditional': True,
            'description': 'N-qubit multi-plexer gate. '
            'The input parameters are the gates for each value.',
            'qasm_def': 'TODO'
        }]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(unitary_controller_execute,
                         QasmBackendConfiguration.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:
            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 ({}) is greater than max ({}) for "{}" with {} GB system memory.'
                .format(n_qubits, max_qubits, name,
                        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 {} instruction {} in circuit {}'.format(
                            name, operation.name, exp_name))
コード例 #10
0
ファイル: parallel.py プロジェクト: AW-AlanWu/PuGua
from the multiprocessing library.
"""

import os
import platform
from concurrent.futures import ProcessPoolExecutor
from qiskit.exceptions import QiskitError
from qiskit.util import local_hardware_info
from qiskit.tools.events.pubsub import Publisher

# Set parallel flag
if os.getenv('QISKIT_IN_PARALLEL') is None:
    os.environ['QISKIT_IN_PARALLEL'] = 'FALSE'

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


def _task_wrapper(param):
    (task, value, task_args, task_kwargs) = param
    return task(value, *task_args, **task_kwargs)


def parallel_map(  # pylint: disable=dangerous-default-value
        task,
        values,
        task_args=tuple(),
        task_kwargs={},
        num_processes=CPU_COUNT):
    """
    Parallel execution of a mapping of `values` to the function `task`. This
コード例 #11
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,
        'coupling_map':
        None,
        '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 BasicAerError('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 BasicAerError("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 BasicAerError("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:
            BasicAerJob: 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 = 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 = []
        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:
            BasicAerError: 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 BasicAerError(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 BasicAerError(
                '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 BasicAerError(
                        'Unsupported "%s" instruction "%s" ' +
                        'in circuit "%s" ', self.name(), operation.name, name)
コード例 #12
0
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

# pylint: disable=invalid-name
"""
Qiskit Aer simulator backend utils
"""
import os
from math import log2
from qiskit.util import local_hardware_info
from qiskit.circuit import QuantumCircuit
from qiskit.compiler import assemble
from qiskit.qobj import QasmQobjInstruction

# Available system memory
SYSTEM_MEMORY_GB = local_hardware_info()['memory']

# Max number of qubits for complex double statevector
# given available system memory
MAX_QUBITS_STATEVECTOR = int(log2(SYSTEM_MEMORY_GB * (1024**3) / 16))

# Location where we put external libraries that will be
# loaded at runtime by the simulator extension
LIBRARY_DIR = os.path.dirname(__file__)

LEGACY_METHOD_MAP = {
    "statevector_cpu": ("statevector", "CPU"),
    "statevector_gpu": ("statevector", "GPU"),
    "statevector_thrust": ("statevector", "Thrust"),
    "density_matrix_cpu": ("density_matrix", "CPU"),
    "density_matrix_gpu": ("density_matrix", "GPU"),
コード例 #13
0
ファイル: qasm_simulator.py プロジェクト: jarcil13/qiskit-aer
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`

        Simulation method option
        ------------------------
        * "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.
            * "extended_stabilizer": 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 extended_stabilizer method (Default: "automatic").

        General options
        ---------------

        * "zero_threshold" (double): Sets the threshold for truncating
            small values to zero in the result data (Default: 1e-10).

        * "validation_threshold" (double): Sets the threshold for checking
            if initial states are valid (Default: 1e-8).

        * "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).

        * "max_memory_mb" (int): Sets the maximum size of memory
            to store a state vector. If a state vector needs more, an error
            is thrown. In general, a state vector of n-qubits uses 2^n complex
            values (16 Bytes). If set to 0, the maximum will be automatically
            set to half the system memory size (Default: 0).

        * "optimize_ideal_threshold" (int): Sets the qubit threshold for
            applying circuit optimization passes on ideal circuits.
            Passes include gate fusion and truncation of unused qubits
            (Default: 5).

        * "optimize_noise_threshold" (int): Sets the qubit threshold for
            applying circuit optimization passes on ideal circuits.
            Passes include gate fusion and truncation of unused qubits
            (Default: 12).

        "statevector" method options
        ----------------------------
        * "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: 14).

        * "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)

        "stabilizer" method options
        ---------------------------
        * "stabilizer_max_snapshot_probabilities" (int): (Default: 32)

        "extended_stabilizer" method options
        ------------------------------------
        * "extended_stabilizer_measure_sampling" (bool): Enable measure
            sampling optimization on supported circuits. This prevents the
            simulator from re-running the measure monte-carlo step for each
            shot. Enabling measure sampling may reduce accuracy of the
            measurement counts if the output distribution is strongly
            peaked. (Default: False)

        * "extended_stabilizer_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 extended_stabilizer_disable_measurement_opt
            to True. (Default: 5000)

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

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

        * "extended_stabilizer_parallel_threshold" (int): Set the minimum
            size of the extended stabilizer 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 noise for qobj files',
        'coupling_map':
        None,
        'basis_gates': [
            'u1', 'u2', 'u3', 'cx', 'cz', 'cu1', 'id', 'x', 'y', 'z', 'h', 's',
            'sdg', 't', 'tdg', 'ccx', 'swap', 'multiplexer', 'snapshot',
            'unitary', 'reset', 'initialize', 'kraus', 'roerror'
        ],
        '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", 'roerror'
        ]
        unsupported_ch_instructions = ["u2", "u3", "cu1"]
        # Check if noise model is Clifford:
        method = "automatic"
        if backend_options and "method" in backend_options:
            method = backend_options["method"]

        clifford_noise = (method != "statevector")

        if clifford_noise:
            if method != "stabilizer" and noise_model:
                for error in noise_model.to_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 ["extended_stabilizer", "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 != "extended_stabilizer":
                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 + '.')

                    if n_qubits > 63:
                        raise AerError('{}, and has too many qubits to fall '
                                       'back to the extended_stabilizer '
                                       'method.'.format(err_string))
                    if not ch_supported:
                        raise AerError(
                            '{}, and contains instructions '
                            'not supported by the extended_etabilizer '
                            'method.'.format(err_string))
                    logger.info(
                        'The QasmSimulator will automatically '
                        'switch to the Extended Stabilizer backend, based on '
                        'the memory requirements.')
コード例 #14
0
ファイル: qasm_simulator.py プロジェクト: jarcil13/qiskit-aer
    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", 'roerror'
        ]
        unsupported_ch_instructions = ["u2", "u3", "cu1"]
        # Check if noise model is Clifford:
        method = "automatic"
        if backend_options and "method" in backend_options:
            method = backend_options["method"]

        clifford_noise = (method != "statevector")

        if clifford_noise:
            if method != "stabilizer" and noise_model:
                for error in noise_model.to_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 ["extended_stabilizer", "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 != "extended_stabilizer":
                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 + '.')

                    if n_qubits > 63:
                        raise AerError('{}, and has too many qubits to fall '
                                       'back to the extended_stabilizer '
                                       'method.'.format(err_string))
                    if not ch_supported:
                        raise AerError(
                            '{}, and contains instructions '
                            'not supported by the extended_etabilizer '
                            'method.'.format(err_string))
                    logger.info(
                        'The QasmSimulator will automatically '
                        'switch to the Extended Stabilizer backend, based on '
                        'the memory requirements.')
コード例 #15
0
class DmSimulatorPy(BaseBackend):
    """Python implementation of a Density Matrix simulator."""

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

    DEFAULT_CONFIGURATION = {
        'backend_name':
        'dm_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,
        'coupling_map':
        None,
        '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_densitymatrix": 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 densitymatrix simulator
    SHOW_FINAL_STATE = True

    def __init__(self, configuration=None, provider=None):

        super().__init__(configuration=(configuration
                                        or QasmBackendConfiguration.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._densitymatrix = 0
        self._probability_of_zero = 0.0
        self._number_of_cmembits = 0
        self._number_of_qubits = 0
        self._shots = 0
        self._memory = False
        self._custom_densitymatrix = None
        self._initial_densitymatrix = self.DEFAULT_OPTIONS[
            "initial_densitymatrix"]
        self._chop_threshold = self.DEFAULT_OPTIONS["chop_threshold"]
        self._qobj_config = None
        # TEMP
        self._sample_measure = False

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

        Args:
            params (list): list of parameters for U1,U2 and U3 gate.
            qubit (int): the qubit to apply gate to
        """
        # Getting parameters
        (theta, phi, lam) = map(float, single_gate_params(gate, params))
        print(theta, phi, lam)

        # changing density matrix
        self._densitymatrix = np.reshape(
            self._densitymatrix,
            (4**qubit, 4, 4**(self._number_of_qubits - qubit - 1)))

        for j in range(4**(self._number_of_qubits - qubit - 1)):
            for i in range(4**(qubit)):
                temp = self._densitymatrix[i, :, j]
                self._densitymatrix[i, 1, j] = temp[1] * (
                    np.sin(lam) * np.sin(phi) +
                    np.cos(theta) * np.cos(phi) * np.cos(lam)) + temp[2] * (
                        np.cos(theta) * np.cos(phi) * np.sin(lam) -
                        np.cos(lam) * np.sin(phi)) + temp[3] * (np.sin(theta) *
                                                                np.cos(phi))
                self._densitymatrix[i, 2, j] = temp[1] * (
                    np.cos(theta) * np.sin(phi) * np.cos(lam) -
                    np.sin(lam) * np.cos(phi)) + temp[2] * (
                        np.cos(phi) * np.cos(lam) + np.cos(theta) *
                        np.sin(phi) * np.sin(lam)) + temp[3] * (np.sin(theta) *
                                                                np.sin(phi))
                self._densitymatrix[
                    i, 3,
                    j] = temp[1] * (-np.cos(lam) * np.sin(theta)) + temp[2] * (
                        np.sin(theta) *
                        np.sin(lam)) + temp[3] * (np.cos(theta))

        self._densitymatrix = np.reshape(self._densitymatrix,
                                         4**(self._number_of_qubits))

    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 float rank-4 tensor
        gate_tensor = np.reshape(np.array(gate, dtype=float), 4 * [4])
        # Apply matrix multiplication
        self._densitymatrix = np.einsum(indexes,
                                        gate_tensor,
                                        self._densitymatrix,
                                        dtype=float,
                                        casting='no')
        #self._densitymatrix = np.reshape(self._densitymatrix,())

    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
        #print('Get_Measure')
        axis = list(range(self._number_of_qubits))
        axis.remove(self._number_of_qubits - 1 - qubit)
        probabilities = np.sum(np.abs(self._densitymatrix)**2,
                               axis=tuple(axis))

        measure_ind = [
            x for x in itertools.product([0, 3], repeat=self._number_of_qubits)
        ]
        operator_ind = [self._densitymatrix[x] for x in measure_ind]
        operator_mes = np.array([[1, 1], [1, -1]])
        for i in range(self._number_of_qubits - 1):
            operator_mes = np.kron(np.array([[1, 1], [1, -1]]), operator_mes)

        probabilities = np.reshape((1 / 2**self._number_of_qubits) * np.array(
            [np.sum(np.multiply(operator_ind, x)) for x in operator_mes]),
                                   self._number_of_qubits * [2])
        #print('Probability Before: ', probabilities)

        probabilities = np.reshape(np.sum(probabilities, axis=tuple(axis)), 2)
        #print('Probability After: ', probabilities)

        # 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_ensemble_measure(self):
        """Perform complete computational basis measurement for current densitymatrix.

        Args:

        Returns:
            list: Complete list of probabilities. 
        """
        measure_ind = [
            x for x in itertools.product([0, 3], repeat=self._number_of_qubits)
        ]
        operator_ind = [self._densitymatrix[x] for x in measure_ind]
        operator_mes = np.array([[1, 1], [1, -1]])
        for i in range(self._number_of_qubits - 1):
            operator_mes = np.kron(np.array([[1, 1], [1, -1]]), operator_mes)

        probabilities = np.reshape((0.5**self._number_of_qubits) * np.array(
            [np.sum(np.multiply(operator_ind, x)) for x in operator_mes]),
                                   self._number_of_qubits * [2])
        return probabilities

    def _add_bell_basis_measure(self, qubit_1, qubit_2):
        """
        Apply a Bell basisi measure instruction to two qubits.
        Post measurement density matrix is returned in the same array.

        Args:
            qubit_1 (int): first qubit of Bell pair.
            qubit_2 (int): second qubit of Bell pair.
        
        Returns:
            Four probabilities in the (|00>+|11>,|00>-|11>,|01>+|10>,|01>-|10>) basis.
        """
        q_1 = min(qubit_1, qubit_2)
        q_2 = max(qubit_1, qubit_2)

        #update density matrix
        self._densitymatrix = np.reshape(
            self._densitymatrix, (4**(self._number_of_qubits - q_2 - 1), 4, 4
                                  **(q_2 - q_1 - 1), 4, 4**q_1))
        bell_probabilities = [0, 0, 0, 0]
        for i in range(4**(self._number_of_qubits - q_2 - 1)):
            for j in range(4**(q_2 - q_1 - 1)):
                for k in range(4**q_1):
                    for l in range(4):
                        for m in range(4):
                            if l != m:
                                self._densitymatrix[i, l, j, m, k] = 0
                    bell_probabilities[0] += 0.25 * (
                        self._densitymatrix[i, 0, j, 0, k] +
                        self._densitymatrix[i, 1, j, 1, k] -
                        self._densitymatrix[i, 2, j, 2, k] +
                        self._densitymatrix[i, 3, j, 3, k])
                    bell_probabilities[1] += 0.25 * (
                        self._densitymatrix[i, 0, j, 0, k] -
                        self._densitymatrix[i, 1, j, 1, k] +
                        self._densitymatrix[i, 2, j, 2, k] +
                        self._densitymatrix[i, 3, j, 3, k])
                    bell_probabilities[2] += 0.25 * (
                        self._densitymatrix[i, 0, j, 0, k] +
                        self._densitymatrix[i, 1, j, 1, k] +
                        self._densitymatrix[i, 2, j, 2, k] -
                        self._densitymatrix[i, 3, j, 3, k])
                    bell_probabilities[3] += 0.25 * (
                        self._densitymatrix[i, 0, j, 0, k] -
                        self._densitymatrix[i, 1, j, 1, k] -
                        self._densitymatrix[i, 2, j, 2, k] -
                        self._densitymatrix[i, 3, j, 3, k])
        return bell_probabilities

    def _add_qasm_measure(self, qubit, probability_of_zero):
        """Apply a computational basis measure instruction to a qubit. 
        Post-measurement density matrix is returned in the same array.

        Args:
            qubit (int): qubit is the qubit measured.
            probability_of_zero (float): is the probability of getting zero state as outcome.
        """

        # update density matrix
        self._densitymatrix = np.reshape(
            self._densitymatrix,
            (4**(qubit), 4, 4**(self._number_of_qubits - qubit - 1)))
        p_0 = 0.0
        p_1 = 0.0
        for j in range(4**(self._number_of_qubits - qubit - 1)):
            for i in range(4**(qubit)):
                self._densitymatrix[i, 1, j] = 0
                self._densitymatrix[i, 2, j] = 0
                p_0 += 0.5 * (self._densitymatrix[i, 0, j] +
                              self._densitymatrix[i, 3, j])
                p_1 += 0.5 * (self._densitymatrix[i, 0, j] -
                              self._densitymatrix[i, 3, j])
        probability_of_zero = p_0
        print(p_0, p_1)

    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) * np.array(
                [[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [1, 0, 0, 0]],
                dtype=float)
            self._add_unitary_single(update, qubit)
        else:
            update = 1 / np.sqrt(probability) * np.array(
                [[1, 0, 0, -1], [0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, -1]],
                dtype=float)
            # update classical state
            self._add_unitary_single(update, qubit)

    def _validate_initial_densitymatrix(self):
        """Validate an initial densitymatrix"""
        # If initial densitymatrix isn't set we don't need to validate
        if self._initial_densitymatrix is None:
            return
        # Check densitymatrix is correct length for number of qubits
        length = np.size(self._initial_densitymatrix)
        #print(length, self._number_of_qubits)
        required_dim = 4**self._number_of_qubits
        if length != required_dim:
            raise BasicAerError('initial densitymatrix is incorrect length: ' +
                                '{} != {}'.format(length, required_dim))
        # Check if Trace is 0
        if self._densitymatrix[0] != 1:
            raise BasicAerError('Trace of initial densitymatrix is not one: ' +
                                '{} != {}'.format(self._densitymatrix[0], 1))

    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_densitymatrix = self.DEFAULT_OPTIONS[
            "initial_densitymatrix"]
        self._chop_threshold = self.DEFAULT_OPTIONS["chop_threshold"]
        if backend_options is None:
            backend_options = {}
        # Check for custom initial densitymatrix in backend_options first,
        # then config second
        if 'initial_densitymatrix' in backend_options:
            self._initial_densitymatrix = np.array(
                backend_options['initial_densitymatrix'], dtype=float)
        elif hasattr(qobj_config, 'initial_densitymatrix'):
            self._initial_densitymatrix = np.array(
                qobj_config.initial_densitymatrix, dtype=float)

        if 'custom_densitymatrix' in backend_options:
            self._custom_densitymatrix = backend_options[
                'custom_densitymatrix']

        #if self._initial_densitymatrix is not None and not isinstance(self._initial_densitymatrix, str):
        # Check the initial densitymatrix is normalized
        #    norm = np.linalg.norm(self._initial_densitymatrix)
        #    if round(norm, 12) != 1:
        #        raise BasicAerError('initial densitymatrix is not normalized: ' + 'norm {} != 1'.format(norm))
        print(self._initial_densitymatrix, self._number_of_qubits)
        # 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_densitymatrix(self):
        """Set the initial densitymatrix for simulation"""
        if self._initial_densitymatrix is None and self._custom_densitymatrix is None:
            self._densitymatrix = 0.5 * np.array([1, 0, 0, 1], dtype=float)
            for i in range(self._number_of_qubits - 1):
                self._densitymatrix = 0.5 * np.kron([1, 0, 0, 1],
                                                    self._densitymatrix)
        elif self._initial_densitymatrix is None and self._custom_densitymatrix == 'max_mixed':
            self._densitymatrix = 0.5 * np.array([1, 0, 0, 0], dtype=float)
            for i in range(self._number_of_qubits - 1):
                self._densitymatrix = 0.5 * np.kron([1, 0, 0, 0],
                                                    self._densitymatrix)
        elif self._initial_densitymatrix is None and self._custom_densitymatrix == 'uniform_superpos':
            self._densitymatrix = 0.5 * np.array([1, 1, 0, 0], dtype=float)
            for i in range(self._number_of_qubits - 1):
                self._densitymatrix = 0.5 * np.kron([1, 1, 0, 0],
                                                    self._densitymatrix)
        else:
            self._densitymatrix = self._initial_densitymatrix.copy()
        # Reshape to rank-N tensor
        # self._densitymatrix = np.reshape(self._densitymatrix,
        #                               self._number_of_qubits * [4])

    def _get_densitymatrix(self):
        """Return the current densitymatrix in JSON Result spec format"""
        vec = np.reshape(self._densitymatrix.real, 4**self._number_of_qubits)
        # Expand float numbers
        # 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.
        """
        # If shots=1 we should disable measure sampling.
        # This is also required for densitymatrix simulator to return the
        # correct final densitymatrix without silently dropping final measurements.
        if self._shots <= 1:
            self._sample_measure = False
            return

        # 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_densitymatrix": vector_like

            The "initial_densitymatrix" option specifies a custom initial
            initial densitymatrix 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_densitymatrix": 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._densitymatrix = 0
        self._classical_memory = 0
        self._classical_register = 0
        #self._sample_measure = False
        # Validate the dimension of initial densitymatrix if set
        self._validate_initial_densitymatrix()
        # Get the seed looking in circuit, qobj, and then random.
        if hasattr(experiment.config, 'seed_simulator'):
            seed_simulator = experiment.config.seed_simulator
        elif hasattr(self._qobj_config, 'seed_simulator'):
            seed_simulator = self._qobj_config.seed_simulator
        else:
            # For compatibility on Windows force dyte to be int32
            ## TODO
            # and set the maximum value to be (4 ** 31) - 1
            seed_simulator = np.random.randint(2147483647, dtype='int32')

        self._local_random.seed(seed=seed_simulator)
        # 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
        #print("No error till now")
        #np.asarray()
        #print(experiment.instructions)
        for _ in range(shots):
            self._initialize_densitymatrix()
            # Initialize classical memory to all 0
            self._classical_memory = 0
            self._classical_register = 0
            #print(self._densitymatrix)
            for operation in experiment.instructions:
                conditional = getattr(operation, 'conditional', None)
                if isinstance(conditional, int):
                    conditional_bit_set = (
                        self._classical_register >> conditional) & 1
                    if not conditional_bit_set:
                        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
                #print(self._initial_densitymatrix)
                #print(self._densitymatrix)
                #print('Operation: ', operation.name)
                if operation.name in ('U', 'u1', 'u2', 'u3'):
                    params = getattr(operation, 'params', None)
                    qubit = operation.qubits[0]
                    #gate = single_gate_dm_matrix(operation.name, params)
                    self._add_unitary_single(operation.name, params, 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_dm_matrix()
                    self._add_unitary_two(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,
                                               self._probability_of_zero)
                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 densitymatrix
                    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 densitymatrix
        if self.SHOW_FINAL_STATE:
            data['densitymatrix'] = self._get_densitymatrix()
            # Remove empty counts and memory for densitymatrix 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_simulator': seed_simulator,
            '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)
コード例 #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`

        Simulation method option
        ------------------------
        * "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.
            * "extended_stabilizer": Uses an approximate simulator that
            decomposes circuits into stabilizer state terms, the number of
            which grows with the number of non-Clifford gates.
            * "matrix_product_state": Uses a Matrix Product State (MPS) simulator.
            * "cyclops_statevector": Uses a dense statevector simulation
            parallelized by Cyclops Tensor Framework (CTF).
            * "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 extended_stabilizer method (Default: "automatic").

        General options
        ---------------

        * "zero_threshold" (double): Sets the threshold for truncating
            small values to zero in the result data (Default: 1e-10).

        * "validation_threshold" (double): Sets the threshold for checking
            if initial states are valid (Default: 1e-8).

        * "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).

        * "max_memory_mb" (int): Sets the maximum size of memory
            to store a state vector. If a state vector needs more, an error
            is thrown. In general, a state vector of n-qubits uses 2^n complex
            values (16 Bytes). If set to 0, the maximum will be automatically
            set to half the system memory size (Default: 0).

        * "optimize_ideal_threshold" (int): Sets the qubit threshold for
            applying circuit optimization passes on ideal circuits.
            Passes include gate fusion and truncation of unused qubits
            (Default: 5).

        * "optimize_noise_threshold" (int): Sets the qubit threshold for
            applying circuit optimization passes on ideal circuits.
            Passes include gate fusion and truncation of unused qubits
            (Default: 12).

        "statevector" method options
        ----------------------------
        * "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: 14).

        * "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)

        "stabilizer" method options
        ---------------------------
        * "stabilizer_max_snapshot_probabilities" (int): (Default: 32)

        "extended_stabilizer" method options
        ------------------------------------
        * "extended_stabilizer_measure_sampling" (bool): Enable measure
            sampling optimization on supported circuits. This prevents the
            simulator from re-running the measure monte-carlo step for each
            shot. Enabling measure sampling may reduce accuracy of the
            measurement counts if the output distribution is strongly
            peaked. (Default: False)

        * "extended_stabilizer_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 extended_stabilizer_disable_measurement_opt
            to True. (Default: 5000)

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

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

        * "extended_stabilizer_parallel_threshold" (int): Set the minimum
            size of the extended stabilizer 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 noise for qobj files',
        'coupling_map':
        None,
        'basis_gates': [
            'u1', 'u2', 'u3', 'cx', 'cz', 'cu1', 'id', 'x', 'y', 'z', 'h', 's',
            'sdg', 't', 'tdg', 'ccx', 'swap', 'multiplexer', 'snapshot',
            'unitary', 'reset', 'initialize', 'kraus', 'roerror'
        ],
        'gates': [{
            'name': 'TODO',
            'parameters': [],
            'qasm_def': 'TODO'
        }]
    }

    def __init__(self, configuration=None, provider=None):
        super().__init__(qasm_controller_execute,
                         QasmBackendConfiguration.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.

        Warn if no measurements in circuit with classical registers.
        """
        for experiment in qobj.experiments:
            # If circuit contains classical registers but not
            # measurements raise a warning
            if experiment.config.memory_slots > 0:
                # Check if measure opts missing
                no_measure = True
                for op in experiment.instructions:
                    if not no_measure:
                        break  # we don't need to check any more ops
                    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.',
                        experiment.header.name)