# -*- coding: utf-8 -*-
# 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.
# ============================================================================
"""Basic module for quantum gate."""
from collections.abc import Iterable
import numpy as np
from rich.console import Console
from mindquantum.utils.type_value_check import _check_input_type
from mindquantum.utils.type_value_check import _check_int_type
from mindquantum.utils.type_value_check import _check_value_should_not_less
from mindquantum import mqbackend as mb
from mindquantum.io.display import measure_text_drawer
from .basic import NoneParameterGate
[docs]class Measure(NoneParameterGate):
"""
Measurement gate that measure quantum qubits.
Args:
name (str): The key of this measurement gate. In a quantum circuit, the
key of different measurement gate should be unique. Default: ""
Examples:
>>> import numpy as np
>>> from mindquantum import qft, Circuit
>>> from mindquantum import Measure
>>> from mindquantum import Simulator
>>> circ = qft(range(2))
>>> circ += Measure('q0').on(0)
>>> circ += Measure().on(1)
>>> circ
q0: ──H────PS(π/2)─────────@────M(q0)──
│ │
q1: ──────────●───────H────@────M(q1)──
>>> sim = Simulator('projectq', circ.n_qubits)
>>> sim.apply_circuit(Circuit().h(0).x(1, 0))
>>> sim
projectq simulator with 2 qubits (little endian).
Current quantum state:
√2/2¦00⟩
√2/2¦11⟩
>>> res = sim.sampling(circ, shots=2000, seed=42)
>>> res
shots: 2000
Keys: q1 q0│0.00 0.124 0.248 0.372 0.496 0.621
───────────┼───────────┴───────────┴───────────┴───────────┴───────────┴
00│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
│
10│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
│
11│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
│
{'00': 993, '10': 506, '11': 501}
>>> sim
projectq simulator with 2 qubits (little endian).
Current quantum state:
√2/2¦00⟩
√2/2¦11⟩
>>> sim.apply_circuit(circ[:-2])
>>> sim
projectq simulator with 2 qubits (little endian).
Current quantum state:
√2/2¦00⟩
(√2/4-√2/4j)¦10⟩
(√2/4+√2/4j)¦11⟩
>>> np.abs(sim.get_qs())**2
array([0.5 , 0. , 0.25, 0.25])
"""
def __init__(self, name=""):
_check_input_type('name', str, name)
self.key = name
NoneParameterGate.__init__(self, name)
self.name = 'M'
def get_cpp_obj(self):
out = mb.get_measure_gate(self.key)
out.obj_qubits = self.obj_qubits
return out
def __str__(self):
info = ""
if self.key and self.obj_qubits:
info = f'({self.obj_qubits[0]}, key={self.key})'
elif self.key:
info = f'(key={self.key})'
elif self.obj_qubits:
info = f'({self.obj_qubits[0]})'
return f"Measure{info}"
def __repr__(self):
return self.__str__()
[docs] def on(self, obj_qubits, ctrl_qubits=None):
"""
Apply this measurement gate on which qubit.
Args:
obj_qubits (int): A non negative int that referring to its index number.
ctrl_qubits (int): Should be None for measure gate. Default: None.
Examples:
>>> from mindquantum import Circuit, Measure
>>> from mindquantum import Simulator
>>> sim = Simulator('projectq', 2)
>>> circ = Circuit().h(0).x(1, 0)
>>> circ += Measure('q0').on(0)
>>> circ += Measure('q1').on(1)
>>> circ
q0: ──H────●────M(q0)──
│
q1: ───────X────M(q1)──
>>> res = sim.apply_circuit(circ)
>>> res
shots: 1
Keys: q1 q0│0.00 0.2 0.4 0.6 0.8 1.0
───────────┼───────────┴───────────┴───────────┴───────────┴───────────┴
11│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
│
{'11': 1}
>>> sim
projectq simulator with 2 qubits (little endian).
Current quantum state:
1¦11⟩
"""
if ctrl_qubits is not None:
raise ValueError("Measure gate can not have control qubit")
if obj_qubits is None:
raise ValueError("The object qubit of measurement can not be none")
_check_int_type('obj_qubits', obj_qubits)
_check_value_should_not_less('obj_qubits', 0, obj_qubits)
new_gate = Measure(self.key)
new_gate.obj_qubits = [obj_qubits]
if not new_gate.key:
new_gate.key = f'q{obj_qubits}'
return new_gate
def __hash__(self):
return hash(self.key)
def __eq__(self, other):
if self.key == other.key:
return True
return False
[docs] def hermitian(self):
"""Hermitian gate of measure return its self"""
if not self.obj_qubits:
raise ValueError("Measurement should apply on some qubit first.")
return self.__class__(self.key).on(self.obj_qubits[0])
def check_obj_qubits(self):
if not self.obj_qubits:
raise ValueError("Empty measure obj qubit")
if len(self.obj_qubits) > 1:
raise ValueError("Measure gate only apply on a single qubit")
def define_projectq_gate(self):
raise NotImplementedError
[docs]class MeasureResult:
"""
Measurement result container
Examples:
>>> from mindquantum import qft
>>> from mindquantum import Simulator
>>> sim = Simulator('projectq', 2)
>>> res = sim.sampling(qft(range(2)).measure_all(), shots=1000, seed=42)
>>> res
shots: 1000
Keys: q1 q0│0.00 0.065 0.13 0.194 0.259 0.324
───────────┼───────────┴───────────┴───────────┴───────────┴───────────┴
00│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
│
01│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
│
10│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
│
11│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
│
{'00': 230, '01': 254, '10': 257, '11': 259}
>>> res.data
{'00': 230, '01': 254, '10': 257, '11': 259}
"""
def __init__(self):
self.measures = []
self.keys = []
self.samples = np.array([])
self.bit_string_data = {}
self.shots = 0
[docs] def add_measure(self, measure):
"""
Add a measurement gate into this measurement result container. Measure key
should be unique in this measurement result container.
Args:
measure (Union[Iterable, Measure]): One or more measure gates.
"""
if not isinstance(measure, Iterable):
measure = [measure]
for m in measure:
if not isinstance(m, Measure):
raise ValueError("Measurement gates need to \
be objects of class 'Measurement' ")
for m in measure:
if m.key in self.keys:
raise ValueError(f"Measure key {m.key} already defined.")
self.measures.append(m)
self.keys.append(m.key)
@property
def keys_map(self):
return {i: j for j, i in enumerate(self.keys)}
[docs] def collect_data(self, samples):
"""
collect the measured bit string
Args:
samples (numpy.ndarray): A two dimensional (N x M) numpy array that stores
the sampling bit string in 0 or 1, where N represents the number of shot
times, and M represents the number of keys in this measurement container
"""
self.samples = samples
out = {}
res = np.fliplr(self.samples)
self.shots = len(self.samples)
for s in res:
s = ''.join([str(i) for i in s])
if s in out:
out[s] += 1
else:
out[s] = 1
keys = sorted(list(out.keys()))
self.bit_string_data = {key: out[key] for key in keys}
[docs] def select_keys(self, *keys):
"""
Select certain measurement keys from this measurement container
Args:
keys (tuple[str]): The key you want to select.
Examples:
>>> from mindquantum import Simulator
>>> from mindquantum import qft, H
>>> circ = qft(range(2)).measure('q0_0', 0).measure('q1_0', 1)
>>> circ.h(0).measure('q0_1', 0)
>>> circ
q0: ──H────PS(π/2)─────────@────M(q0_0)────H────M(q0_1)──
│ │
q1: ──────────●───────H────@────M(q1_0)──────────────────
>>> sim = Simulator('projectq', circ.n_qubits)
>>> res = sim.sampling(circ, shots=500, seed=42)
>>> new_res = res.select_keys('q0_1', 'q1_0')
>>> new_res
shots: 500
Keys: q1_0 q0_1│0.00 0.068 0.136 0.204 0.272 0.34
───────────────┼───────────┴───────────┴───────────┴───────────┴───────────┴
00│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
│
01│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
│
10│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
│
11│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
│
{'00': 127, '01': 107, '10': 136, '11': 130}
"""
for key in keys:
if key not in self.keys:
raise ValueError(f'{key} not in this measure result.')
keys_map = self.keys_map
idx = [keys_map[key] for key in keys]
samples = self.samples[:, idx]
res = MeasureResult()
res.add_measure([self.measures[i] for i in idx])
res.collect_data(samples)
return res
@property
def data(self):
"""
Get the sampling data.
Returns:
dict: The samping data.
"""
return self.bit_string_data
def __str__(self):
return self.__repr__()
def __repr__(self):
from mindquantum.io.display._config import _MEA_RES_STYLE
res = measure_text_drawer(self)
res.append(self.data.__str__())
s = '\n'.join(res)
console = Console(record=True)
if not console.is_jupyter:
with console.capture() as capture:
console.print(s, style=_MEA_RES_STYLE['style'])
s = capture.get()
return s
def _repr_html_(self):
"""repr for jupyter notebook"""
from mindquantum.io.display._config import _MEA_RES_STYLE
from mindquantum.io.display._config import MEA_HTML_FORMAT
res = measure_text_drawer(self)
res.append(self.data.__str__())
s = '\n'.join(res)
console = Console(record=True)
with console.capture() as _:
console.print(s, style=_MEA_RES_STYLE['style'])
s = console.export_html(code_format=MEA_HTML_FORMAT, inline_styles=True)
return '\n'.join(s.split('\n')[1:])
[docs] def svg(self, style=None):
"""
Display current measurement result into SVG picture in jupyter notebook.
Args:
style (dict, str): the style to set svg style. Currently, we support
'official'. Default: None.
"""
from mindquantum.io.display.measure_res_svg_drawer import SVGMeasure
from mindquantum.io.display._config import _svg_measure_config_official
supported_style = {
'official': _svg_measure_config_official,
}
if style is None:
style = _svg_measure_config_official
if isinstance(style, str):
if style not in supported_style:
raise ValueError(f"Style not found, currently we support {list(supported_style.keys())}")
style = supported_style[style]
svg = SVGMeasure(self, style)
return svg