# Copyright 2024 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.
# ============================================================================
"""Mindquantum Clifford Stabilizer Simulator."""
# pylint:disable=abstract-method,keyword-arg-before-vararg,no-member,unused-argument
# pylint:disable=redefined-outer-name
from __future__ import annotations
from typing import TYPE_CHECKING
import numpy as np
from mindquantum import _mq_vector
from mindquantum.core.circuit import Circuit
from mindquantum.core.gates import MeasureResult, S, X
from mindquantum.simulator.backend_base import BackendBase
from mindquantum.utils.type_value_check import (
_check_input_type,
_check_int_type,
_check_seed,
_check_value_should_not_less,
)
if TYPE_CHECKING:
from mindquantum.simulator import Simulator
class Stabilizer(BackendBase):
"""Stabilizer simulator."""
def __init__(self, name: str, n_qubits, seed=None, *args, **kwargs):
"""Initialize a stabilizer simulator."""
super().__init__(name, n_qubits, seed)
if kwargs.get('internal', False):
self.sim = name
self.name = 'stabilizer'
else:
self.sim = _mq_vector.stabilizer.StabilizerTableau(n_qubits, seed)
def __str__(self):
"""Return a string representation of the object."""
return f"stabilizer simulator with {self.n_qubits} qubits.\nCurrent tableau:\n{self.sim.tableau_to_string()}"
def __repr__(self):
"""Return a string representation of the object."""
return self.__str__()
# pylint: disable=arguments-differ
def apply_circuit(self, circuit: Circuit, *args, **kwargs):
"""Apply a quantum circuit."""
_check_input_type('circuit', Circuit, circuit)
if self.n_qubits < circuit.n_qubits:
raise ValueError(f"Circuit has {circuit.n_qubits} qubits, which is more than simulator qubits.")
res = self.sim.apply_circuit(circuit.get_cpp_obj())
if res:
out = MeasureResult()
out.add_measure(circuit.all_measures.keys())
out.collect_data([[res[i] for i in out.keys_map]])
return out
return None
def copy(self) -> Stabilizer:
"""Copy a simulator."""
sim = Stabilizer(self.name, self.n_qubits, self.seed)
sim.sim = self.sim.copy()
return sim
def __eq__(self, other: Stabilizer) -> bool:
"""Check whether two stabilizers are equal or not."""
_check_input_type('other', Stabilizer, other)
return _mq_vector.stabilizer.StabilizerTableau.__eq__(self.sim, other.sim)
# pylint: disable=arguments-differ
def get_qs(self, *args, **kwargs):
"""Return the tableau of stabilizer."""
return np.array(self.sim.tableau_to_vector())
# pylint: disable=arguments-differ
def sampling(self, circuit: Circuit, shots: int = 1, seed: int = None, *args, **kwargs):
"""Sample the quantum state."""
if not circuit.all_measures.map:
raise ValueError("circuit must have at least one measurement gate.")
_check_input_type("circuit", Circuit, circuit)
if self.n_qubits < circuit.n_qubits:
raise ValueError(f"Circuit has {circuit.n_qubits} qubits, which is more than simulator qubits.")
_check_int_type("sampling shots", shots)
_check_value_should_not_less("sampling shots", 1, shots)
if seed is None:
seed = int(np.random.randint(1, 2 << 20))
else:
_check_seed(seed)
res = MeasureResult()
res.add_measure(circuit.all_measures.keys())
if circuit.is_measure_end and not circuit.is_noise_circuit:
sampler = self.sim.sampling_measure_ending_without_noise
else:
sampler = self.sim.sampling
samples = np.array(sampler(circuit.get_cpp_obj(), shots, res.keys_map, seed)).reshape((shots, -1))
res.collect_data(samples)
return res
[docs]def decompose_stabilizer(sim: Simulator | Stabilizer) -> Circuit:
"""
Decompose a stabilizer into clifford quantum circuit.
Args:
sim (Simulator): A stabilizer simulator.
Examples:
>>> from mindquantum.simulator import Simulator, decompose_stabilizer
>>> from mindquantum.core.circuit import Circuit
>>> stabilizer = Simulator('stabilizer', 2)
>>> stabilizer.apply_circuit(Circuit().h(0).x(1, 0))
>>> decompose_stabilizer(stabilizer)
┏━━━┓
q0: ──┨ H ┠───■─────
┗━━━┛ ┃
┏━┻━┓
q1: ────────┨╺╋╸┠───
┗━━━┛
>>> from mindquantum.algorithm.error_mitigation import query_single_qubit_clifford_elem
>>> decompose_stabilizer(query_single_qubit_clifford_elem(10))
┏━━━┓ ┏━━━┓ ┏━━━━┓
q0: ──┨╺╋╸┠─┨ Z ┠─┨ S† ┠───
┗━━━┛ ┗━━━┛ ┗━━━━┛
"""
# pylint: disable=import-outside-toplevel
from mindquantum.simulator import Simulator
if isinstance(sim, Simulator):
if sim.backend.name != 'stabilizer':
raise TypeError(f"Input simulator should be a stabilizer simulator, but get {sim.backend.name}.")
sim = sim.backend
elif not isinstance(sim, Stabilizer):
raise TypeError(f'sim require a type of Simulator or Stabilizer, but get {type(sim)}.')
circ = Circuit()
decomposed = sim.sim.decompose()
for g in decomposed:
g_id = str(g.get_id())
obj = g.get_obj_qubits()
ctrl = g.get_ctrl_qubits()
if g_id in 'HXYZS':
getattr(circ, g_id.lower())(obj, ctrl)
elif g_id == 'Sdag':
circ += S.on(obj, ctrl).hermitian()
elif g_id == 'CNOT':
circ += X.on(obj[0], obj[1])
else:
raise RuntimeError(f"Unknown gate id: {g_id}")
return circ
[docs]def get_tableau_string(sim: Simulator | Stabilizer) -> str:
"""
Get the string expression of a stabilizer tableau.
Args:
sim (Simulator): A stabilizer simulator.
Examples:
>>> from mindquantum.simulator import Simulator, get_tableau_string
>>> from mindquantum.core.circuit import Circuit
>>> stabilizer = Simulator('stabilizer', 2)
>>> stabilizer.apply_circuit(Circuit().h(0).x(1, 0))
>>> print(get_tableau_string(stabilizer))
0 0 | 1 0 | 0
0 1 | 0 0 | 0
-------------
1 1 | 0 0 | 0
0 0 | 1 1 | 0
"""
# pylint: disable=import-outside-toplevel
from mindquantum.simulator import Simulator
if isinstance(sim, Simulator):
if sim.backend.name != 'stabilizer':
raise TypeError(f"Input simulator should be a stabilizer simulator, but get {sim.backend.name}.")
sim = sim.backend
elif not isinstance(sim, Stabilizer):
raise TypeError(f'sim require a type of Simulator or Stabilizer, but get {type(sim)}.')
return sim.sim.tableau_to_string()
[docs]def get_stabilizer_string(sim: Simulator | Stabilizer) -> str:
"""
Get the string expression of a stabilizer.
Args:
sim (Simulator): A stabilizer simulator.
Examples:
>>> from mindquantum.simulator import Simulator, get_stabilizer_string
>>> from mindquantum.core.circuit import Circuit
>>> stabilizer = Simulator('stabilizer', 2)
>>> stabilizer.apply_circuit(Circuit().h(0).x(1, 0))
>>> print(get_stabilizer_string(stabilizer))
destabilizer:
+IZ
+XI
stabilizer:
+XX
+ZZ
"""
# pylint: disable=import-outside-toplevel
from mindquantum.simulator import Simulator
if isinstance(sim, Simulator):
if sim.backend.name != 'stabilizer':
raise TypeError(f"Input simulator should be a stabilizer simulator, but get {sim.backend.name}.")
sim = sim.backend
elif not isinstance(sim, Stabilizer):
raise TypeError(f'sim require a type of Simulator or Stabilizer, but get {type(sim)}.')
return sim.sim.stabilizer_to_string()