mindquantum.simulator.simulator 源代码

# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Simulator."""
import warnings
from functools import partial

import numpy as np

import mindquantum.mqbackend as mb

from ..core.operators import Hamiltonian
from ..utils.type_value_check import (
    _check_input_type,
    _check_int_type,
    _check_seed,
    _check_value_should_not_less,
)
from .backend_base import BackendBase
from .mq_blas import MQBlas
from .mqsim import MQ_SIM_GPU_SUPPORTED, MQSim
from .projectq_sim import Projectq

SUPPORTED_SIMULATOR = {
    'projectq': Projectq,
    'mqvector': partial(MQSim, 'mqvector'),
}

if MQ_SIM_GPU_SUPPORTED:
    SUPPORTED_SIMULATOR['mqvector_gpu'] = partial(MQSim, 'mqvector_gpu')


[文档]def get_supported_simulator(): """ Get simulator name that supported by MindQuantum. Returns: list, The supported simulator list. """ return list(SUPPORTED_SIMULATOR)
[文档]class Simulator: """ Quantum simulator that simulate quantum circuit. Args: backend (str): which backend you want. The supported backend can be found in SUPPORTED_SIMULATOR n_qubits (int): number of quantum simulator. seed (int): the random seed for this simulator, if None, seed will generate by `numpy.random.randint`. Default: None. Raises: TypeError: if `backend` is not str. TypeError: if `n_qubits` is not int. TypeError: if `seed` is not int. ValueError: if `backend` is not supported. ValueError: if `n_qubits` is negative. ValueError: if `seed` is less than 0 or great than 2**23 - 1. Examples: >>> from mindquantum.algorithm.library import qft >>> from mindquantum.simulator import Simulator >>> sim = Simulator('mqvector', 2) >>> sim.apply_circuit(qft(range(2))) >>> sim.get_qs() array([0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j]) """ def __init__(self, backend, n_qubits, *args, seed=None, **kwargs): """Initialize a Simulator object.""" if isinstance(backend, BackendBase): self.backend = backend else: _check_input_type('backend', str, backend) _check_int_type('n_qubits', n_qubits) _check_value_should_not_less('n_qubits', 0, n_qubits) if seed is None: seed = np.random.randint(1, 2**23) _check_seed(seed) if backend == 'projectq': warnings.warn( "projectq will be deprecated in next version, please use mqvector.", category=DeprecationWarning, stacklevel=2, ) try: self.backend = SUPPORTED_SIMULATOR[backend](n_qubits, seed, *args, **kwargs) except KeyError as err: raise ValueError( f"backend {backend} not supported, now we support: {', '.join(SUPPORTED_SIMULATOR)}!" ) from err @property def n_qubits(self): """ Get simulator qubit. Returns: int, the qubit number of simulator. """ return self.backend.n_qubits
[文档] def copy(self): """ Copy this simulator. Returns: Simulator, a copy version of this simulator. Examples: >>> from mindquantum.core.gates import RX >>> from mindquantum.simulator import Simulator >>> sim = Simulator('mqvector', 1) >>> sim.apply_gate(RX(1).on(0)) >>> sim.flush() >>> sim2 = sim.copy() >>> sim2.apply_gate(RX(-1).on(0)) >>> sim2 mqvector simulator with 1 qubit (little endian). Current quantum state: 1¦0⟩ """ return self.__class__(self.backend.copy(), None)
def __str__(self): """Return a string representation of the object.""" return self.backend.__str__() def __repr__(self): """Return a string representation of the object.""" return self.backend.__repr__()
[文档] def reset(self): """ Reset simulator to zero state. Examples: >>> from mindquantum.algorithm.library import qft >>> from mindquantum.simulator import Simulator >>> sim = Simulator('mqvector', 2) >>> sim.apply_circuit(qft(range(2))) >>> sim.reset() >>> sim.get_qs() array([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]) """ self.backend.reset()
[文档] def flush(self): """ Flush gate that works for projectq simulator. The projectq simulator will cache several gate and fusion these gate into a bigger gate, and than act on the quantum state. The flush command will ask the simulator to fusion currently stored gate and act on the quantum state. Examples: >>> from mindquantum.core.gates import H >>> from mindquantum.simulator import Simulator >>> sim = Simulator('projectq', 1) >>> sim.apply_gate(H.on(0)) >>> sim.flush() """ self.backend.flush()
[文档] def apply_gate(self, gate, pr=None, diff=False): """ Apply a gate on this simulator, can be a quantum gate or a measurement operator. Args: gate (BasicGate): The gate you want to apply. pr (Union[numbers.Number, numpy.ndarray, ParameterResolver, list]): The parameter for parameterized gate. Default: None. diff (bool): Whether to apply the derivative gate on this simulator. Default: False. Returns: int or None, if the gate if a measure gate, then return a collapsed state, Otherwise return None. Raises: TypeError: if `gate` is not a BasicGate. ValueError: if any qubit of `gate` is higher than simulator qubits. ValueError: if `gate` is parameterized, but no parameter supplied. TypeError: the `pr` is not a ParameterResolver if `gate` is parameterized. Examples: >>> import numpy as np >>> from mindquantum.core.gates import RY, Measure >>> from mindquantum.simulator import Simulator >>> sim = Simulator('mqvector', 1) >>> sim.apply_gate(RY('a').on(0), np.pi/2) >>> sim.get_qs() array([0.70710678+0.j, 0.70710678+0.j]) >>> sim.apply_gate(Measure().on(0)) 1 >>> sim.get_qs() array([0.+0.j, 1.+0.j]) """ return self.backend.apply_gate(gate, pr, diff)
[文档] def apply_circuit(self, circuit, pr=None): """ Apply a circuit on this simulator. Args: circuit (Circuit): The quantum circuit you want to apply on this simulator. pr (Union[ParameterResolver, dict, numpy.ndarray, list, numbers.Number]): The parameter resolver for this circuit. If the circuit is not parameterized, this arg should be None. Default: None. Returns: MeasureResult or None, if the circuit has measure gate, then return a MeasureResult, otherwise return None. Examples: >>> import numpy as np >>> from mindquantum.core.circuit import Circuit >>> from mindquantum.core.gates import H >>> from mindquantum.simulator import Simulator >>> sim = Simulator('mqvector', 2) >>> sim.apply_circuit(Circuit().un(H, 2)) >>> sim.apply_circuit(Circuit().ry('a', 0).ry('b', 1), np.array([1.1, 2.2])) >>> sim mqvector simulator with 2 qubits (little endian). Current quantum state: -0.0721702531972066¦00⟩ -0.30090405886869676¦01⟩ 0.22178317006196263¦10⟩ 0.9246947752567126¦11⟩ >>> sim.apply_circuit(Circuit().measure(0).measure(1)) shots: 1 Keys: q1 q0│0.00 0.2 0.4 0.6 0.8 1.0 ───────────┼───────────┴───────────┴───────────┴───────────┴───────────┴ 11│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ {'11': 1} """ return self.backend.apply_circuit(circuit, pr)
[文档] def sampling(self, circuit, pr=None, shots=1, seed=None): """ Sample the measure qubit in circuit. Sampling does not change the origin quantum state of this simulator. Args: circuit (Circuit): The circuit that you want to evolution and do sampling. pr (Union[None, dict, ParameterResolver]): The parameter resolver for this circuit, if this circuit is a parameterized circuit. Default: None. shots (int): How many shots you want to sampling this circuit. Default: 1 seed (int): Random seed for random sampling. If None, seed will be a random int number. Default: None. Returns: MeasureResult, the measure result of sampling. Examples: >>> from mindquantum.core.circuit import Circuit >>> from mindquantum.core.gates import Measure >>> from mindquantum.simulator import Simulator >>> circ = Circuit().ry('a', 0).ry('b', 1) >>> circ += Measure('q0_0').on(0) >>> circ += Measure('q0_1').on(0) >>> circ += Measure('q1').on(1) >>> sim = Simulator('mqvector', circ.n_qubits) >>> res = sim.sampling(circ, {'a': 1.1, 'b': 2.2}, shots=100, seed=42) >>> res shots: 100 Keys: q1 q0_1 q0_0│0.00 0.122 0.245 0.367 0.49 0.612 ──────────────────┼───────────┴───────────┴───────────┴───────────┴───────────┴ 000│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 011│▒▒▒▒▒▒▒▒▒ 100│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 111│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ {'000': 18, '011': 9, '100': 49, '111': 24} """ return self.backend.sampling(circuit, pr, shots, seed)
[文档] def apply_hamiltonian(self, hamiltonian: Hamiltonian): """ Apply hamiltonian to a simulator, this hamiltonian can be hermitian or non hermitian. Note: The quantum state may be not a normalized quantum state after apply hamiltonian. Args: hamiltonian (Hamiltonian): the hamiltonian you want to apply. Examples: >>> from mindquantum.core.circuit import Circuit >>> from mindquantum.core.operators import QubitOperator, Hamiltonian >>> from mindquantum.simulator import Simulator >>> import scipy.sparse as sp >>> sim = Simulator('mqvector', 1) >>> sim.apply_circuit(Circuit().h(0)) >>> sim.get_qs() array([0.70710678+0.j, 0.70710678+0.j]) >>> ham1 = Hamiltonian(QubitOperator('Z0')) >>> sim.apply_hamiltonian(ham1) >>> sim.get_qs() array([ 0.70710678+0.j, -0.70710678+0.j]) >>> sim.reset() >>> ham2 = Hamiltonian(sp.csr_matrix([[1, 2], [3, 4]])) >>> sim.apply_hamiltonian(ham2) >>> sim.get_qs() array([1.+0.j, 3.+0.j]) """ self.backend.apply_hamiltonian(hamiltonian)
[文档] def get_expectation(self, hamiltonian): r""" Get expectation of the given hamiltonian. The hamiltonian could be non hermitian. .. math:: E = \left<\psi\right|H\left|\psi\right> Args: hamiltonian (Hamiltonian): The hamiltonian you want to get expectation. Returns: numbers.Number, the expectation value. Examples: >>> from mindquantum.core.circuit import Circuit >>> from mindquantum.core.operators import QubitOperator, Hamiltonian >>> from mindquantum.simulator import Simulator >>> sim = Simulator('mqvector', 1) >>> sim.apply_circuit(Circuit().ry(1.2, 0)) >>> ham = Hamiltonian(QubitOperator('Z0')) >>> sim.get_expectation(ham) (0.36235775447667357+0j) """ return self.backend.get_expectation(hamiltonian)
[文档] def set_threads_number(self, number): """ Set maximum number of threads. Args: number (int): The thread number the simulator will use for thread pool. """ return self.backend.set_threads_number(number)
[文档] def get_qs(self, ket=False): """ Get current quantum state of this simulator. Args: ket (bool): Whether to return the quantum state in ket format or not. Default: False. Returns: numpy.ndarray, the current quantum state. Examples: >>> from mindquantum.algorithm.library import qft >>> from mindquantum.simulator import Simulator >>> sim = Simulator('mqvector', 2) >>> sim.apply_circuit(qft(range(2))) >>> sim.get_qs() array([0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j]) """ return self.backend.get_qs(ket)
[文档] def set_qs(self, quantum_state): """ Set quantum state for this simulation. Args: quantum_state (numpy.ndarray): the quantum state that you want. Examples: >>> import numpy as np >>> from mindquantum.simulator import Simulator >>> sim = Simulator('mqvector', 1) >>> sim.get_qs() array([1.+0.j, 0.+0.j]) >>> sim.set_qs(np.array([1, 1])) >>> sim.get_qs() array([0.70710678+0.j, 0.70710678+0.j]) """ self.backend.set_qs(quantum_state)
# pylint: disable=too-many-arguments
[文档] def get_expectation_with_grad( self, hams, circ_right, circ_left=None, simulator_left=None, parallel_worker=None, ): r""" Get a function that return the forward value and gradient w.r.t circuit parameters. This method is designed to calculate the expectation and its gradient shown as below. .. math:: E = \left<\varphi\right|U_l^\dagger H U_r \left|\psi\right> where :math:`U_l` is circ_left, :math:`U_r` is circ_right, :math:`H` is hams and :math:`\left|\psi\right>` is the current quantum state of this simulator, and :math:`\left|\varphi\right>` is the quantum state of `simulator_left`. Args: hams (Hamiltonian): The hamiltonian that need to get expectation. circ_right (Circuit): The :math:`U_r` circuit described above. circ_left (Circuit): The :math:`U_l` circuit described above. By default, this circuit will be none, and in this situation, :math:`U_l` will be equals to :math:`U_r`. Default: None. simulator_left (Simulator): The simulator that contains :math:`\left|\varphi\right>`. If None, then :math:`\left|\varphi\right>` is assumed to be equals to :math:`\left|\psi\right>`. Default: None. parallel_worker (int): The parallel worker numbers. The parallel workers can handle batch in parallel threads. Default: None. Returns: GradOpsWrapper, a grad ops wrapper than contains information to generate this grad ops. Examples: >>> import numpy as np >>> from mindquantum.core.circuit import Circuit >>> from mindquantum.core.operators import QubitOperator, Hamiltonian >>> from mindquantum.simulator import Simulator >>> circ = Circuit().ry('a', 0) >>> ham = Hamiltonian(QubitOperator('Z0')) >>> sim = Simulator('mqvector', 1) >>> grad_ops = sim.get_expectation_with_grad(ham, circ) >>> grad_ops(np.array([1.0])) (array([[0.54030231+0.j]]), array([[[-0.84147098+0.j]]])) >>> sim1 = Simulator('mqvector', 1) >>> prep_circ = Circuit().h(0) >>> ansatz = Circuit().ry('a', 0).rz('b', 0).ry('c', 0) >>> sim1.apply_circuit(prep_circ) >>> sim2 = Simulator('mqvector', 1) >>> ham = Hamiltonian(QubitOperator("")) >>> grad_ops = sim2.get_expectation_with_grad(ham, ansatz, Circuit(), simulator_left=sim1) >>> f, g = grad_ops(np.array([7.902762e-01, 2.139225e-04, 7.795934e-01])) >>> f array([[0.99999989-7.52279618e-05j]]) """ return self.backend.get_expectation_with_grad( hams, circ_right, circ_left, (simulator_left.backend if simulator_left is not None else None), parallel_worker, )
[文档]def inner_product(bra_simulator: Simulator, ket_simulator: Simulator): """ Calculate the inner product of two state that in the given simulator. Args: bra_simulator (Simulator): The simulator that serve as bra state. ket_simulator (Simulator): The simulator that serve as ket state. Returns: numbers.Number, the inner product of two quantum state. Examples: >>> from mindquantum.core.gates import RX, RY >>> from mindquantum.simulator import inner_product, Simulator >>> bra_simulator = Simulator('mqvector', 1) >>> bra_simulator.apply_gate(RY(1.2).on(0)) >>> ket_simulator = Simulator('mqvector', 1) >>> ket_simulator.apply_gate(RX(2.3).on(0)) >>> inner_product(bra_simulator, ket_simulator) (0.33713923320500694-0.5153852888544989j) """ _check_input_type('bra_simulator', Simulator, bra_simulator) _check_input_type('ket_simulator', Simulator, ket_simulator) if bra_simulator.n_qubits != ket_simulator.n_qubits: raise ValueError( "Two simulator should have same quantum state, " f"but get {bra_simulator.n_qubits} and {ket_simulator.n_qubits}." ) if bra_simulator.backend.name != ket_simulator.backend.name: raise ValueError("The backend of two simulator should be same.") if isinstance(bra_simulator.backend, Projectq) and isinstance(ket_simulator.backend, Projectq): bra_simulator.flush() ket_simulator.flush() return mb.cpu_projectq_inner_product(bra_simulator.backend.sim, ket_simulator.backend.sim) if isinstance(bra_simulator.backend, MQSim): return MQBlas.inner_product(bra_simulator.backend, ket_simulator.backend) raise NotImplementedError(f"inner_product for backend {bra_simulator.backend} not implement.")
__all__ = ['Simulator', 'get_supported_simulator', 'inner_product']