"""
Janus 密度矩阵类
表示混合量子态,支持噪声模拟
"""
from __future__ import annotations
from typing import List, Optional, Union, Dict, Tuple
import numpy as np
from .statevector import Statevector
from .result import Counts
from .exceptions import SimulatorError, InvalidStateError
[文档]
class DensityMatrix:
"""
密度矩阵类
表示混合量子态 ρ,支持:
- 噪声信道演化
- 部分迹
- 保真度计算
Example:
# 从纯态创建
dm = DensityMatrix.from_statevector(sv)
# 应用去极化噪声
dm = dm.apply_channel(depolarizing_channel(0.01), [0])
"""
[文档]
def __init__(
self,
data: Union[np.ndarray, 'DensityMatrix'],
num_qubits: Optional[int] = None
):
"""
初始化密度矩阵
Args:
data: 密度矩阵数据
num_qubits: 量子比特数
"""
if isinstance(data, DensityMatrix):
self._data = data._data.copy()
self._num_qubits = data._num_qubits
else:
self._data = np.asarray(data, dtype=complex)
if self._data.ndim != 2:
raise InvalidStateError("Density matrix must be 2-dimensional")
if self._data.shape[0] != self._data.shape[1]:
raise InvalidStateError("Density matrix must be square")
dim = self._data.shape[0]
if dim == 0 or (dim & (dim - 1)) != 0:
raise InvalidStateError(f"Dimension {dim} is not a power of 2")
self._num_qubits = int(np.log2(dim))
self._rng = np.random.default_rng()
[文档]
@classmethod
def from_statevector(cls, sv: Union[Statevector, np.ndarray]) -> 'DensityMatrix':
"""从纯态创建密度矩阵 ρ = |ψ⟩⟨ψ|"""
if isinstance(sv, Statevector):
data = np.outer(sv.data, np.conj(sv.data))
return cls(data, sv.num_qubits)
else:
sv_arr = np.asarray(sv, dtype=complex).flatten()
data = np.outer(sv_arr, np.conj(sv_arr))
return cls(data)
[文档]
@classmethod
def from_label(cls, label: str) -> 'DensityMatrix':
"""从标签创建密度矩阵"""
sv = Statevector.from_label(label)
return cls.from_statevector(sv)
[文档]
@classmethod
def maximally_mixed(cls, num_qubits: int) -> 'DensityMatrix':
"""创建最大混合态 ρ = I/d"""
dim = 2 ** num_qubits
data = np.eye(dim, dtype=complex) / dim
return cls(data, num_qubits)
@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 self._data.shape[0]
[文档]
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) -> 'DensityMatrix':
"""创建副本"""
dm = DensityMatrix(self._data.copy(), self._num_qubits)
dm._rng = self._rng
return dm
[文档]
def is_valid(self, atol: float = 1e-10) -> bool:
"""检查是否为有效密度矩阵"""
# 检查厄米性
if not np.allclose(self._data, self._data.conj().T, atol=atol):
return False
# 检查迹为 1
if not np.isclose(np.trace(self._data), 1.0, atol=atol):
return False
# 检查半正定性
eigenvalues = np.linalg.eigvalsh(self._data)
if np.any(eigenvalues < -atol):
return False
return True
[文档]
def trace(self) -> complex:
"""计算迹"""
return np.trace(self._data)
[文档]
def purity(self) -> float:
"""计算纯度 Tr(ρ²)"""
return np.real(np.trace(self._data @ self._data))
[文档]
def is_pure(self, atol: float = 1e-10) -> bool:
"""检查是否为纯态"""
return np.isclose(self.purity(), 1.0, atol=atol)
# ==================== 演化方法 ====================
[文档]
def evolve(
self,
operator: np.ndarray,
qargs: Optional[List[int]] = None
) -> 'DensityMatrix':
"""
酉演化 ρ → U ρ U†
Args:
operator: 酉矩阵
qargs: 作用的量子比特
Returns:
DensityMatrix: 演化后的密度矩阵
"""
if qargs is None:
self._data = operator @ self._data @ operator.conj().T
else:
self._data = self._apply_unitary(operator, qargs)
return self
def _apply_unitary(self, U: np.ndarray, qargs: List[int]) -> np.ndarray:
"""将酉算符应用到指定量子比特"""
n = self._num_qubits
num_qargs = len(qargs)
# 将密度矩阵重塑为张量
tensor_shape = [2] * (2 * n)
rho_tensor = self._data.reshape(tensor_shape)
# 计算轴
row_axes = [n - 1 - q for q in qargs]
col_axes = [n + n - 1 - q for q in qargs]
# 应用 U 到行索引
rho_tensor = self._contract_tensor(rho_tensor, U, row_axes, n)
# 应用 U† 到列索引
rho_tensor = self._contract_tensor(rho_tensor, U.conj().T, col_axes, n)
return rho_tensor.reshape(self.dim, self.dim)
def _contract_tensor(
self,
tensor: np.ndarray,
matrix: np.ndarray,
axes: List[int],
n: int
) -> np.ndarray:
"""张量收缩辅助函数"""
num_axes = len(axes)
total_axes = 2 * n
# 将目标轴移到前面
other_axes = [i for i in range(total_axes) if i not in axes]
perm = axes + other_axes
tensor = np.transpose(tensor, perm)
# 重塑并乘法
op_dim = 2 ** num_axes
other_dim = tensor.size // op_dim
tensor = tensor.reshape(op_dim, other_dim)
tensor = matrix @ tensor
# 恢复形状
tensor = tensor.reshape([2] * num_axes + [2] * (total_axes - num_axes))
# 逆转置
inv_perm = np.argsort(perm)
tensor = np.transpose(tensor, inv_perm)
return tensor
[文档]
def apply_channel(
self,
kraus_ops: List[np.ndarray],
qargs: Optional[List[int]] = None
) -> 'DensityMatrix':
"""
应用量子信道(Kraus 表示)
ρ → Σ_k K_k ρ K_k†
Args:
kraus_ops: Kraus 算符列表
qargs: 作用的量子比特
Returns:
DensityMatrix: 演化后的密度矩阵
"""
if qargs is None:
new_data = sum(K @ self._data @ K.conj().T for K in kraus_ops)
else:
new_data = np.zeros_like(self._data)
for K in kraus_ops:
# 对每个 Kraus 算符,应用 K ρ K†
temp_data = self._apply_kraus_single(K, qargs)
new_data += temp_data
self._data = new_data
return self
def _apply_kraus_single(self, K: np.ndarray, qargs: List[int]) -> np.ndarray:
"""应用单个 Kraus 算符 K ρ K†"""
n = self._num_qubits
num_qargs = len(qargs)
# 将密度矩阵重塑为张量 [2]*n (行) + [2]*n (列)
tensor_shape = [2] * (2 * n)
rho_tensor = self._data.reshape(tensor_shape)
# 计算行和列的轴索引
# 对于 n 量子比特,行轴是 0 到 n-1,列轴是 n 到 2n-1
# qargs 中的量子比特 q 对应行轴 n-1-q 和列轴 2n-1-q
row_axes = [n - 1 - q for q in qargs]
col_axes = [2*n - 1 - q for q in qargs]
# 应用 K 到行索引(左乘)
# 将目标行轴移到前面
other_row_axes = [i for i in range(n) if i not in row_axes]
other_col_axes = [i for i in range(n, 2*n) if i not in col_axes]
perm = row_axes + other_row_axes + col_axes + other_col_axes
rho_tensor = np.transpose(rho_tensor, perm)
# 重塑为矩阵形式
k_dim = 2 ** num_qargs
other_dim = 2 ** (n - num_qargs)
# 形状: [k_dim, other_dim, k_dim, other_dim]
rho_tensor = rho_tensor.reshape(k_dim, other_dim, k_dim, other_dim)
# 应用 K ρ K†
# K @ rho @ K† 在目标子空间
result = np.zeros_like(rho_tensor)
for i in range(other_dim):
for j in range(other_dim):
sub_rho = rho_tensor[:, i, :, j] # k_dim x k_dim
result[:, i, :, j] = K @ sub_rho @ K.conj().T
# 恢复形状
result = result.reshape([2] * num_qargs + [2] * (n - num_qargs) +
[2] * num_qargs + [2] * (n - num_qargs))
# 逆转置
inv_perm = np.argsort(perm)
result = np.transpose(result, inv_perm)
return result.reshape(self.dim, self.dim)
[文档]
def evolve_circuit(self, circuit) -> 'DensityMatrix':
"""通过电路演化密度矩阵"""
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}"
)
matrix = inst.operation.to_matrix()
self.evolve(matrix, inst.qubits)
return self
# ==================== 测量方法 ====================
[文档]
def probabilities(self, qargs: Optional[List[int]] = None) -> np.ndarray:
"""计算测量概率分布"""
probs = np.real(np.diag(self._data))
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)
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)
return probs_tensor.flatten()
[文档]
def sample_counts(
self,
shots: int,
qargs: Optional[List[int]] = None
) -> Counts:
"""采样测量结果"""
probs = self.probabilities(qargs)
num_bits = len(qargs) if qargs else self._num_qubits
indices = self._rng.choice(len(probs), size=shots, p=probs)
samples = [format(i, f'0{num_bits}b') for i in indices]
unique, counts = np.unique(samples, return_counts=True)
return Counts(dict(zip(unique, counts.astype(int))))
# ==================== 信息度量 ====================
[文档]
def expectation_value(
self,
operator: np.ndarray,
qargs: Optional[List[int]] = None
) -> complex:
"""计算期望值 Tr(ρO)"""
if qargs is None:
return np.trace(self._data @ operator)
else:
# 扩展算符到全系统
full_op = self._expand_operator(operator, qargs)
return np.trace(self._data @ full_op)
def _expand_operator(self, op: np.ndarray, qargs: List[int]) -> np.ndarray:
"""将子系统算符扩展到全系统"""
n = self._num_qubits
num_qargs = len(qargs)
# 构建全系统算符
full_dim = 2 ** n
full_op = np.zeros((full_dim, full_dim), dtype=complex)
op_dim = 2 ** num_qargs
other_dim = full_dim // op_dim
for i in range(other_dim):
for j in range(other_dim):
for a in range(op_dim):
for b in range(op_dim):
# 计算全系统索引
row = self._compute_index(a, i, qargs, n)
col = self._compute_index(b, j, qargs, n)
if i == j:
full_op[row, col] = op[a, b]
return full_op
def _compute_index(
self,
qarg_bits: int,
other_bits: int,
qargs: List[int],
n: int
) -> int:
"""计算全系统索引"""
result = 0
other_idx = 0
qarg_idx = 0
for q in range(n):
if q in qargs:
bit = (qarg_bits >> qargs.index(q)) & 1
else:
bit = (other_bits >> other_idx) & 1
other_idx += 1
result |= (bit << q)
return result
[文档]
def partial_trace(self, qargs: List[int]) -> 'DensityMatrix':
"""
计算部分迹,保留指定量子比特
Args:
qargs: 要保留的量子比特索引
Returns:
DensityMatrix: 约化密度矩阵
"""
n = self._num_qubits
keep_qubits = sorted(qargs)
trace_qubits = sorted([q for q in range(n) if q not in keep_qubits])
if not trace_qubits:
return self.copy()
if not keep_qubits:
# 迹掉所有量子比特,返回标量
return DensityMatrix(np.array([[self.trace()]]), 0)
# 使用更直接的方法计算部分迹
# 将密度矩阵重塑为张量,然后重新排列轴
# 重塑为张量: [2]*n (行索引) + [2]*n (列索引)
# 轴顺序: 行轴 0..n-1 对应量子比特 n-1..0
# 列轴 n..2n-1 对应量子比特 n-1..0
tensor_shape = [2] * (2 * n)
rho_tensor = self._data.reshape(tensor_shape)
# 重新排列轴,使得要迹掉的量子比特的行列轴相邻
# 新顺序: [keep_row_axes] + [trace_row_axes] + [keep_col_axes] + [trace_col_axes]
keep_row_axes = [n - 1 - q for q in keep_qubits]
trace_row_axes = [n - 1 - q for q in trace_qubits]
keep_col_axes = [2*n - 1 - q for q in keep_qubits]
trace_col_axes = [2*n - 1 - q for q in trace_qubits]
# 重新排列,使得可以方便地求迹
# 目标: [keep_row] + [keep_col] + [trace_row] + [trace_col]
# 然后对 trace_row 和 trace_col 求迹
n_keep = len(keep_qubits)
n_trace = len(trace_qubits)
# 转置到新顺序
new_order = keep_row_axes + keep_col_axes + trace_row_axes + trace_col_axes
rho_tensor = np.transpose(rho_tensor, new_order)
# 重塑为 [keep_dim, keep_dim, trace_dim, trace_dim]
keep_dim = 2 ** n_keep
trace_dim = 2 ** n_trace
rho_tensor = rho_tensor.reshape(keep_dim, keep_dim, trace_dim, trace_dim)
# 对 trace 维度求迹: sum over diagonal elements
# 即 result[i,j] = sum_k rho_tensor[i,j,k,k]
result = np.trace(rho_tensor, axis1=2, axis2=3)
return DensityMatrix(result, n_keep)
[文档]
def fidelity(self, other: Union['DensityMatrix', Statevector]) -> float:
"""
计算保真度
F(ρ, σ) = (Tr√(√ρ σ √ρ))²
F(ρ, |ψ⟩) = ⟨ψ|ρ|ψ⟩
"""
if isinstance(other, Statevector):
return np.real(np.vdot(other.data, self._data @ other.data))
else:
# 一般情况
sqrt_rho = self._matrix_sqrt(self._data)
product = sqrt_rho @ other._data @ sqrt_rho
sqrt_product = self._matrix_sqrt(product)
return np.real(np.trace(sqrt_product)) ** 2
def _matrix_sqrt(self, A: np.ndarray) -> np.ndarray:
"""计算矩阵平方根"""
eigenvalues, eigenvectors = np.linalg.eigh(A)
eigenvalues = np.maximum(eigenvalues, 0) # 数值稳定性
sqrt_eigenvalues = np.sqrt(eigenvalues)
return eigenvectors @ np.diag(sqrt_eigenvalues) @ eigenvectors.conj().T
[文档]
def von_neumann_entropy(self) -> float:
"""计算冯诺依曼熵 S(ρ) = -Tr(ρ log ρ)"""
eigenvalues = np.linalg.eigvalsh(self._data)
eigenvalues = eigenvalues[eigenvalues > 1e-15]
return -np.sum(eigenvalues * np.log2(eigenvalues))
# ==================== 特殊方法 ====================
def __repr__(self) -> str:
return f"DensityMatrix(shape={self._data.shape}, num_qubits={self._num_qubits})"
def __eq__(self, other) -> bool:
if not isinstance(other, DensityMatrix):
return False
return np.allclose(self._data, other._data)