"""
Janus 状态向量类
表示量子态的状态向量,支持演化、测量、期望值计算等操作
"""
from __future__ import annotations
import copy
import math
from typing import List, Optional, Union, Dict, Tuple
import numpy as np
from .exceptions import SimulatorError, InvalidStateError
from .result import Counts
[文档]
class Statevector:
"""
状态向量类
表示纯量子态 |ψ⟩,支持:
- 从电路演化
- 测量采样
- 期望值计算
- 子系统操作
Example:
# 创建 |00⟩ 态
sv = Statevector.from_label('00')
# 从电路创建
sv = Statevector.from_circuit(circuit)
# 测量
counts = sv.sample_counts(shots=1000)
"""
[文档]
def __init__(
self,
data: Union[np.ndarray, List, 'Statevector'],
num_qubits: Optional[int] = None
):
"""
初始化状态向量
Args:
data: 状态向量数据,可以是数组、列表或另一个 Statevector
num_qubits: 量子比特数(可选,自动推断)
"""
if isinstance(data, Statevector):
self._data = data._data.copy()
self._num_qubits = data._num_qubits
elif isinstance(data, (list, np.ndarray)):
self._data = np.asarray(data, dtype=complex).flatten()
else:
raise InvalidStateError(f"Invalid data type: {type(data)}")
# 推断量子比特数
dim = len(self._data)
if dim == 0 or (dim & (dim - 1)) != 0:
raise InvalidStateError(f"Statevector dimension {dim} is not a power of 2")
inferred_qubits = int(np.log2(dim))
if num_qubits is not None and num_qubits != inferred_qubits:
raise InvalidStateError(
f"num_qubits={num_qubits} does not match data dimension {dim}"
)
self._num_qubits = inferred_qubits
# 随机数生成器
self._rng = np.random.default_rng()
[文档]
@classmethod
def from_int(cls, i: int, num_qubits: int) -> 'Statevector':
"""
从计算基态创建状态向量
Args:
i: 基态索引
num_qubits: 量子比特数
Returns:
Statevector: |i⟩ 态
Example:
sv = Statevector.from_int(0, 2) # |00⟩
sv = Statevector.from_int(3, 2) # |11⟩
"""
dim = 2 ** num_qubits
if i < 0 or i >= dim:
raise InvalidStateError(f"Index {i} out of range for {num_qubits} qubits")
data = np.zeros(dim, dtype=complex)
data[i] = 1.0
return cls(data, num_qubits)
[文档]
@classmethod
def from_label(cls, label: str) -> 'Statevector':
"""
从标签创建状态向量
支持的标签:
- '0', '1': 计算基态
- '+', '-': X 基态
- 'r', 'l': Y 基态
Args:
label: 状态标签字符串
Returns:
Statevector: 对应的状态向量
Example:
sv = Statevector.from_label('00') # |00⟩
sv = Statevector.from_label('+0') # |+0⟩ = (|00⟩ + |10⟩)/√2
"""
# 验证标签
valid_chars = set('01+-rl')
if not all(c in valid_chars for c in label):
raise InvalidStateError(f"Invalid label characters in '{label}'")
num_qubits = len(label)
# 先创建 Z 基态
z_label = label.replace('+', '0').replace('-', '1').replace('r', '0').replace('l', '1')
idx = int(z_label, 2)
sv = cls.from_int(idx, num_qubits)
# 应用 Hadamard 和 S 门转换到正确的基态
h_mat = np.array([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2)
sh_mat = np.dot(np.diag([1, 1j]), h_mat) # S·H
for qubit, char in enumerate(reversed(label)):
if char in ['+', '-']:
sv = sv.evolve(h_mat, [qubit])
elif char in ['r', 'l']:
sv = sv.evolve(sh_mat, [qubit])
return sv
[文档]
@classmethod
def from_circuit(cls, circuit) -> 'Statevector':
"""
从电路创建状态向量
Args:
circuit: Janus Circuit 对象
Returns:
Statevector: 电路作用后的状态向量
"""
sv = cls.from_int(0, circuit.n_qubits)
return sv.evolve_circuit(circuit)
@property
def data(self) -> np.ndarray:
"""获取状态向量数据"""
return self._data
@property
def num_qubits(self) -> int:
"""获取量子比特数"""
return self._num_qubits
@property
def dim(self) -> int:
"""获取状态向量维度"""
return len(self._data)
[文档]
def seed(self, value: Optional[Union[int, np.random.Generator]] = None):
"""设置随机数种子"""
if value is None:
self._rng = np.random.default_rng()
elif isinstance(value, np.random.Generator):
self._rng = value
else:
self._rng = np.random.default_rng(value)
[文档]
def copy(self) -> 'Statevector':
"""创建副本"""
new_sv = Statevector(self._data.copy(), self._num_qubits)
new_sv._rng = self._rng
return new_sv
[文档]
def is_valid(self, atol: float = 1e-10) -> bool:
"""检查是否为有效的归一化状态向量"""
norm = np.linalg.norm(self._data)
return np.isclose(norm, 1.0, atol=atol)
[文档]
def normalize(self) -> 'Statevector':
"""归一化状态向量"""
norm = np.linalg.norm(self._data)
if norm < 1e-15:
raise InvalidStateError("Cannot normalize zero vector")
self._data = self._data / norm
return self
# ==================== 演化方法 ====================
[文档]
def evolve(
self,
operator: np.ndarray,
qargs: Optional[List[int]] = None
) -> 'Statevector':
"""
通过算符演化状态向量
Args:
operator: 酉矩阵
qargs: 作用的量子比特索引列表
Returns:
Statevector: 演化后的状态向量(原地修改)
"""
if qargs is None:
# 全系统演化
if operator.shape[0] != self.dim:
raise SimulatorError(
f"Operator dimension {operator.shape[0]} does not match "
f"statevector dimension {self.dim}"
)
self._data = operator @ self._data
else:
# 子系统演化
self._data = self._apply_operator(operator, qargs)
return self
def _apply_operator(self, operator: np.ndarray, qargs: List[int]) -> np.ndarray:
"""
将算符应用到指定量子比特
使用张量收缩实现高效的子系统操作
"""
n = self._num_qubits
num_qargs = len(qargs)
# 验证量子比特索引
for q in qargs:
if q < 0 or q >= n:
raise SimulatorError(f"Qubit index {q} out of range [0, {n})")
# 将状态向量重塑为张量
tensor_shape = [2] * n
state_tensor = self._data.reshape(tensor_shape)
# 计算转置轴:将目标量子比特移到前面
# 注意:Janus 使用小端序(qubit 0 是最低位)
axes = list(range(n))
target_axes = [n - 1 - q for q in qargs] # 转换为张量索引
other_axes = [i for i in range(n) if i not in target_axes]
# 重排轴
perm = target_axes + other_axes
state_tensor = np.transpose(state_tensor, perm)
# 重塑为矩阵形式进行乘法
op_dim = 2 ** num_qargs
other_dim = 2 ** (n - num_qargs)
state_matrix = state_tensor.reshape(op_dim, other_dim)
# 应用算符
result_matrix = operator @ state_matrix
# 恢复张量形状
result_tensor = result_matrix.reshape([2] * num_qargs + [2] * (n - num_qargs))
# 逆转置恢复原始顺序
inv_perm = np.argsort(perm)
result_tensor = np.transpose(result_tensor, inv_perm)
return result_tensor.flatten()
[文档]
def evolve_circuit(self, circuit) -> 'Statevector':
"""
通过电路演化状态向量
Args:
circuit: Janus Circuit 对象
Returns:
Statevector: 演化后的状态向量
"""
from ..circuit.parameter import is_parameterized
for inst in circuit.instructions:
# 检查参数化
if is_parameterized(inst.operation):
raise SimulatorError(
f"Circuit contains unbound parameter in gate {inst.name}. "
"Please bind parameters before simulation."
)
# 获取门矩阵
try:
matrix = inst.operation.to_matrix()
except NotImplementedError:
raise SimulatorError(
f"Gate {inst.name} does not have a matrix representation"
)
# 应用门
self.evolve(matrix, inst.qubits)
return self
# ==================== 测量方法 ====================
[文档]
def probabilities(self, qargs: Optional[List[int]] = None) -> np.ndarray:
"""
计算测量概率分布
Args:
qargs: 要测量的量子比特索引,None 表示全部
Returns:
np.ndarray: 概率分布数组
"""
probs = np.abs(self._data) ** 2
if qargs is None:
return probs
# 边缘化到指定量子比特
return self._marginalize_probabilities(probs, qargs)
def _marginalize_probabilities(
self,
probs: np.ndarray,
qargs: List[int]
) -> np.ndarray:
"""边缘化概率分布到指定量子比特"""
n = self._num_qubits
# 重塑为张量
probs_tensor = probs.reshape([2] * n)
# 计算要求和的轴(不在 qargs 中的量子比特)
# 注意张量索引和量子比特索引的对应关系
keep_axes = [n - 1 - q for q in qargs]
sum_axes = tuple(i for i in range(n) if i not in keep_axes)
# 求和边缘化
if sum_axes:
probs_tensor = np.sum(probs_tensor, axis=sum_axes)
# 重排轴以匹配 qargs 顺序
current_order = sorted(range(len(qargs)), key=lambda i: keep_axes[i])
target_order = list(range(len(qargs)))
if current_order != target_order:
# 需要转置
inv_order = [current_order.index(i) for i in target_order]
probs_tensor = np.transpose(probs_tensor, inv_order)
return probs_tensor.flatten()
[文档]
def probabilities_dict(
self,
qargs: Optional[List[int]] = None,
decimals: Optional[int] = None
) -> Dict[str, float]:
"""
获取概率分布字典
Args:
qargs: 要测量的量子比特索引
decimals: 小数位数
Returns:
Dict[str, float]: 比特串到概率的映射
"""
probs = self.probabilities(qargs)
num_bits = len(qargs) if qargs else self._num_qubits
result = {}
for i, p in enumerate(probs):
if p > 1e-15:
bitstring = format(i, f'0{num_bits}b')
if decimals is not None:
p = round(p, decimals)
result[bitstring] = p
return result
[文档]
def sample_memory(
self,
shots: int,
qargs: Optional[List[int]] = None
) -> np.ndarray:
"""
采样测量结果(保留顺序)
Args:
shots: 采样次数
qargs: 要测量的量子比特索引
Returns:
np.ndarray: 测量结果数组,每个元素是比特串
"""
probs = self.probabilities(qargs)
num_bits = len(qargs) if qargs else self._num_qubits
# 采样索引
indices = self._rng.choice(len(probs), size=shots, p=probs)
# 转换为比特串
return np.array([format(i, f'0{num_bits}b') for i in indices])
[文档]
def sample_counts(
self,
shots: int,
qargs: Optional[List[int]] = None
) -> Counts:
"""
采样测量结果(返回计数)
Args:
shots: 采样次数
qargs: 要测量的量子比特索引
Returns:
Counts: 测量计数
"""
samples = self.sample_memory(shots, qargs)
unique, counts = np.unique(samples, return_counts=True)
return Counts(dict(zip(unique, counts.astype(int))))
[文档]
def measure(
self,
qargs: Optional[List[int]] = None
) -> Tuple[str, 'Statevector']:
"""
执行测量并返回结果和坍缩后的状态
Args:
qargs: 要测量的量子比特索引
Returns:
Tuple[str, Statevector]: (测量结果, 坍缩后的状态)
"""
probs = self.probabilities(qargs)
num_bits = len(qargs) if qargs else self._num_qubits
# 采样一个结果
outcome_idx = self._rng.choice(len(probs), p=probs)
outcome = format(outcome_idx, f'0{num_bits}b')
# 计算坍缩后的状态
if qargs is None:
# 全系统测量,坍缩到计算基态
new_data = np.zeros_like(self._data)
new_data[outcome_idx] = 1.0
else:
# 部分测量,需要投影
new_data = self._project_measurement(qargs, outcome_idx)
new_sv = Statevector(new_data, self._num_qubits)
new_sv._rng = self._rng
return outcome, new_sv
def _project_measurement(self, qargs: List[int], outcome: int) -> np.ndarray:
"""投影测量后的状态"""
n = self._num_qubits
num_measured = len(qargs)
# 创建投影后的状态
new_data = np.zeros_like(self._data)
# 遍历所有基态,保留与测量结果一致的分量
for i in range(self.dim):
# 提取测量比特的值
measured_bits = 0
for j, q in enumerate(qargs):
bit = (i >> q) & 1
measured_bits |= (bit << j)
if measured_bits == outcome:
new_data[i] = self._data[i]
# 归一化
norm = np.linalg.norm(new_data)
if norm > 1e-15:
new_data = new_data / norm
return new_data
# ==================== 期望值计算 ====================
[文档]
def expectation_value(
self,
operator: np.ndarray,
qargs: Optional[List[int]] = None
) -> complex:
"""
计算可观测量的期望值 ⟨ψ|O|ψ⟩
Args:
operator: 可观测量矩阵
qargs: 作用的量子比特索引
Returns:
complex: 期望值
"""
if qargs is None:
# 全系统算符
return np.vdot(self._data, operator @ self._data)
else:
# 子系统算符
evolved = self.copy()
evolved._data = evolved._apply_operator(operator, qargs)
return np.vdot(self._data, evolved._data)
# ==================== 状态操作 ====================
[文档]
def tensor(self, other: 'Statevector') -> 'Statevector':
"""
张量积 self ⊗ other
Args:
other: 另一个状态向量
Returns:
Statevector: 张量积状态
"""
new_data = np.kron(self._data, other._data)
return Statevector(new_data)
[文档]
def inner(self, other: 'Statevector') -> complex:
"""
内积 ⟨self|other⟩
Args:
other: 另一个状态向量
Returns:
complex: 内积值
"""
if self.dim != other.dim:
raise SimulatorError("Statevector dimensions do not match")
return np.vdot(self._data, other._data)
[文档]
def conjugate(self) -> 'Statevector':
"""返回共轭状态向量"""
return Statevector(np.conj(self._data), self._num_qubits)
[文档]
def equiv(
self,
other: 'Statevector',
atol: float = 1e-10
) -> bool:
"""
检查两个状态向量是否等价(忽略全局相位)
Args:
other: 另一个状态向量
atol: 绝对容差
Returns:
bool: 是否等价
"""
if self.dim != other.dim:
return False
# 找到第一个非零元素
for i in range(self.dim):
if abs(self._data[i]) > atol:
if abs(other._data[i]) < atol:
return False
# 计算相位差
phase = other._data[i] / self._data[i]
# 检查所有元素是否相差相同的相位
return np.allclose(self._data * phase, other._data, atol=atol)
# self 是零向量
return np.allclose(other._data, 0, atol=atol)
# ==================== 特殊方法 ====================
def __repr__(self) -> str:
return f"Statevector({self._data}, num_qubits={self._num_qubits})"
def __len__(self) -> int:
return self.dim
def __getitem__(self, key: Union[int, str]) -> complex:
"""
获取状态向量元素
Args:
key: 索引(整数)或比特串
"""
if isinstance(key, str):
key = int(key, 2)
return self._data[key]
def __eq__(self, other) -> bool:
if not isinstance(other, Statevector):
return False
return np.allclose(self._data, other._data)
def __array__(self, dtype=None):
return np.asarray(self._data, dtype=dtype)
[文档]
def to_dict(self, decimals: Optional[int] = None) -> Dict[str, complex]:
"""
转换为字典表示
Args:
decimals: 小数位数
Returns:
Dict[str, complex]: 比特串到振幅的映射
"""
result = {}
for i, amp in enumerate(self._data):
if abs(amp) > 1e-15:
bitstring = format(i, f'0{self._num_qubits}b')
if decimals is not None:
amp = complex(round(amp.real, decimals), round(amp.imag, decimals))
result[bitstring] = amp
return result
[文档]
def draw(self, output: str = 'text', **kwargs) -> str:
"""
绘制状态向量
Args:
output: 输出格式 ('text', 'latex')
Returns:
str: 状态向量的字符串表示
"""
if output == 'text':
return self._draw_text(**kwargs)
elif output == 'latex':
return self._draw_latex(**kwargs)
else:
raise ValueError(f"Unknown output format: {output}")
def _draw_text(self, decimals: int = 4) -> str:
"""文本格式绘制"""
lines = []
for i, amp in enumerate(self._data):
if abs(amp) > 1e-10:
bitstring = format(i, f'0{self._num_qubits}b')
if abs(amp.imag) < 1e-10:
amp_str = f"{amp.real:.{decimals}f}"
elif abs(amp.real) < 1e-10:
amp_str = f"{amp.imag:.{decimals}f}j"
else:
amp_str = f"({amp.real:.{decimals}f}{amp.imag:+.{decimals}f}j)"
lines.append(f"{amp_str}|{bitstring}⟩")
return " + ".join(lines) if lines else "0"
def _draw_latex(self, decimals: int = 4) -> str:
"""LaTeX 格式绘制"""
terms = []
for i, amp in enumerate(self._data):
if abs(amp) > 1e-10:
bitstring = format(i, f'0{self._num_qubits}b')
if abs(amp.imag) < 1e-10:
amp_str = f"{amp.real:.{decimals}f}"
elif abs(amp.real) < 1e-10:
amp_str = f"{amp.imag:.{decimals}f}i"
else:
amp_str = f"({amp.real:.{decimals}f}{amp.imag:+.{decimals}f}i)"
terms.append(f"{amp_str}|{bitstring}\\rangle")
return " + ".join(terms) if terms else "0"