逐步深入理解 GPT 架构




microgpt 教程

本文翻译自 https://karpathy.github.io/2026/02/12/microgpt/


仅用200行纯Python代码(零依赖),即可训练并推理GPT——这已经是我能压缩到的极致了。

2026-02-12 07:00:00


这是我的新艺术项目 microgpt 的简要指南。一个200行的纯Python单文件,无任何依赖,能够训练和推理GPT。该文件包含了所需的完整算法内容:文档数据集、分词器、自动微分引擎、GPT-2风格的神经网络架构、Adam优化器、训练循环和推理循环。其他一切都只是效率问题。这个脚本是多个项目(micrograd、makemore、nanogpt等)以及十年执着于将LLM简化到其本质的结晶。

相关链接:

以下是我带领感兴趣的读者逐步讲解代码的指南。

数据集

大型语言模型的燃料是一个文本数据流,可选地分成多个文档集。在生产级应用中,每个文档可能是一个互联网网页,但对于microgpt,我们使用一个更简单的例子——32,000个名字,每行一个:

In [1]:
import os       # os.path.exists
import random   # random.seed, random.choices, random.gauss, random.shuffle
random.seed(42) # 在混沌中建立秩序

# 让输入数据集 `docs`:list[str] 文档列表(例如名字数据集)
if not os.path.exists('input.txt'):
    import urllib.request
    names_url = 'https://raw.githubusercontent.com/karpathy/makemore/master/names.txt'
    urllib.request.urlretrieve(names_url, 'input.txt')
docs = [l.strip() for l in open('input.txt').read().strip().split('\n') if l.strip()] # list[str] 文档列表
random.shuffle(docs)
print(f"num docs: {len(docs)}")
num docs: 32033

数据集看起来像这样,每个名字是一个文档:

emma
olivia
ava
isabella
sophia
charlotte
mia
amelia
harper
... (大约32,000个名字)

模型的目标是学习数据中的模式,然后生成具有相似统计特征的新文档。提前剧透:脚本运行结束时,我们的模型将生成(『幻觉』!)新的、看似合理的名字。抢先看:

sample  1: kamon
sample  2: ann
sample  3: karai
sample  4: jaire
sample  5: vialan
sample  6: karia
sample  7: yeran
sample  8: anna
sample  9: areli
sample 10: kaina
sample 11: konna
sample 12: keylen
sample 13: liole
sample 14: alerin
sample 15: earan
sample 16: lenne
sample 17: kana
sample 18: lara
sample 19: alela
sample 20: anton

看起来平平无奇,但从ChatGPT这样的模型角度来看,你与它的对话只是一个看起来有点特别的『文档』。当你用提示初始化文档时,从模型的角度来看,它的响应只是文档的统计式补全。

分词器

在后台,神经网络处理数字而不是字符,所以我们需要一种方法将文本转换为整数标记ID序列,反之亦然。像tiktoken(由GPT-4使用)这样的生产分词器为了效率在字符块上操作,但最简单的可能的分词器只是为数据集中的每个唯一字符分配一个整数:

In [2]:
# 让有一个分词器将字符串翻译为离散符号并反向翻译
uchars = sorted(set(''.join(docs))) # 数据集中的唯一字符变成标记ID 0..n-1
BOS = len(uchars) # 特殊“序列开始”(BOS)标记的标记ID
vocab_size = len(uchars) + 1 # 总的唯一标记数,+1是BOS
print(f"vocab size: {vocab_size}")
vocab size: 27

在上面的代码中,我们收集数据集中所有唯一字符(即所有小写字母a-z),排序后每个字母按其索引获得一个ID。请注意,整数值本身没有任何意义;每个标记只是一个离散符号。代替0、1、2,它们完全可以是不同的emoji。此外,我们还创建了一个特殊标记BOS(序列开始),它充当分隔符:告诉模型『一个新文档在这里开始/结束』。训练期间,每个文档两侧都被BOS包裹:[BOS, e, m, m, a, BOS]。模型学会BOS启动一个新名字,另一个BOS结束它。因此,最终词汇表大小为27(26个可能的小写字母a-z加上1个BOS标记)。

自动微分

训练神经网络需要梯度:对于模型中的每个参数,我们需要知道『如果我把这个数稍微调高一点,损失会上升还是下降,幅度多大?』。计算图有多个输入(模型参数和输入标记),但汇聚到单个标量输出:损失(我们将在下面精确定义损失是什么)。反向传播从该单个输出开始,沿着图反向工作,计算损失相对于每个输入的梯度。它依赖微积分中的链式法则。在生产中,像PyTorch这样的库会自动处理。这里,我们在一个叫Value的类中从头实现:

In [3]:
import math     # math.log, math.exp

class Value:
    __slots__ = ('data', 'grad', '_children', '_local_grads')

    def __init__(self, data, children=(), local_grads=()):
        self.data = data                # 在前向传播中计算的此节点的标量值
        self.grad = 0                   # 损失相对于此节点的导数,在反向传播中计算
        self._children = children       # 计算图中此节点的子节点
        self._local_grads = local_grads # 此节点相对于其子节点的局部导数

    def __add__(self, other):
        other = other if isinstance(other, Value) else Value(other)
        return Value(self.data + other.data, (self, other), (1, 1))

    def __mul__(self, other):
        other = other if isinstance(other, Value) else Value(other)
        return Value(self.data * other.data, (self, other), (other.data, self.data))

    def __pow__(self, other): return Value(self.data**other, (self,), (other * self.data**(other-1),))
    def log(self): return Value(math.log(self.data), (self,), (1/self.data,))
    def exp(self): return Value(math.exp(self.data), (self,), (math.exp(self.data),))
    def relu(self): return Value(max(0, self.data), (self,), (float(self.data > 0),))
    def __neg__(self): return self * -1
    def __radd__(self, other): return self + other
    def __sub__(self, other): return self + (-other)
    def __rsub__(self, other): return other + (-self)
    def __rmul__(self, other): return self * other
    def __truediv__(self, other): return self * other**-1
    def __rtruediv__(self, other): return other * self**-1

    def backward(self):
        topo = []
        visited = set()
        def build_topo(v):
            if v not in visited:
                visited.add(v)
                for child in v._children:
                    build_topo(child)
                topo.append(v)
        build_topo(self)
        self.grad = 1
        for v in reversed(topo):
            for child, local_grad in zip(v._children, v._local_grads):
                child.grad += local_grad * v.grad

我意识到这是数学和算法上最密集的部分,我为此录制了一个2.5小时的视频:micrograd视频。简而言之,Value包装单个标量数字(.data)并跟踪它是如何被计算出来的。把每个运算想象成一块小乐高积木:它接受一些输入,产生一个输出(前向传播),并且知道它的输出相对于每个输入会如何变化(局部梯度)。这就是自动微分需要每块积木提供的全部信息。其他一切只是链式法则——把积木串在一起。

每次你对Value对象进行数学运算(加法、乘法等),结果是一个新的Value,它记住其输入(_children)和该运算的局部导数(_local_grads)。例如,__mul__记录$\frac{\partial(a \cdot b)}{\partial a} = b$和$\frac{\partial(a \cdot b)}{\partial b} = a$。完整的乐高积木集:

操作 前向 局部梯度
a + b $$a + b$$ $$\frac{\partial}{\partial a} = 1, \quad \frac{\partial}{\partial b} = 1$$
a * b $$a \cdot b$$ $$\frac{\partial}{\partial a} = b, \quad \frac{\partial}{\partial b} = a$$
a ** n $$a^n$$ $$\frac{\partial}{\partial a} = n \cdot a^{n-1}$$
log(a) $$\ln(a)$$ $$\frac{\partial}{\partial a} = \frac{1}{a}$$
exp(a) $$e^a$$ $$\frac{\partial}{\partial a} = e^a$$
relu(a) $$\max(0, a)$$ $$\frac{\partial}{\partial a} = \mathbf{1}_{a > 0}$$

backward()方法以反向拓扑顺序遍历此图(从损失开始,到参数结束),在每一步应用链式法则。如果损失是$L$,节点$v$有一个子节点$c$,局部梯度为$\frac{\partial v}{\partial c}$,那么:

$$\frac{\partial L}{\partial c} \mathrel{+}= \frac{\partial v}{\partial c} \cdot \frac{\partial L}{\partial v}$$

如果你对微积分不太熟悉,这看起来可能有点吓人,但这本质上只是以直观的方式将两个数相乘。可以这样理解:『如果汽车的速度是自行车的两倍,自行车的速度是步行人的四倍,那么汽车的速度就是步行人的2 × 4 = 8倍。』链式法则就是同样的思想:你沿着路径乘以变化率。

我们从在损失节点设置self.grad = 1开始,因为$\frac{\partial L}{\partial L} = 1$:损失相对于自身的变化率就是1。从那里开始,链式法则沿着回到参数的每条路径乘以局部梯度。

注意+=(累加,不是赋值)。当一个值在图中的多个地方被使用时(即图有分支),梯度沿着每条分支独立流回,必须求和。这是多变量链式法则的结果:如果$c$通过多条路径对$L$有贡献,总导数是来自每条路径的贡献之和。

backward()完成后,图中的每个Value都有一个.grad包含$\frac{\partial L}{\partial v}$,这告诉我们微调该值会使最终损失如何变化。

以下是一个具体例子。注意a被使用了两次(图有分支),所以它的梯度是两条路径之和:

a = Value(2.0)
b = Value(3.0)
c = a * b       # c = 6.0
L = c + a       # L = 8.0
L.backward()
print(a.grad)   # 4.0 (dL/da = b + 1 = 3 + 1, 经由两条路径)
print(b.grad)   # 2.0 (dL/db = a = 2)

这正是PyTorch的.backward()给出的结果:

import torch
a = torch.tensor(2.0, requires_grad=True)
b = torch.tensor(3.0, requires_grad=True)
c = a * b
L = c + a
L.backward()
print(a.grad)   # tensor(4.)
print(b.grad)   # tensor(2.)

这和PyTorch的loss.backward()运行的是相同算法——只是在标量上而非张量(标量数组)上运行——算法上完全一致,显著更小更简单,但当然效率低得多。

让我们详细说明.backward()的计算结果。自动微分计算出,如果L = a*b + a,且a=2b=3,那么a.grad = 4.0告诉我们aL的局部影响。如果你微调输入aL会朝什么方向变化?这里La的导数是4.0,意味着如果我们增加a一小点(比如0.001),L将增加大约4倍(0.004)。同样,b.grad = 2.0意味着同样的微调b会增加L大约2倍(0.002)。换句话说,这些梯度告诉我们每个输入对最终输出(损失)的影响方向(正或负取决于符号)和陡峭程度(大小)。这让我们能够迭代地微调神经网络参数来降低损失,从而改进模型的预测。

参数

参数是模型的知识。它们是一个大集合的浮点数(为自动微分用Value包装),从随机开始,在训练期间迭代优化。每个参数的确切角色将在我们下面定义模型架构后更有意义,但现在我们只需要初始化它们:

In [4]:
n_embd = 16     # 嵌入维度
n_head = 4      # 注意力头数
n_layer = 1     # 层数
block_size = 16 # 最大序列长度
head_dim = n_embd // n_head # 每个头的维度
matrix = lambda nout, nin, std=0.08: [[Value(random.gauss(0, std)) for _ in range(nin)] for _ in range(nout)]
state_dict = {'wte': matrix(vocab_size, n_embd), 'wpe': matrix(block_size, n_embd), 'lm_head': matrix(vocab_size, n_embd)}
for i in range(n_layer):
    state_dict[f'layer{i}.attn_wq'] = matrix(n_embd, n_embd)
    state_dict[f'layer{i}.attn_wk'] = matrix(n_embd, n_embd)
    state_dict[f'layer{i}.attn_wv'] = matrix(n_embd, n_embd)
    state_dict[f'layer{i}.attn_wo'] = matrix(n_embd, n_embd)
    state_dict[f'layer{i}.mlp_fc1'] = matrix(4 * n_embd, n_embd)
    state_dict[f'layer{i}.mlp_fc2'] = matrix(n_embd, 4 * n_embd)
params = [p for mat in state_dict.values() for row in mat for p in row]
print(f"num params: {len(params)}")
num params: 4192

每个参数从高斯分布中抽取的小随机数初始化。state_dict将它们组织成命名矩阵(借用PyTorch的术语):嵌入表、注意力权重、MLP权重和最终输出投影。我们还将所有参数展平为单个列表params,以便优化器稍后遍历它们。在我们的小模型中,总共4,192个参数。GPT-2有16亿,现代LLM有数千亿。

架构

模型架构是一个无状态函数:它接收一个标记、一个位置、参数以及来自前面位置的缓存键/值,并返回logits(分数),表示模型认为接下来应该出现哪个标记。我们遵循GPT-2的设计,做了一些简化:用RMSNorm代替LayerNorm,无偏置,用ReLU代替GeLU。首先是三个小辅助函数:

In [5]:
def linear(x, w):
    return [sum(wi * xi for wi, xi in zip(wo, x)) for wo in w]

linear是矩阵-向量乘法。它接收向量x和权重矩阵w,计算w每行的点积。这是神经网络的基本构建块:一个可学习的线性变换。

In [6]:
def softmax(logits):
    max_val = max(val.data for val in logits)
    exps = [(val - max_val).exp() for val in logits]
    total = sum(exps)
    return [e / total for e in exps]

softmax将原始分数向量(logits,范围从$-\infty$到$+\infty$)转换为概率分布:转换后所有值在$[0, 1]$范围内且求和为1。我们先减去最大值以保证数值稳定性(数学上不改变结果,但防止exp溢出)。

In [7]:
def rmsnorm(x):
    ms = sum(xi * xi for xi in x) / len(x)
    scale = (ms + 1e-5) ** -0.5
    return [xi * scale for xi in x]

rmsnorm(均方根归一化)将向量重新缩放,使其值的均方根为1。这防止激活值在网络中流动时膨胀或缩小,从而稳定训练。它是原始GPT-2中使用的LayerNorm的更简单变体。

现在看模型本身:

In [ ]:
def gpt(token_id, pos_id, keys, values):
    tok_emb = state_dict['wte'][token_id] # 标记嵌入
    pos_emb = state_dict['wpe'][pos_id] # 位置嵌入
    # 隐藏状态:包含 n_embd 个标量 Value 对象,代表当前位置的上下文信息
    x = [t + p for t, p in zip(tok_emb, pos_emb)] # 联合标记和位置嵌入
    x = rmsnorm(x)

    for li in range(n_layer):
        # 1) 多头注意力块
        x_residual = x
        x = rmsnorm(x)
        # 注意力块:这是GPT相对于MLP的核心创新
        q = linear(x, state_dict[f'layer{li}.attn_wq'])
        k = linear(x, state_dict[f'layer{li}.attn_wk'])
        v = linear(x, state_dict[f'layer{li}.attn_wv'])
        # 将当前K/V加入缓存,与历史对比
        keys[li].append(k)  # 记住所有历史的key
        values[li].append(v)  # 记住所有历史的value
        x_attn = []
        # 注意力头是注意力块内部的并行处理单元
        for h in range(n_head):
            # 对每个注意力头计算
            hs = h * head_dim  # 头的起始位置
            # 切分维度:每个头处理维度的一个子集
            q_h = q[hs:hs+head_dim]  # 当前查询
            k_h = [ki[hs:hs+head_dim] for ki in keys[li]]  # 所有历史键
            v_h = [vi[hs:hs+head_dim] for vi in values[li]]  # 所有历史值
            # ⭐ 核心计算:点积匹配 + Softmax = 注意力权重
            attn_logits = [sum(q_h[j] * k_h[t][j] for j in range(head_dim)) / head_dim**0.5 for t in range(len(k_h))]
            attn_weights = softmax(attn_logits)  # 权重都 ∈ [0,1] 且求和为1
            # 加权求和历史信息
            head_out = [sum(attn_weights[t] * v_h[t][j] for t in range(len(v_h))) for j in range(head_dim)]
            x_attn.extend(head_out)  # 拼接到总输出
        # 合并所有头的输出
        x = linear(x_attn, state_dict[f'layer{li}.attn_wo'])  # 投影回去
        x = [a + b for a, b in zip(x, x_residual)]  # 残差连接
        # 2) MLP块:两层前馈网络
        x_residual = x
        x = rmsnorm(x)
        x = linear(x, state_dict[f'layer{li}.mlp_fc1'])
        x = [xi.relu() for xi in x]
        x = linear(x, state_dict[f'layer{li}.mlp_fc2'])
        x = [a + b for a, b in zip(x, x_residual)]  # 残差连接

    logits = linear(x, state_dict['lm_head'])
    return logits

该函数处理一个标记(ID为token_id)在特定位置和时刻(pos_id),以及来自之前迭代的一些上下文,这些上下文总结在keysvalues的激活中,称为KV缓存。以下是逐步讲解:

嵌入。 神经网络无法直接处理像5这样的原始标记ID。它只能处理向量(数字列表)。所以我们为每个可能的标记关联一个可学习的向量,将其作为神经签名输入。标记ID和位置ID各自从各自的嵌入表(wtewpe)中查找一行。这两个向量相加,给模型一个既编码标记是什么又编码它在序列中在哪里的表示。现代LLM通常跳过位置嵌入,改用其他基于相对位置的方案,例如RoPE。

注意力块。 当前标记被投影为三个向量:查询(Q)、键(K)和值(V)。直观地说,查询(Q)说『我在找什么?』,键(K)说『我包含什么?』,值(V)说『如果被选中,我会提供什么?』。例如,在名字『emma』中,当模型在第二个『m』并试图预测接下来会出现什么时,它可能学到一个类似『最近出现了哪些元音?』的查询。早前的『e』会有一个与这个查询很好匹配的键,因此它获得高注意力权重,它的值(关于是元音的信息)流入当前位置。键和值被追加到KV缓存中,以便之前的位置可用。每个注意力头计算其查询与所有缓存键之间的点积(按$\sqrt{d_{head}}$缩放),应用softmax获得注意力权重,然后取缓存值的加权和。所有头的输出被拼接并通过attn_wo投影。值得强调的是,注意力块是位置t处的标记唯一可以『查看』过去0..t-1中标记的确切且唯一的地方。注意力是一种标记通信机制

MLP块。 MLP是『多层感知器』的缩写,它是一个两层前馈网络:先投影到嵌入维度的4倍,应用ReLU,再投影回来。这是模型每个位置进行大部分『思考』的地方。与注意力不同,这部分计算完全局限于当前时刻t的信息。Transformer将通信(注意力)与计算(MLP)穿插。

残差连接。 注意力和MLP块都将其输出加回其输入(x = [a + b for ...])。这让梯度直接流过网络,使更深的模型可训练。

输出。 最终隐藏状态由lm_head投影到词汇表大小,为词汇表中的每个标记产生一个logit。在我们的情况下,只是27个数字。logit越高 = 模型认为对应标记更可能接下来出现。

你可能会注意到我们在训练期间使用了KV缓存,这不太寻常。人们通常将KV缓存仅与推理关联。但KV缓存在概念上始终存在,即使在训练期间也是如此。在生产实现中,它只是隐藏在高度向量化的注意力计算中,该计算同时处理序列中的所有位置。由于microgpt一次处理一个标记(无批处理维度,无并行时间步),我们显式构建KV缓存。与典型的推理设置不同(KV缓存持有分离的张量),这里缓存的键和值是计算图中的活Value节点,所以我们实际上通过它们进行反向传播。

训练循环

现在我们将所有东西串联起来。训练循环重复:(1)选择一个文档,(2)在其标记上运行模型前向传播,(3)计算损失,(4)反向传播获得梯度,(5)更新参数。

In [9]:
# 让有Adam——被祝福的优化器及其缓冲区
learning_rate, beta1, beta2, eps_adam = 0.01, 0.85, 0.99, 1e-8
m = [0.0] * len(params) # 第一矩缓冲区
v = [0.0] * len(params) # 第二矩缓冲区

# 按顺序重复
num_steps = 1000 # 训练步数
for step in range(num_steps):

    # 取单个文档,分词,用特殊BOS标记在两侧包裹
    doc = docs[step % len(docs)]
    tokens = [BOS] + [uchars.index(ch) for ch in doc] + [BOS]
    n = min(block_size, len(tokens) - 1)

    # 将标记序列通过模型前向传播,一路构建到损失的计算图。
    keys, values = [[] for _ in range(n_layer)], [[] for _ in range(n_layer)]
    losses = []
    for pos_id in range(n):
        token_id, target_id = tokens[pos_id], tokens[pos_id + 1]
        logits = gpt(token_id, pos_id, keys, values)
        probs = softmax(logits)
        loss_t = -probs[target_id].log()
        losses.append(loss_t)
    loss = (1 / n) * sum(losses) # 文档序列上的最终平均损失。愿你的损失很低。

    # 反向传播损失,计算相对于所有模型参数的梯度。
    loss.backward()

    # Adam优化器更新:根据相应梯度更新模型参数。
    lr_t = learning_rate * (1 - step / num_steps) # 线性学习率衰减
    for i, p in enumerate(params):
        m[i] = beta1 * m[i] + (1 - beta1) * p.grad
        v[i] = beta2 * v[i] + (1 - beta2) * p.grad ** 2
        m_hat = m[i] / (1 - beta1 ** (step + 1))
        v_hat = v[i] / (1 - beta2 ** (step + 1))
        p.data -= lr_t * m_hat / (v_hat ** 0.5 + eps_adam)
        p.grad = 0

    print(f"step {step+1:4d} / {num_steps:4d} | loss {loss.data:.4f}")
step    1 / 1000 | loss 3.3660
step    2 / 1000 | loss 3.4243
step    3 / 1000 | loss 3.1778
step    4 / 1000 | loss 3.0664
step    5 / 1000 | loss 3.2209
step    6 / 1000 | loss 2.9452
step    7 / 1000 | loss 3.2894
step    8 / 1000 | loss 3.3245
step    9 / 1000 | loss 2.8990
step   10 / 1000 | loss 3.2229
step   11 / 1000 | loss 2.7964
step   12 / 1000 | loss 2.9345
step   13 / 1000 | loss 3.0544
step   14 / 1000 | loss 3.0905
step   15 / 1000 | loss 3.0651
step   16 / 1000 | loss 2.7337
step   17 / 1000 | loss 2.8839
step   18 / 1000 | loss 2.8977
step   19 / 1000 | loss 2.7073
step   20 / 1000 | loss 2.7453
step   21 / 1000 | loss 3.7212
step   22 / 1000 | loss 2.8026
step   23 / 1000 | loss 2.8241
step   24 / 1000 | loss 2.0374
step   25 / 1000 | loss 3.3698
step   26 / 1000 | loss 2.9154
step   27 / 1000 | loss 3.2795
step   28 / 1000 | loss 2.9195
step   29 / 1000 | loss 2.3027
step   30 / 1000 | loss 2.2691
step   31 / 1000 | loss 2.8957
step   32 / 1000 | loss 2.9539
step   33 / 1000 | loss 2.6819
step   34 / 1000 | loss 2.1899
step   35 / 1000 | loss 3.1121
step   36 / 1000 | loss 2.7269
step   37 / 1000 | loss 2.4928
step   38 / 1000 | loss 2.9746
step   39 / 1000 | loss 2.2992
step   40 / 1000 | loss 2.8604
step   41 / 1000 | loss 2.3052
step   42 / 1000 | loss 2.5615
step   43 / 1000 | loss 2.9018
step   44 / 1000 | loss 2.4472
step   45 / 1000 | loss 2.1513
step   46 / 1000 | loss 3.0613
step   47 / 1000 | loss 2.5581
step   48 / 1000 | loss 3.0171
step   49 / 1000 | loss 2.6902
step   50 / 1000 | loss 2.4050
step   51 / 1000 | loss 3.6813
step   52 / 1000 | loss 2.8990
step   53 / 1000 | loss 3.0358
step   54 / 1000 | loss 2.2217
step   55 / 1000 | loss 2.7366
step   56 / 1000 | loss 2.2113
step   57 / 1000 | loss 2.6736
step   58 / 1000 | loss 2.4947
step   59 / 1000 | loss 2.6330
step   60 / 1000 | loss 2.9024
step   61 / 1000 | loss 2.6594
step   62 / 1000 | loss 2.4527
step   63 / 1000 | loss 2.7178
step   64 / 1000 | loss 2.8619
step   65 / 1000 | loss 2.8474
step   66 / 1000 | loss 2.8673
step   67 / 1000 | loss 2.7473
step   68 / 1000 | loss 2.5459
step   69 / 1000 | loss 2.5597
step   70 / 1000 | loss 2.8365
step   71 / 1000 | loss 3.4163
step   72 / 1000 | loss 2.5205
step   73 / 1000 | loss 2.5853
step   74 / 1000 | loss 2.4236
step   75 / 1000 | loss 2.4053
step   76 / 1000 | loss 2.7836
step   77 / 1000 | loss 2.8438
step   78 / 1000 | loss 3.0302
step   79 / 1000 | loss 2.3869
step   80 / 1000 | loss 2.3910
step   81 / 1000 | loss 2.3688
step   82 / 1000 | loss 3.1079
step   83 / 1000 | loss 2.5942
step   84 / 1000 | loss 2.1857
step   85 / 1000 | loss 2.1898
step   86 / 1000 | loss 2.4624
step   87 / 1000 | loss 2.9832
step   88 / 1000 | loss 2.7304
step   89 / 1000 | loss 2.6506
step   90 / 1000 | loss 2.5744
step   91 / 1000 | loss 2.5922
step   92 / 1000 | loss 3.1646
step   93 / 1000 | loss 2.9175
step   94 / 1000 | loss 2.9945
step   95 / 1000 | loss 2.3016
step   96 / 1000 | loss 2.4319
step   97 / 1000 | loss 2.0333
step   98 / 1000 | loss 3.3962
step   99 / 1000 | loss 2.2613
step  100 / 1000 | loss 3.3669
step  101 / 1000 | loss 2.4483
step  102 / 1000 | loss 2.2889
step  103 / 1000 | loss 2.7991
step  104 / 1000 | loss 2.6872
step  105 / 1000 | loss 2.6311
step  106 / 1000 | loss 2.4243
step  107 / 1000 | loss 2.8984
step  108 / 1000 | loss 2.2613
step  109 / 1000 | loss 2.2090
step  110 / 1000 | loss 2.5113
step  111 / 1000 | loss 2.6165
step  112 / 1000 | loss 2.6483
step  113 / 1000 | loss 2.6772
step  114 / 1000 | loss 2.2588
step  115 / 1000 | loss 2.2152
step  116 / 1000 | loss 2.8152
step  117 / 1000 | loss 2.6372
step  118 / 1000 | loss 2.0875
step  119 / 1000 | loss 2.5167
step  120 / 1000 | loss 2.6920
step  121 / 1000 | loss 2.3495
step  122 / 1000 | loss 2.2998
step  123 / 1000 | loss 2.7507
step  124 / 1000 | loss 2.5124
step  125 / 1000 | loss 3.0075
step  126 / 1000 | loss 2.2402
step  127 / 1000 | loss 2.6489
step  128 / 1000 | loss 2.2248
step  129 / 1000 | loss 2.1412
step  130 / 1000 | loss 3.0851
step  131 / 1000 | loss 2.8208
step  132 / 1000 | loss 2.0810
step  133 / 1000 | loss 2.8060
step  134 / 1000 | loss 2.7096
step  135 / 1000 | loss 2.6401
step  136 / 1000 | loss 3.1040
step  137 / 1000 | loss 2.1829
step  138 / 1000 | loss 2.2031
step  139 / 1000 | loss 2.7783
step  140 / 1000 | loss 2.5121
step  141 / 1000 | loss 3.2092
step  142 / 1000 | loss 2.3187
step  143 / 1000 | loss 3.1077
step  144 / 1000 | loss 2.2708
step  145 / 1000 | loss 2.6134
step  146 / 1000 | loss 2.5703
step  147 / 1000 | loss 2.2524
step  148 / 1000 | loss 2.3684
step  149 / 1000 | loss 2.1724
step  150 / 1000 | loss 2.5351
step  151 / 1000 | loss 2.9063
step  152 / 1000 | loss 2.4862
step  153 / 1000 | loss 2.8622
step  154 / 1000 | loss 3.1861
step  155 / 1000 | loss 2.5595
step  156 / 1000 | loss 2.8595
step  157 / 1000 | loss 3.2600
step  158 / 1000 | loss 2.0037
step  159 / 1000 | loss 3.3867
step  160 / 1000 | loss 2.1104
step  161 / 1000 | loss 2.2022
step  162 / 1000 | loss 2.6278
step  163 / 1000 | loss 2.5539
step  164 / 1000 | loss 2.4285
step  165 / 1000 | loss 2.3338
step  166 / 1000 | loss 2.6923
step  167 / 1000 | loss 2.1427
step  168 / 1000 | loss 2.5276
step  169 / 1000 | loss 3.1430
step  170 / 1000 | loss 2.5338
step  171 / 1000 | loss 2.6454
step  172 / 1000 | loss 2.3900
step  173 / 1000 | loss 2.2324
step  174 / 1000 | loss 3.0033
step  175 / 1000 | loss 2.6798
step  176 / 1000 | loss 2.5880
step  177 / 1000 | loss 3.1046
step  178 / 1000 | loss 2.3841
step  179 / 1000 | loss 1.9982
step  180 / 1000 | loss 2.3556
step  181 / 1000 | loss 2.1472
step  182 / 1000 | loss 2.0537
step  183 / 1000 | loss 1.9403
step  184 / 1000 | loss 3.3390
step  185 / 1000 | loss 2.1482
step  186 / 1000 | loss 2.4919
step  187 / 1000 | loss 2.4610
step  188 / 1000 | loss 2.4055
step  189 / 1000 | loss 1.9792
step  190 / 1000 | loss 2.6377
step  191 / 1000 | loss 1.7000
step  192 / 1000 | loss 2.4035
step  193 / 1000 | loss 2.2961
step  194 / 1000 | loss 2.8886
step  195 / 1000 | loss 2.8026
step  196 / 1000 | loss 2.4264
step  197 / 1000 | loss 2.3991
step  198 / 1000 | loss 3.0697
step  199 / 1000 | loss 2.5300
step  200 / 1000 | loss 2.3097
step  201 / 1000 | loss 2.4874
step  202 / 1000 | loss 2.5649
step  203 / 1000 | loss 2.1233
step  204 / 1000 | loss 1.8898
step  205 / 1000 | loss 2.6302
step  206 / 1000 | loss 3.1559
step  207 / 1000 | loss 2.8998
step  208 / 1000 | loss 2.1443
step  209 / 1000 | loss 2.2206
step  210 / 1000 | loss 2.5670
step  211 / 1000 | loss 2.0186
step  212 / 1000 | loss 2.3012
step  213 / 1000 | loss 3.8427
step  214 / 1000 | loss 2.2129
step  215 / 1000 | loss 2.4124
step  216 / 1000 | loss 2.5136
step  217 / 1000 | loss 2.3378
step  218 / 1000 | loss 2.5365
step  219 / 1000 | loss 2.3739
step  220 / 1000 | loss 2.4205
step  221 / 1000 | loss 3.0695
step  222 / 1000 | loss 2.3135
step  223 / 1000 | loss 2.1625
step  224 / 1000 | loss 2.3273
step  225 / 1000 | loss 2.2527
step  226 / 1000 | loss 2.4193
step  227 / 1000 | loss 2.4528
step  228 / 1000 | loss 2.5524
step  229 / 1000 | loss 3.0859
step  230 / 1000 | loss 1.7900
step  231 / 1000 | loss 3.1017
step  232 / 1000 | loss 2.4001
step  233 / 1000 | loss 2.3035
step  234 / 1000 | loss 2.7662
step  235 / 1000 | loss 2.0570
step  236 / 1000 | loss 2.7383
step  237 / 1000 | loss 2.2569
step  238 / 1000 | loss 2.6960
step  239 / 1000 | loss 2.4001
step  240 / 1000 | loss 3.6365
step  241 / 1000 | loss 2.8041
step  242 / 1000 | loss 2.5392
step  243 / 1000 | loss 2.3092
step  244 / 1000 | loss 2.6435
step  245 / 1000 | loss 2.2066
step  246 / 1000 | loss 2.7219
step  247 / 1000 | loss 2.4871
step  248 / 1000 | loss 2.7047
step  249 / 1000 | loss 2.0570
step  250 / 1000 | loss 2.1581
step  251 / 1000 | loss 1.9875
step  252 / 1000 | loss 2.4351
step  253 / 1000 | loss 2.7340
step  254 / 1000 | loss 1.9832
step  255 / 1000 | loss 2.4915
step  256 / 1000 | loss 3.5044
step  257 / 1000 | loss 2.3991
step  258 / 1000 | loss 1.8618
step  259 / 1000 | loss 1.9200
step  260 / 1000 | loss 1.7671
step  261 / 1000 | loss 2.6093
step  262 / 1000 | loss 2.2438
step  263 / 1000 | loss 2.9581
step  264 / 1000 | loss 3.0106
step  265 / 1000 | loss 1.8756
step  266 / 1000 | loss 2.7724
step  267 / 1000 | loss 1.9729
step  268 / 1000 | loss 2.1480
step  269 / 1000 | loss 2.1096
step  270 / 1000 | loss 2.8207
step  271 / 1000 | loss 2.2624
step  272 / 1000 | loss 1.9211
step  273 / 1000 | loss 2.6192
step  274 / 1000 | loss 3.0047
step  275 / 1000 | loss 2.0174
step  276 / 1000 | loss 2.5915
step  277 / 1000 | loss 3.1114
step  278 / 1000 | loss 2.3490
step  279 / 1000 | loss 2.3004
step  280 / 1000 | loss 1.9486
step  281 / 1000 | loss 3.1744
step  282 / 1000 | loss 1.9351
step  283 / 1000 | loss 2.4215
step  284 / 1000 | loss 2.7351
step  285 / 1000 | loss 3.3271
step  286 / 1000 | loss 2.1280
step  287 / 1000 | loss 2.3728
step  288 / 1000 | loss 2.5311
step  289 / 1000 | loss 2.4675
step  290 / 1000 | loss 2.1163
step  291 / 1000 | loss 3.0499
step  292 / 1000 | loss 2.3976
step  293 / 1000 | loss 1.9984
step  294 / 1000 | loss 2.5432
step  295 / 1000 | loss 2.7180
step  296 / 1000 | loss 2.1555
step  297 / 1000 | loss 2.3680
step  298 / 1000 | loss 2.6502
step  299 / 1000 | loss 2.1947
step  300 / 1000 | loss 2.3178
step  301 / 1000 | loss 2.6931
step  302 / 1000 | loss 2.1736
step  303 / 1000 | loss 2.6196
step  304 / 1000 | loss 2.3674
step  305 / 1000 | loss 2.8884
step  306 / 1000 | loss 2.5560
step  307 / 1000 | loss 1.9077
step  308 / 1000 | loss 2.5663
step  309 / 1000 | loss 2.0727
step  310 / 1000 | loss 2.3818
step  311 / 1000 | loss 3.0383
step  312 / 1000 | loss 2.0074
step  313 / 1000 | loss 1.7555
step  314 / 1000 | loss 2.3456
step  315 / 1000 | loss 2.6081
step  316 / 1000 | loss 2.0439
step  317 / 1000 | loss 2.0600
step  318 / 1000 | loss 1.8657
step  319 / 1000 | loss 1.9699
step  320 / 1000 | loss 1.7969
step  321 / 1000 | loss 2.5492
step  322 / 1000 | loss 2.4804
step  323 / 1000 | loss 2.7345
step  324 / 1000 | loss 3.1487
step  325 / 1000 | loss 2.4556
step  326 / 1000 | loss 2.0597
step  327 / 1000 | loss 2.3248
step  328 / 1000 | loss 1.9027
step  329 / 1000 | loss 2.1499
step  330 / 1000 | loss 2.3627
step  331 / 1000 | loss 2.1251
step  332 / 1000 | loss 2.4151
step  333 / 1000 | loss 1.9030
step  334 / 1000 | loss 2.8525
step  335 / 1000 | loss 3.9066
step  336 / 1000 | loss 3.3516
step  337 / 1000 | loss 2.6985
step  338 / 1000 | loss 2.5921
step  339 / 1000 | loss 2.2606
step  340 / 1000 | loss 2.1589
step  341 / 1000 | loss 3.0280
step  342 / 1000 | loss 2.8235
step  343 / 1000 | loss 1.8070
step  344 / 1000 | loss 2.5350
step  345 / 1000 | loss 2.4687
step  346 / 1000 | loss 2.4156
step  347 / 1000 | loss 2.9995
step  348 / 1000 | loss 2.4981
step  349 / 1000 | loss 2.6259
step  350 / 1000 | loss 2.2592
step  351 / 1000 | loss 3.1636
step  352 / 1000 | loss 1.9862
step  353 / 1000 | loss 2.3807
step  354 / 1000 | loss 2.8480
step  355 / 1000 | loss 2.5412
step  356 / 1000 | loss 2.1290
step  357 / 1000 | loss 2.7031
step  358 / 1000 | loss 2.0749
step  359 / 1000 | loss 1.9376
step  360 / 1000 | loss 2.4932
step  361 / 1000 | loss 2.5539
step  362 / 1000 | loss 2.4702
step  363 / 1000 | loss 1.8193
step  364 / 1000 | loss 1.9877
step  365 / 1000 | loss 2.6337
step  366 / 1000 | loss 1.9184
step  367 / 1000 | loss 3.0730
step  368 / 1000 | loss 2.5106
step  369 / 1000 | loss 2.8109
step  370 / 1000 | loss 2.0459
step  371 / 1000 | loss 2.9865
step  372 / 1000 | loss 2.1616
step  373 / 1000 | loss 2.7536
step  374 / 1000 | loss 2.1206
step  375 / 1000 | loss 1.9970
step  376 / 1000 | loss 2.4778
step  377 / 1000 | loss 2.3444
step  378 / 1000 | loss 2.2609
step  379 / 1000 | loss 2.4662
step  380 / 1000 | loss 2.2087
step  381 / 1000 | loss 2.4502
step  382 / 1000 | loss 2.7536
step  383 / 1000 | loss 2.3231
step  384 / 1000 | loss 3.2495
step  385 / 1000 | loss 2.9181
step  386 / 1000 | loss 2.3336
step  387 / 1000 | loss 3.6985
step  388 / 1000 | loss 2.2499
step  389 / 1000 | loss 2.3085
step  390 / 1000 | loss 3.1236
step  391 / 1000 | loss 2.4739
step  392 / 1000 | loss 2.1051
step  393 / 1000 | loss 2.1702
step  394 / 1000 | loss 2.2743
step  395 / 1000 | loss 2.6582
step  396 / 1000 | loss 1.8241
step  397 / 1000 | loss 2.0875
step  398 / 1000 | loss 2.8767
step  399 / 1000 | loss 2.7444
step  400 / 1000 | loss 2.3428
step  401 / 1000 | loss 2.6035
step  402 / 1000 | loss 2.7292
step  403 / 1000 | loss 1.9550
step  404 / 1000 | loss 2.2429
step  405 / 1000 | loss 2.7119
step  406 / 1000 | loss 2.5498
step  407 / 1000 | loss 2.2875
step  408 / 1000 | loss 2.6208
step  409 / 1000 | loss 2.8385
step  410 / 1000 | loss 2.9415
step  411 / 1000 | loss 2.2064
step  412 / 1000 | loss 2.1636
step  413 / 1000 | loss 2.2308
step  414 / 1000 | loss 2.8363
step  415 / 1000 | loss 2.0398
step  416 / 1000 | loss 2.4377
step  417 / 1000 | loss 2.9288
step  418 / 1000 | loss 1.9164
step  419 / 1000 | loss 2.4943
step  420 / 1000 | loss 2.7135
step  421 / 1000 | loss 2.5427
step  422 / 1000 | loss 2.4804
step  423 / 1000 | loss 1.9508
step  424 / 1000 | loss 2.5618
step  425 / 1000 | loss 2.6098
step  426 / 1000 | loss 2.8338
step  427 / 1000 | loss 2.4871
step  428 / 1000 | loss 2.3602
step  429 / 1000 | loss 2.0358
step  430 / 1000 | loss 2.3998
step  431 / 1000 | loss 2.1980
step  432 / 1000 | loss 2.0428
step  433 / 1000 | loss 2.3457
step  434 / 1000 | loss 2.3509
step  435 / 1000 | loss 2.4827
step  436 / 1000 | loss 3.3131
step  437 / 1000 | loss 2.7833
step  438 / 1000 | loss 1.8821
step  439 / 1000 | loss 1.9444
step  440 / 1000 | loss 2.1377
step  441 / 1000 | loss 2.6178
step  442 / 1000 | loss 3.1046
step  443 / 1000 | loss 3.2299
step  444 / 1000 | loss 2.3159
step  445 / 1000 | loss 2.3500
step  446 / 1000 | loss 2.0550
step  447 / 1000 | loss 2.0598
step  448 / 1000 | loss 3.0721
step  449 / 1000 | loss 2.1660
step  450 / 1000 | loss 3.0903
step  451 / 1000 | loss 2.8054
step  452 / 1000 | loss 2.8289
step  453 / 1000 | loss 2.5301
step  454 / 1000 | loss 1.9576
step  455 / 1000 | loss 2.2373
step  456 / 1000 | loss 2.7489
step  457 / 1000 | loss 2.5852
step  458 / 1000 | loss 2.7530
step  459 / 1000 | loss 1.7114
step  460 / 1000 | loss 2.3958
step  461 / 1000 | loss 1.8254
step  462 / 1000 | loss 2.7834
step  463 / 1000 | loss 2.0794
step  464 / 1000 | loss 2.2029
step  465 / 1000 | loss 2.7421
step  466 / 1000 | loss 2.2871
step  467 / 1000 | loss 2.2345
step  468 / 1000 | loss 2.2340
step  469 / 1000 | loss 2.3651
step  470 / 1000 | loss 3.8820
step  471 / 1000 | loss 2.5910
step  472 / 1000 | loss 2.7750
step  473 / 1000 | loss 2.6283
step  474 / 1000 | loss 2.3571
step  475 / 1000 | loss 2.4745
step  476 / 1000 | loss 2.1805
step  477 / 1000 | loss 2.9413
step  478 / 1000 | loss 2.2745
step  479 / 1000 | loss 2.0886
step  480 / 1000 | loss 2.0120
step  481 / 1000 | loss 2.9853
step  482 / 1000 | loss 2.7189
step  483 / 1000 | loss 2.3466
step  484 / 1000 | loss 2.8693
step  485 / 1000 | loss 2.4805
step  486 / 1000 | loss 2.1715
step  487 / 1000 | loss 2.7516
step  488 / 1000 | loss 2.6655
step  489 / 1000 | loss 2.3425
step  490 / 1000 | loss 2.2978
step  491 / 1000 | loss 2.2573
step  492 / 1000 | loss 2.3424
step  493 / 1000 | loss 2.4360
step  494 / 1000 | loss 2.1313
step  495 / 1000 | loss 2.4870
step  496 / 1000 | loss 2.5856
step  497 / 1000 | loss 2.9952
step  498 / 1000 | loss 2.4689
step  499 / 1000 | loss 2.2353
step  500 / 1000 | loss 2.0645
step  501 / 1000 | loss 2.4261
step  502 / 1000 | loss 2.1254
step  503 / 1000 | loss 2.7352
step  504 / 1000 | loss 2.0662
step  505 / 1000 | loss 2.3327
step  506 / 1000 | loss 2.4337
step  507 / 1000 | loss 2.4315
step  508 / 1000 | loss 2.6284
step  509 / 1000 | loss 2.8761
step  510 / 1000 | loss 2.7854
step  511 / 1000 | loss 2.2922
step  512 / 1000 | loss 2.2573
step  513 / 1000 | loss 2.4115
step  514 / 1000 | loss 2.8810
step  515 / 1000 | loss 2.6771
step  516 / 1000 | loss 3.0052
step  517 / 1000 | loss 2.1366
step  518 / 1000 | loss 2.2575
step  519 / 1000 | loss 2.0644
step  520 / 1000 | loss 2.7970
step  521 / 1000 | loss 1.6685
step  522 / 1000 | loss 1.8816
step  523 / 1000 | loss 2.1512
step  524 / 1000 | loss 2.4364
step  525 / 1000 | loss 2.3002
step  526 / 1000 | loss 2.6904
step  527 / 1000 | loss 1.7979
step  528 / 1000 | loss 2.5294
step  529 / 1000 | loss 2.3032
step  530 / 1000 | loss 1.6063
step  531 / 1000 | loss 2.5921
step  532 / 1000 | loss 2.3464
step  533 / 1000 | loss 3.5815
step  534 / 1000 | loss 2.2109
step  535 / 1000 | loss 3.1679
step  536 / 1000 | loss 1.8492
step  537 / 1000 | loss 1.5782
step  538 / 1000 | loss 2.4474
step  539 / 1000 | loss 1.8286
step  540 / 1000 | loss 2.7201
step  541 / 1000 | loss 2.7791
step  542 / 1000 | loss 1.9045
step  543 / 1000 | loss 3.2878
step  544 / 1000 | loss 2.3980
step  545 / 1000 | loss 2.8266
step  546 / 1000 | loss 2.4227
step  547 / 1000 | loss 2.1204
step  548 / 1000 | loss 2.8575
step  549 / 1000 | loss 2.0631
step  550 / 1000 | loss 1.9310
step  551 / 1000 | loss 2.6828
step  552 / 1000 | loss 2.4919
step  553 / 1000 | loss 2.5412
step  554 / 1000 | loss 2.7195
step  555 / 1000 | loss 2.9065
step  556 / 1000 | loss 2.3740
step  557 / 1000 | loss 2.5296
step  558 / 1000 | loss 1.9853
step  559 / 1000 | loss 2.5890
step  560 / 1000 | loss 3.1969
step  561 / 1000 | loss 1.8082
step  562 / 1000 | loss 2.9966
step  563 / 1000 | loss 2.3597
step  564 / 1000 | loss 2.0989
step  565 / 1000 | loss 3.0321
step  566 / 1000 | loss 1.7108
step  567 / 1000 | loss 2.5155
step  568 / 1000 | loss 2.7469
step  569 / 1000 | loss 2.5179
step  570 / 1000 | loss 2.8211
step  571 / 1000 | loss 1.9473
step  572 / 1000 | loss 3.1226
step  573 / 1000 | loss 3.0085
step  574 / 1000 | loss 2.4998
step  575 / 1000 | loss 2.2788
step  576 / 1000 | loss 2.0708
step  577 / 1000 | loss 1.9976
step  578 / 1000 | loss 2.6646
step  579 / 1000 | loss 2.0727
step  580 / 1000 | loss 2.4337
step  581 / 1000 | loss 2.3260
step  582 / 1000 | loss 2.4394
step  583 / 1000 | loss 2.7697
step  584 / 1000 | loss 2.9226
step  585 / 1000 | loss 2.2863
step  586 / 1000 | loss 2.4610
step  587 / 1000 | loss 2.1763
step  588 / 1000 | loss 2.0816
step  589 / 1000 | loss 1.8776
step  590 / 1000 | loss 2.4569
step  591 / 1000 | loss 2.4763
step  592 / 1000 | loss 2.1966
step  593 / 1000 | loss 2.3452
step  594 / 1000 | loss 2.8074
step  595 / 1000 | loss 2.4341
step  596 / 1000 | loss 2.1553
step  597 / 1000 | loss 2.6157
step  598 / 1000 | loss 2.1024
step  599 / 1000 | loss 2.3983
step  600 / 1000 | loss 2.4851
step  601 / 1000 | loss 2.1083
step  602 / 1000 | loss 2.4919
step  603 / 1000 | loss 2.7452
step  604 / 1000 | loss 2.0589
step  605 / 1000 | loss 2.7860
step  606 / 1000 | loss 1.7675
step  607 / 1000 | loss 2.7445
step  608 / 1000 | loss 2.2072
step  609 / 1000 | loss 2.3056
step  610 / 1000 | loss 2.4470
step  611 / 1000 | loss 2.6861
step  612 / 1000 | loss 2.5383
step  613 / 1000 | loss 1.9791
step  614 / 1000 | loss 2.1122
step  615 / 1000 | loss 2.4416
step  616 / 1000 | loss 2.9865
step  617 / 1000 | loss 2.7236
step  618 / 1000 | loss 2.3293
step  619 / 1000 | loss 2.4571
step  620 / 1000 | loss 2.6560
step  621 / 1000 | loss 1.8379
step  622 / 1000 | loss 2.2556
step  623 / 1000 | loss 2.0642
step  624 / 1000 | loss 2.4819
step  625 / 1000 | loss 1.7747
step  626 / 1000 | loss 2.5039
step  627 / 1000 | loss 2.0995
step  628 / 1000 | loss 2.2031
step  629 / 1000 | loss 2.6526
step  630 / 1000 | loss 2.6197
step  631 / 1000 | loss 3.0481
step  632 / 1000 | loss 1.7443
step  633 / 1000 | loss 2.6695
step  634 / 1000 | loss 2.5338
step  635 / 1000 | loss 3.2450
step  636 / 1000 | loss 2.8575
step  637 / 1000 | loss 2.5257
step  638 / 1000 | loss 2.2855
step  639 / 1000 | loss 2.6202
step  640 / 1000 | loss 1.9703
step  641 / 1000 | loss 2.2895
step  642 / 1000 | loss 1.9095
step  643 / 1000 | loss 2.5737
step  644 / 1000 | loss 2.2433
step  645 / 1000 | loss 2.3000
step  646 / 1000 | loss 2.0239
step  647 / 1000 | loss 2.3138
step  648 / 1000 | loss 3.1185
step  649 / 1000 | loss 2.1672
step  650 / 1000 | loss 2.6138
step  651 / 1000 | loss 2.4730
step  652 / 1000 | loss 2.4868
step  653 / 1000 | loss 2.3750
step  654 / 1000 | loss 2.1639
step  655 / 1000 | loss 3.0494
step  656 / 1000 | loss 2.4772
step  657 / 1000 | loss 2.1428
step  658 / 1000 | loss 2.9535
step  659 / 1000 | loss 2.5928
step  660 / 1000 | loss 2.4115
step  661 / 1000 | loss 2.1242
step  662 / 1000 | loss 2.9471
step  663 / 1000 | loss 2.6772
step  664 / 1000 | loss 2.6958
step  665 / 1000 | loss 2.4493
step  666 / 1000 | loss 2.0646
step  667 / 1000 | loss 2.9612
step  668 / 1000 | loss 2.8441
step  669 / 1000 | loss 2.1719
step  670 / 1000 | loss 2.1952
step  671 / 1000 | loss 2.1350
step  672 / 1000 | loss 1.8856
step  673 / 1000 | loss 2.5404
step  674 / 1000 | loss 2.4887
step  675 / 1000 | loss 2.7627
step  676 / 1000 | loss 2.1296
step  677 / 1000 | loss 2.0944
step  678 / 1000 | loss 2.2733
step  679 / 1000 | loss 2.3283
step  680 / 1000 | loss 2.2191
step  681 / 1000 | loss 2.9738
step  682 / 1000 | loss 2.0353
step  683 / 1000 | loss 1.5894
step  684 / 1000 | loss 2.3880
step  685 / 1000 | loss 1.8963
step  686 / 1000 | loss 2.4264
step  687 / 1000 | loss 1.8933
step  688 / 1000 | loss 2.3557
step  689 / 1000 | loss 2.3917
step  690 / 1000 | loss 2.3202
step  691 / 1000 | loss 2.0521
step  692 / 1000 | loss 1.8742
step  693 / 1000 | loss 2.1245
step  694 / 1000 | loss 3.7008
step  695 / 1000 | loss 2.7782
step  696 / 1000 | loss 2.4651
step  697 / 1000 | loss 3.2385
step  698 / 1000 | loss 2.6590
step  699 / 1000 | loss 2.6012
step  700 / 1000 | loss 2.3357
step  701 / 1000 | loss 2.1908
step  702 / 1000 | loss 3.2303
step  703 / 1000 | loss 2.5401
step  704 / 1000 | loss 2.0141
step  705 / 1000 | loss 2.2466
step  706 / 1000 | loss 2.2559
step  707 / 1000 | loss 2.6487
step  708 / 1000 | loss 2.7316
step  709 / 1000 | loss 2.0201
step  710 / 1000 | loss 2.2398
step  711 / 1000 | loss 2.8304
step  712 / 1000 | loss 2.4438
step  713 / 1000 | loss 2.4199
step  714 / 1000 | loss 2.5542
step  715 / 1000 | loss 1.9634
step  716 / 1000 | loss 1.8876
step  717 / 1000 | loss 2.1661
step  718 / 1000 | loss 2.0400
step  719 / 1000 | loss 2.6692
step  720 / 1000 | loss 2.1266
step  721 / 1000 | loss 2.1274
step  722 / 1000 | loss 2.6668
step  723 / 1000 | loss 2.1620
step  724 / 1000 | loss 2.7405
step  725 / 1000 | loss 2.8878
step  726 / 1000 | loss 2.6247
step  727 / 1000 | loss 1.7349
step  728 / 1000 | loss 2.1850
step  729 / 1000 | loss 2.2787
step  730 / 1000 | loss 2.2568
step  731 / 1000 | loss 2.5408
step  732 / 1000 | loss 2.5605
step  733 / 1000 | loss 2.5687
step  734 / 1000 | loss 2.9981
step  735 / 1000 | loss 3.1957
step  736 / 1000 | loss 2.4961
step  737 / 1000 | loss 3.1245
step  738 / 1000 | loss 1.8570
step  739 / 1000 | loss 2.1931
step  740 / 1000 | loss 3.2648
step  741 / 1000 | loss 2.7264
step  742 / 1000 | loss 2.7551
step  743 / 1000 | loss 2.4624
step  744 / 1000 | loss 2.4762
step  745 / 1000 | loss 2.1545
step  746 / 1000 | loss 2.8443
step  747 / 1000 | loss 2.7363
step  748 / 1000 | loss 2.8508
step  749 / 1000 | loss 2.4379
step  750 / 1000 | loss 2.0780
step  751 / 1000 | loss 2.3346
step  752 / 1000 | loss 1.8021
step  753 / 1000 | loss 3.0455
step  754 / 1000 | loss 2.4193
step  755 / 1000 | loss 2.6941
step  756 / 1000 | loss 2.6088
step  757 / 1000 | loss 2.4175
step  758 / 1000 | loss 2.3642
step  759 / 1000 | loss 2.2976
step  760 / 1000 | loss 2.6314
step  761 / 1000 | loss 2.4459
step  762 / 1000 | loss 2.1060
step  763 / 1000 | loss 2.1103
step  764 / 1000 | loss 2.0754
step  765 / 1000 | loss 2.0023
step  766 / 1000 | loss 2.4650
step  767 / 1000 | loss 2.7972
step  768 / 1000 | loss 2.5172
step  769 / 1000 | loss 1.9687
step  770 / 1000 | loss 2.2378
step  771 / 1000 | loss 2.4757
step  772 / 1000 | loss 2.0182
step  773 / 1000 | loss 1.7604
step  774 / 1000 | loss 2.6085
step  775 / 1000 | loss 2.8517
step  776 / 1000 | loss 1.8031
step  777 / 1000 | loss 2.8917
step  778 / 1000 | loss 2.0491
step  779 / 1000 | loss 2.7976
step  780 / 1000 | loss 2.0455
step  781 / 1000 | loss 1.8593
step  782 / 1000 | loss 2.8120
step  783 / 1000 | loss 1.7626
step  784 / 1000 | loss 2.4415
step  785 / 1000 | loss 2.2726
step  786 / 1000 | loss 2.1308
step  787 / 1000 | loss 2.6911
step  788 / 1000 | loss 2.7761
step  789 / 1000 | loss 1.9803
step  790 / 1000 | loss 2.2568
step  791 / 1000 | loss 2.1672
step  792 / 1000 | loss 2.1728
step  793 / 1000 | loss 2.1669
step  794 / 1000 | loss 2.2664
step  795 / 1000 | loss 1.9224
step  796 / 1000 | loss 2.4312
step  797 / 1000 | loss 2.1678
step  798 / 1000 | loss 2.7734
step  799 / 1000 | loss 1.8007
step  800 / 1000 | loss 2.2632
step  801 / 1000 | loss 2.1064
step  802 / 1000 | loss 2.1354
step  803 / 1000 | loss 1.8741
step  804 / 1000 | loss 1.9609
step  805 / 1000 | loss 2.6330
step  806 / 1000 | loss 2.1938
step  807 / 1000 | loss 2.1032
step  808 / 1000 | loss 1.9303
step  809 / 1000 | loss 2.2945
step  810 / 1000 | loss 2.0318
step  811 / 1000 | loss 2.0004
step  812 / 1000 | loss 2.3280
step  813 / 1000 | loss 2.4433
step  814 / 1000 | loss 2.2201
step  815 / 1000 | loss 2.2991
step  816 / 1000 | loss 2.7418
step  817 / 1000 | loss 1.7048
step  818 / 1000 | loss 2.5284
step  819 / 1000 | loss 1.9636
step  820 / 1000 | loss 2.4420
step  821 / 1000 | loss 2.2038
step  822 / 1000 | loss 2.5172
step  823 / 1000 | loss 1.7077
step  824 / 1000 | loss 2.2398
step  825 / 1000 | loss 2.8151
step  826 / 1000 | loss 2.4977
step  827 / 1000 | loss 2.6141
step  828 / 1000 | loss 2.7821
step  829 / 1000 | loss 1.8019
step  830 / 1000 | loss 2.4835
step  831 / 1000 | loss 2.0712
step  832 / 1000 | loss 2.0766
step  833 / 1000 | loss 1.8469
step  834 / 1000 | loss 2.2951
step  835 / 1000 | loss 2.4414
step  836 / 1000 | loss 2.5103
step  837 / 1000 | loss 3.3694
step  838 / 1000 | loss 2.3500
step  839 / 1000 | loss 2.3950
step  840 / 1000 | loss 2.5399
step  841 / 1000 | loss 2.9150
step  842 / 1000 | loss 2.4967
step  843 / 1000 | loss 1.9816
step  844 / 1000 | loss 2.6846
step  845 / 1000 | loss 2.5020
step  846 / 1000 | loss 1.8127
step  847 / 1000 | loss 2.8528
step  848 / 1000 | loss 2.0746
step  849 / 1000 | loss 1.5794
step  850 / 1000 | loss 2.4860
step  851 / 1000 | loss 2.7039
step  852 / 1000 | loss 2.1478
step  853 / 1000 | loss 2.3845
step  854 / 1000 | loss 2.3782
step  855 / 1000 | loss 2.3659
step  856 / 1000 | loss 2.1089
step  857 / 1000 | loss 2.8112
step  858 / 1000 | loss 2.7589
step  859 / 1000 | loss 2.1425
step  860 / 1000 | loss 2.4466
step  861 / 1000 | loss 2.6435
step  862 / 1000 | loss 2.6565
step  863 / 1000 | loss 2.5271
step  864 / 1000 | loss 3.1404
step  865 / 1000 | loss 2.0112
step  866 / 1000 | loss 2.0564
step  867 / 1000 | loss 2.1266
step  868 / 1000 | loss 1.8993
step  869 / 1000 | loss 2.4955
step  870 / 1000 | loss 2.7364
step  871 / 1000 | loss 2.2273
step  872 / 1000 | loss 2.3312
step  873 / 1000 | loss 2.7687
step  874 / 1000 | loss 2.2820
step  875 / 1000 | loss 2.2595
step  876 / 1000 | loss 2.3459
step  877 / 1000 | loss 2.0663
step  878 / 1000 | loss 2.7865
step  879 / 1000 | loss 2.1826
step  880 / 1000 | loss 2.6298
step  881 / 1000 | loss 2.3814
step  882 / 1000 | loss 1.8578
step  883 / 1000 | loss 2.1931
step  884 / 1000 | loss 2.1980
step  885 / 1000 | loss 2.2070
step  886 / 1000 | loss 2.1261
step  887 / 1000 | loss 3.0004
step  888 / 1000 | loss 2.2790
step  889 / 1000 | loss 2.6385
step  890 / 1000 | loss 2.0798
step  891 / 1000 | loss 2.1188
step  892 / 1000 | loss 3.4579
step  893 / 1000 | loss 2.0826
step  894 / 1000 | loss 1.7378
step  895 / 1000 | loss 2.0197
step  896 / 1000 | loss 2.4508
step  897 / 1000 | loss 2.2737
step  898 / 1000 | loss 1.9217
step  899 / 1000 | loss 2.2933
step  900 / 1000 | loss 2.7785
step  901 / 1000 | loss 2.0881
step  902 / 1000 | loss 2.3490
step  903 / 1000 | loss 1.7459
step  904 / 1000 | loss 2.0612
step  905 / 1000 | loss 2.1511
step  906 / 1000 | loss 1.9278
step  907 / 1000 | loss 2.6180
step  908 / 1000 | loss 2.3714
step  909 / 1000 | loss 2.2607
step  910 / 1000 | loss 2.7556
step  911 / 1000 | loss 2.2940
step  912 / 1000 | loss 2.6726
step  913 / 1000 | loss 2.4291
step  914 / 1000 | loss 2.8404
step  915 / 1000 | loss 2.2663
step  916 / 1000 | loss 2.3037
step  917 / 1000 | loss 2.2782
step  918 / 1000 | loss 2.4194
step  919 / 1000 | loss 2.4164
step  920 / 1000 | loss 2.6305
step  921 / 1000 | loss 1.9157
step  922 / 1000 | loss 1.8924
step  923 / 1000 | loss 2.0604
step  924 / 1000 | loss 2.5970
step  925 / 1000 | loss 2.1268
step  926 / 1000 | loss 2.0386
step  927 / 1000 | loss 2.5987
step  928 / 1000 | loss 2.3180
step  929 / 1000 | loss 1.8104
step  930 / 1000 | loss 2.4971
step  931 / 1000 | loss 3.1351
step  932 / 1000 | loss 2.3636
step  933 / 1000 | loss 2.4958
step  934 / 1000 | loss 2.1538
step  935 / 1000 | loss 2.0586
step  936 / 1000 | loss 1.8687
step  937 / 1000 | loss 1.8116
step  938 / 1000 | loss 1.6251
step  939 / 1000 | loss 1.9955
step  940 / 1000 | loss 1.7995
step  941 / 1000 | loss 1.9697
step  942 / 1000 | loss 2.1796
step  943 / 1000 | loss 1.9453
step  944 / 1000 | loss 2.6730
step  945 / 1000 | loss 2.1508
step  946 / 1000 | loss 2.3271
step  947 / 1000 | loss 2.0929
step  948 / 1000 | loss 1.7849
step  949 / 1000 | loss 1.9801
step  950 / 1000 | loss 2.3016
step  951 / 1000 | loss 2.7790
step  952 / 1000 | loss 2.0783
step  953 / 1000 | loss 2.2319
step  954 / 1000 | loss 2.1295
step  955 / 1000 | loss 2.5928
step  956 / 1000 | loss 3.0061
step  957 / 1000 | loss 2.1160
step  958 / 1000 | loss 2.2593
step  959 / 1000 | loss 2.0209
step  960 / 1000 | loss 2.1214
step  961 / 1000 | loss 2.2633
step  962 / 1000 | loss 2.3385
step  963 / 1000 | loss 2.5537
step  964 / 1000 | loss 2.7235
step  965 / 1000 | loss 3.3042
step  966 / 1000 | loss 2.1621
step  967 / 1000 | loss 2.9326
step  968 / 1000 | loss 1.8063
step  969 / 1000 | loss 2.2380
step  970 / 1000 | loss 1.9579
step  971 / 1000 | loss 2.3572
step  972 / 1000 | loss 2.1710
step  973 / 1000 | loss 2.5142
step  974 / 1000 | loss 2.0779
step  975 / 1000 | loss 1.9271
step  976 / 1000 | loss 2.0277
step  977 / 1000 | loss 2.5328
step  978 / 1000 | loss 1.8817
step  979 / 1000 | loss 1.9636
step  980 / 1000 | loss 1.9525
step  981 / 1000 | loss 2.4269
step  982 / 1000 | loss 2.8226
step  983 / 1000 | loss 2.4713
step  984 / 1000 | loss 2.0303
step  985 / 1000 | loss 2.7422
step  986 / 1000 | loss 2.6811
step  987 / 1000 | loss 1.9173
step  988 / 1000 | loss 2.4303
step  989 / 1000 | loss 2.4466
step  990 / 1000 | loss 2.6354
step  991 / 1000 | loss 2.1729
step  992 / 1000 | loss 1.9659
step  993 / 1000 | loss 2.4409
step  994 / 1000 | loss 1.9618
step  995 / 1000 | loss 2.5188
step  996 / 1000 | loss 2.1018
step  997 / 1000 | loss 1.7791
step  998 / 1000 | loss 2.4764
step  999 / 1000 | loss 2.4730
step 1000 / 1000 | loss 2.6497

让我们逐一讲解:

分词。 每个训练步骤选择一个文档,并用BOS在两侧包裹:名字『emma』变成[BOS, e, m, m, a, BOS]。模型的任务是:给定前面的标记,预测每个下一个标记。

前向传播与损失。 我们一次一个地将标记馈入模型,随着进行构建KV缓存。在每个位置,模型输出27个logits,我们通过softmax将其转换为概率。每个位置的损失是正确下一个标记的负对数概率:$-\log p(\text{target})$。这被称为交叉熵损失。直观地,损失衡量预测错误程度:模型对实际接下来发生的内容有多惊讶。如果模型给正确标记分配概率1.0,它完全不惊讶,损失为0。如果分配接近0的概率,模型非常惊讶,损失趋向$+\infty$。我们对文档上每个位置的损失取平均,获得单个标量损失。

反向传播。 一次调用loss.backward()通过整个计算图运行反向传播,从损失一路回到softmax、模型和每个参数。之后,每个参数的.grad告诉我们如何改变它以降低损失。

Adam优化器。 我们可以直接做p.data -= lr * p.grad(梯度下降),但Adam更聪明。它为每个参数维护两个运行平均值:m跟踪最近梯度的均值(动量,像一个滚动的球),v跟踪最近平方梯度的均值(为每个参数自适应调整学习率)。m_hatv_hat是偏差校正,用于解释mv被初始化为零且需要预热的事实。学习率在训练过程中线性衰减。更新后,我们为下一步重置.grad = 0

在1,000步上,损失从约3.3(在27个标记中随机猜测:$-\log(1/27) \approx 3.3$)下降到约2.37。越低越好,最低可能为0(完美预测),所以仍有改进空间,但模型显然在学习名字的统计模式。

推理

训练完成后,我们可以从模型中采样新名字。参数被冻结,我们只是在循环中运行前向传播,将每个生成的标记反馈为下一个输入:

In [ ]:
# 推理:愿模型对我们胡言乱语
temperature = 0.5 # 在 (0, 1] 中,控制生成文本的『创意』程度,从低到高
print("\n--- 推理(新的、幻觉的名字) ---")
for sample_idx in range(20):
    keys, values = [[] for _ in range(n_layer)], [[] for _ in range(n_layer)]
    token_id = BOS
    sample = []
    for pos_id in range(block_size):
        logits = gpt(token_id, pos_id, keys, values)
        probs = softmax([l / temperature for l in logits])
        token_id = random.choices(range(vocab_size), weights=[p.data for p in probs])[0]
        if token_id == BOS:
            break
        sample.append(uchars[token_id])
    print(f"sample {sample_idx+1:2d}: {''.join(sample)}")
--- 推理(新的、幻觉的名字) ---
sample  1: kamon
sample  2: ann
sample  3: karai
sample  4: jaire
sample  5: vialan
sample  6: karia
sample  7: yeran
sample  8: anna
sample  9: areli
sample 10: kaina
sample 11: konna
sample 12: keylen
sample 13: liole
sample 14: alerin
sample 15: earan
sample 16: lenne
sample 17: kana
sample 18: lara
sample 19: alela
sample 20: anton

我们用BOS标记开始每个样本,它告诉模型『开始一个新名字』。模型产生27个logits,我们将其转换为概率,并根据这些概率随机采样一个标记。那个标记被反馈为下一个输入,我们重复直到模型再次产生BOS(意思是『我完成了』)或达到最大序列长度。

temperature参数控制随机性。在softmax之前,我们将logits除以温度。温度1.0直接从模型学习的分布中采样。较低的温度(如这里的0.5)锐化分布,使模型更保守,倾向于选择最高概率的选项。接近0的温度将始终选择单个最可能的标记(贪心解码)。较高的温度使分布更平坦,产生更多样但可能不太连贯的输出。

运行它

你只需要Python(无需pip install,零依赖):

python train.py

该脚本在我的MacBook上大约需要1分钟。你会看到每一步打印的损失:

train.py
num docs: 32033
vocab size: 27
num params: 4192
step    1 / 1000 | loss 3.3660
step    2 / 1000 | loss 3.4243
step    3 / 1000 | loss 3.1778
step    4 / 1000 | loss 3.0664
step    5 / 1000 | loss 3.2209
step    6 / 1000 | loss 2.9452
step    7 / 1000 | loss 3.2894
step    8 / 1000 | loss 3.3245
step    9 / 1000 | loss 2.8990
step   10 / 1000 | loss 3.2229
step   11 / 1000 | loss 2.7964
step   12 / 1000 | loss 2.9345
step   13 / 1000 | loss 3.0544
...

观察它从~3.3(随机水平)下降到~2.37。这个数字越低,网络对序列中接下来会出现什么的预测就越好。训练结束时,训练标记序列的统计模式知识被提炼在模型参数中。固定这些参数,我们现在可以生成新的、幻觉的名字。你会看到(再次):

sample  1: kamon
sample  2: ann
sample  3: karai
sample  4: jaire
sample  5: vialan
sample  6: karia
sample  7: yeran
sample  8: anna
sample  9: areli
sample 10: kaina
sample 11: konna
sample 12: keylen
sample 13: liole
sample 14: alerin
sample 15: earan
sample 16: lenne
sample 17: kana
sample 18: lara
sample 19: alela
sample 20: anton

作为在你自己电脑上运行脚本的替代方案,你可以直接在 Google Colab笔记本 上运行它,并向Gemini提问相关问题。试着玩一玩这个脚本!你可以尝试不同的数据集,或者训练更长时间(增加num_steps),或增大模型尺寸来获得越来越好的结果。

渐进式构建

要像剥洋葱一样逐层看代码构建,建议的演进路径大致如下:

文件 新增内容
train0.py 二元语法计数表——无神经网络,无梯度
train1.py MLP + 手动梯度(数值与分析)+ SGD
train2.py 自动微分(Value类)——替换手动梯度
train3.py 位置嵌入 + 单头注意力 + RMSNorm + 残差连接
train4.py 多头注意力 + 层循环——完整GPT架构
train5.py Adam优化器——这就是 train.py

我创建了一个名为 build_microgpt.py 的Gist,在Revisions中你可以看到所有这些版本以及每步之间的差异。我认为这可能是逐步浏览代码库的一种有帮助的方式——每次只添加一个组件。

现实世界的GPT

microgpt包含了训练和运行GPT的完整算法本质。但在这和像ChatGPT这样的生产级LLM之间,有一长串不同的东西。它们都不改变核心算法和整体布局,但它们是使其在规模上实际运作的关键。按相同章节顺序来看:

数据。 代替3.2万个短名字,生产模型在数万亿标记的互联网文本上训练:网页、书籍、代码等。数据被去重、按质量过滤,并在各领域之间精心配比。

分词器。 代替单个字符,生产模型使用子词分词器如BPE(字节对编码),这些分词器学会将经常共同出现的字符序列合并为单个标记。常见词如『the』变成单个标记,稀有词被分解为片段。这给出约10万个标记的词汇表,并且因为模型在每个位置看到更多内容而效率高得多。

自动微分。 microgpt在纯Python中的标量Value对象上操作。生产系统使用张量(数字的大型多维数组)并在GPU/TPU上运行,每秒执行数十亿次浮点运算。像PyTorch这样的库在张量上处理自动微分,像FlashAttention这样的CUDA内核融合多个操作以加速。数学完全一样,只是对应许多标量并行处理。

架构。 microgpt有4,192个参数。GPT-4级别的模型有数千亿。总体上它是一个非常相似的Transformer神经网络,只是宽得多(嵌入维度超过10,000)和深得多(100+层)。现代LLM还融入了一些更多类型的乐高积木并调整了顺序:例子包括RoPE(旋转位置嵌入)替代学习位置嵌入、GQA(分组查询注意力)减少KV缓存大小、门控线性激活替代ReLU、混合专家(MoE)层等。但注意力(通信)和MLP(计算)穿插在残差流上的核心结构被很好地保留。

训练。 代替每步一个文档,生产训练使用大批量(每步数百万标记)、梯度累积、混合精度(float16/bfloat16)和谨慎的超参数调优。训练一个前沿模型需要数千个GPU运行数月。

优化。 microgpt使用Adam和简单的线性学习率衰减,仅此而已。在规模上,优化成为一门独立的学科。模型以降低精度(bfloat16甚至fp8)在大型GPU集群上训练以求效率,这引入了自身的数值挑战。优化器设置(学习率、权重衰减、beta参数、预热计划、衰减计划)必须精确调优,正确的值取决于模型大小、批大小和数据集组成。缩放定律(如Chinchilla)指导如何在固定计算预算下在模型大小和训练标记数之间分配。在规模上将任何这些细节搞错可能浪费数百万美元的计算资源,因此团队在投入完整训练之前会运行大量较小规模的实验来预测正确设置。

后训练。 训练出来的基础模型(称为『预训练』模型)是一个文档补全器,不是聊天机器人。将其转变为ChatGPT发生在两个阶段。首先,SFT(监督微调):你只需将文档替换为策划的对话并继续训练。算法上没有任何改变。其次,RL(强化学习):模型生成响应,它们被评分(由人、另一个『裁判』模型或算法),模型从该反馈中学习。从根本上说,模型仍在文档上训练,但现在这些文档由来自模型本身的标记组成。

推理。 为数百万用户提供模型服务需要自己的工程堆栈:将请求批处理在一起、KV缓存管理和分页(vLLM等)、推测解码以提高速度、量化(以int8/int4运行而不是float16)以减少内存,以及将模型分布到多个GPU。从根本上说,我们仍在预测序列中的下一个标记,但花了大量工程使其更快。

所有这些都是重要的工程和研究贡献,但如果你理解microgpt,你就理解了算法本质。

常见问题

模型『理解』任何东西吗? 这是一个哲学问题,但从机制上讲:没有魔法在发生。模型是一个大数学函数,将输入标记映射到下一个标记的概率分布。在训练期间,参数被调整以使正确的下一个标记更可能。这是否构成『理解』取决于你,但机制完全包含在上面的200行中。

它为什么能工作? 模型有数千个可调参数,优化器每一步将它们微调一点点以使损失下降。经过许多步,参数定居为捕获数据统计规律的值。对于名字,这意味着诸如:名字通常以辅音开头,『qu』倾向于一起出现,名字很少有三个连续辅音等。模型不学习显式规则,它学习一个恰好反映这些规则的概率分布。

这与ChatGPT有什么关系? ChatGPT是相同的核心循环(预测下一个标记、采样、重复)大规模扩展,加上后训练使其具有对话能力。当你与它聊天时,系统提示、你的消息和它的回复都只是序列中的标记。模型一次一个标记地补全文档,和microgpt补全名字完全一样。

『幻觉』是怎么回事? 模型通过从概率分布采样来生成标记。它没有真理的概念,它只知道给定训练数据,哪些序列在统计上是看似合理的。microgpt『幻觉』一个像『karia』这样的名字,与ChatGPT自信地陈述一个错误事实是相同的现象。两者都是看似合理的补全,恰好不是真实的。

为什么这么慢? microgpt在纯Python中一次处理一个标量。单个训练步骤需要几秒钟。相同的数学在GPU上并行处理数百万个标量,速度快几个数量级。

我能让它生成更好的名字吗? 可以。训练更长时间(增加num_steps),使模型更大(n_embdn_layern_head),或使用更大的数据集。这些是在规模上同样重要的旋钮。

如果我更换数据集会怎样? 模型将学习数据中的任何模式。换成城市名、宝可梦名、英文单词或短诗的文件,模型将学习生成那些代替。代码的其余部分不需要改变。


量子计算语言生态:从底层汇编到高级框架的层次化解析

描述量子原生算法有非常特定的『语言』体系。根据使用场景的不同,这些语言主要分为两大类:理论与数学描述语言(用于论文、设计和交流)和 量子编程语言(用于实际编写、模拟和在硬件上执行)。

以下是具体的分类和代表性语言:


一、 理论与数学描述语言(学术界通用)

在论文、教科书和算法设计阶段,研究人员通常使用以下方式来『描述』量子算法:

1. 狄拉克符号 (Dirac Notation / Bra-Ket 记号)

  • 定位:量子力学的『代数语言』。
  • 作用:这是描述量子态(如 $|\psi\rangle$)、量子门(酉算符 $U$)和测量(如 $\langle\phi|U|\psi\rangle$)最严谨、最基础的数学语言。任何量子算法的理论推导都离不开它。

2. 量子回路图 (Quantum Circuit Diagrams)

  • 定位:量子计算的『视觉语言』或『工程图纸』。
  • 作用:正如上一问所述,量子回路模型是主流。研究人员通过绘制水平线(代表量子比特)和在其上的方框/符号(代表量子门,如 H, CNOT, Rz)来直观地描述算法的执行流程。它是学术界交流量子算法事实上的标准。

3. 张量网络 (Tensor Networks)

  • 定位:描述复杂多体量子态的『图形化代数语言』。
  • 作用:对于包含大量纠缠的量子算法(或经典模拟量子算法时),传统的狄拉克符号会变得极其冗长。张量网络(如 PEPS, MPS)通过图形化的节点和连线来表示高阶张量的缩并,是描述变分量子算法(VQE)和量子机器学习底层数学结构的强大工具。

二、 量子编程语言 (Quantum Programming Languages, QPLs)

如果要将算法真正交给计算机(模拟器或真实量子芯片)去执行,就需要使用专门的量子编程语言。目前的量子编程生态主要分为三个层次:

1. 量子汇编语言 (Quantum Assembly Languages)

这是最接近硬件的底层描述语言,类似于经典计算中的汇编语言。它们直接描述量子门序列和测量操作。

  • OpenQASM (Open Quantum Assembly Language):由 IBM 主导开发,目前是行业事实上的标准。高级量子代码最终都会被编译器翻译成 OpenQASM,然后发送给量子硬件执行。它专门用于描述基于量子回路的算法。
  • Quil (Quantum Instruction Language):由 Rigetti 公司提出,与 OpenQASM 类似,但特别强调了对经典-量子混合控制流(如根据测量结果实时反馈调整后续量子门)的支持。

2. 高级量子编程语言 / 框架 (High-Level QPLs)

这是开发者实际编写算法时使用的语言。由于量子计算目前高度依赖经典计算进行优化和控制(即混合量子-经典架构),绝大多数高级量子语言都是作为经典语言的扩展或库存在的

  • Qiskit (IBM):基于 Python。目前生态最庞大,提供了从高级算法构建(如 QuantumCircuit 类)到底层脉冲控制的完整工具链。
  • Cirq (Google):基于 Python。设计初衷是为了更精细地控制 NISQ(含噪声中等规模量子)设备,对量子门的时间和物理拓扑映射描述得非常细致。
  • PennyLane (Xanadu):基于 Python。特别推荐用于量子机器学习和变分算法(如 VQE, QAOA)。它首创了『量子微分编程』(Quantum Differentiable Programming),可以像训练经典神经网络一样,使用 PyTorch 或 TensorFlow 自动计算量子回路参数的梯度。
  • Q# (Microsoft):这是一种真正的领域特定语言 (DSL),语法类似 C# 或 F#。它不是 Python 的库,而是独立的语言。它内置了对量子特性(如自动内存管理、量子态准备、复杂的量子控制流)的原生支持,通常与 Python 宿主程序结合使用。

3. 函数式与形式化验证语言 (学术与前沿探索)

用于从数学上严格证明量子算法的正确性或进行高级抽象。

  • Quipper:一种基于 Haskell 的函数式量子编程语言,擅长描述和控制极其庞大的量子回路(例如包含数十亿个门的 Shor 算法)。
  • Silq:由苏黎世联邦理工学院 (ETH Zurich) 开发,它的特点是拥有自动量子态释放 (Automatic Uncomputation) 机制,能自动处理量子计算中棘手的『垃圾态』清理问题,语法非常接近高级经典语言。

三、 为什么需要『特定』的量子语言?

经典编程语言(如 C++ 或 Java)无法直接描述量子算法,因为量子力学有一些反直觉的特性,需要语言层面提供特殊支持:

  1. 不可克隆定理 (No-Cloning Theorem):在经典语言中,a = b 会复制变量。但在量子语言中,复制未知的量子态是物理禁止的。量子语言必须在类型系统上禁止这种操作。
  2. 测量的破坏性与概率性:测量量子态会使其坍缩,且结果是概率性的。量子语言需要专门的语法来处理这种『概率分支』和『经典-量子混合控制流』(例如:测量 qubit 0,如果结果是 1,则对 qubit 1 施加 X 门)。
  3. 纠缠的非局部性:语言需要能够优雅地表达多个量子比特之间的全局关联,而不是简单的独立变量操作。
  4. 可逆性 (Reversibility):许多量子操作必须是可逆的(酉变换),量子语言(如 Q# 或 Silq)通常提供专门的机制来确保或自动推导操作的可逆性。

总结与建议

  • 如果是在阅读论文或设计算法逻辑,需要掌握的是 狄拉克符号量子回路图 的读法。
  • 如果是想动手实现量子原生算法(尤其是结合机器学习的混合算法,如之前提到的 QPSAN/QLAM),Python + PennyLanePython + Qiskit 是目前最主流、最高效的选择。
  • 如果是关注算法如何最终在硬件上运行,了解 OpenQASM 的基本语法会非常有帮助,它是连接软件与硬件的『世界语』。

In [1]:
from IPython.display import YouTubeVideo
YouTubeVideo('RQWpF2Gb-gU', width=600, height=400)
Out[1]:
In [2]:
from IPython.display import YouTubeVideo
YouTubeVideo('Dlsa9EBKDGI', width=600, height=400)
Out[2]:




Claude Code - Dynamic Workflows 相关提示词的范例:

  1. 用 deepseek-v4-flash 模型来拓展研究广度。用 deepseek-v4-pro 模型来挖掘研究深度。
  2. 执行代码审计:用 deepseek-v4-flash 模型按文件并行寻找代码缺陷,以 file:line 形式结构化输出;对于每条发现的代码缺陷,再用三个 deepseek-v4-flash 模型的智能体进行对抗验证;对于通过验证的代码缺陷,最后用 deepseek-v4-pro 模型进行深入验证。

To Claude Code:


/deep-research 用 deepseek-v4-flash 模型来拓展研究广度。用 deepseek-v4-pro 模型来挖掘研究深度。通过对 Quixer 的调研,用中文制作一份针对只具有 GPT 架构和量子计算基础概念的人士的教程,一步一步引导这类人士理解、实现并运行 Quixer 。不限制教程的篇幅。确保广度、深度并达成教学目标。


Best regards,
Andrew


Quixer 量子 Transformer 完全教程

从 GPT 到量子 Transformer:一步一步理解、实现并运行 Quixer


关于本教程

目标受众

本教程面向具备以下背景的读者:

  • 熟悉 GPT / Transformer 架构:理解自注意力(Self-Attention)、多头注意力(Multi-Head Attention)、前馈网络(Feed-Forward Network)、层归一化(Layer Normalization)、位置编码(Positional Encoding)、残差连接(Residual Connection)等核心组件。
  • 具备量子计算基础概念:理解量子比特(Qubit)、叠加态(Superposition)、酉变换(Unitary Transformation)、测量(Measurement)、量子门(Pauli 门、旋转门等)的基本含义。

你将学到什么

完成本教程后,你将能够:

  1. 解释 Quixer 与经典 GPT / Transformer 的本质区别
  2. 理解线性酉组合(LCU)和量子奇异值变换(QSVT)的数学原理
  3. 掌握 Quixer 的完整架构:从词嵌入到最终输出的每一步
  4. 阅读并理解 Quixer 的 PyTorch 经典模拟源码
  5. 在自己的机器上搭建环境、训练并运行 Quixer
  6. 评估 Quixer 的当前局限性和未来发展方向

教程结构

本教程采用『原理与真实代码穿插』的组织方式:第一至七章每讲完一个概念(Ansatz 14、LCU、QSVT、完整架构等)就立刻插入真实执行 quixer_model.py 源码的代码块。

章节 内容 建议时间
第一章 GPT / Transformer 架构回顾 20 分钟
第二章 量子计算基础回顾 30 分钟
第三章 Quixer 宏观概览 15 分钟
第四章 块编码(Block Encoding) 30 分钟
第五章 线性酉组合(LCU) 45 分钟
第六章 量子奇异值变换(QSVT) 45 分钟
第七章 Quixer 完整架构详解 40 分钟
第八章 源码结构与训练流程 30 分钟
第九章 环境搭建与模型训练 40 分钟
第十章 实验结果与性能分析 20 分钟
第十一章 模型推理与预测 20 分钟
第十二章 端到端测试与评价 20 分钟
第十三章 局限性与开放问题 20 分钟
第十四章 后续研究方向与生态 20 分钟

运行本教程前的准备

本教程后续所有代码 cell 都会真实调用 QuixerRepo 中的源码并展示真实输出,而不仅仅是复述代码。为此需要先让 Python 能 import quixer。这里先做最小化准备;详细的环境搭建过程、pip install -e . 的一个真实踩坑记录见第九章。

In [1]:
import os
import sys
from pathlib import Path

QUIXER_REPO = Path("/Users/saintway/Downloads/Quixer/QuixerRepo")
if str(QUIXER_REPO) not in sys.path:
    sys.path.insert(0, str(QUIXER_REPO))

# 切换工作目录到 QuixerRepo,这样后面 train_cycle() 写入的 "./trained_models"
# checkpoint 会落在 QuixerRepo/trained_models/ 里,和仓库原有的 4 个 checkpoint 放在一起
os.chdir(QUIXER_REPO)

import torch
import torchquantum as tq
import quixer.quixer_model as qm
import quixer.baseline_models as bm
import quixer.setup_training as st

print("Python:", sys.version.split()[0])
print("当前工作目录:", os.getcwd())
print("torch:", torch.__version__)
print("quixer_model 源文件:", qm.__file__)
print("baseline_models 源文件:", bm.__file__)
print("setup_training 源文件:", st.__file__)
Python: 3.11.12
当前工作目录: /Users/saintway/Downloads/Quixer/QuixerRepo
torch: 2.3.0
quixer_model 源文件: /Users/saintway/Downloads/Quixer/QuixerRepo/quixer/quixer_model.py
baseline_models 源文件: /Users/saintway/Downloads/Quixer/QuixerRepo/quixer/baseline_models.py
setup_training 源文件: /Users/saintway/Downloads/Quixer/QuixerRepo/quixer/setup_training.py
/Users/saintway/Downloads/Quixer/QuixerRepo/.venv311/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm
/Users/saintway/Downloads/Quixer/QuixerRepo/.venv311/lib/python3.11/site-packages/torchtext/vocab/__init__.py:4: UserWarning: 
/!\ IMPORTANT WARNING ABOUT TORCHTEXT STATUS /!\ 
Torchtext is deprecated and the last released version will be 0.18 (this one). You can silence this warning by calling the following at the beginnign of your scripts: `import torchtext; torchtext.disable_torchtext_deprecation_warning()`
  warnings.warn(torchtext._TORCHTEXT_DEPRECATION_MSG)
/Users/saintway/Downloads/Quixer/QuixerRepo/.venv311/lib/python3.11/site-packages/torchtext/utils.py:4: UserWarning: 
/!\ IMPORTANT WARNING ABOUT TORCHTEXT STATUS /!\ 
Torchtext is deprecated and the last released version will be 0.18 (this one). You can silence this warning by calling the following at the beginnign of your scripts: `import torchtext; torchtext.disable_torchtext_deprecation_warning()`
  warnings.warn(torchtext._TORCHTEXT_DEPRECATION_MSG)
/Users/saintway/Downloads/Quixer/QuixerRepo/.venv311/lib/python3.11/site-packages/torchtext/data/__init__.py:4: UserWarning: 
/!\ IMPORTANT WARNING ABOUT TORCHTEXT STATUS /!\ 
Torchtext is deprecated and the last released version will be 0.18 (this one). You can silence this warning by calling the following at the beginnign of your scripts: `import torchtext; torchtext.disable_torchtext_deprecation_warning()`
  warnings.warn(torchtext._TORCHTEXT_DEPRECATION_MSG)

第一章:GPT/Transformer 架构回顾

目标:建立一个清晰的经典 Transformer 心智模型,为理解 Quixer 的『量子替代方案』铺路。

1.1 核心处理管线

GPT 系列模型的本质是一个下一令牌预测器(Next-Token Predictor)。给定一段序列 $[w_1, w_2, ..., w_n]$,模型预测第 n+1 个令牌的概率分布。其核心处理管线如下:

---
config:
  theme: base
  themeVariables:
    primaryColor: '#FAFAFC'
    primaryTextColor: '#222222'
    primaryBorderColor: '#28749A'
    lineColor: '#28749A'
    secondaryColor: '#E9E9E9'
    tertiaryColor: '#FFFFFF'
---
flowchart TD
    INPUT["输入令牌
[w1, ..., wn]"] INPUT --> EMB["词嵌入 (Embedding)
[n, dmodel] 实数矩阵"] EMB --> PE["位置编码 (Positional Encoding)
→ 注入序列位置信息"] PE --> BLOCK subgraph BLOCK["Transformer 块 (×N 层)"] direction TB ATTN["多头自注意力
(Multi-Head Self-Attention)
+ 残差连接 + 层归一化"] FFN["前馈网络
(Feed-Forward Network)
+ 残差连接 + 层归一化"] ATTN --> FFN end BLOCK --> PROJ["输出投影 (Output Projection)
[n, vocab_size] logits"] PROJ --> SM["Softmax
→ 下一令牌的概率分布"]

1.2 最关键的部分:点积自注意力

自注意力是 Transformer 的灵魂。其核心公式为:

$$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right) V$$

展开来解释:

  1. 线性投影:输入 $X \in \mathbb{R}^{n \times d}$ 通过三个可训练的权重矩阵 $W_Q$, $W_K$, $W_V$ 投影为查询(Query)、键(Key)、值(Value): $$Q = X W_Q, \quad K = X W_K, \quad V = X W_V$$

  2. 注意力分数:计算 Query 和 Key 之间的相似度(点积),得到 n × n 的注意力矩阵: $$A = \frac{QK^T}{\sqrt{d_k}}$$

  3. Softmax 归一化:将每行的相似度转换为概率分布(和为 1): $$\text{softmax}(A)_{ij} = \frac{\exp(A_{ij})}{\sum_k \exp(A_{ik})}$$

  4. 加权聚合:用注意力权重对 Value 矩阵加权求和,实现令牌混合(Token Mixing): $$\text{Output} = \text{softmax}(A) \cdot V$$

1.3 自注意力的本质:令牌混合

从更高层次看,自注意力的本质是令牌混合(Token Mixing):

  • 输入是 n 个令牌的表示
  • 每个令牌的输出是所有输入令牌表示的加权平均
  • 权重由令牌之间的『相关性』(Query-Key 相似度)决定
  • 这是内容相关的混合:混合权重依赖于输入内容本身

1.4 为什么量子计算可能提供更好的混合方式?

经典自注意力有两个著名的计算瓶颈:

  1. 二次复杂度:注意力矩阵是 n × n 的,计算和存储复杂度都是 O(n²),对长序列不友好
  2. 参数规模:$W_Q$, $W_K$, $W_V$, $W_O$ 等权重矩阵占据大量参数

量子计算提供了几种潜在的替代方案:

  • 酉变换天然保持范数,可能实现更稳定的信息传播
  • 量子叠加可以在指数大的希尔伯特空间中编码信息
  • 量子干涉可以实现经典计算难以表达的函数
  • 量子原语(如 QSVT)可以直接对矩阵施加多项式变换

关键思想:Quixer 不是对『点积自注意力』做量子化改造,而是用全新的量子令牌混合机制来替代整个自注意力模块。


第二章:量子计算基础回顾

目标:快速回顾理解 Quixer 所需的量子计算核心概念。

2.1 量子比特与态矢量

单量子比特的态可以写作:

$$|\psi\rangle = \alpha|0\rangle + \beta|1\rangle, \quad |\alpha|^2 + |\beta|^2 = 1, \quad \alpha, \beta \in \mathbb{C}$$

其中:

  • $|0\rangle = \begin{bmatrix} 1 \\ 0 \end{bmatrix}$,$|1\rangle = \begin{bmatrix} 0 \\ 1 \end{bmatrix}$
  • $\alpha$ 和 $\beta$ 称为振幅(Amplitude)
  • $|\alpha|^2$ 是测量得到 0 的概率

多量子比特系统的态是各量子比特态的张量积。例如,2 量子比特系统:

$$|\psi\rangle = \alpha_{00}|00\rangle + \alpha_{01}|01\rangle + \alpha_{10}|10\rangle + \alpha_{11}|11\rangle$$

$q$ 个量子比特的态矢量在 $\mathbb{C}^{2^q}$ 空间中——维数随量子比特数指数增长

2.2 酉变换与量子门

量子计算通过酉变换(Unitary Transformation)操作量子态。酉矩阵 $U$ 满足 $U^\dagger U = I$。酉变换的性质是可逆且保范数($\|U|\psi\rangle\| = \||\psi\rangle\|$)。

常用量子门:

矩阵 说明
Pauli-X $$\begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix}$$ 量子版 NOT
Pauli-Y $$\begin{bmatrix} 0 & -i \\ i & 0 \end{bmatrix}$$
Pauli-Z $$\begin{bmatrix} 1 & 0 \\ 0 & -1 \end{bmatrix}$$ 相位翻转
$$R_X(\theta)$$ $$e^{-i\theta X} = \begin{bmatrix} \cos(\theta) & -i\sin(\theta) \\ -i\sin(\theta) & \cos(\theta) \end{bmatrix}$$ 绕 X 轴旋转
$$R_Y(\theta)$$ $$e^{-i\theta Y} = \begin{bmatrix} \cos(\theta) & -\sin(\theta) \\ \sin(\theta) & \cos(\theta) \end{bmatrix}$$ 绕 Y 轴旋转
$$R_Z(\theta)$$ $$e^{-i\theta Z} = \begin{bmatrix} e^{-i\theta} & 0 \\ 0 & e^{i\theta} \end{bmatrix}$$ 绕 Z 轴旋转
CNOT $$\begin{bmatrix} 1&0&0&0 \\ 0&1&0&0 \\ 0&0&0&1 \\ 0&0&1&0 \end{bmatrix}$$ 受控非门

2.3 受控门

受控门 CU 的行为是:

  • 当控制量子比特为 $|0\rangle$ 时,目标量子比特不变
  • 当控制量子比特为 $|1\rangle$ 时,对目标量子比特施加 $U$

由于量子力学是线性的,当控制量子比特处于叠加态时,受控门产生纠缠:

$$CU(\alpha|0\rangle + \beta|1\rangle) \otimes |\psi\rangle = \alpha|0\rangle \otimes |\psi\rangle + \beta|1\rangle \otimes U|\psi\rangle$$

2.4 测量与期望值

测量量子态 $|\psi\rangle$ 中某个可观测量(Observable)$O$($O$ 是厄米矩阵,$O^\dagger = O$):

$$\langle O \rangle_{|\psi\rangle} = \langle\psi|O|\psi\rangle \in \mathbb{R}$$

例如,测量 Pauli-Z 的期望值给出量子比特偏向 $|0\rangle$ 还是 $|1\rangle$。

2.5 参数化量子回路(PQC)

参数化量子回路(Parameterized Quantum Circuit, PQC)是量子机器学习的核心构件。PQC 由一系列参数化的量子门(如 $R_X(\theta), R_Y(\phi), R_Z(\omega)$)组成,其中角度参数是可训练的。

PQC 结构示例(Ansatz 14):

---
config:
  theme: base
  themeVariables:
    primaryColor: '#FAFAFC'
    primaryTextColor: '#222222'
    primaryBorderColor: '#28749A'
    lineColor: '#28749A'
    secondaryColor: '#E9E9E9'
    tertiaryColor: '#FFFFFF'
---
flowchart LR
    B1["第 1 块:RY
(前向环形)
RY(θ₀) RY(θ₁) ⋮ RY(θ_{q−1})"] B2["第 2 块:CRX
(反向环形)
CRX(φ₀) CRX(φ₁) ⋮ CRX(φ_{q−1})"] B3["第 3 块:RY

RY(θ′₀) RY(θ′₁) ⋮ RY(θ′_{q−1})"] B4["第 4 块:CRX

CRX(φ′₀) CRX(φ′₁) ⋮ CRX(φ′_{q−1})"] B1 --> B2 --> B3 --> B4 B1:::ry B2:::crx B3:::ry B4:::crx classDef ry fill:#f8fbfd,stroke:#28749A,stroke-width:1.5px,color:#222 classDef crx fill:#f5f5f5,stroke:#28749A,stroke-width:1.5px,color:#222

下面直接看 Ansatz 14 回路的具体构造:先是一份教学性复述(帮助理解回路结构的思路),然后真实执行 quixer_model.py 里的原始实现并对比两者的差异。

In [2]:
import itertools


def ansatz_14_torchquantum_specification(n_qubits, layers=1):
    """
    构建 Ansatz 14 的回路规格。
    
    每层包含 4 个块,共 4 * n_qubits 个参数:
    - 块1: RY 旋转(所有量子比特)
    - 块2: CRX 向前环形连接 (i → i+1)
    - 块3: RY 旋转(所有量子比特)
    - 块4: CRX 向后环形连接 (i → i-1)
    """
    enc = []
    counter = itertools.count(0)
    
    for _ in range(layers):
        # 块1: RY 旋转
        for i in range(n_qubits):
            enc.append({
                "input_idx": [next(counter)],
                "func": "ry",
                "wires": [i]
            })
        # 块2: CRX 向前环形
        for i in range(n_qubits - 1, -1, -1):  # 反向遍历
            enc.append({
                "input_idx": [next(counter)],
                "func": "crx",
                "wires": [i, (i + 1) % n_qubits]
            })
        # 块3: RY 旋转
        for i in range(n_qubits):
            enc.append({
                "input_idx": [next(counter)],
                "func": "ry",
                "wires": [i]
            })
        # 块4: CRX 向后环形
        for last_qubit in range(n_qubits - 1, -1, -1):
            i = (last_qubit - 1) % n_qubits  # 控制量子比特
            j = last_qubit
            enc.append({
                "input_idx": [next(counter)],
                "func": "crx",
                "wires": [i, j]
            })
    
    return enc
In [3]:
# 真实执行上面的复述版本,并与 quixer_model.py 中的真实实现对比
paraphrase_spec = ansatz_14_torchquantum_specification(n_qubits=3, layers=1)
real_spec = qm.ansatz_14_torchquantum_specification(n_qubits=3, layers=1)

print(f"复述版本生成 {len(paraphrase_spec)} 个门操作(预期 4*3*1={4*3*1})")
print("前 6 个门(两版本相同):")
for gate in paraphrase_spec[:6]:
    print(" ", gate)

print("\n复述版本与真实源码是否逐项完全一致:", paraphrase_spec == real_spec)
diffs = [(i, p, r) for i, (p, r) in enumerate(zip(paraphrase_spec, real_spec)) if p != r]
print(f"不一致的门数量:{len(diffs)} / {len(real_spec)}")
for i, p, r in diffs:
    print(f"  第 {i} 个门 — 复述版本: {p}  |  真实源码: {r}")
复述版本生成 12 个门操作(预期 4*3*1=12)
前 6 个门(两版本相同):
  {'input_idx': [0], 'func': 'ry', 'wires': [0]}
  {'input_idx': [1], 'func': 'ry', 'wires': [1]}
  {'input_idx': [2], 'func': 'ry', 'wires': [2]}
  {'input_idx': [3], 'func': 'crx', 'wires': [2, 0]}
  {'input_idx': [4], 'func': 'crx', 'wires': [1, 2]}
  {'input_idx': [5], 'func': 'crx', 'wires': [0, 1]}

复述版本与真实源码是否逐项完全一致: False
不一致的门数量:3 / 12
  第 9 个门 — 复述版本: {'input_idx': [9], 'func': 'crx', 'wires': [1, 2]}  |  真实源码: {'input_idx': [9], 'func': 'crx', 'wires': [2, 1]}
  第 10 个门 — 复述版本: {'input_idx': [10], 'func': 'crx', 'wires': [0, 1]}  |  真实源码: {'input_idx': [10], 'func': 'crx', 'wires': [0, 2]}
  第 11 个门 — 复述版本: {'input_idx': [11], 'func': 'crx', 'wires': [2, 0]}  |  真实源码: {'input_idx': [11], 'func': 'crx', 'wires': [1, 0]}

真实执行发现的细节差异:复述版本与真实源码在『块4:CRX 向后环形』的具体控制/目标比特配对上并不完全相同(虽然两者的整体设计思路——正向环形 + 反向环形——是一致的)。这提醒我们:教学性复述适合理解算法思路,但涉及具体连接方式的细节仍应以真实源码为准。本教程从这里开始,所有实际调用都会 import quixer.quixer_model as qm 使用真实源码(如上面 real_spec 所示),而不是手写复述版本。

Ansatz 14 的环形连接设计解读

n_qubits = 4 时一轮的 CRX 连接:

块2: CRX 向前环形       块4: CRX 向后环形
q0 ←── q3               q0 ──→ q3  
 ↑                         ↓
q1 ←── q0               q1 ──→ q0
 ↑                         ↓
q2 ←── q1               q2 ──→ q1
 ↑                         ↓
q3 ←── q2               q3 ──→ q2

这种双向环形连接确保信息可以在所有量子比特之间传播。

2.6 后选择(Postselection)

后选择是量子计算中的一个重要技术:只保留那些某个辅助测量结果为特定值的执行分支,丢弃其他分支。

在回路图中,后选择通常表示为控制量子比特末端的 $\langle 0|$ 标记:

|0⟩ ──[U_PREP]──●──[U_PREP†]── ⟨0|
                │
|ψ⟩ ───────────[U_SEL]─────────

这意味着:只有当控制量子比特的最终测量结果是 $|0\rangle$ 时,我们才保留这次执行的结果。后选择使得我们可以在酉回路框架中实现非酉操作(如矩阵的非酉线性组合)。

2.7 关键概念:块编码(Block Encoding)

块编码是理解整个 Quixer 架构的最重要的前置概念。我们将其单独放在第四章深入讲解。这里先给出直觉:

块编码是一种用更大的酉矩阵来『编码』一个任意(可能非酉)矩阵的技术。具体来说,如果对某个矩阵 $M$ 存在酉矩阵 $U_M$ 使得 $M$ 是 $U_M$ 的左上角子矩阵,即 $M = (\langle 0| \otimes I) U_M (|0\rangle \otimes I)$,我们就说 $U_M$ 是 $M$ 的一个块编码。


第三章:Quixer 宏观概览

目标:在深入技术细节之前,先建立对 Quixer 的全局理解。

3.1 一句话定义

Quixer(Quantum Transformer)是 Quantinuum 于 2024 年 6 月发布的量子 Transformer 模型。其核心创新在于:用两种量子计算原语——线性酉组合(LCU, Linear Combination of Unitaries)量子奇异值变换(QSVT, Quantum Singular Value Transformation)——完全替代了经典 Transformer 的点积自注意力机制。

3.2 Quixer 与 GPT 的本质区别

维度 GPT / 经典 Transformer Quixer
令牌混合方式 $\operatorname{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)V$ 点积注意力 LCU:酉变换的加权叠加
非线性来源 ReLU / GELU 前馈网络 QSVT:矩阵的多项式变换
表示空间 $\mathbb{R}^d$(实向量空间) $\mathbb{C}^{2^q}$(希尔伯特空间)
参数形式 实矩阵 $W_Q$, $W_K$, $W_V$ 复数权重 + 量子门旋转角度
混合机制 内容相关($QK^\top$ 相似度) 参数化酉回路(结构先验)

3.3 Quixer 的完整处理管线

---
config:
  theme: base
  themeVariables:
    primaryColor: '#FAFAFC'
    primaryTextColor: '#222222'
    primaryBorderColor: '#28749A'
    lineColor: '#28749A'
    secondaryColor: '#E9E9E9'
    tertiaryColor: '#FFFFFF'
---
flowchart TD
  INPUT["输入令牌
[w1,w2,,wn]
(窗口大小 = n)"] S1["步骤 1:经典词嵌入
Embedding(wi)wiRdemb"] S2["步骤 2:角度参数化
θi=WEwi (线性变换)
将这些角度作为 PQC 的旋转门参数"] S3["步骤 3:酉令牌嵌入 (Unitary Token Embedding)
Ui=PQC(θi)
每个令牌获得一个酉矩阵表示
经典模拟中:量子回路作用于|0q"] S4["步骤 4:LCU 令牌混合
M=j=0n1bj·Uj
其中 bj=eiγj|aj|2 (可训练的复数权重)
满足j|bj|=1"] S5["步骤 5:QSVT 非线性多项式变换 P(M)=cdMd+cd1Md1++c1M+c0I
多项式系数ck是可训练的"] S6["步骤 6:量子前馈网络 (Quantum Feed-Forward)
再次应用 PQC
共享参数的单层 Ansatz 14"] S7["步骤 7:测量
在所有量子比特上测量
Pauli-X、Y、Z 的期望值
3q 个实数测量值"] S8["步骤 8:经典输出投影
fout:3qvocab_size
两层 MLP:Linear → ReLU → Linear"] OUTPUT["输出 logits
↓ Softmax ↓
下一令牌预测"] INPUT --> S1 --> S2 --> S3 --> S4 --> S5 --> S6 --> S7 --> S8 --> OUTPUT classDef input fill:#FFFFFF,stroke:#28749A,stroke-width:1.5px,color:#222222 classDef classical fill:#FAFAFC,stroke:#28749A,stroke-width:1.5px,color:#222222 classDef quantum fill:#E9E9E9,stroke:#28749A,stroke-width:1.5px,color:#222222 classDef output fill:#FFFFFF,stroke:#28749A,stroke-width:1.5px,color:#222222 class INPUT,OUTPUT input class S1,S2,S8 classical class S3,S4,S5,S6,S7 quantum

3.4 为什么要这样设计?

Quixer 的设计哲学(引自原论文第 2.1 节):

"Our focus in this work is not to quantise the dot product self-attention, but instead propose a novel form of token mixing built from quantum primitives."

翻译:我们不试图用量子回路逐模块地『翻译』经典自注意力,而是从量子计算的基本原语出发,构建一种全新的令牌混合范式。

这意味着 Quixer 的思路是量子原生(Quantum-Native)的:从量子计算能高效做什么出发,而不是从经典 Transformer 有什么出发。

3.5 重要声明:经典模拟

Quixer 的官方 GitHub 仓库(github.com/Quantinuum/Quixer)提供的仅仅是经典模拟实现——所有量子操作都用 PyTorch 张量在 CPU/CUDA 上模拟,没有真正的量子硬件后端。这就像在普通计算机上运行量子回路的仿真器。截至 2026 年 5 月,虽然 Quantinuum 宣称已将 Quixer 部署到其 H 系列量子硬件上,但该声明缺乏独立的第三方验证。


第四章:块编码(Block Encoding)

目标:深入理解块编码——这是 LCU 和 QSVT 的基础。

4.1 动机:为什么需要块编码?

量子计算的基本操作是酉变换。但机器学习中涉及的许多矩阵操作——如线性组合、非线性激活——本质上是非酉的

块编码(Block Encoding)是一个巧妙的桥接技术:它允许我们用酉矩阵的左上角子矩阵来表示任意(可能非酉的)矩阵。

4.2 形式化定义

给定一个矩阵 $M$(不一定方阵,不一定酉),如果存在酉矩阵 $U$ 使得:

$$M = (\langle 0^a| \otimes I) \; U \; (|0^a\rangle \otimes I)$$

其中:

  • $a$ 是辅助量子比特的数量
  • $|0^a\rangle$ 是 $a$ 个辅助量子比特全部处于 $|0\rangle$ 的状态
  • 等式右端表示:将 $U$ 作用于 $|0^a\rangle \otimes |\psi\rangle$,然后在辅助量子比特上后选择 $|0^a\rangle$

则称 $U$ 是 $M$ 的一个块编码,$M$ 是 $U$ 的左上角子矩阵。

4.3 直观理解

想象 $U$ 是一个分块矩阵:

$$U = \begin{bmatrix} M & \cdot \\ \cdot & \cdot \end{bmatrix}$$

当我们:

  1. 把辅助量子比特初始化为 $|0^a\rangle$
  2. 把数据量子比特初始化为 $|\psi\rangle$
  3. 对整个系统施加 $U$
  4. 测量辅助量子比特,只保留结果为 $|0^a\rangle$ 的分支

那么数据量子比特经历的有效变换正好是 $M$:

$$|\psi\rangle \xrightarrow{\text{block-encoded } U} M|\psi\rangle \quad (\text{归一化前})$$

4.4 块编码的经典类比

这个思想并不是量子力学的专利。在经典计算中也有类似的做法:

  • 经典类比 1(嵌入矩阵):如果你想用正交矩阵表示非正交的线性变换,可以将其嵌入一个更高维的正交矩阵中。

  • 经典类比 2(条件执行):就像程序中的条件分支——

    if flag == 0:
        result = M @ vector  # 这种『如果前置条件满足就执行 M』的逻辑
    

    块编码用叠加态的线性性质将这种『条件执行』延拓为对所有分支同时运算。

  • 经典类比 3(分块矩阵):就像你可以在一个更大的矩阵中『隐藏』一个小的子矩阵,块编码在一个更大的酉矩阵中『隐藏』了非酉的 $M$,而量子力学的线性性和后选择机制将这种『隐藏』变成了可执行的回路。

4.5 块编码的关键性质

  1. 谱性质:如果 $M$ 的奇异值在 $[0, 1]$ 内,块编码存在且可用标准技术构造
  2. 多项式变换:如果我们能对块编码 $U_M$ 做某些操作,就能对整个 $M$ 做相应的矩阵多项式变换——这就是 QSVT 的核心思想
  3. 线性组合:如果我们能构造一系列矩阵的块编码,就能构造它们线性组合的块编码——这就是 LCU 的核心思想

第五章:线性酉组合(LCU, Linear Combination of Unitaries)

目标:掌握 LCU 的完整数学原理和回路实现。

5.1 回到经典 Transformer 的令牌混合

在经典 Transformer 中,每个令牌的输出是所有令牌 Value 的加权平均:

$$\text{Output}_i = \sum_{j=1}^n \alpha_{ij} V_j, \quad \sum_j \alpha_{ij} = 1$$

权重 $\alpha_{ij}$ 是 softmax 归一化的注意力分数。

5.2 LCU 的量子类比

Quixer 用酉变换的加权叠加来类比这种混合:

$$M = \sum_{j=0}^{n-1} b_j \, U_j$$

其中:

  • $U_j \in \mathbb{C}^{2^q \times 2^q}$ 是第 $j$ 个令牌对应的酉矩阵(由参数化量子回路生成)
  • $b_j \in \mathbb{C}$ 是可训练的复数权重
  • $n$ 是上下文窗口大小
  • 满足归一化条件:$\sum_j |b_j| = 1$

直觉类比:经典注意力是 $\sum \alpha_{ij} V_j$(向量的加权平均);LCU 令牌混合是 $\sum b_j U_j$(酉矩阵的复数加权线性组合)。$V_j$ 是向量,$U_j$ 是矩阵(更强大的表示)。

5.3 SELECT-PREPARE 回路:从数学到回路

LCU 的关键问题是:如何在量子回路中实现这种『酉变换的非酉组合』? 答案是通过 SELECT-PREPARE 回路架构。

准备阶段(PREPARE)

首先,构造一个酉变换 $U_{\text{PREP}}$ 作用于控制寄存器(大小为 $\lceil \log_2 n \rceil$ 个量子比特),将 $|0\ldots 0\rangle$ 准备为所需的系数叠加态:

$$U_{\text{PREP}} |0\rangle = |a\rangle = \sum_{j=0}^{n-1} a_j |j\rangle$$

其中 $a_j$ 是复数振幅,$|j\rangle$ 是控制寄存器的计算基态。

选择阶段(SELECT)

然后,构造一个受控酉变换 $U_{\text{SEL}}$:

$$U_{\text{SEL}} = \sum_{j=0}^{n-1} |j\rangle\langle j| \otimes U_j$$

解读:$U_{\text{SEL}}$ 的每一『块』对应控制寄存器的一个计算基态:

  • 当控制寄存器为 $|0\rangle$ 时,对数据寄存器施加 $U_0$
  • 当控制寄存器为 $|1\rangle$ 时,对数据寄存器施加 $U_1$
  • ...
  • 当控制寄存器为 $|n-1\rangle$ 时,对数据寄存器施加 $U_{n-1}$

完整的 LCU 回路 $U_M$

组合这两个操作:

$$U_M = (U_{\text{PREP}}^\dagger \otimes I) \; U_{\text{SEL}} \; (U_{\text{PREP}} \otimes I)$$

然后在控制寄存器上后选择 $|0\rangle$:

$$M = (\langle 0| \otimes I) \; U_M \; (|0\rangle \otimes I)$$

数学上可以证明(见论文附录 A.2 的引理 1):

$$M = (\langle 0| \otimes I) \; U_M \; (|0\rangle \otimes I) = \sum_{j=0}^{n-1} |a_j|^2 \, U_j$$

5.4 为什么要设计成这种形式:直观的数学推导

让我们一步一步追踪态在回路中的演化,理解为什么最终结果正好是 $\sum |a_j|^2 U_j$。

初始态:控制寄存器在 $|0\rangle$,数据寄存器在 $|\psi\rangle$: $$|\Phi_0\rangle = |0\rangle \otimes |\psi\rangle$$

步骤 1:应用 $U_{\text{PREP}} \otimes I$。控制寄存器变为叠加态 $|a\rangle = \sum a_j |j\rangle$: $$|\Phi_1\rangle = \left(\sum_{j=0}^{n-1} a_j |j\rangle\right) \otimes |\psi\rangle$$

步骤 2:应用 $U_{\text{SEL}}$。每个 $|j\rangle$ 分支上数据寄存器被施加对应的 $U_j$: $$|\Phi_2\rangle = \sum_{j=0}^{n-1} a_j |j\rangle \otimes U_j |\psi\rangle$$

步骤 3:应用 $U_{\text{PREP}}^\dagger \otimes I$。这『解纠缠』控制寄存器。考虑后选择 $|0\rangle$: $$|\Phi_3\rangle = |0\rangle \otimes \left(\sum_{j=0}^{n-1} a_j \langle 0|U_{\text{PREP}}^\dagger|j\rangle \cdot U_j |\psi\rangle\right)$$

由于 $U_{\text{PREP}}^\dagger |j\rangle$ 投影到 $\langle 0|$ 给出 $a_j^*$(因为 $U_{\text{PREP}}|0\rangle = \sum a_j|j\rangle$),所以: $$\langle 0|U_{\text{PREP}}^\dagger|j\rangle = a_j^*$$

因此: $$|\Phi_3\rangle = |0\rangle \otimes \sum_{j=0}^{n-1} |a_j|^2 \, U_j |\psi\rangle$$

后选择成功概率 $p_{\text{success}} = \|\sum_{j} |a_j|^2 U_j |\psi\rangle\|^2$。

这完美地实现了:酉矩阵的线性组合,系数为 $|a_j|^2$(非负实数)。

5.5 复数系数的引入

但实数系数 $|a_j|^2$ 的表达力有限!为了实现复数系数 $b_j \in \mathbb{C}$,Quixer 在每个令牌的酉回路前加入了一个相位门:

$$U_j' = e^{i\gamma_j} \cdot U_j$$

这样,最终的有效混合变为:

$$M = \sum_{j=0}^{n-1} \underbrace{e^{i\gamma_j} |a_j|^2}_{b_j \in \mathbb{C}} \, U_j$$

其中 $b_j = e^{i\gamma_j} |a_j|^2$ 是可训练的复数系数,满足 $\sum |b_j| = 1$。

5.6 后选择概率与资源开销

虽然 LCU 回路使用了辅助量子比特和测量的组合实现了看似非酉的操作,但这并非没有代价:

  • 后选择成功率 $p_{\text{success}} = \|\sum b_j U_j|\psi\rangle\|^2$ 并不总是 1
  • 在实际量子硬件上,你需要执行 $O(1/p_{\text{success}})$ 次测量才能获得一个有效样本
  • 当系数 $|b_j|$ 分布极不均匀(某个系数远大于其他系数)时,成功率可能接近 1;当系数分布均匀时,成功率可能较低

5.7 LCU 与经典注意力的深层对比

属性 经典注意力 LCU 令牌混合
混合对象 Value 向量 $V_j$ 酉矩阵 $U_j$
混合权重 $$\operatorname{softmax}(QK^\top/\sqrt{d_k})$$(内容相关) $b_j$(可训练参数)
权重空间 实数、正数、和为 1 复数、L1 范数为 1
复杂度 $O(n^2d)$(经典计算) $$O(n \cdot 4^q)$$(经典模拟)或 $$O(n \cdot \mathrm{poly}(q))$$(量子实现)
信息传播 点积交互 酉变换的干涉效应

5.8 一个具体的数值例子

假设窗口大小 $n = 3$,意味着我们有 3 个令牌,每个令牌有一个对应的酉矩阵 $U_0, U_1, U_2$。

# 经典注意力(抽象表示)
Q, K, V = X @ W_Q, X @ W_K, X @ W_V
attn = softmax(Q @ K.T / sqrt(d_k))
output = attn @ V  # 每个输出是 V 的加权平均

# Quixer 的 LCU(抽象表示)
U = [U_0, U_1, U_2]          # 每个令牌的酉矩阵
b = [0.5+0.0j, 0.3+0.1j, 0.1+0.0j]  # 可训练复数权重
M = b[0]*U[0] + b[1]*U[1] + b[2]*U[2]  # 酉矩阵的线性组合
output_state = M @ |ψ_in⟩     # 混合算子作用于量子态

注意关键区别:

  • 经典注意力中,softmax 的归一化是对『行』施加的(每个查询的输出权重之和为 1)
  • LCU 中,归一化是对整个系数向量施加的($\sum|b_j|=1$)
  • 这意味着 LCU 对所有令牌使用同一组全局混合权重,而不是每个令牌有独立的注意力分布——这是一种全局令牌混合而非内容相关的自适应混合

5.9 LCU 的经典模拟:真实代码

下面直接执行 quixer_model.py 里的 apply_linear_combination_of_unitaries(先看教学性复述,再执行真实源码并对比)。这里用到的酉矩阵 $U_j$ 就是上面 2.5 节里已经跑过的 Ansatz 14 回路。

In [4]:
def apply_linear_combination_of_unitaries(
    initial_states,          # [batch_size, 2**n_qubits]
    pqc_parameters,          # [batch_size, n_tokens, n_pqc_params]
    parameterized_quantum_circuit,  # TorchQuantum GeneralEncoder
    torchquantum_device,
    n_qubits,
    lcu_coefficients,        # [batch_size, n_tokens], 复数
):
    """
    经典模拟 LCU: M|ψ⟩ = (Σ b_j U_j) |ψ⟩
    
    关键思路:
    1. 将初始态复制 n_tokens 份
    2. 每份分别施加对应令牌的 PQC
    3. 用复数系数加权求和
    """
    batch_size, n_tokens, _ = pqc_parameters.shape
    
    # 复制初始态:每个 batch 元素的态复制 n_tokens 次
    states = initial_states.repeat(1, n_tokens).reshape(-1, 2**n_qubits)
    # states: [batch_size * n_tokens, 2**n_qubits]
    
    # 展平 PQC 参数
    flat_params = pqc_parameters.reshape(-1, pqc_parameters.shape[-1])
    # flat_params: [batch_size * n_tokens, n_pqc_params]
    
    # 将态加载到量子设备中
    torchquantum_device.reset_batch_size(batch_size * n_tokens)
    
    # 对每份态施加对应令牌的 PQC
    parameterized_quantum_circuit(
        torchquantum_device, 
        flat_params.float()
    )
    
    # 提取演化后的态
    evolved = torchquantum_device.get_states_1d()
    evolved = evolved.reshape(batch_size, n_tokens, 2**n_qubits)
    
    # 加权求和: Σ b_j |ψ_j⟩
    mixed = torch.einsum("bti,bt->bi", evolved, lcu_coefficients)
    
    return mixed  # [batch_size, 2**n_qubits]

关于上面的复述版本:它在 torchquantum_device.reset_batch_size(...) 这一步使用了一个真实 torchquantum.QuantumDevice API 中并不存在的方法(真实源码用的是 set_states(...)),如果直接调用会报 AttributeError。这是复述用来突出算法思路时引入的简化偏差。下面直接执行真实源码中的 apply_linear_combination_of_unitaries,用一组很小的样例输入(n_qubits=3window=4batch=2)观察真实运行结果,包括函数内部已有的 shape 调试 print

In [5]:
# 真实执行 quixer_model.py 中的 apply_linear_combination_of_unitaries
DEMO_N_QUBITS = 3
DEMO_WINDOW = 4
DEMO_BATCH = 2
DEMO_ANSATZ_LAYERS = 1

demo_pqc = tq.GeneralEncoder(
    qm.ansatz_14_torchquantum_specification(DEMO_N_QUBITS, DEMO_ANSATZ_LAYERS)
)
demo_qdev = tq.QuantumDevice(n_wires=DEMO_N_QUBITS, bsz=DEMO_BATCH * DEMO_WINDOW)

# 初始态 |000>,对每个 batch 元素都一样
demo_initial_states = torch.zeros(DEMO_BATCH, 2**DEMO_N_QUBITS, dtype=torch.complex64)
demo_initial_states[:, 0] = 1.0

# 随机的 PQC 角度参数与 LCU 系数(真实使用中来自 embedding_to_angles / 可训练参数)
demo_pqc_parameters = torch.rand(DEMO_BATCH, DEMO_WINDOW, 4 * DEMO_N_QUBITS * DEMO_ANSATZ_LAYERS)
demo_lcu_coefficients = torch.rand(DEMO_BATCH, DEMO_WINDOW, dtype=torch.complex64)

lcu_output = qm.apply_linear_combination_of_unitaries(
    demo_initial_states,
    demo_pqc_parameters,
    demo_pqc,
    demo_qdev,
    DEMO_N_QUBITS,
    demo_lcu_coefficients,
)

print("\napply_linear_combination_of_unitaries 最终返回 shape:", lcu_output.shape, lcu_output.dtype)
print("每个 batch 元素的态范数(LCU 后一般不再是 1):", torch.linalg.vector_norm(lcu_output, dim=-1))
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([2, 4, 12]) lcu_coefficients=torch.Size([2, 4])
[Quixer] apply_lcu states=torch.Size([2, 4, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([2, 8])

apply_linear_combination_of_unitaries 最终返回 shape: torch.Size([2, 8]) torch.complex64
每个 batch 元素的态范数(LCU 后一般不再是 1): tensor([2.6517, 3.5883])

设计洞察:经典模拟中并没有真正构造 SELECT-PREPARE 回路。代码采用了一种更高效的方式——直接枚举每个酉矩阵 $U_j$,分别计算 $U_j \lvert \psi \rangle$,然后做复数加权的线性求和。这是因为在经典计算机上,显式计算每个 $2^q$ 维态矢量并求和比模拟一个完整的 LCU 回路(需要额外的 $\log_2 n$ 个控制比特)开销更小。在真实量子计算机上,你才需要 SELECT-PREPARE 来利用量子并行性。


第六章:量子奇异值变换(QSVT, Quantum Singular Value Transformation)

目标:掌握 QSVT 的原理,理解它如何为量子回路提供非线性。

6.1 动机:量子回路缺少非线性

在经典深度学习中,网络成功的一个关键因素是非线性激活函数(ReLU、GELU、tanh 等)。没有非线性,多层网络等价于单层线性变换。

但量子力学本质上是线性的——薛定谔方程是线性方程,量子门是酉(线性)变换。那么如何在量子回路中引入非线性?

QSVT 提供了一种方法:对块编码的矩阵施加多项式变换。多项式是非线性的(在矩阵意义上:$M^2 \neq cM$),因此 QSVT 为量子计算提供了一种『矩阵级』的非线性机制。

6.2 QSVT 的数学定义

给定一个矩阵 $M$ 和它的块编码酉矩阵 $U_M$,QSVT 允许我们构造一个回路来实现 $M$ 的任意奇偶性匹配的多项式

$$P_c(M) = c_d M^d + c_{d-1} M^{d-1} + \cdots + c_1 M + c_0 I$$

条件是:

  1. 有界性:$|P_c(x)| \leq 1$ 对所有 $x \in [-1, 1]$ 成立(多项式在 $[-1,1]$ 上有界)
  2. 奇偶性约束:$\text{parity}(P_c) = d \bmod 2$(多项式的奇偶性必须与其次数一致)

6.3 QSVT 回路结构

QSVT 的核心思想是:交替应用 $U_M$、$U_M^\dagger$ 和投影算子 $\Pi_\phi$

奇数次多项式($d$ 为奇数)

$$\Pi_{\phi_1} U_M \left[ \prod_{k=1}^{(d-1)/2} \Pi_{\phi_{2k}} U_M^\dagger \Pi_{\phi_{2k+1}} U_M \right]$$

偶数次多项式($d$ 为偶数)

$$\prod_{k=1}^{d/2} \Pi_{\phi_{2k-1}} U_M^\dagger \Pi_{\phi_{2k}} U_M$$

其中:

  • $\Pi_\phi = e^{i\phi(2\Pi - I)} = e^{i\phi Z}$ 是辅助量子比特上的相位旋转
  • $\{\phi_1, \phi_2, \ldots, \phi_d\}$ 是与多项式系数 $\{c_0, c_1, \ldots, c_d\}$ 一一对应的相位角
  • $U_M$ 块编码 $M$,$U_M^\dagger$ 块编码 $M^\dagger$

6.4 相位角度 $\{\phi_k\}$ 的计算

从多项式系数 $\{c_k\}$ 到 QSVT 相位角度 $\{\phi_k\}$ 的映射是非平凡的——需要通过数值算法计算。这个算法在量子计算领域是标准化的,常用工具(如 QSPPACK、pyqsp)提供实现。

关键理解:在 Quixer 的经典模拟中,由于模拟的是『QSVT 的数学效果』而非『QSVT 回路』,因此不需要计算相位角度——直接用多项式系数 $c_k$ 做代数计算即可(详见本章末尾的真实代码)。

6.5 奇偶性约束的解除

QSVT 的核心约束是:多项式的奇偶性必须等于其次数的奇偶性。即:

  • 如果 $d = 3$(奇数),那么 $P_c(x)$ 只能包含奇次项:$c_3 x^3 + c_1 x$($c_2 = c_0 = 0$)
  • 如果 $d = 2$(偶数),那么 $P_c(x)$ 只能包含偶次项:$c_2 x^2 + c_0 I$($c_1 = 0$)

如何获得任意奇偶性的多项式? 论文提供了一种技巧:引入一个额外的辅助量子比特来控制两种奇偶性多项式的线性组合:

$$P_{\text{arbitrary}}(M) = \alpha \cdot P_{\text{even}}(M) + \beta \cdot P_{\text{odd}}(M)$$

这本质上是用 LCU 的方法组合两个 QSVT 回路——量子原语的组合!

6.6 QSVT 与经典非线性激活的类比

经典 量子(QSVT)
σ(Wx) = ReLU(Wx) P(M) = c₀I + c₁M + c₂M² + ...
逐元素非线性 矩阵级多项式非线性
作用于向量 作用于整个矩阵
简单的代数操作 需要块编码 + 交替门回路
计算开销可忽略 多项式次数 d 倍于一次块编码调用

核心理解:QSVT 的『非线性』不是经典意义上的逐元素非线性(如对每个元素取 max(0, x)),而是矩阵多项式带来的非线性。$M^2 \neq cM$(除非 M 是数量矩阵的倍数),所以即使二次多项式 $M^2$ 也是『矩阵非线性』的。

6.7 一个简单的例子:d=2 的 QSVT

假设 $M$ 是一个 2×2 的 Hermitian 矩阵:

$$M = \begin{bmatrix} 0.5 & 0.2 \\ 0.2 & -0.3 \end{bmatrix}$$

二次 QSVT 的变换($d=2$,$c_0=0.1, c_1=0, c_2=0.9$,满足偶次约束):

$$P(M) = 0.1 I + 0.9 M^2$$

手动计算: $$M^2 = M \cdot M = \begin{bmatrix} 0.29 & 0.04 \\ 0.04 & 0.13 \end{bmatrix}$$

$$P(M) = 0.1\begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix} + 0.9\begin{bmatrix} 0.29 & 0.04 \\ 0.04 & 0.13 \end{bmatrix} = \begin{bmatrix} 0.361 & 0.036 \\ 0.036 & 0.217 \end{bmatrix}$$

注意 $P(M)$ 与 $M$ 的关系不是简单的缩放——这就是矩阵多项式的『非线性』效果。它改变了矩阵的谱(特征值分布),这与经典 FFN 中 ReLU 改变向量激活模式的效果类似。

6.8 QSVT 的经典模拟:真实代码

下面直接执行 quixer_model.py 里的 apply_qsvt_and_lcu(先看教学性复述,再执行真实源码),复用上面 5.9 节 LCU 部分构造的样例输入。

In [6]:
def apply_qsvt_and_lcu(
    initial_states,           # [batch_size, 2**n_qubits]
    pqc_parameters,
    parameterized_quantum_circuit,
    torchquantum_device,
    n_qubits,
    lcu_coefficients,
    qsvt_polynomial_coefficients,  # [degree + 1]
):
    """
    经典模拟 QSVT+LCU: P(M)|ψ⟩ = (Σ c_k M^k) |ψ⟩
    
    通过反复调用 apply_lcu 来构建 M 的幂次。
    """
    # 初始化累加器:c_0 I |ψ⟩ = c_0 |ψ⟩
    accumulated = qsvt_polynomial_coefficients[0] * initial_states
    
    # monomial 追踪 M^{k-1} |ψ⟩
    monomial = initial_states
    
    for k in range(1, len(qsvt_polynomial_coefficients)):
        # M^k |ψ⟩ = M · (M^{k-1} |ψ⟩)
        monomial = apply_lcu(
            monomial, pqc_parameters, 
            parameterized_quantum_circuit,
            torchquantum_device, n_qubits, lcu_coefficients
        )
        
        # 累加:c_k · M^k |ψ⟩
        accumulated = accumulated + qsvt_polynomial_coefficients[k] * monomial
    
    # 归一化(按系数 L1 范数)
    norm = torch.linalg.vector_norm(qsvt_polynomial_coefficients, ord=1)
    
    return accumulated / norm

关于上面的复述版本:其内部调用了 apply_lcu(...),但复述版本自己定义的函数名其实叫 apply_linear_combination_of_unitaries——apply_lcu 这个名字在这段代码里从未被定义过,如果真的调用会直接报 NameError。下面改为执行真实源码中的 apply_qsvt_and_lcudegree=2,即调用 2 次 LCU),继续用上一步的样例输入,可以看到函数内部已有的调试 print 完整展示了 QSVT 多项式逐项累加的过程。

In [7]:
# 真实执行 quixer_model.py 中的 apply_qsvt_and_lcu(复用上一个 cell 的 demo_* 样例输入)
DEMO_DEGREE = 2
demo_qsvt_coefficients = torch.rand(DEMO_DEGREE + 1)

qsvt_lcu_output = qm.apply_qsvt_and_lcu(
    demo_initial_states,
    demo_pqc_parameters,
    demo_pqc,
    demo_qdev,
    DEMO_N_QUBITS,
    demo_lcu_coefficients,
    demo_qsvt_coefficients,
)

print("\napply_qsvt_and_lcu 最终返回 shape:", qsvt_lcu_output.shape, qsvt_lcu_output.dtype)
print("QSVT 多项式系数:", demo_qsvt_coefficients)
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([2, 8]) pqc_parameters=torch.Size([2, 4, 12]) lcu_coefficients=torch.Size([2, 4]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([2, 4, 12]) lcu_coefficients=torch.Size([2, 4])
[Quixer] apply_lcu states=torch.Size([2, 4, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([2, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([2, 8]) accumulated_state=torch.Size([2, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([2, 4, 12]) lcu_coefficients=torch.Size([2, 4])
[Quixer] apply_lcu states=torch.Size([2, 4, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([2, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([2, 8]) accumulated_state=torch.Size([2, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([2, 8])

apply_qsvt_and_lcu 最终返回 shape: torch.Size([2, 8]) torch.complex64
QSVT 多项式系数: tensor([0.2464, 0.5288, 0.6357])

为什么这样设计? 经典模拟中直接使用多项式求值的代数公式而非构造 QSVT 回路,避免了相位角度 $\{\phi_k\}$ 的繁琐计算。但对于degree = 3(论文默认值),这意味着需要调用 apply_lcu1+2+3 = 6 次。模型的参数数量不直接增加(只有 d+1 个可训练系数),但计算量随 d 线性增长。


第七章:Quixer 完整架构详解

目标:逐步骤理解 Quixer 从输入到输出的完整数据流。

7.1 架构全景图

Quixer 架构全景图

---
config:
  theme: base
  themeVariables:
    primaryColor: '#FAFAFC'
    primaryTextColor: '#222222'
    primaryBorderColor: '#28749A'
    lineColor: '#28749A'
    secondaryColor: '#E9E9E9'
    tertiaryColor: '#FFFFFF'
---
flowchart TD

    INPUT["输入序列:
    [The, cat, sat, on, mat, <eos>]"]

    S1["1. TOKEN EMBEDDING
wiEmbedding(wi)demb 每个令牌映射到 demb 维实向量"] S2["2. ANGLE PARAMETRIZATION
θi=Dropout(WE·wi) 实向量 → PQC 旋转角度 θinpqc_params"] S3["3. UNITARY TOKEN EMBEDDING
对每个令牌 j: • 初始化为 |00 • 施加 PQC(θj) → 获得 |ψj|ψj 编码了令牌 j 的量子表示"] S4["4. LCU TOKEN MIXING
M=j=0n1bj·Uj bj=eiγj|aj|2 |bj|=1
在经典模拟中的等价操作: 对 |ψmix=M|00 直接计算"] S5["5. QSVT NONLINEAR TRANSFORM
P(M)|00=(ckMk)|00
多项式次数 = qsvt_polynomial_degree 系数 ck 可训练"] S6["6. L2 NORMALIZATION
|ψnorm=|ψ|ψ"] S7["7. QUANTUM FEED-FORWARD
单层 Ansatz 14 PQC |ψout=UFF(θshared)|ψnorm"] S8["8. MEASUREMENT
所有量子比特上测量: XiYiZi   i=0q13q 个实数期望值"] S9["9. CLASSICAL OUTPUT HEAD
fout:3qvocab_size
Linear(3qdemb) → ReLU → Linear(dembvocab_size)"] OUTPUT["输出:词汇表大小的 logits → 下一令牌概率"] INPUT --> S1 --> S2 --> S3 --> S4 --> S5 --> S6 --> S7 --> S8 --> S9 --> OUTPUT classDef input fill:#FFFFFF,stroke:#28749A,stroke-width:1.5px,color:#222222 classDef classical fill:#FAFAFC,stroke:#28749A,stroke-width:1.5px,color:#222222 classDef quantum fill:#E9E9E9,stroke:#28749A,stroke-width:1.5px,color:#222222 classDef output fill:#FFFFFF,stroke:#28749A,stroke-width:1.5px,color:#222222 class INPUT,OUTPUT input class S1,S2,S9 classical class S3,S4,S5,S6,S7,S8 quantum

7.2 步骤详解

步骤 1-2:经典词嵌入 + 角度参数化

这是模型中最『经典』的部分。输入令牌索引通过标准的 nn.Embedding 映射为 dense 向量,然后通过一个线性层映射为 PQC 的旋转参数:

# 伪代码
x: [batch_size, n_tokens]   # 令牌索引
emb = embedding(x)           # [batch_size, n_tokens, d_emb]
angles = linear(dropout(emb))  # [batch_size, n_tokens, n_pqc_params]

其中 n_pqc_params = 4 * n_qubits * n_ansatz_layers(Ansatz 14 每个量子比特每层 4 个旋转参数)。

步骤 3:酉令牌嵌入

对于每个令牌 $j$,用其角度 $\theta_j$ 参数化一个 PQC(Ansatz 14),将其作用于初始态 $|0\ldots0\rangle$:

$$|\psi_j\rangle = \text{PQC}(\theta_j) |0\ldots0\rangle$$

在经典模拟中,这意味着对 $|0\ldots0\rangle$(即振幅向量 [1, 0, 0, ..., 0])施加一系列旋转门和受控旋转门,得到 $2^q$ 维的复数态矢量。

# 伪代码(经典模拟中的实际计算)
initial_state = torch.zeros(batch_size, 2**n_qubits, dtype=torch.complex64)
initial_state[:, 0] = 1.0 + 0.0j  # |0...0⟩

# 对每个令牌应用 PQC
states = apply_pqc(initial_state, angles)  # [batch_size, n_tokens, 2**n_qubits]

下面用真实的 ansatz_14_torchquantum_specification 输出,通过 TorchQuantum 自带的 tq2qiskit_parameterized(Quixer 的依赖里本来就有 qiskit)把它转换成一个参数化的 Qiskit QuantumCircuit,再用 Qiskit 内置的 circuit.draw() 画图——不需要手写任何布局代码,门的先后顺序、控制/目标比特、并行度都由 Qiskit 自动处理。这里用的是 Quixer 论文/run.py 实际默认的量子比特数 n_qubits=6

In [8]:
from torchquantum.plugin import tq2qiskit_parameterized

n_qubits_demo = 6
ansatz_14_spec = qm.ansatz_14_torchquantum_specification(n_qubits_demo, layers=1)

qdev_demo = tq.QuantumDevice(n_wires=n_qubits_demo, bsz=1)
ansatz_14_circuit, ansatz_14_params = tq2qiskit_parameterized(qdev_demo, ansatz_14_spec)

print(f"{len(ansatz_14_params)} 个参数,回路深度 = {ansatz_14_circuit.depth()}")
ansatz_14_circuit.draw(output="mpl", style={"name": "bw"})
24 个参数,回路深度 = 13
Out[8]:

步骤 4:LCU 令牌混合

在经典模拟中,这一步直接计算混合:

# 伪代码
# 将初始态复制 n_tokens 份
replicated = initial_state.repeat(1, n_tokens)  # [B, n_tokens * 2**n_qubits]

# 对每份施加对应的 PQC(不同令牌不同参数)
evolved = apply_pqc_all_tokens(replicated, all_angles)
evolved = evolved.reshape(B, n_tokens, 2**n_qubits)

# 加权求和
mixed = einsum("bti,bt->bi", evolved, lcu_coefficients)

步骤 5:QSVT 非线性变换

在经典模拟中,QSVT 通过迭代应用 LCU 来构建 $M^k$:

# 伪代码:P(M)|0⟩ = Σ c_k M^k |0⟩
accumulated = c[0] * initial_state     # 常数项 c_0 I
monomial = initial_state

for k in range(1, degree + 1):
    # M^k |0⟩ = M · M^{k-1} |0⟩
    monomial = apply_lcu(monomial, ...)  # 乘以 M
    accumulated += c[k] * monomial       # 累加 c_k M^k |0⟩

# 归一化
result = accumulated / norm(c, ord=1)

步骤 6:L2 归一化

LCU + QSVT 的『有效计算』本质上是非酉的(因为是酉矩阵的加权组合,不保持范数),所以需要对量子态做 L2 归一化。这是一个关键操作——在真正的量子硬件中,这种归一化需要利用后选择概率进行,成本较高。经典模拟中直接向量归一化即可。

在 PyTorch 代码中实现为:

state = torch.nn.functional.normalize(state, dim=-1)

步骤 7:量子前馈网络

一个共享参数的单层 PQC 作用在所有 batch 的量子态上:

# 伪代码
shared_params = ff_params.repeat(1, batch_size)
final_state = apply_single_layer_pqc(normalized_state, shared_params)

这里的设计很重要:与令牌嵌入的 PQC(每个令牌独立参数)不同,前馈 PQC 使用单一参数集,类似于经典 FFN 对每个位置使用相同的权重矩阵。

步骤 8:测量

对每个量子比特测量 Pauli-X, Y, Z 的三个期望值:

$$e_X^{(i)} = \langle \psi | X_i | \psi \rangle$$$$e_Y^{(i)} = \langle \psi | Y_i | \psi \rangle$$$$e_Z^{(i)} = \langle \psi | Z_i | \psi \rangle$$

总共 $3q$ 个实数值。这三个期望值形成了一个完整的单量子比特态的布洛赫球表示(Bloch Vector)。

# 伪代码
measurements = []
for qubit in range(n_qubits):
    measurements.append(measure_x(qubit))  # X 期望值
    measurements.append(measure_y(qubit))  # Y 期望值
    measurements.append(measure_z(qubit))  # Z 期望值
# 结果: [batch_size, 3 * n_qubits]

为什么测量 X, Y, Z 三者? 经典上,一个量子比特的密度矩阵可以写作 $\rho = \frac{1}{2}(I + xX + yY + zZ)$,其中 $(x, y, z) = (\langle X\rangle, \langle Y\rangle, \langle Z\rangle)$ 完全确定了单量子比特态。因此这三个期望值提供了量子态的完整局部信息。

步骤 9:经典输出投影

$$f_{\text{out}}(x) = W_2 \cdot \text{ReLU}(W_1 \cdot x + b_1) + b_2$$

两层 MLP 将 $3q$ 维的测量向量映射到 vocab_size 维的 logits。

# 代码
output = Sequential(
    Linear(3*n_qubits → embedding_dimension),
    ReLU(),
    Linear(embedding_dimension → vocabulary_size)
)(measurements)  # [batch_size, vocabulary_size]

7.3 Quixer 的返回值

Quixer 的 forward 方法返回一个元组 (logits, mean_prob)

  • logits: [batch_size, vocabulary_size] — 用于 CrossEntropyLoss 的标准分类 logits
  • mean_prob: 标量 — QSVT+LCU 之后、量子前馈网络之前的态矢量的 L2 范数的 batch 平均值。论文将其称为『最终概率』(final probability),它反映了通过 LCU+QSVT 混合后有多少『概率质量』得以保留(因为非酉操作会导致范数衰减)。

7.4 完整实现:Quixer 类(真实代码)

把上面 7.1-7.3 节讲的 9 个步骤组装起来,就是 quixer_model.py 里的 Quixer 类。下面先看一份教学性复述(__init__+forward),再导入并真实运行 quixer_model.py 里的原始实现,做一次端到端的前向传播冒烟测试。

In [9]:
class Quixer(torch.nn.Module):
    def __init__(
        self,
        n_qubits: int,                   # 量子比特数(默认 6)
        n_tokens: int,                   # 上下文窗口大小(默认 32)
        qsvt_polynomial_degree: int,     # QSVT 多项式次数(默认 3)
        n_ansatz_layers: int,            # PQC 层数(默认 4)
        vocabulary_size: int,            # 词表大小
        embedding_dimension: int,        # 嵌入维度(默认 512)
        dropout: float,                  # Dropout 率(默认 0.2)
        batch_size: int,                 # 批次大小(默认 20)
        device: torch.device,
    ):
        super().__init__()
        
        # === 参数推导 ===
        n_pqc_parameters = 4 * n_qubits * n_ansatz_layers
        n_polynomial_coefficients = qsvt_polynomial_degree + 1
        
        # === 经典嵌入 + 角度参数化 ===
        self.embedding = nn.Embedding(vocabulary_size, embedding_dimension)
        self.embedding_to_angles = nn.Linear(
            embedding_dimension, n_pqc_parameters
        )
        self.dropout = nn.Dropout(dropout)
        
        # === 量子设备(经典模拟) ===
        self.torchquantum_device = tq.QuantumDevice(
            n_wires=n_qubits, bsz=batch_size
        )
        
        # === 令牌 PQC(Ansatz 14) ===
        token_ansatz = ansatz_14_torchquantum_specification(
            n_qubits, n_ansatz_layers
        )
        self.token_pqc = tq.GeneralEncoder(token_ansatz)
        
        # === 可训练参数 ===
        # QSVT 多项式系数(实数)
        self.qsvt_coefficients = nn.Parameter(
            torch.randn(n_polynomial_coefficients)
        )
        # LCU 混合系数(复数!)
        self.lcu_coefficients = nn.Parameter(
            torch.randn(n_tokens, dtype=torch.complex64)
        )
        
        # === 量子前馈网络 ===
        ff_ansatz = ansatz_14_torchquantum_specification(n_qubits, layers=1)
        self.quantum_feedforward = tq.GeneralEncoder(ff_ansatz)
        self.ff_parameters = nn.Parameter(
            torch.randn(1, n_pqc_parameters)
        )
        
        # === 测量 ===
        self.measure_all_x_y_z = tq.MeasureMultipleTimes(
            [list(range(n_qubits))] * 3,  # 三组:X, Y, Z
            ["pauli_x", "pauli_y", "pauli_z"]
        )
        
        # === 经典输出头 ===
        self.output_head = nn.Sequential(
            nn.Linear(3 * n_qubits, embedding_dimension),
            nn.ReLU(),
            nn.Linear(embedding_dimension, vocabulary_size),
        )
    
    def forward(self, x):
        """
        x: [batch_size, n_tokens] — 令牌索引
        返回: (logits, mean_probability)
        """
        batch_size = x.shape[0]
        
        # === 1. LCU 系数准备 ===
        lcu_coeffs = self.lcu_coefficients.repeat(batch_size, 1)
        lcu_coeffs = lcu_coeffs / lcu_coeffs.abs().sum(dim=1, keepdim=True)
        # L1 归一化:Σ |b_j| = 1
        
        # === 2. 经典嵌入 + 角度参数化 ===
        emb = self.embedding(x)              # [B, n_tokens, d_emb]
        angles = self.embedding_to_angles(
            self.dropout(emb)
        )                                    # [B, n_tokens, n_pqc_params]
        
        # === 3. 初始量子态 |0...0⟩ ===
        initial_state = torch.zeros(
            batch_size, 2**self.n_qubits, 
            dtype=torch.complex64, device=x.device
        )
        initial_state[:, 0] = 1.0 + 0.0j
        
        # === 4. QSVT + LCU:核心量子令牌混合 ===
        qsvt_lcu_state = apply_qsvt_and_lcu(
            initial_state, angles, self.token_pqc,
            self.torchquantum_device, self.n_qubits,
            lcu_coeffs, self.qsvt_coefficients,
        )
        
        # === 5. 记录后选择概率 ===
        probabilities = torch.linalg.vector_norm(
            qsvt_lcu_state, dim=-1
        )  # L2 范数 × 1
        
        # === 6. L2 归一化 ===
        qsvt_lcu_state = torch.nn.functional.normalize(
            qsvt_lcu_state, dim=-1
        )
        
        # === 7. 量子前馈网络 ===
        # 将归一化态加载到量子设备
        self.torchquantum_device.reset_batch_size(batch_size)
        self.torchquantum_device.set_states(qsvt_lcu_state)
        
        # 应用单层 PQC
        ff_params = self.ff_parameters.repeat(1, batch_size)
        self.quantum_feedforward(
            self.torchquantum_device, ff_params.float()
        )
        
        # === 8. 测量 ===
        expectation_values = self.measure_all_x_y_z(
            self.torchquantum_device
        )
        # 重整为 [batch_size, 3 * n_qubits]
        measurements = expectation_values \
            .reshape(3, batch_size, self.n_qubits) \
            .moveaxis(0, 1) \
            .reshape(batch_size, -1)
        
        # === 9. 经典输出投影 ===
        logits = self.output_head(measurements)
        
        return logits, probabilities.mean()

关于上面的复述版本:作为教学示意,它省略/简化了一些实现细节——例如从未保存 self.n_qubitsforward 里却用到了它)、measure_all_x_y_z 的构造方式也和真实 tq.MeasureMultipleTimes 的调用签名不同——如果直接实例化并跑 forward 会报错。下面导入并真实运行 quixer_model.py 里的 Quixer 类本身,使用缩小的超参数(n_qubits=3, n_tokens=8, qsvt_polynomial_degree=2, n_ansatz_layers=1, embedding_dimension=16,对比论文默认的 n_qubits=6, n_tokens=32, degree=3, n_ansatz_layers=4, embedding_dimension=512)做一次前向传播冒烟测试——这组缩小的超参数也是本教程第九章训练演示会用到的配置。

In [10]:
# 真实执行 quixer_model.py 中的 Quixer 类(缩小超参数,用于本教程贯穿始终的演示)
QUIXER_DEMO_HPARAMS = dict(
    n_qubits=3,
    n_tokens=8,               # 上下文窗口大小,即 run.py 中的 "window"
    qsvt_polynomial_degree=2,
    n_ansatz_layers=1,
    embedding_dimension=16,
    dropout=0.1,
    batch_size=4,
)
DEMO_VOCAB_SIZE = 50  # 这里只是冒烟测试,用一个玩具词表大小;第九章会换成真实 PTB 词表大小

quixer_demo_model = qm.Quixer(
    n_qubits=QUIXER_DEMO_HPARAMS["n_qubits"],
    n_tokens=QUIXER_DEMO_HPARAMS["n_tokens"],
    qsvt_polynomial_degree=QUIXER_DEMO_HPARAMS["qsvt_polynomial_degree"],
    n_ansatz_layers=QUIXER_DEMO_HPARAMS["n_ansatz_layers"],
    vocabulary_size=DEMO_VOCAB_SIZE,
    embedding_dimension=QUIXER_DEMO_HPARAMS["embedding_dimension"],
    dropout=QUIXER_DEMO_HPARAMS["dropout"],
    batch_size=QUIXER_DEMO_HPARAMS["batch_size"],
    device=torch.device("cpu"),
)

demo_token_ids = torch.randint(
    0, DEMO_VOCAB_SIZE, (QUIXER_DEMO_HPARAMS["batch_size"], QUIXER_DEMO_HPARAMS["n_tokens"])
)
demo_logits, demo_mean_prob = quixer_demo_model(demo_token_ids)

n_params = sum(p.numel() for p in quixer_demo_model.parameters())
print("\n真实 Quixer 类前向传播结果:")
print("  logits.shape       =", demo_logits.shape)
print("  mean_probability   =", demo_mean_prob.item())
print("  可训练参数总数      =", n_params)
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([4, 8]) pqc_parameters=torch.Size([4, 8, 12]) lcu_coefficients=torch.Size([4, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([32, 8]) pqc_parameters=torch.Size([4, 8, 12]) lcu_coefficients=torch.Size([4, 8])
[Quixer] apply_lcu states=torch.Size([4, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([4, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([4, 8]) accumulated_state=torch.Size([4, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([32, 8]) pqc_parameters=torch.Size([4, 8, 12]) lcu_coefficients=torch.Size([4, 8])
[Quixer] apply_lcu states=torch.Size([4, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([4, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([4, 8]) accumulated_state=torch.Size([4, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([4, 8])

真实 Quixer 类前向传播结果:
  logits.shape       = torch.Size([4, 50])
  mean_probability   = 0.8133127689361572
  可训练参数总数      = 2037

第八章:源码结构与训练流程

目标:了解 QuixerRepo 的项目结构,以及 run.py 如何驱动 Quixer 和三个经典基线模型的训练/评估。quixer_model.py/baseline_models.py 的源码走读已经在第二、五、六、七章穿插讲解并真实执行过了,这里只补充项目整体结构和 run.py 本身。

8.1 run.py 的核心逻辑

In [11]:
# run.py 的核心逻辑 —— 精确复制自当前磁盘上 QuixerRepo/run.py(本地已把 epochs 从 30 改成了 5)
import argparse

quixer_hparams = {
    "qubits": 6,
    "layers": 3,
    "ansatz_layers": 4,
    "window": 32,
    "epochs": 5,
    "restart_epochs": 30000,
    "dropout": 0.10,
    "lr": 0.002,
    "lr_sched": "cos",
    "wd": 0.0001,
    "eps": 1e-10,
    "batch_size": 32,
    "max_grad_norm": 5.0,
    "model": "Quixer",
    "print_iter": 50,
}

lstm_hparams = {
    "layers": 2,
    "window": 32,
    "residuals": False,
    "epochs": 5,
    "restart_epochs": 30000,
    "dropout": 0.30,
    "lr": 0.002,
    "lr_sched": "cos",
    "wd": 0.0001,
    "eps": 1e-10,
    "batch_size": 32,
    "max_grad_norm": 5.0,
    "print_iter": 50,
}

fnet_hparams = {
    "layers": 2,
    "window": 32,
    "epochs": 5,
    "restart_epochs": 30000,
    "dropout": 0.10,
    "lr": 0.002,
    "lr_sched": "cos",
    "wd": 0.0001,
    "eps": 1e-10,
    "batch_size": 32,
    "max_grad_norm": 5.0,
    "model": "FNet",
    "print_iter": 50,
}

transformer_hparams = {
    "layers": 1,
    "heads": 1,
    "window": 32,
    "epochs": 5,
    "restart_epochs": 30000,
    "dropout": 0.10,
    "lr": 0.001,
    "lr_sched": "cos",
    "wd": 0.0001,
    "eps": 1e-10,
    "batch_size": 32,
    "max_grad_norm": 5.0,
    "model": "Transformer",
    "print_iter": 50,
}

# 嵌入维度配置
classical_embedding_dimensions = [96, 128]
quantum_embedding_dimensions = [512]

# 模型注册表
model_map = {
    "Quixer": (quixer_hparams, quantum_embedding_dimensions),
    "Transformer": (transformer_hparams, classical_embedding_dimensions),
    "LSTM": (lstm_hparams, classical_embedding_dimensions),
    "FNet": (fnet_hparams, classical_embedding_dimensions),
}
available_models = list(model_map.keys())

# 解析命令行参数 —— 在笔记本里用显式参数列表代替 sys.argv
args = argparse.ArgumentParser(
    prog="Quixer", description="Runs the Quixer model and/or classical baselines"
)
args.add_argument(
    "-m", "--model", default="Quixer", choices=available_models, nargs="*", help="Model(s) to run.",
)
args.add_argument("-d", "--device", default="cpu", help="Device to run training on.")
parsed = args.parse_args(["-d", "cpu", "-m", "Quixer", "Transformer", "LSTM", "FNet"])

device_name = parsed.device
models_to_run = parsed.model if type(parsed.model) is list else [parsed.model]

print("device_name  :", device_name)
print("models_to_run:", models_to_run)
print("model_map keys:", list(model_map.keys()))
print("\nquixer_hparams     =", quixer_hparams)
print("classical_embedding_dimensions =", classical_embedding_dimensions)
print("quantum_embedding_dimensions   =", quantum_embedding_dimensions)
device_name  : cpu
models_to_run: ['Quixer', 'Transformer', 'LSTM', 'FNet']
model_map keys: ['Quixer', 'Transformer', 'LSTM', 'FNet']

quixer_hparams     = {'qubits': 6, 'layers': 3, 'ansatz_layers': 4, 'window': 32, 'epochs': 5, 'restart_epochs': 30000, 'dropout': 0.1, 'lr': 0.002, 'lr_sched': 'cos', 'wd': 0.0001, 'eps': 1e-10, 'batch_size': 32, 'max_grad_norm': 5.0, 'model': 'Quixer', 'print_iter': 50}
classical_embedding_dimensions = [96, 128]
quantum_embedding_dimensions   = [512]

关键观察

  • Quixer 的嵌入维度固定为 512,而经典基线是 96 或 128——Quixer 需要更大的嵌入维度来提供足够的参数给 PQC
  • 对每个配置运行 10 个随机种子,确保统计显著性
  • Quixer 默认使用 6 个量子比特、4 层 ansatz、3 次 QSVT 多项式

8.2 模型复杂度分析

组件 参数量 计算复杂度
Embedding $$\mathrm{vocab\_size} \times d_{\mathrm{emb}}$$ $$O(B \cdot n \cdot d_{\mathrm{emb}})$$
Embedding → Angles $$d_{\mathrm{emb}} \times (4q n_{\mathrm{layers}})$$ $$O(B \cdot n \cdot d_{\mathrm{emb}} \cdot 4q n_{\mathrm{layers}})$$
LCU(每步) $n$(复数系数) $$O(B \cdot n \cdot 4^q)$$
QSVT((d) 次) $d+1$(多项式系数) $$O(B \cdot d \cdot n \cdot 4^q)$$
Quantum FF $4q$(角度参数) $$O(B \cdot 4^q)$$
测量 $3q$(期望值读取) $$O(B \cdot q \cdot 4^q)$$
输出头 $$3q \times d_{\mathrm{emb}} + d_{\mathrm{emb}} \times \mathrm{vocab}$$ $$O(B \cdot 3q \cdot d_{\mathrm{emb}} + B \cdot d_{\mathrm{emb}} \cdot \mathrm{vocab})$$

关键瓶颈:所有量子操作的计算复杂度都是 $O(4^q)$——这是经典模拟量子回路的根本限制。$q=6$ 时,$4^6 = 4096$(尚可处理);$q=10$ 时,$4^{10} \approx 10^6$(勉强可处理);$q=20$ 时,$4^{20} \approx 10^{12}$(完全不可处理)。这是 Quixer 当前限制在 6 个量子比特的根本原因。

8.3 baseline_models.py — 经典基线模型

run.py 中除了 Quixer,还会训练三个经典基线模型用于对比:TransformerFNetLSTM。它们共享同一个"下一词元预测"接口——输入 [batch_size, n_tokens] 的词元 id,输出 (logits, None)None 是为了和 Quixer 的 (logits, mean_probability) 接口保持一致,经典模型没有后选择概率)。下面逐个真实导入并执行 quixer/baseline_models.py 中的类。

8.3.1 PositionalEncoding

标准的正弦位置编码(与 PyTorch 官方 Transformer 教程 一致),被 TransformerFNet 共用;LSTM 不需要位置编码(其顺序信息来自循环结构本身)。

In [12]:
# 真实执行 baseline_models.py 中的 PositionalEncoding
BASELINE_EMB_DIM = 16  # 与前面 QUIXER_DEMO_HPARAMS["embedding_dimension"] 保持一致,方便对比

pos_enc = bm.PositionalEncoding(d_model=BASELINE_EMB_DIM)
demo_embeddings = torch.zeros(2, QUIXER_DEMO_HPARAMS["n_tokens"], BASELINE_EMB_DIM)
pos_encoded = pos_enc(demo_embeddings)

print("PositionalEncoding 输出 shape:", pos_encoded.shape)
print("位置 0 的编码前 4 维:", pos_encoded[0, 0, :4])
print("位置 1 的编码前 4 维:", pos_encoded[0, 1, :4])
PositionalEncoding 输出 shape: torch.Size([2, 8, 16])
位置 0 的编码前 4 维: tensor([0.0000, 1.1111, 0.0000, 1.1111])
位置 1 的编码前 4 维: tensor([0.0000, 0.6003, 0.3455, 1.0560])

8.3.2 Transformer

标准的因果(causal)nn.TransformerEncoderembedding → 位置编码 → 带下三角 mask 的多层自注意力 → 只取最后一个位置的输出 → 线性投影到词表create_model() 里固定用 hid_dim = 4 * emb_dim(前馈层维度是嵌入维度的 4 倍,与原始 Transformer 论文的比例一致)。

In [13]:
# 真实执行 baseline_models.py 中的 Transformer
transformer_demo_model = bm.Transformer(
    emb_dim=BASELINE_EMB_DIM,
    hid_dim=4 * BASELINE_EMB_DIM,
    n_heads=1,
    n_layers=1,
    vocab_size=DEMO_VOCAB_SIZE,
    dropout=0.1,
)

transformer_logits, transformer_aux = transformer_demo_model(demo_token_ids)
n_params = sum(p.numel() for p in transformer_demo_model.parameters())
print("Transformer logits.shape:", transformer_logits.shape, " 第二个返回值:", transformer_aux)
print("Transformer 可训练参数总数:", n_params)
Transformer logits.shape: torch.Size([4, 50])  第二个返回值: None
Transformer 可训练参数总数: 4930
/Users/saintway/Downloads/Quixer/QuixerRepo/.venv311/lib/python3.11/site-packages/torch/nn/modules/transformer.py:306: UserWarning: enable_nested_tensor is True, but self.use_nested_tensor is False because encoder_layer.self_attn.num_heads is odd
  warnings.warn(f"enable_nested_tensor is True, but self.use_nested_tensor is False because {why_not_sparsity_fast_path}")

8.3.3 FNet

傅里叶变换替代自注意力做词元混合(torch.fft.fft2(x).real,取实部):embedding → 位置编码 → 若干层 [LayerNorm(FFT(x)+x) → LayerNorm(FeedForward(x)+x)] → 取最后一个位置 → 投影到词表。相比 Transformer,FNet 不含任何可训练的混合参数(FFT 是固定变换),所以参数量通常更少。

In [14]:
# 真实执行 baseline_models.py 中的 FNet
fnet_demo_model = bm.FNet(
    emb_dim=BASELINE_EMB_DIM,
    hid_dim=4 * BASELINE_EMB_DIM,
    n_layers=1,
    vocab_size=DEMO_VOCAB_SIZE,
    dropout=0.1,
)

fnet_logits, fnet_aux = fnet_demo_model(demo_token_ids)
n_params = sum(p.numel() for p in fnet_demo_model.parameters())
print("FNet logits.shape:", fnet_logits.shape, " 第二个返回值:", fnet_aux)
print("FNet 可训练参数总数:", n_params)
FNet logits.shape: torch.Size([4, 50])  第二个返回值: None
FNet 可训练参数总数: 3842

8.3.4 LSTM

最朴素的基线:embedding → 多层 LSTM → 取最后一个时间步的隐藏状态 → 投影到词表注意一个源码细节self.project = nn.Linear(emb_dim, vocab_size),但 LSTM 的输出维度其实是 hid_dim——如果 hid_dim != emb_dim 会在 forward 里直接报形状不匹配的错误。这不是 bug 被隐藏了,而是 create_model() 里固定传入 hid_dim=hyperparams["dimension"](即恒等于 emb_dim)来避免触发它——下面的调用同样保持 emb_dim == hid_dim

In [15]:
# 真实执行 baseline_models.py 中的 LSTM(hid_dim 必须等于 emb_dim,见上面的说明)
lstm_demo_model = bm.LSTM(
    emb_dim=BASELINE_EMB_DIM,
    hid_dim=BASELINE_EMB_DIM,
    n_layers=1,
    vocab_size=DEMO_VOCAB_SIZE,
    dropout=0.1,
)

lstm_logits, lstm_aux = lstm_demo_model(demo_token_ids)
n_params = sum(p.numel() for p in lstm_demo_model.parameters())
print("LSTM logits.shape:", lstm_logits.shape, " 第二个返回值:", lstm_aux)
print("LSTM 可训练参数总数:", n_params)
LSTM logits.shape: torch.Size([4, 50])  第二个返回值: None
LSTM 可训练参数总数: 3826
/Users/saintway/Downloads/Quixer/QuixerRepo/.venv311/lib/python3.11/site-packages/torch/nn/modules/rnn.py:83: UserWarning: dropout option adds dropout after all but last recurrent layer, so non-zero dropout expects num_layers greater than 1, but got dropout=0.1 and num_layers=1
  warnings.warn("dropout option adds dropout after all but last "

第九章:环境搭建与模型训练

目标:在你的机器上成功安装并训练 Quixer。

9.1 环境要求

依赖 精确版本 说明
Python ≥ 3.11 严格要求
PyTorch 2.3.0(推荐) 核心深度学习框架
torchtext 0.18.0 文本处理
torchvision 0.18.0 随 PyTorch 安装
torchquantum ≥ 0.1.8, < 0.2 量子回路模拟
Qiskit < 1.0 禁用 1.x 版本(API 重构)
Qiskit Aer 0.13.3 量子模拟后端
Qiskit IBM Provider 0.10.0 IBM 量子硬件接口
Qiskit IBM Runtime 0.20.0 IBM 量子运行时
datasets ≥ 3.2, < 3.3 Hugging Face 数据集
tqdm ≥ 4.67, < 4.68 进度条
CUDA 可选 GPU 加速(训练约 100 倍快)

9.2 踩坑预警:版本兼容性问题

Qiskit 1.0 进行了颠覆性的 API 重构(移除了元包架构,引入了 V2 原语)。Quixer 的代码是在 Qiskit < 1.0 的环境下编写的。如果你直接 pip install qiskit(会安装 ≥1.0),Quixer 将无法运行。

因此强烈建议创建独立的虚拟环境

9.3 安装步骤(macOS/Linux)

本机的 QuixerRepo/.venv311 虚拟环境(2.6GB)已经按照下面的步骤创建好了,所有依赖也已装好——这是一次性步骤,不需要重复执行。下面这段是记录当初怎么做的(供在新机器上从零搭建时参考),紧接着的代码 cell 会真实验证这个已存在的环境现在是否仍然可用。

#!/usr/bin/env bash
set -euo pipefail

# 1. 进入工作目录
cd /Users/saintway/Downloads/Quixer

# 2. 创建存放克隆仓库的目录
mkdir -p QuixerRepo
cd QuixerRepo

# 3. 克隆 Quixer 仓库
git clone https://github.com/Quantinuum/Quixer.git .

# 4. 创建 Python 3.11 虚拟环境
/opt/homebrew/bin/python3.11 -m venv .venv311

# 5. 激活虚拟环境,并升级 pip/依赖工具
source .venv311/bin/activate
python -m pip install --upgrade pip setuptools wheel

# 6. 安装 PyTorch 2.3.0 及配套包(CPU 版本)
python -m pip install torch==2.3.0 torchvision==0.18.0 torchtext==0.18.0 \
  --index-url https://download.pytorch.org/whl/cpu

# 7. 安装 Quixer 所需的量子和数据包依赖
python -m pip install 'torchquantum>=0.1.8,<0.2' 'qiskit<1.0' \
  'qiskit-aer==0.13.3' 'qiskit-ibm-provider==0.10.0' \
  'qiskit-ibm-runtime==0.20.0' 'datasets>=3.2,<3.3' 'tqdm>=4.67,<4.68'

# 8. 以可编辑模式安装 quixer 包本身(详见下方代码 cell 中关于这一步的真实踩坑记录)
python -m pip install -e .

这段脚本记录了本机 .venv311 最初是如何创建的(仅供参考,不再重复执行)。下面开始是真实执行的验证 cell:确认这个已经建好的环境现在依然可用,并演示 pip install -e . 这一步实际遇到的一个真实问题。

In [16]:
import subprocess

print("sys.executable:", sys.executable)

pip_show = subprocess.run(
    [sys.executable, "-m", "pip", "show", "quixer"],
    capture_output=True, text=True, cwd=str(QUIXER_REPO),
)
print("\n$ pip show quixer\n" + pip_show.stdout)
sys.executable: /Users/saintway/Downloads/Quixer/QuixerRepo/.venv311/bin/python

$ pip show quixer
Name: quixer
Version: 0.1.0
Summary: Quixer
Home-page: 
Author: 
Author-email: Nikhil Khatri <nikhil.khatri@quantinuum.com>, Gabriel Matos <gabriel.matos@quantinuum.com>
License: Apache-2.0
Location: /Users/saintway/Downloads/Quixer/QuixerRepo/.venv311/lib/python3.11/site-packages
Editable project location: /Users/saintway/Downloads/Quixer/QuixerRepo
Requires: datasets, qiskit, qiskit-aer, qiskit-ibm-provider, qiskit-ibm-runtime, qiskit-terra, torch, torchquantum, torchtext, torchvision, tqdm
Required-by: 

pip show quixer 确认包"已安装"(pip install -e . 显示 Successfully installed quixer-0.1.0,不报错)。但这里有一个真实踩坑pyproject.tomlpy-modules = ["quixer"] 声明它,这是给"单个 .py 文件模块"用的写法;而 quixer/ 实际上是一个没有 __init__.py 的目录(一个隐式命名空间包)。setuptools 生成的可编辑安装 finder 只会去找 quixer/__init__.pyquixer.py,两者都不存在,于是这个 finder 实际上什么也找不到。下面从 QuixerRepo 以外的目录(也就是本笔记本所在的 /Users/saintway/Downloads/Quixer)起一个全新的子进程,验证仅凭 pip install -e . 是否真的能 import quixer

In [17]:
# 在一个全新的子进程里,从 /Users/saintway/Downloads/Quixer(不是 QuixerRepo)尝试 `import quixer`
# 不做任何 sys.path 处理,只依赖 `pip install -e .` 本身
check_script = "import quixer\nprint('import quixer 成功,quixer.__path__ =', list(quixer.__path__))\n"

result = subprocess.run(
    [sys.executable, "-c", check_script],
    capture_output=True, text=True,
    cwd="/Users/saintway/Downloads/Quixer",
)
print("returncode:", result.returncode)
print("STDOUT:", result.stdout)
print("STDERR (末尾几行):")
print("\n".join(result.stderr.strip().splitlines()[-5:]))
returncode: 1
STDOUT: 
STDERR (末尾几行):
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ModuleNotFoundError: No module named 'quixer'

如上面真实输出所示,returncode 非 0——单靠 pip install -e . 并不能让 import quixer 在任意目录下工作。这正是本教程『运行本教程前的准备』一开始就手动 sys.path.insert(0, str(QUIXER_REPO)) 的原因:这是目前真正有效的修复方式(README 里写的 pip install -e . 命令本身没错,只是这个仓库的 pyproject.toml 打包配置对目录形式的包声明有误)。

最后,确认一下这个已存在的 .venv311 环境本身(依赖版本)依然完好:

In [18]:
import qiskit
import torchtext
import datasets as hf_datasets

torchtext.disable_torchtext_deprecation_warning()

print("torch          :", torch.__version__)
print("torchtext      :", torchtext.__version__)
print("torchquantum   :", tq.__version__ if hasattr(tq, "__version__") else "(无 __version__ 属性)")
print("qiskit         :", qiskit.__version__)
print("datasets       :", hf_datasets.__version__)
print("\n环境验证通过:所有 Quixer 所需依赖均可在当前 kernel(.venv311)中正常导入。")
torch          : 2.3.0
torchtext      : 0.18.0
torchquantum   : 0.1.8
qiskit         : 0.46.3
datasets       : 3.2.0

环境验证通过:所有 Quixer 所需依赖均可在当前 kernel(.venv311)中正常导入。
# === 步骤 1:创建虚拟环境 ===
conda create -n quixer python=3.11 -y
conda activate quixer

# === 步骤 2:安装 PyTorch 2.3.0(CPU 版本) ===
conda install pytorch==2.3.0 torchvision==0.18.0 \
    torchtext==0.18.0 torchaudio==2.3.0 cpuonly -c pytorch -y

# 或者 CUDA 版本(如果有 NVIDIA GPU):
# conda install pytorch==2.3.0 torchvision==0.18.0 \
#     torchtext==0.18.0 torchaudio==2.3.0 pytorch-cuda=12.1 -c pytorch -y

# === 步骤 3:克隆仓库 ===
git clone https://github.com/Quantinuum/Quixer.git
cd Quixer

# === 步骤 4:安装 Quixer 及其依赖 ===
pip install -e .

# === 步骤 5:验证安装 ===
python -c "import torch; import qiskit; import torchquantum; \
    print(f'PyTorch {torch.__version__}'); \
    print(f'Qiskit {qiskit.__version__}'); \
    print('安装成功!')"

常见错误与解决方案

错误 原因 解决
ModuleNotFoundError: No module named 'qiskit.providers' Qiskit 1.0+ 移除了旧 API 降级到 pip install qiskit<1.0
RuntimeError: CUDA out of memory GPU 显存不足 减小 batch_size 或用 -d cpu
ImportError: cannot import name 'get_tokenizer' torchtext 版本不对 确保 torchtext==0.18.0
AttributeError: 'QuantumDevice' object has no attribute 'set_states' torchquantum 版本问题 确保 torchquantum>=0.1.8,<0.2

9.4 运行训练:真实执行 setup_training.py

run.py 默认会跑 10 个随机种子 × 多个嵌入维度 × 4 个模型的完整训练——量子回路的经典模拟很慢,完整跑一遍可能要数小时甚至更久(参见下面 9.5 节的时间估算)。本教程不直接调用那个完整扫描,而是手动、逐个调用 setup_training.py 里的底层函数,用大幅缩小的超参数(小 batch、小 window、少量数据、2 个 epoch)跑一次真实、可验证的端到端训练,重点是看到真实的 loss 数字和 checkpoint 保存过程,而不是追求训练出能用的模型。

为什么不能只是简单调小 run.py 的超参数直接跑? setup_dataset() 返回的是完整 Penn Treebank 数据;train_epoch/evaluate 会遍历传入张量里的所有 batch(n_batches = iterator.shape[0] - window_size)。缩小 window/batch_size 反而会增加每个 epoch 的 batch 数量(batch_nr_of_elements 变小)。所以下面会先拿到完整数据,再手动切片成一小段,之后再喂给训练/评估函数。

下面依次真实执行 setup_training.py 中的每一个函数。

In [19]:
# 真实执行 setup_training.py 中的 epoch_time / seed / initialise_weights
print("epoch_time(0.0, 125.0) =", st.epoch_time(0.0, 125.0), "  # (分钟, 秒)")

st.seed(42)
print("调用 st.seed(42) 后,torch 生成的一个随机数:", torch.rand(1).item())

before_std = quixer_demo_model.embedding.weight.std().item()
st.initialise_weights(quixer_demo_model)
after_std = quixer_demo_model.embedding.weight.std().item()
print(
    f"initialise_weights 应用到 7.4 节的 quixer_demo_model 前后,"
    f"embedding.weight 标准差: {before_std:.4f} -> {after_std:.4f}"
    f"(Embedding 层被重新初始化为 std=0.02 的正态分布)"
)
epoch_time(0.0, 125.0) = (2, 5)   # (分钟, 秒)
调用 st.seed(42) 后,torch 生成的一个随机数: 0.8822692632675171
initialise_weights 应用到 7.4 节的 quixer_demo_model 前后,embedding.weight 标准差: 0.1677 -> 0.0197(Embedding 层被重新初始化为 std=0.02 的正态分布)
In [20]:
# 真实执行 setup_training.py 中的 batchify_s2s / get_batch_s2s(用手工小张量演示,不需要真实数据集)
toy_data = torch.arange(0, 30)  # 30 个"词元" id: 0..29
toy_batch_size = 2
toy_window_size = 3

toy_batched = st.batchify_s2s(
    toy_data, batch_size=toy_batch_size, window_size=toy_window_size, pad_token_id=-1, device="cpu"
)
print("batchify_s2s 输出 shape:", toy_batched.shape)
print(toy_batched)

x0, y0 = st.get_batch_s2s(toy_batched, i=0, window_size=toy_window_size)
print("\nget_batch_s2s(i=0):")
print("  x.shape =", x0.shape, "\n  x =\n", x0)
print("  y.shape =", y0.shape, "\n  y =", y0)
batchify_s2s 输出 shape: torch.Size([7, 6])
tensor([[-1,  1,  5,  9, 13, 17],
        [-1,  2,  6, 10, 14, 18],
        [-1,  3,  7, 11, 15, 19],
        [ 0,  4,  8, 12, 16, 20],
        [ 1,  5,  9, 13, 17, 21],
        [ 2,  6, 10, 14, 18, 22],
        [ 3,  7, 11, 15, 19, 23]])

get_batch_s2s(i=0):
  x.shape = torch.Size([6, 3]) 
  x =
 tensor([[-1, -1, -1],
        [ 1,  2,  3],
        [ 5,  6,  7],
        [ 9, 10, 11],
        [13, 14, 15],
        [17, 18, 19]])
  y.shape = torch.Size([6]) 
  y = tensor([ 0,  4,  8, 12, 16, 20])

真实执行发现的细节x0.shape[6, 3],第一维是 6,不是我们传入的 batch_size=2!原因是 batchify_s2s 把数据先按 batch_size × window_size 分组,get_batch_s2s 再用 .T 把它变成 [batch_size * window_size, window_size]。也就是说,喂给模型 forward()实际 batch 维度是 batch_size * window_size,而不是超参数表里写的 batch_size

顺带验证了一个容易担心的问题:Quixer 构造函数里的 batch_size 参数会被用来创建 self.torchquantum_device = tq.QuantumDevice(n_wires=..., bsz=batch_size),那如果这个 batch_sizeforward() 实际收到的批大小(这里的 batch_size * window_size)不一致会不会报错?实测不会——torchquantumQuantumDevice.set_states(...) 会根据传入张量的行数自动调整设备内部的批大小,构造函数里的 bsz 只是初始值,并非强约束。

接下来加载真实 Penn Treebank 数据(已在本机缓存,走本地缓存不需要联网下载),然后切片成一小段用于训练演示。

In [21]:
import time

# 真实执行 setup_training.py 中的 setup_dataset —— 加载真实 Penn Treebank(本机已缓存,走本地缓存)
t0 = time.time()
vocab, (train_iter, val_iter, test_iter), PAD_TOKEN = st.setup_dataset(
    torch.device("cpu"), batch_size=QUIXER_DEMO_HPARAMS["batch_size"], window_size=QUIXER_DEMO_HPARAMS["n_tokens"]
)
print(f"setup_dataset 耗时: {time.time() - t0:.1f}s")
print("词表大小 len(vocab):", len(vocab))
print("PAD_TOKEN id:", PAD_TOKEN)
print("完整 train_iter.shape:", train_iter.shape)
print("完整 val_iter.shape  :", val_iter.shape)
print("完整 test_iter.shape :", test_iter.shape)

# 手动切片成一小段用于训练演示(原因见 9.4 开头的说明)
WINDOW = QUIXER_DEMO_HPARAMS["n_tokens"]
N_TRAIN_STEPS = 24
N_EVAL_STEPS = 12

train_small = train_iter[: WINDOW + N_TRAIN_STEPS]
val_small = val_iter[: WINDOW + N_EVAL_STEPS]
test_small = test_iter[: WINDOW + N_EVAL_STEPS]

print("\n切片后 train_small.shape:", train_small.shape, f"({N_TRAIN_STEPS} 个训练 batch)")
print("切片后 val_small.shape  :", val_small.shape, f"({N_EVAL_STEPS} 个验证 batch)")
print("切片后 test_small.shape :", test_small.shape, f"({N_EVAL_STEPS} 个测试 batch)")
setup_dataset 耗时: 3.0s
词表大小 len(vocab): 9924
PAD_TOKEN id: 0
完整 train_iter.shape: torch.Size([30210, 32])
完整 val_iter.shape  : torch.Size([2405, 32])
完整 test_iter.shape : torch.Size([2691, 32])

切片后 train_small.shape: torch.Size([32, 32]) (24 个训练 batch)
切片后 val_small.shape  : torch.Size([20, 32]) (12 个验证 batch)
切片后 test_small.shape : torch.Size([20, 32]) (12 个测试 batch)

9.4.1 create_model:为四个模型分别构造超参数字典

create_model() 从一个共享的超参数字典里按 hyperparams["model"] 分派到对应的模型类(见 8.3 节代码)。这里为四个模型分别配置一份缩小版超参数(对应真实词表大小 len(vocab),而不是之前 7.4 节冒烟测试用的玩具词表 DEMO_VOCAB_SIZE=50),并额外加上 train_cycle 需要的优化器/调度器相关字段(lrwdepslr_schedmax_grad_normepochsseed)。

In [22]:
DEMO_TRAINING_EXTRAS = dict(
    lr=1e-3,
    wd=1e-4,
    eps=1e-10,
    lr_sched="cos",
    restart_epochs=100,
    max_grad_norm=5.0,
    epochs=2,
    seed=42,
    window=WINDOW,
)

demo_hparams_by_model = {
    "Quixer": dict(
        model="Quixer",
        qubits=QUIXER_DEMO_HPARAMS["n_qubits"],
        layers=QUIXER_DEMO_HPARAMS["qsvt_polynomial_degree"],
        ansatz_layers=QUIXER_DEMO_HPARAMS["n_ansatz_layers"],
        dimension=QUIXER_DEMO_HPARAMS["embedding_dimension"],
        dropout=QUIXER_DEMO_HPARAMS["dropout"],
        batch_size=QUIXER_DEMO_HPARAMS["batch_size"],
        **DEMO_TRAINING_EXTRAS,
    ),
    "Transformer": dict(
        model="Transformer", dimension=16, heads=1, layers=1, dropout=0.1, **DEMO_TRAINING_EXTRAS
    ),
    "LSTM": dict(model="LSTM", dimension=16, layers=1, dropout=0.1, **DEMO_TRAINING_EXTRAS),
    "FNet": dict(model="FNet", dimension=16, layers=1, dropout=0.1, **DEMO_TRAINING_EXTRAS),
}

demo_models = {}
for model_name, hparams in demo_hparams_by_model.items():
    model = st.create_model(hparams, torch.device("cpu"), len(vocab))
    st.initialise_weights(model)
    n_params = sum(p.numel() for p in model.parameters())
    demo_models[model_name] = model
    print(f"{model_name:12s} 创建成功,可训练参数总数 = {n_params}")
Quixer       创建成功,可训练参数总数 = 327879
Transformer  创建成功,可训练参数总数 = 330772
LSTM         创建成功,可训练参数总数 = 329668
FNet         创建成功,可训练参数总数 = 329684

9.4.2 单独调用 train_epoch / evaluate

在跑完整的 train_cycle 之前,先单独调用一次 train_epochevaluate,看真实的 loss 数值(用 demo_models["Quixer"],跑在切片后的 train_small/val_small 上)。

In [23]:
import math

standalone_model = demo_models["Quixer"]
standalone_hparams = demo_hparams_by_model["Quixer"]

optimizer = torch.optim.Adam(
    standalone_model.parameters(),
    lr=standalone_hparams["lr"],
    weight_decay=standalone_hparams["wd"],
    eps=standalone_hparams["eps"],
)
loss_function = torch.nn.CrossEntropyLoss()

train_loss = st.train_epoch(
    standalone_model, train_small, optimizer, loss_function,
    standalone_hparams["max_grad_norm"], None, standalone_hparams["window"],
)
val_loss = st.evaluate(standalone_model, val_small, loss_function, standalone_hparams["window"])

print(f"\n单独调用一次 train_epoch: loss = {train_loss:.4f}  (ppl = {math.exp(train_loss):.1f})")
print(f"单独调用一次 evaluate  : loss = {val_loss:.4f}  (ppl = {math.exp(val_loss):.1f})")
  0%|          | 0/24 [00:00<?, ?it/s]
 50%|█████     | 12/24 [00:00<00:00, 113.53it/s]
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
100%|██████████| 24/24 [00:00<00:00, 126.97it/s]

  0%|          | 0/12 [00:00<?, ?it/s]
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
100%|██████████| 12/12 [00:00<00:00, 361.98it/s]
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])

单独调用一次 train_epoch: loss = 9.1774  (ppl = 9675.7)
单独调用一次 evaluate  : loss = 9.1345  (ppl = 9269.9)

9.4.3 train_cycle:完整的多轮训练 + checkpoint 保存/加载

train_cycletrain_epoch/evaluate 包装成完整的多轮训练循环:每轮结束后,如果验证集 loss 创新低就把模型权重存到 ./trained_models/(这里已经 os.chdir 到了 QuixerRepo,所以真实落盘在 QuixerRepo/trained_models/,文件名带时间戳,不会和仓库里已有的 4 个 checkpoint 冲突);训练结束后重新加载最优 checkpoint,再分别在验证集/测试集上评估一次。下面各用一个全新初始化的模型(不复用上一节 standalone_model 的训练状态)分别跑一次 Quixer 和 Transformer,epochs=2

In [24]:
# train_cycle 完整跑一次 Quixer(全新初始化,与上面 9.4.2 的 standalone_model 无关)
fresh_quixer = st.create_model(demo_hparams_by_model["Quixer"], torch.device("cpu"), len(vocab))
st.initialise_weights(fresh_quixer)

st.seed(demo_hparams_by_model["Quixer"]["seed"])
quixer_test_loss = st.train_cycle(
    fresh_quixer, demo_hparams_by_model["Quixer"], train_small, val_small, test_small
)
print("\nQuixer train_cycle 返回的 test_loss:", quixer_test_loss)
  0%|          | 0/24 [00:00<?, ?it/s]
 62%|██████▎   | 15/24 [00:00<00:00, 144.51it/s]
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
100%|██████████| 24/24 [00:00<00:00, 143.94it/s]
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
  0%|          | 0/12 [00:00<?, ?it/s]
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
100%|██████████| 12/12 [00:00<00:00, 358.44it/s]
Epoch: 01 | Time: 0m 0s
	Train Loss: 9.154 | Train ppl: 9456.083227465262
	 Val. Loss: 9.086 |  Val. ppl: 8835.17782685029
  0%|          | 0/24 [00:00<?, ?it/s]
 62%|██████▎   | 15/24 [00:00<00:00, 142.58it/s]
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
100%|██████████| 24/24 [00:00<00:00, 142.45it/s]

  0%|          | 0/12 [00:00<?, ?it/s]
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
100%|██████████| 12/12 [00:00<00:00, 353.11it/s]
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
Epoch: 02 | Time: 0m 0s
	Train Loss: 8.932 | Train ppl: 7567.902158371404
	 Val. Loss: 8.934 |  Val. ppl: 7582.106214149917
  0%|          | 0/12 [00:00<?, ?it/s]
100%|██████████| 12/12 [00:00<00:00, 353.08it/s]
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
  0%|          | 0/12 [00:00<?, ?it/s]
100%|██████████| 12/12 [00:00<00:00, 375.05it/s]
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
FINAL TRAINED MODEL STATS:
	 Val. Loss: 8.934 |  Val. ppl: 7582.106214149917
	 Test Loss: 8.935 |  Test ppl: 7595.152255255066

Quixer train_cycle 返回的 test_loss: 8.9352654616038

In [25]:
# train_cycle 完整跑一次 Transformer 基线,作对比
fresh_transformer = st.create_model(demo_hparams_by_model["Transformer"], torch.device("cpu"), len(vocab))
st.initialise_weights(fresh_transformer)

st.seed(demo_hparams_by_model["Transformer"]["seed"])
transformer_test_loss = st.train_cycle(
    fresh_transformer, demo_hparams_by_model["Transformer"], train_small, val_small, test_small
)
print("\nTransformer train_cycle 返回的 test_loss:", transformer_test_loss)
  0%|          | 0/24 [00:00<?, ?it/s]
100%|██████████| 24/24 [00:00<00:00, 428.18it/s]

  0%|          | 0/12 [00:00<?, ?it/s]
100%|██████████| 12/12 [00:00<00:00, 1713.77it/s]
Epoch: 01 | Time: 0m 0s
	Train Loss: 9.086 | Train ppl: 8834.56872678152
	 Val. Loss: 8.908 |  Val. ppl: 7393.463516778335
  0%|          | 0/24 [00:00<?, ?it/s]
100%|██████████| 24/24 [00:00<00:00, 433.99it/s]

  0%|          | 0/12 [00:00<?, ?it/s]
100%|██████████| 12/12 [00:00<00:00, 1275.28it/s]
Epoch: 02 | Time: 0m 0s
	Train Loss: 8.618 | Train ppl: 5529.659594557086
	 Val. Loss: 8.630 |  Val. ppl: 5597.9476861535995
  0%|          | 0/12 [00:00<?, ?it/s]
100%|██████████| 12/12 [00:00<00:00, 1765.84it/s]

  0%|          | 0/12 [00:00<?, ?it/s]
100%|██████████| 12/12 [00:00<00:00, 1804.00it/s]
FINAL TRAINED MODEL STATS:
	 Val. Loss: 8.630 |  Val. ppl: 5597.9476861535995
	 Test Loss: 8.639 |  Test ppl: 5648.563703036973

Transformer train_cycle 返回的 test_loss: 8.639156579971313

9.4.4 get_train_evaluate:run.py 实际使用的闭包

run.py(见 9.6 节)并不直接调用 create_model/train_cycle,而是调用 get_train_evaluate(device) 拿到一个闭包 train_evaluate,再对着完整数据集和完整超参数反复调用它。本教程没有直接调用这个闭包跑全量训练——原因见本节开头的说明(setup_dataset() 内部会加载完整 PTB,且不接受"只用前 N 个 batch"这样的参数,无法在闭包内部做我们上面手动做的切片)。这里只检视一下它的类型,确认它确实是一个可调用对象:

In [26]:
train_evaluate = st.get_train_evaluate(torch.device("cpu"))
print("get_train_evaluate 返回类型:", type(train_evaluate))
print("是否可调用:", callable(train_evaluate))
print(
    "\n真实用法(run.py 里,不在本教程中执行):"
    "\n  train_evaluate({**quixer_hyperparams, 'model': 'Quixer', 'dimension': 512, 'seed': 1234})"
)

# 顺带确认 train_cycle 在上面真实写入了新的 checkpoint 文件
checkpoint_files = sorted((QUIXER_REPO / "trained_models").glob("*.pt"))
print(f"\nQuixerRepo/trained_models/ 目前共有 {len(checkpoint_files)} 个 checkpoint 文件:")
for f in checkpoint_files:
    print(" ", f.name, f"({f.stat().st_size / 1e6:.2f} MB)")
get_train_evaluate 返回类型: <class 'function'>
是否可调用: True

真实用法(run.py 里,不在本教程中执行):
  train_evaluate({**quixer_hyperparams, 'model': 'Quixer', 'dimension': 512, 'seed': 1234})

QuixerRepo/trained_models/ 目前共有 38 个 checkpoint 文件:
  q_transformer_lm_FNet_42_1786164481.pt (1.64 MB)
  q_transformer_lm_FNet_518593_1786198586.pt (1.64 MB)
  q_transformer_lm_FNet_518593_1786198636.pt (1.64 MB)
  q_transformer_lm_FNet_518593_1786228635.pt (1.64 MB)
  q_transformer_lm_FNet_518593_1786228660.pt (1.64 MB)
  q_transformer_lm_FNet_518593_1786263780.pt (1.64 MB)
  q_transformer_lm_FNet_518593_1786277931.pt (1.64 MB)
  q_transformer_lm_FNet_518593_1786278071.pt (1.64 MB)
  q_transformer_lm_LSTM_348798_1786198586.pt (1.32 MB)
  q_transformer_lm_LSTM_348798_1786198636.pt (1.32 MB)
  q_transformer_lm_LSTM_348798_1786228635.pt (1.32 MB)
  q_transformer_lm_LSTM_348798_1786228660.pt (1.32 MB)
  q_transformer_lm_LSTM_348798_1786263779.pt (1.32 MB)
  q_transformer_lm_LSTM_348798_1786277931.pt (1.32 MB)
  q_transformer_lm_LSTM_348798_1786278070.pt (1.32 MB)
  q_transformer_lm_LSTM_42_1786164481.pt (1.32 MB)
  q_transformer_lm_Quixer_144381_1784515348.pt (41.46 MB)
  q_transformer_lm_Quixer_1589_1784447350.pt (41.46 MB)
  q_transformer_lm_Quixer_42_1786164346.pt (1.32 MB)
  q_transformer_lm_Quixer_42_1786198585.pt (1.32 MB)
  q_transformer_lm_Quixer_42_1786198635.pt (1.32 MB)
  q_transformer_lm_Quixer_42_1786228635.pt (1.32 MB)
  q_transformer_lm_Quixer_42_1786228659.pt (1.32 MB)
  q_transformer_lm_Quixer_42_1786263764.pt (1.32 MB)
  q_transformer_lm_Quixer_42_1786277931.pt (1.32 MB)
  q_transformer_lm_Quixer_42_1786278070.pt (1.32 MB)
  q_transformer_lm_Quixer_42_1786278180.pt (1.32 MB)
  q_transformer_lm_Quixer_729401_1784459220.pt (41.46 MB)
  q_transformer_lm_Quixer_856586_1784503401.pt (41.46 MB)
  q_transformer_lm_Transformer_42_1786164366.pt (1.65 MB)
  q_transformer_lm_Transformer_42_1786198586.pt (1.65 MB)
  q_transformer_lm_Transformer_42_1786198636.pt (1.65 MB)
  q_transformer_lm_Transformer_42_1786228635.pt (1.65 MB)
  q_transformer_lm_Transformer_42_1786228660.pt (1.65 MB)
  q_transformer_lm_Transformer_42_1786263769.pt (1.65 MB)
  q_transformer_lm_Transformer_42_1786277931.pt (1.65 MB)
  q_transformer_lm_Transformer_42_1786278070.pt (1.65 MB)
  q_transformer_lm_Transformer_42_1786278181.pt (1.65 MB)

9.4.5 结构忠实但缩小规模的 run.py 主循环

真实 run.py(8.1 节复现的核心逻辑)的主循环是 for model_name in models_to_run: for embedding_dimension in embedding_dimensions: for seed in torch.randint(..., size=(10,)),对每个组合都调用未切片的 train_evaluate(hyperparameters)(即完整 PTB + 论文默认超参数)。下面复现同样的三层循环结构,但做两处改动:种子数量从 10 降到 1;对每个模型改用上面 9.4.1 节的缩小超参数 + create_model/train_cycle 手动 pipeline(而不是未切片的 train_evaluate 闭包)。QuixerTransformer 在 9.4.3 节已经真实训练过,这里直接复用那次的结果而不重复训练;LSTMFNet 在这里首次真实训练。

In [27]:
DEMO_SEEDS_PER_MODEL = 1  # 真实 run.py 是 10

demo_run_results = {
    "Quixer": quixer_test_loss,           # 复用 9.4.3 节的真实训练结果
    "Transformer": transformer_test_loss,  # 复用 9.4.3 节的真实训练结果
}

for model_name in available_models:
    hyperparameters, embedding_dimensions = model_map[model_name]
    embedding_dimension = embedding_dimensions[0]  # 真实 run.py 会遍历全部维度,这里演示只取第一个

    if model_name in demo_run_results:
        print(
            f"{model_name:12s} 已在 9.4.3 节用缩小超参数真实训练过,"
            f"直接复用 test_loss = {demo_run_results[model_name]:.4f},跳过重复训练"
        )
        continue

    for seed_value in torch.randint(high=1000000, size=(DEMO_SEEDS_PER_MODEL,)).tolist():
        demo_hp = dict(demo_hparams_by_model[model_name])
        demo_hp["seed"] = seed_value

        print(
            f"\n=== 运行 {model_name}(真实 run.py 会用 embedding_dimension={embedding_dimension},"
            f"这里的演示用的是缩小超参数 dimension={demo_hp['dimension']})==="
        )

        model = st.create_model(demo_hp, torch.device("cpu"), len(vocab))
        st.initialise_weights(model)
        st.seed(seed_value)
        test_loss = st.train_cycle(model, demo_hp, train_small, val_small, test_small)
        demo_run_results[model_name] = test_loss

print("\n所有模型的演示 test_loss 汇总(缩小超参数、极小数据切片,仅用于验证 pipeline 能跑通):")
for name in available_models:
    print(f"  {name:12s}: {demo_run_results[name]:.4f}")
Quixer       已在 9.4.3 节用缩小超参数真实训练过,直接复用 test_loss = 8.9353,跳过重复训练
Transformer  已在 9.4.3 节用缩小超参数真实训练过,直接复用 test_loss = 8.6392,跳过重复训练

=== 运行 LSTM(真实 run.py 会用 embedding_dimension=96,这里的演示用的是缩小超参数 dimension=16)===
  0%|          | 0/24 [00:00<?, ?it/s]
100%|██████████| 24/24 [00:00<00:00, 626.80it/s]

  0%|          | 0/12 [00:00<?, ?it/s]
100%|██████████| 12/12 [00:00<00:00, 2123.88it/s]
Epoch: 01 | Time: 0m 0s
	Train Loss: 9.180 | Train ppl: 9696.503993321992
	 Val. Loss: 9.148 |  Val. ppl: 9391.783387226706
  0%|          | 0/24 [00:00<?, ?it/s]
100%|██████████| 24/24 [00:00<00:00, 570.02it/s]

  0%|          | 0/12 [00:00<?, ?it/s]
100%|██████████| 12/12 [00:00<00:00, 2394.81it/s]
Epoch: 02 | Time: 0m 0s
	Train Loss: 9.062 | Train ppl: 8618.749620925759
	 Val. Loss: 9.048 |  Val. ppl: 8497.530658532572
  0%|          | 0/12 [00:00<?, ?it/s]
100%|██████████| 12/12 [00:00<00:00, 1964.39it/s]

  0%|          | 0/12 [00:00<?, ?it/s]
100%|██████████| 12/12 [00:00<00:00, 1995.31it/s]
FINAL TRAINED MODEL STATS:
	 Val. Loss: 9.048 |  Val. ppl: 8497.530658532572
	 Test Loss: 9.047 |  Test ppl: 8497.030258878018

=== 运行 FNet(真实 run.py 会用 embedding_dimension=96,这里的演示用的是缩小超参数 dimension=16)===
  0%|          | 0/24 [00:00<?, ?it/s]
100%|██████████| 24/24 [00:00<00:00, 539.29it/s]

  0%|          | 0/12 [00:00<?, ?it/s]
100%|██████████| 12/12 [00:00<00:00, 2382.34it/s]
Epoch: 01 | Time: 0m 0s
	Train Loss: 9.089 | Train ppl: 8853.173450657781
	 Val. Loss: 8.914 |  Val. ppl: 7432.632730619386
  0%|          | 0/24 [00:00<?, ?it/s]
100%|██████████| 24/24 [00:00<00:00, 532.20it/s]

  0%|          | 0/12 [00:00<?, ?it/s]
100%|██████████| 12/12 [00:00<00:00, 2281.79it/s]
Epoch: 02 | Time: 0m 0s
	Train Loss: 8.638 | Train ppl: 5644.254937334612
	 Val. Loss: 8.640 |  Val. ppl: 5656.044308867482
  0%|          | 0/12 [00:00<?, ?it/s]
100%|██████████| 12/12 [00:00<00:00, 2124.95it/s]

  0%|          | 0/12 [00:00<?, ?it/s]
100%|██████████| 12/12 [00:00<00:00, 2129.09it/s]
FINAL TRAINED MODEL STATS:
	 Val. Loss: 8.640 |  Val. ppl: 5656.044308867482
	 Test Loss: 8.640 |  Test ppl: 5655.509426761683

所有模型的演示 test_loss 汇总(缩小超参数、极小数据切片,仅用于验证 pipeline 能跑通):
  Quixer      : 8.9353
  Transformer : 8.6392
  LSTM        : 9.0475
  FNet        : 8.6404

# === 仅运行 Quixer(CPU) ===
python run.py -d cpu -m Quixer

# === 运行 Quixer + 经典基线(CPU) ===
python run.py -d cpu -m Quixer Transformer LSTM FNet

# === GPU 版本(如果有 CUDA) ===
python run.py -d cuda -m Quixer

# === 仅运行经典基线(用于对比) ===
python run.py -d cpu -m Transformer LSTM FNet

9.5 训练过程详解

训练过程将:

  1. 下载数据:自动从 Hugging Face Hub 下载 Penn Treebank 数据集
  2. 构建词表:使用 basic_english 分词器,构建包含 <pad><unk><eos> 特殊令牌的词表
  3. 批量化:将数据按 window=32 的上下文窗口组织为批次
  4. 多轮训练:默认 40 个 epoch,使用 Adam 优化器,Cosine Annealing 学习率调度
  5. 自动保存:将每个 epoch 的最优模型保存到 ./trained_models/ 目录
  6. 多种子评估:对每个配置运行 10 个不同的随机种子

单次完整训练(1 个模型 + 1 个嵌入维度 + 10 个种子 + 40 epochs)在 CPU 上大约需要 6-12 小时,在 GPU 上大约需要 10-20 分钟

9.6 训练过程中的输出解读

Epoch: 01 | Time: 2m 30s
    Train Loss: 5.832 | Train ppl: 341.2
     Val. Loss: 5.651 |  Val. ppl: 284.5

Epoch: 10 | Time: 2m 15s
    Train Loss: 4.912 | Train ppl: 135.8
     Val. Loss: 5.012 |  Val. ppl: 149.9
...
FINAL TRAINED MODEL STATS:
     Val. Loss: 4.897 |  Val. ppl: 133.8
     Test Loss: 4.912 |  Test ppl: 135.7

其中:

  • Loss:Cross-Entropy Loss(越低越好)
  • ppl(Perplexity,困惑度):$\text{ppl} = e^{\text{loss}}$,衡量模型对下一令牌预测的不确定性。困惑度越低,模型越『自信』且准确。作为参考:随机猜测的困惑度 ≈ 词表大小(Penn Treebank 约 10,000)

9.7 实验设计解读

run.py 中 Quixer 和经典基线的超参数:

参数 Quixer Transformer LSTM FNet
嵌入维度 512 96, 128 96, 128 96, 128
层数 3 3 3 3
窗口 32 32 32 32
Epochs 40 40 40 40
Batch 20 20 20 20
Dropout 0.2 0.2 0.2 0.2
量子比特 6 N/A N/A N/A
Ansatz 层数 4 N/A N/A N/A

为什么 Quixer 的嵌入维度高达 512?

Quixer 不使用多头注意力,其『表达能力』主要来自:(1) PQC 在 $2^6=64$ 维希尔伯特空间中的演化;(2) 经典嵌入向高维角度参数的线性映射。需要 $d_{\text{emb}} \to n_{\text{pqc\_params}} = 4 \times 6 \times 4 = 96$ 的映射。512 维嵌入提供了充足的过完备表示能力。经典 Transformer 用 128 维嵌入则因为多头注意力提供了足够的交互表达能力。


第十章:实验结果与性能分析

目标:理解 Quixer 目前的性能水平及其含义。

10.1 Penn Treebank 结果

Quixer 在 Penn Treebank 语言建模任务上的表现(论文表 1):

模型 测试困惑度 (Test PPL) 参数量
LSTM (128d) ~100-110 ~6M
FNet (128d) ~110-120 ~1M
Transformer (128d) ~95-105 ~4M
Quixer (512d) ~135-145 ~10M

谨慎解读:论文声称 Quixer 表现『competitive with an equivalent classical baseline』(与等效的经典基线有竞争力)。但这一声明在对抗验证中以 0-3 被否定——三个独立验证者均无法确认 Quixer 在相同参数量条件下接近经典基线的性能。Quixer 的实际表现显著弱于同等参数规模的经典 Transformer。

本教程实机演示 vs. 上表基准的关系:第九章跑的小规模演示(3 量子比特、8 词元窗口、2 个 epoch、几十个 batch)只是为了验证代码流程能端到端跑通、产出真实 loss 和 checkpoint,不是对上表基准数字的复现尝试——数据量和训练规模相差几个数量级,演示得到的 loss/ppl 数字本身没有参考价值,评估 Quixer 真实性能请以上表、原论文和下面 10.3 节的 Aalto 复现结果为准。

10.2 为什么 Quixer 不如经典模型?

这涉及几个根本原因:

  1. 量子态矢量维度限制:$q=6$ 意味着量子态只有 64 维,远小于经典 Transformer 的隐藏维度。即使有 512 维的嵌入『外衣』,真正进行令牌混合的空间只有 64 维。

  2. 梯度消失问题(Barren Plateau):参数化量子回路在随机初始化时,梯度指数级趋近于零。这是量子机器学习的根本性挑战——Quixer 无法幸免。原论文第 6 节将此明确列为开放问题。

  3. 有限上下文:窗口仅 32 个令牌——后续研究 QMamba 指出这是 Quixer 的一大瓶颈。

  4. 经典模拟的近似:经典模拟中 QSVT 通过直接计算 $M^k|0\rangle$ 来实现,免去了构造 SELECT-PREPARE 回路和计算相位角度 $\{\phi_k\}$ 的困难。但这意味着,经典模拟并不能完全代表真实量子硬件上的行为(后者可能因为干涉效应产生更多表达力,也可能因为噪声产生更差表现)。

10.3 Aalto 大学复现结果

2024 年 9 月,Aalto 大学的一篇学士论文在经典模拟环境下独立复现了 Quixer 实验。关键发现:

  • Quixer 可以在 Penn Treebank 上训练并获得合理的结果
  • 训练极其缓慢(CPU 上需要数小时到数天)
  • 性能低于经典 Transformer 基线
  • 论文确认了梯度消失问题的存在

10.4 性能评估的正确视角

评估 Quixer 的性能时,以下几点非常重要:

  1. 概念验证 > 性能竞赛:Quixer 的价值在于证明了量子原语可以构建可训练的语言模型,而不是与 GPT-4 竞争。

  2. 经典模拟 ≠ 量子实现:当前所有 Quixer 结果都来自经典模拟,其计算瓶颈($O(4^q)$)完全不同于真实量子硬件的瓶颈。经典模拟中表现一般的模型在量子硬件上可能有完全不同的行为。

  3. 第一代产品:Quixer 是量子 Transformer 领域的开创性工作。第一个晶体管计算机也无法与现代 CPU 比性能。

  4. 框架价值:论文强调 Quixer 是一个框架——其参数化组件可以被替换为不同结构,从而产生一类全新的量子 Transformer。比较『单个实例』的性能是不全面的。


第十一章:模型推理与预测

目标:用第九章真实训练好的模型(fresh_quixerfresh_transformer)做真实推理——观察 Quixer 到底『预测』出了什么,并实现一个简单的自回归文本生成循环。

11.1 从 logits 到词元预测

Quixer.forward 返回的 logits(形状 [batch, vocabulary_size])本身只是未归一化的分数,要变成『预测』还需要几步:

  1. Softmax:把 logits 转换成一个合法的概率分布 $p(v) = \dfrac{e^{\text{logit}_v}}{\sum_{v'} e^{\text{logit}_{v'}}}$。
  2. Argmax(贪心预测):概率最高的词元就是模型认为『最可能的下一个词』——评估 Top-1 准确率、以及本章 11.3 节的贪心解码都基于它。
  3. Top-k:只看概率最高的 k 个候选词,比单一 argmax 更能反映模型的『犹豫程度』——如果真实词元落在 Top-5 而不是 Top-1,说明模型『方向大致正确,但不够自信』。

对 Quixer 而言还有第三个量:mean_prob(第 7.3 节介绍过)。它不是词表上的概率分布,而是 LCU+QSVT 混合之后量子态矢量 L2 范数的批平均值——一个介于 0 和 1 之间的标量,反映了非酉操作导致的『概率质量』保留比例。经典基线模型(Transformer/LSTM/FNet)没有这个量,forward 直接返回 None 作为第二个值。

11.2 真实执行:单步预测对比(Quixer vs Transformer)

从 9.4 节切片后的真实 Penn Treebank 测试数据 test_small 里取出一个真实样本,分别喂给 fresh_quixerfresh_transformer(两者都已经过 9.4.3 节的 train_cycle 训练并重新加载了各自的最优 checkpoint),对比两者的 Top-5 预测:

In [28]:
import torch.nn.functional as F

TOPK = 5
# 选 eval_batch_idx=N_EVAL_STEPS-1(即最后一个可用 batch):batchify_s2s 会在数据最前面
# 用 window_size-1 个 <pad> 撑出第一份上下文(第 5.6/9.5 节提到的边界处理),batch_idx=0 时
# 刚好会取到这段纯 padding 的上下文——选最后一个 batch 可以绕开这个边界效应,拿到一段真实文本。
eval_batch_idx = N_EVAL_STEPS - 1
sample_row = 0

x_eval, y_eval = st.get_batch_s2s(test_small, eval_batch_idx, WINDOW)
itos = vocab.get_itos()

context_ids = x_eval[sample_row].tolist()
true_next_id = y_eval[sample_row].item()

context_text = " ".join(itos[t] for t in context_ids)
true_next_text = itos[true_next_id]

print(f"上下文窗口(WINDOW={WINDOW} 个真实 PTB 测试集词元):")
print(" ", context_text)
print("\n真实的下一个词元:", repr(true_next_text))

for model_name, model in [("Quixer", fresh_quixer), ("Transformer", fresh_transformer)]:
    model.eval()
    with torch.no_grad():
        logits, mean_prob = model(x_eval[sample_row : sample_row + 1])
    probs = F.softmax(logits[0], dim=-1)
    top_probs, top_ids = probs.topk(TOPK)

    mean_prob_str = f"{mean_prob.item():.4f}" if mean_prob is not None else "N/A(经典基线无此量)"
    print(f"\n[{model_name}] mean_prob = {mean_prob_str}")
    print(f"[{model_name}] Top-{TOPK} 预测:")
    for rank, (p, tid) in enumerate(zip(top_probs.tolist(), top_ids.tolist()), start=1):
        hit = "  <-- 命中真实词" if tid == true_next_id else ""
        print(f"    {rank}. {itos[tid]!r:15s}  p={p:.4f}{hit}")
上下文窗口(WINDOW=8 个真实 PTB 测试集词元):
  n ' t black monday <unk> but while

真实的下一个词元: 'the'
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])

[Quixer] mean_prob = 0.7779
[Quixer] Top-5 预测:
    1. '<unk>'          p=0.0002
    2. 'n'              p=0.0002
    3. 'a'              p=0.0002
    4. 'of'             p=0.0002
    5. '.'              p=0.0002

[Transformer] mean_prob = N/A(经典基线无此量)
[Transformer] Top-5 预测:
    1. '<unk>'          p=0.0004
    2. 'to'             p=0.0003
    3. 'of'             p=0.0003
    4. 'and'            p=0.0003
    5. 'the'            p=0.0003  <-- 命中真实词

11.3 自回归文本生成:贪心解码

run.py 和 9.4 节都只做『单步下一词预测』的训练/评估,从没有让模型真正连续生成一段文本。这里手动实现一个最简单的自回归生成循环:每一步用当前的 WINDOW 个词元预测下一个词元,把预测结果拼接到序列末尾,再丢弃最旧的一个词元以保持窗口大小不变(因为 Quixer/经典基线的 forward 都要求输入正好是 WINDOW 个词元),重复 GENERATE_STEPS 次。

In [29]:
GENERATE_STEPS = 20

def greedy_generate(model, context_ids, steps, window):
    model.eval()
    generated = list(context_ids)
    mean_probs = []
    for _ in range(steps):
        window_ids = generated[-window:]
        x_step = torch.tensor(window_ids, dtype=torch.long).unsqueeze(0)  # [1, window]
        with torch.no_grad():
            logits, mean_prob = model(x_step)
        next_id = logits[0].argmax().item()
        generated.append(next_id)
        mean_probs.append(mean_prob.item() if mean_prob is not None else float("nan"))
    return generated, mean_probs

quixer_generated, quixer_mean_probs = greedy_generate(fresh_quixer, context_ids, GENERATE_STEPS, WINDOW)
transformer_generated, _ = greedy_generate(fresh_transformer, context_ids, GENERATE_STEPS, WINDOW)

print("原始上下文:")
print(" ", " ".join(itos[t] for t in context_ids))
print(f"\n[Quixer] 贪心解码生成的 {GENERATE_STEPS} 个新词元:")
print(" ", " ".join(itos[t] for t in quixer_generated[len(context_ids):]))
print(f"\n[Transformer] 贪心解码生成的 {GENERATE_STEPS} 个新词元:")
print(" ", " ".join(itos[t] for t in transformer_generated[len(context_ids):]))
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([1, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([8, 8]) pqc_parameters=torch.Size([1, 8, 12]) lcu_coefficients=torch.Size([1, 8])
[Quixer] apply_lcu states=torch.Size([1, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([1, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([1, 8]) accumulated_state=torch.Size([1, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([1, 8])
原始上下文:
  n ' t black monday <unk> but while

[Quixer] 贪心解码生成的 20 个新词元:
  <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk>

[Transformer] 贪心解码生成的 20 个新词元:
  <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk> <unk>

11.4 mean_prob 在推理阶段的含义

如何看待上面生成的文本fresh_quixer/fresh_transformer 只在 9.4 节的极小数据切片(几十个 batch、2 个 epoch)上训练过,远未收敛——生成的文本大概率是重复或不连贯的词元序列,这是预期之中的结果,不代表 Quixer 的真实生成能力上限。这里的重点是验证生成 pipeline(滑动窗口 + 贪心解码)本身能真实跑通,具体文本质量的评估请参考第十章的论文基准数字。

下面看 Quixer 在生成过程中每一步的 mean_prob 如何变化。回顾第五、六章:LCU 和 QSVT 都是非酉操作(分别通过 PREPARE-SELECT 的后选择、以及 QSVT 多项式变换的振幅衰减实现),因此量子态矢量的范数会随着这些操作逐步衰减;mean_prob 就是这个衰减后范数的批平均值,也近似对应了真实量子硬件上做后选择测量时的成功概率 $p_{\text{success}}$(第十一章 11.1.5 节提到的采样开销正来自于这里)。

In [30]:
print("Quixer 每一步生成时的 mean_prob(LCU+QSVT 归一化后的态矢量范数均值):")
for step, p in enumerate(quixer_mean_probs, start=1):
    print(f"  步骤 {step:2d}: mean_prob = {p:.4f}")

print(
    f"\n均值: {sum(quixer_mean_probs) / len(quixer_mean_probs):.4f}"
    f"   最小值: {min(quixer_mean_probs):.4f}"
    f"   最大值: {max(quixer_mean_probs):.4f}"
)
print(
    "\n注意:mean_prob 越低,意味着如果这是在真实量子硬件上运行,"
    "后选择丢弃的采样比例就越高——这是 Quixer 在真实硬件部署时的核心开销来源之一。"
)
Quixer 每一步生成时的 mean_prob(LCU+QSVT 归一化后的态矢量范数均值):
  步骤  1: mean_prob = 0.7779
  步骤  2: mean_prob = 0.7774
  步骤  3: mean_prob = 0.7776
  步骤  4: mean_prob = 0.7783
  步骤  5: mean_prob = 0.7772
  步骤  6: mean_prob = 0.7770
  步骤  7: mean_prob = 0.7775
  步骤  8: mean_prob = 0.7772
  步骤  9: mean_prob = 0.7770
  步骤 10: mean_prob = 0.7770
  步骤 11: mean_prob = 0.7770
  步骤 12: mean_prob = 0.7770
  步骤 13: mean_prob = 0.7770
  步骤 14: mean_prob = 0.7770
  步骤 15: mean_prob = 0.7770
  步骤 16: mean_prob = 0.7770
  步骤 17: mean_prob = 0.7770
  步骤 18: mean_prob = 0.7770
  步骤 19: mean_prob = 0.7770
  步骤 20: mean_prob = 0.7770

均值: 0.7772   最小值: 0.7770   最大值: 0.7783

注意:mean_prob 越低,意味着如果这是在真实量子硬件上运行,后选择丢弃的采样比例就越高——这是 Quixer 在真实硬件部署时的核心开销来源之一。

第十二章:端到端测试与评价

目标:搭建一个完整的端到端评价流程——测试集困惑度、词元级准确率、checkpoint 保存/加载回归测试——并把结果汇总成一份最终评价报告。

12.1 测试集困惑度评估

train_cycle(9.4.3 节)在训练结束后已经自动算过一次测试集 loss(存在 quixer_test_loss/transformer_test_loss 里)。这里把 st.evaluate 包装成一个独立、可复用的评价函数,重新跑一遍同样的评估,并验证它与 train_cycle 内部算出的数字完全一致——这本身就是一个有用的正确性检查:如果两次算出的数字不一致,说明模型状态或数据切片之间出现了不该有的差异。

In [31]:
import math

loss_function_eval = torch.nn.CrossEntropyLoss()

def evaluate_perplexity(model, data, window):
    loss = st.evaluate(model, data, loss_function_eval, window)
    return loss, math.exp(loss)

quixer_eval_loss, quixer_eval_ppl = evaluate_perplexity(fresh_quixer, test_small, WINDOW)
transformer_eval_loss, transformer_eval_ppl = evaluate_perplexity(fresh_transformer, test_small, WINDOW)

print(
    f"[Quixer]      test loss = {quixer_eval_loss:.4f}  ppl = {quixer_eval_ppl:.1f}\n"
    f"              与 9.4.3 节 train_cycle 内部算出的 quixer_test_loss={quixer_test_loss:.4f} 一致: "
    f"{math.isclose(quixer_eval_loss, quixer_test_loss)}"
)
print(
    f"\n[Transformer] test loss = {transformer_eval_loss:.4f}  ppl = {transformer_eval_ppl:.1f}\n"
    f"              与 9.4.3 节 train_cycle 内部算出的 transformer_test_loss={transformer_test_loss:.4f} 一致: "
    f"{math.isclose(transformer_eval_loss, transformer_test_loss)}"
)
  0%|          | 0/12 [00:00<?, ?it/s]
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
100%|██████████| 12/12 [00:00<00:00, 348.28it/s]
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
  0%|          | 0/12 [00:00<?, ?it/s]
100%|██████████| 12/12 [00:00<00:00, 1719.92it/s]
[Quixer]      test loss = 8.9353  ppl = 7595.2
              与 9.4.3 节 train_cycle 内部算出的 quixer_test_loss=8.9353 一致: True

[Transformer] test loss = 8.6392  ppl = 5648.6
              与 9.4.3 节 train_cycle 内部算出的 transformer_test_loss=8.6392 一致: True

12.2 词元级 Top-1 / Top-5 准确率

困惑度是一个『整体』指标,不容易直觉地判断『模型每次预测对不对』。这里换一个更直观的指标:遍历 test_small 里的每一个 batch,统计模型的 Top-1(argmax)和 Top-5 预测命中真实下一词元的比例。

In [32]:
def compute_accuracy(model, data, window, topk=5):
    model.eval()
    n_batches = data.shape[0] - window
    correct_top1 = 0
    correct_topk = 0
    total = 0
    with torch.no_grad():
        for batch_idx in range(n_batches):
            x, y = st.get_batch_s2s(data, batch_idx, window)
            logits, _ = model(x)
            top1_pred = logits.argmax(dim=-1)
            topk_pred = logits.topk(topk, dim=-1).indices
            correct_top1 += (top1_pred == y).sum().item()
            correct_topk += (topk_pred == y.unsqueeze(1)).any(dim=1).sum().item()
            total += y.numel()
    return correct_top1 / total, correct_topk / total, total

accuracy_results = {}
for model_name, model in [("Quixer", fresh_quixer), ("Transformer", fresh_transformer)]:
    top1_acc, top5_acc, n_tokens_eval = compute_accuracy(model, test_small, WINDOW, topk=5)
    accuracy_results[model_name] = (top1_acc, top5_acc)
    print(f"[{model_name}] 在测试集切片上评价了 {n_tokens_eval} 个词元预测:")
    print(f"    Top-1 准确率: {top1_acc:.2%}")
    print(f"    Top-5 准确率: {top5_acc:.2%}")
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([32, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([256, 8]) pqc_parameters=torch.Size([32, 8, 12]) lcu_coefficients=torch.Size([32, 8])
[Quixer] apply_lcu states=torch.Size([32, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([32, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([32, 8]) accumulated_state=torch.Size([32, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([32, 8])
[Quixer] 在测试集切片上评价了 384 个词元预测:
    Top-1 准确率: 8.59%
    Top-5 准确率: 19.27%
[Transformer] 在测试集切片上评价了 384 个词元预测:
    Top-1 准确率: 8.59%
    Top-5 准确率: 20.05%

12.3 Checkpoint 回归测试:保存/加载一致性

9.4.3 节的 train_cycle 已经把最优权重存到了 ./trained_models/,并在训练结束后重新 load_state_dictfresh_quixer。这里做一个独立的回归测试,直接验证『保存 -> 重新加载』这个流程本身不会改变模型的行为:把 fresh_quixer 当前权重存到一个临时文件,用同样的超参数新建一个模型实例并加载这个临时文件,再对同一份真实输入比较两者的输出是否完全一致。

In [33]:
import tempfile
from pathlib import Path as _Path

regression_test_input = x_eval[:2]  # 复用 11.2 节的真实测试数据

fresh_quixer.eval()
with torch.no_grad():
    reference_logits, reference_mean_prob = fresh_quixer(regression_test_input)

with tempfile.TemporaryDirectory() as tmpdir:
    checkpoint_path = _Path(tmpdir) / "regression_test_checkpoint.pt"
    # 真实踩坑:state_dict() 里混入了 torchquantum_device.states 这个缓冲区——它是量子设备内部的
    # 态矢量,每次 forward 都会被 set_states 按当前输入的 batch 大小重新覆写,并不是训练学到的参数。
    # 如果加载到一个从未跑过 forward、构造时 batch_size 超参数不同的全新实例上,形状对不上,
    # load_state_dict 会报 size mismatch。这里把它过滤掉,只保存/加载真正的可训练参数。
    trainable_state_dict = {
        k: v for k, v in fresh_quixer.state_dict().items() if "torchquantum_device" not in k
    }
    torch.save(trainable_state_dict, checkpoint_path)

    reloaded_quixer = st.create_model(demo_hparams_by_model["Quixer"], torch.device("cpu"), len(vocab))
    reloaded_quixer.load_state_dict(torch.load(checkpoint_path), strict=False)
    reloaded_quixer.eval()

    with torch.no_grad():
        reloaded_logits, reloaded_mean_prob = reloaded_quixer(regression_test_input)

logits_match = torch.allclose(reference_logits, reloaded_logits, atol=1e-6)
mean_prob_match = torch.allclose(reference_mean_prob, reloaded_mean_prob, atol=1e-6)

print("Checkpoint 保存/重新加载后,输出是否与内存中的原模型完全一致:")
print("  logits 一致:    ", logits_match)
print("  mean_prob 一致: ", mean_prob_match)
print(f"  logits 最大差值: {(reference_logits - reloaded_logits).abs().max().item():.2e}")
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([2, 8]) pqc_parameters=torch.Size([2, 8, 12]) lcu_coefficients=torch.Size([2, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([16, 8]) pqc_parameters=torch.Size([2, 8, 12]) lcu_coefficients=torch.Size([2, 8])
[Quixer] apply_lcu states=torch.Size([2, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([2, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([2, 8]) accumulated_state=torch.Size([2, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([16, 8]) pqc_parameters=torch.Size([2, 8, 12]) lcu_coefficients=torch.Size([2, 8])
[Quixer] apply_lcu states=torch.Size([2, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([2, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([2, 8]) accumulated_state=torch.Size([2, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([2, 8])
[Quixer] apply_qsvt_and_lcu initial_states=torch.Size([2, 8]) pqc_parameters=torch.Size([2, 8, 12]) lcu_coefficients=torch.Size([2, 8]) qsvt_polynomial_coefficients=torch.Size([3])
[Quixer] apply_lcu repeated_initial_state=torch.Size([16, 8]) pqc_parameters=torch.Size([2, 8, 12]) lcu_coefficients=torch.Size([2, 8])
[Quixer] apply_lcu states=torch.Size([2, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([2, 8])
[Quixer] qsvt step 1: monomial_state=torch.Size([2, 8]) accumulated_state=torch.Size([2, 8])
[Quixer] apply_lcu repeated_initial_state=torch.Size([16, 8]) pqc_parameters=torch.Size([2, 8, 12]) lcu_coefficients=torch.Size([2, 8])
[Quixer] apply_lcu states=torch.Size([2, 8, 8])
[Quixer] apply_lcu lcu_applied_to_state=torch.Size([2, 8])
[Quixer] qsvt step 2: monomial_state=torch.Size([2, 8]) accumulated_state=torch.Size([2, 8])
[Quixer] apply_qsvt_and_lcu result=torch.Size([2, 8])
Checkpoint 保存/重新加载后,输出是否与内存中的原模型完全一致:
  logits 一致:     True
  mean_prob 一致:  True
  logits 最大差值: 0.00e+00

12.4 端到端评价报告汇总

把本章算出的困惑度、Top-1/Top-5 准确率汇总成一张表,和 9.4.5 节缩小规模的 demo_run_results 放在一起看:

In [34]:
print("=" * 70)
print("端到端评价报告汇总(本教程缩小规模的真实结果,不是论文基准数字)")
print("=" * 70)
print(f"{'模型':<14}{'Test PPL':>12}{'Top-1 Acc':>12}{'Top-5 Acc':>12}")
for model_name in ["Quixer", "Transformer"]:
    ppl = quixer_eval_ppl if model_name == "Quixer" else transformer_eval_ppl
    top1_acc, top5_acc = accuracy_results[model_name]
    print(f"{model_name:<14}{ppl:>12.1f}{top1_acc:>12.2%}{top5_acc:>12.2%}")
print("=" * 70)
print(
    "\n注意:以上数字来自本教程 9.4 节切片后的极小数据集(几十个 batch、2 个 epoch),"
    "\n仅用于验证推理/评价 pipeline 的正确性,不能反映 Quixer 在完整 Penn Treebank 上的真实性能——"
    "\n后者请参考第十章 10.1 节的论文基准表和 10.3 节的 Aalto 大学复现结果。"
)
======================================================================
端到端评价报告汇总(本教程缩小规模的真实结果,不是论文基准数字)
======================================================================
模型                Test PPL   Top-1 Acc   Top-5 Acc
Quixer              7595.2       8.59%      19.27%
Transformer         5648.6       8.59%      20.05%
======================================================================

注意:以上数字来自本教程 9.4 节切片后的极小数据集(几十个 batch、2 个 epoch),
仅用于验证推理/评价 pipeline 的正确性,不能反映 Quixer 在完整 Penn Treebank 上的真实性能——
后者请参考第十章 10.1 节的论文基准表和 10.3 节的 Aalto 大学复现结果。

第十三章:局限性与开放问题

目标:诚实评估 Quixer 当前的局限性,识别值得关注的开放问题。

13.1 已确认的局限性

13.1.1 梯度消失(Vanishing Gradients)

这是 Quixer 最根本的挑战。参数化量子回路(PQC)普遍存在 Barren Plateau 现象:当回路深度或量子比特数增加时,梯度以指数方式趋近于零。

具体到 Quixer:

  • Ansatz 14 使用 4 层,在 6 量子比特下仍然可训练
  • 但扩展到更多量子比特或更深回路时,梯度消失将变得严重
  • 这限制了模型规模的扩展

原论文原文(第 6 节):

"Finding an instance of Quixer that does not suffer from vanishing gradients while being too expressive for classical simulation is left to future work."

翻译:找到一个既无梯度消失问题、又超出经典模拟能力的 Quixer 实例,是留给未来工作的开放问题。

13.1.2 经典可模拟性(Classical Simulability)

当前所有 Quixer 实验使用的量子回路(6 量子比特、4 层 PQC、3 次 QSVT)在经典计算机上可以精确模拟。这意味着:

  • 没有任何量子优势——经典计算机完全可以模拟这些回路
  • 要证明量子优势,需要扩大到经典不可模拟的规模
  • 但扩大规模又面临梯度消失问题——两难困境

13.1.3 有限上下文窗口(32 令牌)

在 Penn Treebank 实验中,Quixer 的上下文窗口仅为 32 个令牌。这远小于现代 LLM 的数万到数十万令牌上下文。后续工作 QMamba(ICAART 2025)以 Quixer 为对比基线,指出这个限制是其核心瓶颈之一。

13.1.4 对经典输出头的强依赖

当前模型最后的『经典输出头』(两层 MLP,从 3q 维到 vocab_size 维)承担了大量的计算工作。没有这个输出头,模型的性能会大幅下降。这引发了一个问题:量子部分到底学到了多少,还是经典 MLP 在做大部分『重活』?

13.1.5 未解决的量子硬件部署

  • 论文中的资源估算(§3.5)考虑了后选择概率和门复杂度,但实际硬件部署尚未被独立验证
  • Quantinuum 2025 年 6 月的博客宣称已将 Quixer 部署到真实硬件,但缺少性能数据和第三方验证
  • 后选择导致的 $O(1/p_{\text{success}})$ 采样开销在实际中可能非常高

13.2 关键开放问题

这些问题不仅对 Quixer 重要,对整个量子机器学习领域也至关重要:

  1. 量子优势边界:量子比特数、回路深度、QSVT 多项式次数——需要达到什么规模的组合,才能在语言建模任务上产生可验证的量子优势?

  2. 梯度消失的解决:是否存在某种回路结构或初始化策略,能从根本上避免 PQC 的 Barren Plateau?

  3. LCU 系数的可解释性:$b_j = e^{i\gamma_j}|a_j|^2$ 究竟代表了什么语义?能否像经典注意力的 softmax 权重那样被可视化和解读?

  4. 与经典架构的融合:Quixer 使用纯粹的 LCU+QSVT 替代注意力——但如果采用混合策略(部分量子、部分经典)会更好吗?

  5. 扩展到更大模型:能否设计一种『量子蒸馏』方案,用小规模量子 Transformer 指导大规模经典 Transformer 训练?


第十四章:后续研究方向与生态

目标:了解 Quixer 启发了哪些后续工作,以及你可以在哪些方向上贡献。

14.1 QMamba:从 Transformer 到状态空间模型

QMamba(ICAART 2025)是 Quixer 最直接的后续工作。它基于以下观察:

  • Quixer 在 Penn Treebank 上的上下文窗口仅 32 个令牌
  • Mamba(选择性状态空间模型)在长序列建模上表现出色
  • 将 Mamba 的选择性 SSM 机制移植到量子域可能克服 Transformer 的上下文限制

QMamba 是首个量子 Mamba 模型,以 Quixer 为对比基线,代表了量子序列建模从 Transformer 范式向 SSM 范式的转移。

14.2 Quixer 框架的可扩展性

原论文第 5 节特别强调了 Quixer 作为框架的价值:

"its parameterised components can be substituted with fixed structures to yield new classes of quantum transformers."

翻译:Quixer 的参数化组件可以被替换为固定结构,从而产生全新类别的量子 Transformer。

具体来说,你可以在以下组件上做文章:

组件 当前选择 替代方案
酉嵌入回路 Ansatz 14 其他 PQC ansatz、Hardware-Efficient Ansatz
LCU 系数结构 可训练复数权重 固定权重(如相等权重、基于令牌距离的衰减权重)
QSVT 多项式 通用多项式 特定函数的多项式近似(如 sigmoid、tanh)
测量方案 X, Y, Z 全测量 稀疏测量、自适应测量、基于任务的测量选择

14.3 相关项目与工具链

项目 说明 链接
TorchQuantum MIT 开发的量子机器学习框架,Quixer 的核心依赖 github.com/mit-han-lab/torchquantum
PennyLane Xanadu 的量子机器学习库,支持自动微分和 QSVT pennylane.ai
Qiskit IBM 的量子计算框架(注意版本 < 1.0) qiskit.org
Lambeq Quantinuum 的量子 NLP 框架 github.com/Quantinuum/lambeq
pyqsp QSVT 相位角度计算工具 github.com/ichuang/pyqsp
QSPPACK QSVT 的 MATLAB 实现 github.com/qsppack/QSPPACK

14.4 入门的动手实验建议

如果你想在 Quixer 的基础上做研究和实验,以下是一条推荐的路径:

阶段 1:理解代码

  1. 成功安装并运行 Quixer(按照第九章)
  2. quixer_model.py 中添加 print 语句,观察每个步骤的 Tensor Shape
  3. 修改 run.py 中的超参数(如减少 epochs 到 5,观察训练过程)

阶段 2:小改动

  1. 修改 LCU 系数初始化方式(如改为均匀分布)
  2. 改变 QSVT 多项式次数(改为 1 或 5,观察性能变化)
  3. embedding_dimension 从 512 改为 256,观察效果

阶段 3:组件替换

  1. 用不同的 PQC ansatz 替换 Ansatz 14
  2. 尝试不同的测量方案(如只测量 Z 期望值)
  3. 实现一个无需输出头的『纯量子』版本

阶段 4:新模型设计

  1. 参考 Quixer 框架设计你自己的量子 Transformer 变体
  2. 探索 LCU+QSVT 的组合在分类、序列标注等任务上的应用
  3. 研究混合量子-经典架构:用 Quixer 作为『注意力替代』插入经典 Transformer

14.5 追踪最新进展

由于该领域发展迅速,建议关注以下渠道:

  • arXiv 量子物理学(quant-ph)和机器学习(cs.LG)子类别
  • Quantinuum 官方博客及研究页面
  • 量子机器学习会议:QML(Quantum Machine Learning)、QTML(Quantum Techniques in Machine Learning)
  • ICAART(International Conference on Agents and Artificial Intelligence)— QMamba 发表于此

附录 A:术语对照表

英文 中文 说明
Ansatz 拟设 / 回路模板 参数化量子回路的固定结构
Barren Plateau 贫瘠高原 梯度指数衰减到零的现象
Block Encoding 块编码 用酉矩阵的子块表示任意矩阵
Expectation Value 期望值 $\langle \psi \lvert O \rvert \psi \rangle$,可观测量的平均测量结果
Feed-Forward Network (FFN) 前馈网络 逐位置的经典非线性变换
Linear Combination of Unitaries (LCU) 线性酉组合 $\sum_j b_j U_j$
Pauli Matrices (X, Y, Z) 泡利矩阵 基本量子门/可观测量
Penn Treebank (PTB) 宾州树库 经典语言建模基准数据集
Perplexity (PPL) 困惑度 $\exp(\text{cross-entropy loss})$
Postselection 后选择 仅保留满足条件的测量结果
Quantum Singular Value Transform (QSVT) 量子奇异值变换 对块编码矩阵施加多项式变换
SELECT-PREPARE 选择-准备回路 LCU 的标准回路实现
Token Mixing 令牌混合 不同令牌表示之间交换信息
Unitary 酉(矩阵/变换) 满足 $U^\dagger U = I$ 的复矩阵

附录 B:关键公式汇总

公式 编号 说明
$$\text{Attention}(Q,K,V) = \text{softmax}(\frac{QK^T}{\sqrt{d_k}})V$$ (1) 经典点积自注意力
$$M = \sum_{j=0}^{n-1} b_j U_j$$ (2) LCU 令牌混合
$$U_{\text{SEL}} = \sum_j |j\rangle\langle j| \otimes U_j$$ (3) SELECT 酉变换
$$U_{\text{PREP}}|0\rangle = \sum_j a_j |j\rangle$$ (4) PREPARE 酉变换
$$U_M = (U_{\text{PREP}}^\dagger \otimes I) U_{\text{SEL}} (U_{\text{PREP}} \otimes I)$$ (5) LCU 完整回路
$$M = (\langle 0| \otimes I) U_M (|0\rangle \otimes I)$$ (6) 块编码投影
$$P_c(M) = \sum_{k=0}^d c_k M^k$$ (7) QSVT 多项式变换
$$|P_c(x)| \leq 1, \forall x \in [-1,1]$$ (8) QSVT 有界性约束
$$\text{parity}(P_c) = d \bmod 2$$ (9) QSVT 奇偶性约束

附录 C:推荐阅读路径

如果你只想快速了解

  1. 第三章:Quixer 宏观概览(15 分钟)
  2. 第七章:Quixer 完整架构详解(40 分钟)

如果你想深入理解原理

  1. 第一章:GPT/Transformer 回顾
  2. 第四章:块编码
  3. 第五章:LCU
  4. 第六章:QSVT
  5. 第七章:完整架构

如果你想动手实现

  1. 直接跳到第九章:环境搭建与动手运行
  2. 遇到不理解的地方回查对应章节
  3. 配合各章节穿插的源码讲解阅读代码

如果你想做研究

  1. 完整阅读所有章节
  2. 仔细阅读原论文 arXiv:2406.04305
  3. 研究第十三章的开放问题,选择你感兴趣的方向
  4. 阅读 QMamba 论文了解最新进展

参考资料

  1. Khatri, N., Matos, G., Coopmans, L., & Clark, S. (2024). Quixer: A Quantum Transformer Model. arXiv:2406.04305. https://arxiv.org/abs/2406.04305
  2. Quixer 官方代码库. https://github.com/Quantinuum/Quixer
  3. Quantinuum Blog. "Announcing Quixer — Quantinuum's State-of-the-Art Quantum Transformer". https://www.quantinuum.com/blog/announcing-quixer
  4. Quantinuum Blog. "Our Hardware is Now Running Quantum Transformers". https://www.quantinuum.com/blog/our-hardware-is-now-running-quantum-transformers
  5. Vaswani, A., et al. (2017). Attention Is All You Need. NeurIPS 2017.
  6. Lee-Thorp, J., et al. (2022). FNet: Mixing Tokens with Fourier Transforms. NAACL 2022.
  7. Stenzel, L., & Kölle, M. (2025). QMamba: Quantum Selective State Space Models for Language Modeling. ICAART 2025.
  8. Sim, S., et al. (2019). Expressibility and entangling capability of parameterized quantum circuits for hybrid quantum-classical algorithms. arXiv:1905.10876. (Ansatz 14 的来源)
  9. Gilyén, A., et al. (2019). Quantum singular value transformation and beyond: exponential improvements for quantum matrix arithmetics. STOC 2019. (QSVT 的原始论文)
  10. Childs, A. M., & Wiebe, N. (2012). Hamiltonian simulation using linear combinations of unitary operations. QIC 2012. (LCU 的原始论文)
  11. Shah, J. (2024). A Comparative Study of Classical and Quantum Transformer Models and Their Applications [Bachelor's thesis, Aalto University]. Aaltodoc. https://aaltodoc.aalto.fi/server/api/core/items/a3998701-7b17-48e2-a74e-bcf87d927c26
  12. TorchQuantum. https://github.com/mit-han-lab/torchquantum
  13. PennyLane QSVT 文档. https://docs.pennylane.ai/en/stable/code/api/pennylane.qsvt.html

教程版本:v1.0(2026 年 5 月)

基于工作流:deep-research(94 个搜索/验证代理,1,928,809 tokens)

声明:本教程基于对 Quixer 论文、源代码和第三方资料的深入研究编写。所有核心声明均经过 3 票对抗验证(共验证 25 项声明,确认 13 项,否定 12 项)。教程中的代码解析基于 Apache 2.0 许可证下的官方开源实现。

致谢:感谢 Quantinuum 团队开源 Quixer 代码和数据,使社区能够学习、复现和在此基础上继续创新。

Comments

2026-05-30