创建自己的Q#模拟器-第1部分

模拟器是QDK的一项特别通用的功能。它们使您无需更改即可在Q#程序中执行各种任务。这样的任务包括全状态模拟资源估计跟踪模拟。新界面IQuantumProcessor使创建自己的模拟器并将其集成到Q#项目中变得非常容易。



这篇文章是该界面系列中的第一篇。首先,我们将实现可逆模拟器作为第一个示例,我们将在以后的博客文章中对其进行扩展。可逆模拟器可以模拟只包括经典的操作量子节目:XCNOTCCNOT(Toffoli门)或随机控制的X运算。由于可逆模拟器可以通过为每个量子位分配一个布尔值来表示量子状态,因此它甚至可以运行数千个量子位的量子程序。该模拟器对于测试评估布尔函数的量子运算非常有用。







C#中的模拟器实现



这篇博客文章介绍了基本的代码片段。完整的源代码可以在Microsoft QDK Samples存储库中找到。


您可以通过扩展类来开始编写自己的模拟器QuantumProcessorBase



class ReversibleSimulatorProcessor : QuantumProcessorBase {
    private IDictionary<Qubit, bool> simulationValues = new Dictionary<Qubit, bool>();

    //       (intrinsic operations)...
}


该类中已经添加了一个字典,该字典将存储程序中每个量子位的当前仿真值。经典量子态|0⟩和|1⟩分别表示为布尔值false和true。模拟器在Q#程序中定义内部操作。QuantumProcessorBase包含每个内部操作的方法,您可以重写该方法以在新模拟器中定义其行为。请注意,未实现的方法将引发默认异常。这将帮助您确定仍然需要执行该操作的情况,并将通知模拟器用户模拟器不支持嵌入式操作。例如,不可逆模拟器不能用于模拟包含非经典运算(例如H或T)的量子程序。



首先,通过重写OnAllocateQubits方法来错误地初始化新分配的qubit。同样,当释放量子位时,它们也会从字典中删除。在这种情况下,将调用OnReleaseQubits。



public override void OnAllocateQubits(IQArray qubits) {
    foreach (var qubit in qubits) {
        simulationValues[qubit] = false;
    }
}

public override void OnReleaseQubits(IQArray qubits) {
    foreach (var qubit in qubits) {
        simulationValues.Remove(qubit);
    }
}


通过这两个操作,可以确保将操作应用于某个量子位时可以使用仿真值,并且对于不在当前区域中的量子位,字典中不存在任何仿真值。



下一步,让我们实现经典的操作动作。在这种情况下,两个方法就足够了:模拟X操作时调用X方法,模拟X操作时调用ControlledX方法,其中也包括CNOT和CCNOT。 X运算会反转量子位的模拟值,而在任意控制的X运算的情况下,当且仅当所有控制量子位都设置为true时,目标量子位才会反转。



public override void X(Qubit qubit) {
    simulationValues[qubit] = !simulationValues[qubit];
}

public override void ControlledX(IQArray controls, Qubit qubit) {
    simulationValues[qubit] ^= And(controls);
}


最后,在Q#程序中测量一个量子位将返回结果(一个或零),该结果可以基于该量子位的当前模拟值进行计算。我们还实现了一个Reset方法,该方法在Q#程序中调用复位操作时将被调用。后者不会从当前区域中删除qubit,而是将模拟值重置回其初始值,该值为false。



public override Result M(Qubit qubit) {
    return simulationValues[qubit] ? Result.One : Result.Zero;
}

public override void Reset(Qubit qubit) {
    simulationValues[qubit] = false;
}


通过实例化QuantumProcessorDispatcher,可将ReversibleSimulatorProcessor用作模拟器。推荐的设计样式是为模拟器提供一个自定义类:



public class ReversibleSimulator : QuantumProcessorDispatcher {
    public ReversibleSimulator() : base(new ReversibleSimulatorProcessor()) {}
}


使用新的模拟器



让我们在以下Q#操作上尝试新的模拟器:



operation ApplyMajority(a : Qubit, b : Qubit, c : Qubit, f : Qubit) : Unit {
    within {
        CNOT(b, a);
        CNOT(b, c);
    } apply {
        CCNOT(a, c, f);
        CNOT(b, f);
    }
}


我们还编写了一个通过提供三个布尔输入执行多数操作的操作:



operation RunMajority(a : Bool, b : Bool, c : Bool) : Bool {
    using ((qa, qb, qc, f) = (Qubit(), Qubit(), Qubit(), Qubit())) {
        within {
            ApplyPauliFromBitString(PauliX, true, [a, b, c], [qa, qb, qc]);
        } apply {
            ApplyMajority(qa, qb, qc, f);
        }
        return MResetZ(f) == One;
    }
}


最后,您可以通过在C#主机程序中使用新的模拟器调用Q#操作来将所有内容组合在一起。以下示例评估所有不同输入目标的主要操作并输出所有仿真结果:



public static void Main(string[] args) {
    var sim = new ReversibleSimulator();
    var bits = new[] {false, true};

    foreach (var a in bits) {
        foreach (var b in bits) {
            foreach (var c in bits) {
                var f = RunMajority.Run(sim, a, b, c).Result;
                Console.WriteLine($"Majority({a,5}, {b,5}, {c,5})  =  {f,5}");
            }
        }
    }
}


准备编写自己的模拟器了吗?



我们希望这篇文章能激励您创建自己的模拟器。在接下来的步骤中,我们将讨论如何提高当前实现的性能(例如,不使用字典来存储模拟值),如何将其转换为独立的Q#项目,如何为非内部操作提供自定义操作以及如何提供诊断操作来帮助调试。



All Articles