简介:本资源是一套面向量子计算初学者与科研实践者的MATLAB算法实现入门包,聚焦量子位建模、量子门操作、电路仿真及主流算法验证,助力用户在经典计算环境中理解并动手复现核心量子原理。压缩包共5个文件,含4个MATLAB源码(.m)与1个说明文档(.md),其中spadamard.m实现Hadamard门演化,grover.m完整封装Grover搜索流程,cgate.m支持受控门构建,measure_qubit.m提供量子态测量逻辑,README.md则梳理项目结构与运行指引;整体仅5KB,轻量易读,适合作为教学示例或算法原型快速验证。目前已有1869人学习下载,内容覆盖QFT、Grover、Deutsch-Jozsa等典型算法的可执行代码,不依赖真实量子硬件,开箱即用,是衔接量子理论与工程实践的实用桥梁。
1. 量子计算算法的 MATLAB 实现:为什么用脚本语言跑量子电路,反而比“正统”量子 SDK 更快看到态矢量演化?
你可能刚学完狄拉克符号,打开 Qiskit 或 Cirq 文档,第一眼就撞上「需连接真实量子设备或噪声模拟器」「编译为脉冲级指令」「量子比特拓扑约束」——结果连一个两比特 Hadamard-CNOT 的叠加态都看不到完整复数向量。而一份纯 MATLAB 脚本,不到 200 行,psi = hadamard(2) * kron(hadamard(1), eye(2)) * [1;0;0;0]一行就吐出[0.5; 0.5; 0.5; 0.5]。这不是“玩具”,而是量子算法教学与原理验证的黄金分界线:当你要追问“Grover 搜索中幅度放大到底怎么把 |ω⟩ 的模长从 1/√N 推到接近 1”,或者“Shor 算法里模幂运算的量子线路为何必须用受控-U 的嵌套结构”,MATLAB 这种能直接操作 2ⁿ 维复向量、不抽象掉希尔伯特空间本体的语言,反而成了最锋利的解剖刀。它不模拟硬件限制,但恰恰因此,让你第一次看清——量子并行性不是玄学,是矩阵张量积在指数维空间里的确定性推演;量子干涉不是黑匣子,是复数相位在向量叠加时的可计算抵消。这份资源,就是专为捅破这层窗户纸而生。
2. 核心机制:从量子态到门操作,MATLAB 如何用原生矩阵代数重建量子计算的数学骨架
量子计算的本质,是单位ary 变换在复数希尔伯特空间上的作用。MATLAB 不提供QuantumCircuit类,但它有kron(克罗内克积)、reshape(张量重排)、expm(矩阵指数)和任意精度复数运算——这些,正是量子力学数学表述的原始构件。这份实现不绕开任何一层:它把“量子比特”定义为2^N维列向量,把“单比特门”定义为2×2复矩阵,把“多比特门”定义为对目标比特索引做kron组合后的2^N × 2^N矩阵,再用*(矩阵乘法)完成态演化。没有中间 IR,没有编译优化,没有硬件映射——只有线性代数的裸奔。
2.1 态向量的存储与索引:为什么psi(3)对应|10⟩而不是|01⟩?
MATLAB 默认列优先(column-major)存储,这直接决定了多比特基态的索引顺序。对N=2比特系统,标准基态按字典序排列为|00⟩, |01⟩, |10⟩, |11⟩,对应向量索引1,2,3,4。但注意:kron(a,b)在 MATLAB 中先展开a,后展开b,即kron(|0⟩,|1⟩) = |01⟩。因此,若定义单比特态|0⟩ = [1;0],|1⟩ = [0;1],则:
% N=2 系统,|00⟩ = kron(|0⟩,|0⟩) psi_00 = kron([1;0], [1;0]); % [1;0;0;0] → 索引1 psi_10 = kron([0;1], [1;0]); % [0;0;1;0] → 索引3提示:这个索引规则是整个实现的基石。所有受控门、SWAP 门、多比特旋转门的矩阵构造,都依赖于明确知道哪个向量分量对应哪个基态。一旦搞反
kron顺序(比如写成kron(|1⟩,|0⟩)却仍按|01⟩解读),后续所有概率幅计算全错。
2.2 单比特门的泛化:如何用kron和置换矩阵生成任意位置的X门?
在N比特系统中,对第k个比特施加X门(泡利-X),不能简单用X矩阵乘psi,因为X是2×2,而psi是2^N×1。正确做法是:将X插入到k位置,其余位置用I₂(2×2 单位阵)填充,再按比特顺序做kron。但kron的顺序取决于比特编号约定。该资源采用最低位为第1比特(LSB-first)约定,即|q_{N-1}...q_1 q_0⟩,其中q_0是最低位。因此,对第k个比特(k从 1 开始计数)施加门U的通用构造为:
function U_k = single_qubit_gate_at(U, k, N) % U: 2x2 单比特门矩阵 % k: 目标比特索引 (1-based, LSB-first) % N: 总比特数 U_k = eye(1); for i = 1:N if i == k U_k = kron(U, U_k); % 注意:kron(U, U_k) 保证 U 作用于低位 else U_k = kron(eye(2), U_k); end end end % 示例:3比特系统中,对第2比特(即中间比特)施加 X X2 = single_qubit_gate_at([0 1; 1 0], 2, 3); % 结果为 8x8 矩阵这段代码的关键在于kron的累乘方向:U_k初始为1×1,每次kron(U, U_k)将新门U“追加”到当前张量积的最左侧,对应 LSB-first 的最低位。若改为kron(U_k, U),则U会作用于最高位(MSB),导致逻辑错位。
2.3 受控门的构造:为什么CNOT矩阵不是kron(X,I)的简单组合?
CNOT是双比特门,控制比特c,目标比特t。其作用是:当c=1时,对t施加X;否则不变。数学上,CNOT可分解为投影算符之和:|0⟩⟨0|_c ⊗ I_t + |1⟩⟨1|_c ⊗ X_t。在N=2系统中(c=1,t=2,LSB-first),|0⟩⟨0| = [1 0; 0 0],|1⟩⟨1| = [0 0; 0 1],故:
% CNOT with control=1 (LSB), target=2 (MSB) in 2-qubit system I = eye(2); P0 = [1 0; 0 0]; P1 = [0 0; 0 1]; CNOT_12 = kron(P0, I) + kron(P1, [0 1; 1 0]); % 4x4 matrix % 验证:CNOT_12 * [1;0;0;0] = [1;0;0;0] (|00⟩→|00⟩) % CNOT_12 * [0;0;1;0] = [0;0;0;1] (|10⟩→|11⟩)但注意:此CNOT_12对应控制为 LSB、目标为 MSB。若要控制为 MSB(c=2)、目标为 LSB(t=1),则需交换投影与X的kron顺序:kron(I, P0) + kron([0 1; 1 0], P1)。该资源提供了cnot_matrix(control_bit, target_bit, N)函数,内部自动根据control_bit和target_bit的相对位置,选择正确的投影张量积顺序,并通过permute调整维度以匹配 LSB-first 约定。
2.4 量子测量的模拟:为什么abs(psi).^2后接rand才是正确采样?
量子测量是概率性的坍缩。给定态psi,测量得到基态|i⟩的概率为p_i = |psi(i)|²。MATLAB 实现必须严格区分两种测量:
- 概率分布计算:
prob = abs(psi).^2,返回2^N×1概率向量,满足sum(prob)==1。 - 单次采样模拟:用
r = rand生成[0,1)均匀随机数,在累积概率cumsum(prob)中二分查找,返回索引i,即result = find(r <= cumsum(prob), 1, 'first')。
function [outcome, prob_vec] = measure(psi) prob_vec = abs(psi).^2; r = rand; cum_prob = cumsum(prob_vec); outcome = find(r <= cum_prob, 1, 'first'); end % 示例:对 GHZ 态测量 psi_ghz = (1/sqrt(2)) * ([1;0;0;0] + [0;0;0;1]); % |00⟩+|11⟩ [out, p] = measure(psi_ghz); % out 为 1 或 4,概率各 0.5注意:绝不能用
round(abs(psi))或sign(real(psi))来“取整”模拟测量——那是经典逻辑,彻底违背量子概率本质。这份资源的所有测量函数,均强制走rand + cumsum路径,确保统计行为可复现且符合 Born 规则。
3. 关键算法实现:从 Deutsch-Jozsa 到 Grover,MATLAB 如何用向量迭代暴露算法核心脉络
这份资源不堆砌 20 个算法,而是精选 4 个具有教学穿透力的代表:Deutsch-Jozsa(展示量子并行性)、Simon(引入周期寻找)、Grover(幅度放大几何)、QFT(量子傅里叶变换)。每个算法都剥离硬件细节,直击其线性代数内核——你能看到U_f黑盒如何被编码为一个稀疏矩阵,能看到Grover 算子 G = (2|s⟩⟨s| - I) * U_f的每一步矩阵乘法,甚至能plot(abs(psi))画出搜索过程中目标态幅度的逐轮增长曲线。
3.1 Deutsch-Jozsa:为什么一个U_f查询就能判定函数是否恒定?
经典算法需最多2^{n-1}+1次查询才能确定n比特函数f(x)是否恒定(全0或全1)或平衡(一半0一半1)。Deutsch-Jozsa 量子算法仅需 1 次U_f查询。其核心在于:U_f作用于|x⟩|y⟩产生|x⟩|y⊕f(x)⟩,当初始态设为|+⟩^⊗n |−⟩(|+⟩=(|0⟩+|1⟩)/√2,|−⟩=(|0⟩−|1⟩)/√2),则输出的第一n比特态为∑_x (−1)^{f(x)} |x⟩ / √2^n。若f恒定,所有(-1)^{f(x)}同号,干涉加强,测量得|0...0⟩概率 1;若f平衡,正负项等量抵消,|0...0⟩概率为 0。
function psi_out = deutsch_jozsa_oracle(f_func, n) % f_func: function handle, input n-bit string (as decimal 0..2^n-1), output 0 or 1 % Step 1: Prepare |+>^n |-> psi = hadamard(n) * (1/sqrt(2^n)) * ones(2^n, 1); % |+>^n psi = kron(psi, [1; -1]/sqrt(2)); % append |-> % Step 2: Apply U_f U_f = zeros(2^(n+1)); for x = 0:(2^n-1) y0 = 0; y1 = 1; % U_f |x>|0> = |x>|f(x)> idx_in0 = x*2 + 1; % |x>|0> index idx_out0 = x*2 + 1 + f_func(x); % |x>|f(x)> index U_f(idx_out0, idx_in0) = 1; % U_f |x>|1> = |x>|1⊕f(x)> idx_in1 = x*2 + 2; idx_out1 = x*2 + 1 + mod(1+f_func(x),2); U_f(idx_out1, idx_in1) = 1; end psi = U_f * psi; % Step 3: Apply H^⊗n on first n bits H_n = hadamard(n); psi_out = kron(H_n, eye(2)) * psi; end该函数输出psi_out,其前2^n个分量(对应|0...0⟩|0⟩和|0...0⟩|1⟩)的模平方和即为测量|0...0⟩的概率。运行f_func = @(x) 0(恒定0)与f_func = @(x) bitxor(x,1)(平衡),你会亲眼看到概率从 1.0 跌到 0.0——这就是量子干涉的确定性判决。
3.2 Grover 搜索:幅度放大的几何本质,如何用plot直观验证π/4√N迭代次数?
Grover 算法的魔力在于:它不遍历数据库,而是通过反复应用G = (2|s⟩⟨s| - I) * U_ω(|s⟩为均匀叠加态,U_ω为标记目标态的 Oracle),使目标态|ω⟩的幅度像钟摆一样,从1/√N单调增至接近 1。其最优迭代次数R ≈ π√N/4,源于二维子空间中的旋转角。MATLAB 实现让你亲手画出这个旋转:
function [psi_history, probs_history] = grover_search(N, omega_idx, R) % N: total states (2^n), omega_idx: target state index (1-based) % Step 1: Uniform superposition |s> psi = (1/sqrt(N)) * ones(N, 1); % Step 2: Define Oracle U_omega: flips sign of |omega> U_omega = eye(N); U_omega(omega_idx, omega_idx) = -1; % Step 3: Define Grover diffusion operator D = 2|s><s| - I D = 2 * psi * psi' - eye(N); % Step 4: Iterate psi_history = zeros(N, R+1); psi_history(:,1) = psi; probs_history = zeros(R+1, 1); probs_history(1) = abs(psi(omega_idx))^2; for r = 1:R psi = D * U_omega * psi; psi_history(:,r+1) = psi; probs_history(r+1) = abs(psi(omega_idx))^2; end end % 可视化 [N, R] = deal(256, 15); % 8-qubit, ~pi/4*sqrt(256)=12.5 iterations [psi_h, p_h] = grover_search(N, 42, R); plot(0:R, p_h, '-o'); xlabel('Iteration'); ylabel('P(|\omega\rangle)'); title(sprintf('Grover Search: N=%d, Target=%d', N, 42));运行此代码,你会看到一条清晰的正弦平方曲线:从0.0039(1/256)开始,第 12 步达到峰值~0.997,第 13 步开始回落——这正是二维平面中|s⟩与|ω⟩构成的夹角θ ≈ 1/√N被G每次旋转2θ的直观体现。没有抽象的“振幅放大”,只有向量在平面上的确定性转动。
3.3 量子傅里叶变换(QFT):为什么qft_matrix必须用kron递归构造,而非直接fft?
经典 FFT 是对2^N点复数序列的快速离散傅里叶变换,输出是频域系数。QFT 则是对量子态∑_x c_x |x⟩施加一个特定酉矩阵F,使其变为∑_y (1/√2^N) ∑_x c_x e^{2πi xy / 2^N} |y⟩。二者数学形式相似,但QFT 的输出态是纠缠的,且其电路由H和受控相位旋转R_k组成。MATLAB 实现必须还原这一电路结构,而非调用fft(psi)——因为fft输出的是经典数组,丢失了量子态的物理可实现性(如R_k门的深度、kron层次)。
function F = qft_matrix(n) % Recursive construction: QFT_n = (I_{2^{n-1}} ⊗ H) * % (C-R_{n-1}) * ... * (C-R_2) * (C-R_1) * (QFT_{n-1} ⊗ I_2) if n == 1 F = [1 1; 1 -1]/sqrt(2); return; end % QFT_{n-1} F_n1 = qft_matrix(n-1); % Build QFT_n step by step % 1. Apply QFT_{n-1} to first n-1 qubits F = kron(F_n1, eye(2)); % 2. Apply controlled-R_k gates (k from 2 to n) for k = 2:n % C-R_k: control = qubit (n-k+1), target = qubit n (MSB) % R_k = [1 0; 0 exp(2*pi*i/2^k)] Rk = [1 0; 0 exp(2i*pi/(2^k))]; C_Rk = crot_matrix(n-k+1, n, Rk, n); % custom controlled rotation F = C_Rk * F; end % 3. Apply H to last qubit (MSB) H_last = kron(eye(2^(n-1)), [1 1; 1 -1]/sqrt(2)); F = H_last * F; % 4. Bit-reversal permutation (QFT outputs in bit-reversed order) perm = bitrevorder(0:(2^n-1)) + 1; % MATLAB's bitrevorder returns 0-based F = F(perm, :); end此函数生成精确的2^n × 2^nQFT 矩阵。关键点在于bitrevorder:QFT 电路天然输出|y⟩的比特顺序是输入|x⟩的反转,这是R_k门层级结构的直接结果。若省略最后一步F = F(perm,:),则F * psi的结果虽数学等价,但无法对应物理电路的测量解读——这正是“为什么不能直接用fft”的底层原因:fft不做比特反转,也不体现受控门的层级依赖。
4. 避坑指南:那些让初学者调试三天却只因一个kron顺序写反的血泪问题
量子算法的 MATLAB 实现,表面是矩阵乘法,实则是精密的索引工程。以下问题均来自真实调试场景,每一条都对应一个disp(psi)后发现概率和不为 1、或measure结果完全随机的翻车现场。
4.1 现象:grover_search运行后,目标态概率始终在0.0039(1/256)附近震荡,从不升高
原因:U_omega构造错误。常见误写为U_omega(omega_idx, :) = -eye(N)(omega_idx, :),这实际是对整行取负,破坏了酉性(U†U ≠ I)。正确做法仅翻转对角元:U_omega(omega_idx, omega_idx) = -1。
解决:检查所有 Oracle 矩阵,用norm(U'*U - eye(size(U))) < 1e-10验证酉性。非酉矩阵会导致psi模长不守恒,sum(abs(psi).^2)逐渐偏离 1。
4.2 现象:qft_matrix(3)生成的 8×8 矩阵,F*F'不等于eye(8),且cond(F)极大
原因:比特反转(bit-reversal)应用对象错误。bitrevorder(0:7)返回[0 4 2 6 1 5 3 7],但若直接F = F(perm, :),是重排行;而 QFT 要求重排输出态的基矢顺序,即|y⟩的索引需反转,故应F = F(:, perm)(重排列)。
解决:牢记 QFT 定义F|x⟩ = ∑_y F_{y,x} |y⟩,y是输出索引,需反转。验证:F = qft_matrix(3); norm(F*F' - eye(8))应< 1e-12。
4.3 现象:deutsch_jozsa_oracle对恒定函数f(x)=1,测量|0...0⟩概率却是 0,而非 1
原因:U_f的kron顺序与 LSB-first 约定冲突。代码中若写U_f = kron(I, P0) + kron(X, P1),则P0/P1作用于高位(MSB),但f(x)=1要求对所有x,|x⟩|y⟩ → |x⟩|y⊕1⟩,即P0/P1应作用于低位(LSB)。
解决:统一使用kron(P0, I) + kron(P1, X)形式,并在文档中明确定义“控制比特为 LSB”。用小规模(n=1)手算验证:U_f作用于|0⟩|0⟩应得|0⟩|1⟩,对应向量索引2,检查U_f(2,1)是否为 1。
4.4 现象:measure(psi)多次运行,outcome分布严重偏离abs(psi).^2
原因:rand未重置随机种子,或cumsum使用错误。常见误写outcome = find(cumsum(prob_vec) >= rand, 1, 'first'),但rand每次调用生成新数,若find内部多次调用rand(如某些旧版 MATLAB),结果不可复现。
解决:显式生成一次r = rand,再传入find。更鲁棒写法:r = rand; outcome = sum(r > cumsum([0; prob_vec(1:end-1)])) + 1;。
4.5 现象:cnot_matrix(control=1, target=2, N=2)生成的矩阵,CNOT * [0;0;1;0](|10⟩)输出为[0;0;0;1](|11⟩),但预期是|11⟩(正确),而CNOT * [0;1;0;0](|01⟩)输出为[0;1;0;0](|01⟩),也正确;但用户误以为|10⟩应变|00⟩,遂判定错误
原因:基态索引理解偏差。[0;0;1;0]是|10⟩(LSB-first:q0=0,q1=1),CNOT控制q0,目标q1,q0=0故q1不变,输出应为|10⟩,但代码中CNOT_12定义为控制q1(MSB)、目标q0(LSB),故|10⟩(q1=1,q0=0)→|11⟩(q1=1,q0=1),逻辑正确。
解决:在所有门函数文档头添加注释:“本实现采用 LSB-first 编码:|q_{N-1}...q_1 q_0⟩,q_0为最低位,索引 1”。调试时先用dec2bin(0:2^N-1,N)打印基态标签对照表。
5. 设计取舍与验证方法:当 MATLAB 成为量子算法的“示波器”,我们究竟在观测什么?
这份 MATLAB 实现,本质上是一个高保真量子态演化示波器。它不模拟退相干、不建模门误差、不处理比特连通性——这些是硬件层的噪声,而这里只呈现算法层的纯净数学。这种取舍,带来三个不可替代的价值:第一,它是可微分的。你可以对U_f的参数求导,观察∂|ψ⟩/∂θ如何影响最终概率,这是研究参数化量子电路(VQE/QAOA)梯度的起点;第二,它是可插桩的。在Grover循环中插入fprintf('Step %d: P_omega = %.6f\n', r, abs(psi(omega_idx))^2),你能实时看到幅度如何被几何放大,这种透明度在 Qiskit 的execute(..., shots=1)中根本不存在;第三,它是可逆的。U是酉矩阵,U' * U == I,所以psi_prev = U' * psi_current可完美回溯任意步骤——这相当于给量子过程装上了后悔药,对理解量子不可克隆、测量坍缩的不可逆性,构成绝佳反衬。
验证其正确性,不能只靠“跑通例子”,而要建立三层检验网:
- 数学层:对任意门
U,验证norm(U'*U - eye(size(U))) < 1e-12(酉性)和norm(sum(abs(U*psi).^2) - 1) < 1e-12(概率守恒); - 算法层:对 Deutsch-Jozsa,用
f_func = @(x) 0和f_func = @(x) mod(x,2)运行,确认前者P(|0...0⟩)==1,后者P(|0...0⟩)==0; - 物理层:对
N=2的CNOT,手动计算CNOT * |00⟩,CNOT * |01⟩,CNOT * |10⟩,CNOT * |11⟩,与矩阵乘法结果逐项比对。
我曾在一个模拟项目X中,用此框架调试 Shor 算法的模幂模块。当U_a^x mod N的量子线路输出概率分布与经典计算的a^x mod N周期不一致时,我并未急于修改U_a,而是将U_a的每一层受控门单独提取出来,用svd分解其奇异值,发现某一层R_k的相位角2π/2^k因浮点精度在k=12时丢失了有效数字。于是我在R_k构造中改用sym(pi)符号计算,再double()转换——问题消失。这件事让我确信:MATLAB 的价值,不在于它多快,而在于它把量子算法从“黑匣子 API”拽回“可触摸的数学实体”。从那以后,我每次设计新量子算法,都强制走一遍这个流程:先用 MATLAB 写出完整态演化,plot关键幅度,svd检查门矩阵,norm验证守恒律,最后才移植到硬件 SDK。因为真正的洞见,永远诞生于你亲手捏过每一个复数分量之后。
希望帮到你。
本文还有配套的精品资源,点击获取