热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Q_02_02WritingaQuantumProgram

你会学到什么如何在VisualStudio中设置量子解决方案和项目Q#操作的组件如何从C#调用Q#操作如何构建和执行您的量子算法

你会学到什么

  • 如何在Visual Studio中设置量子解决方案和项目
  • Q#操作的组件
  • 如何从C#调用Q#操作
  • 如何构建和执行您的量子算法

在Q#中创建一个钟状态

现在您已经安装了Microsoft Quantum Development Kit,并了解了它的工作原理,我们来编写您的第一个量子应用程序。 我们将从最简单的程序开始,并将其构建起来以展示量子叠加和量子纠缠。 我们将从基础状态 

中开始一个量子位,对其执行一些操作,然后测量结果。

该页面上的说明是为Visual Studio 2017和Visual Studio代码编写的。 如果您使用的是其他开发环境,请按照Visual Studio代码的说明使用命令行调用.NET Core SDK。

第1步:创建一个项目和解决方案

  • Visual Studio 2017
  • 命令行/ Visual Studio代码

打开Visual Studio 2017.转到File菜单并选择New > Project... 在项目模板资源管理Installed > Visual C# ,选择Q# Application模板。 确保您在New Project对话框顶部的列表中选择了.NET Framework 4.6.1 给你的项目命名Bell

第2步:输入Q#代码

我们的目标是创建一个显示纠缠的贝尔状态 。 我们将逐个建立起来,以展示量子比特状态,门限和测量的概念。

您的开发环境应该打开两个文件: Driver.cs ,它将保存您的量子代码的C#驱动程序,以及Operation.qs ,它将保存量子代码本身。

第一步是将Q#文件重命名为Bell.qs 右键单击Visual Studio解决方案资源管理器中的Operation.qs (Ctrl + Alt + L进行聚焦)或Visual Studio代码资源管理器(Ctrl /⌘+ Shift + E进行聚焦),然后选择重命名选项。 Bell替换Operation并返回。

要输入Q#代码,请确保您正在编辑Bell.qs窗口。 Bell.qs文件应该包含以下内容:

Q#

 
 
namespace Quantum.Bell
{
    open Microsoft.Quantum.Primitive;
    open Microsoft.Quantum.Canon;

    operation Operation () : () {
        body
        {

        }
    }
}

首先,将字符串Operation替换为Set ,并更改操作参数(第一对括号)以包含desired: Result, q1: Qubit的字符串desired: Result, q1: Qubit 该文件现在应该如下所示:

Q#

 
 
namespace Quantum.Bell
{
    open Microsoft.Quantum.Primitive;
    open Microsoft.Quantum.Canon;

    operation Set (desired: Result, q1: Qubit) : () {
        body
        {

        }
    }
}

现在,在包围操作体的大括号之间键入以下内容:

Q#

 
 
            let current = M(q1);

            if (desired != current)
            {
                X(q1);
            }

该文件现在应该如下所示:

Q#

 
 
namespace Quantum.Bell
{
    open Microsoft.Quantum.Primitive;
    open Microsoft.Quantum.Canon;

    operation Set (desired: Result, q1: Qubit) : () {
        body
        {
            let current = M(q1);
            if (desired != current)
            {
                X(q1);
            }
        }
    }
}

现在可以调用这个操作来设置一个已知状态的量子位( ZeroOne )。 我们测量量子位,如果它处于我们想要的状态,我们将它放在一边,否则,我们用X门翻转它。


这段代码定义了一个Q# 操作 操作是Q#中量子执行的基本单位。 它大致相当于C或C ++或Python中的函数,或者C#或Java中的静态方法。

操作的参数在括号内被指定为一个元组。 操作的返回类型是在冒号后指定的。 在这种情况下, Set操作没有返回,所以它被标记为返回一个空元组() 这是F#中Q# unit等价物,大致类似于C#中的void

操作具有主体部分,其中包含操作的实现。 一个操作也可以有伴随的受控的受控的伴随节。 这些用于指定适当操作的特定变体。 有关更多信息,请参阅Q#语言参考 。

Set操作结束后,将以下操作添加到名称空间中:

Q#

 
 
operation BellTest (count : Int, initial: Result) : (Int,Int) {
        body
        {
            mutable numOnes= 0;
            using (qubits = Qubit[1])
            {
                for (test in 1..count)
                {
                    Set (initial, qubits[0]);

                    let res = M (qubits[0]);

                    // Count the number of ones we saw:
                    if (res == One)
                    {
                        set numOnes= numOnes + 1;
                    }
                }
                Set(Zero, qubits[0]);
            }
            // Return number of times we saw a |0> and number of times we saw a |1>
            return (count-numOnes, numOnes);
        }
    }

此操作( BellTest )将循环进行count迭代,在量子位上设置指定的initial值,并测量( M )结果。 它会收集统计我们已经测量了多少零和一些零并将它们返回给调用者。 它执行另一个必要的操作。 它在返回它之前将量子位重置为已知状态( Zero ),以允许其他人以已知状态分配该量子位。 这是using语句所要求的。

所有这些调用都使用Microsoft.Quantum.Primitive命名空间中定义的原始量子操作。 例如, M操作在计算( Z )基础上测量其变元量子位,并且X将围绕x轴的状态翻转应用到其变元量子位。

正如你所看到的,Q#使用类似C#的分号和大括号来表示程序结构。

Q#有一个非常类似于C#的if语句。

Q#以独特的方式处理变量。 默认情况下,Q#中的变量是不可变的; 他们的价值在绑定后可能不会改变。 let关键字用于指示不可变变量的绑定。 操作参数总是不变的。

如果你需要一个变量值可以改变的变量,例如这个例子中的numOnes ,你可以用mutable关键字声明变量。 可变变量的值可以使用set语句进行更改。

在这两种情况下,编译器都会推断变量的类型。 Q#不需要变量的任何类型注释。

using语句对Q#也是特殊的。 它用于分配一个用于代码块的量子比特数组。 在Q#中,所有量子位都是动态分配和释放的,而不是在复杂算法的整个生命周期中存在的固定资源。 using语句在开始时分配一组量子位,并在块的末尾释放这些量子位。 有一个类似的borrowing说明用于分配潜在的肮脏的附属量子比特。

Q#中的for循环总是迭代一个范围。 没有Q#相当于一个传统的C型电脑声明。 范围可以由范围中的第一个和最后一个整数指定,如下例所示: 1..10是范围1,2,3,4,5,6,7,8,9和10.如果a需要使用除+1以外的步骤,然后使用三个整数,它们之间有.. 1..2..10是范围1,3,5,7和9.请注意,范围在两端都包含在内。

BellTest操作以元组的BellTest返回两个值。 该操作的返回类型是(Int, Int) ,并且return语句具有包含两个整数变量的显式元组。 Q#使用元组来传递多个值,而不是使用结构或记录。

第3步:输入C#驱动程序代码

切换到开发环境中的Driver.cs文件。 这个文件应该有以下内容:

C#

 
 
using Microsoft.Quantum.Simulation.Core;
using Microsoft.Quantum.Simulation.Simulators;

namespace Quantum.Bell
{
    class Driver
    {
        static void Main(string[] args) {

        }
    }
}

Main方法中,输入以下代码:

C#
 
            using (var sim = new QuantumSimulator())
            {
                // Try initial values
                Result[] initials = new Result[] { Result.Zero, Result.One };
                foreach (Result initial in initials)
                {
                    var res = BellTest.Run(sim, 1000, initial).Result;
                    var (numZeros, numOnes) = res;
                    System.Console.WriteLine(
                        $"Init:{initial,-4} 0s={numZeros,-4} 1s={numOnes,-4}");
                }
            }
            System.Console.WriteLine("Press any key to continue...");
            System.Console.ReadKey();

C#驱动程序有四个部分:

  • 构建量子模拟器。 在这个例子中, sim是模拟器。
  • 计算量子算法所需的任何参数。 在这个例子中, count固定为1000, initial是量子比特的初始值。
  • 运行量子算法。 每个Q#操作都会生成一个具有相同名称的C#类。 这个类有一个Run方法, 异步执行该操作。 执行是异步的,因为在实际硬件上的执行将是异步的。

    由于Run方法是异步的,因此我们获取Result属性; 这会阻止执行,直到任务完成并同步返回结果。

  • 处理操作的结果。 在这个例子中, res会收到操作的结果。 这里的结果是由模拟器测量的零个数( numZeros )和个数( numZeros )的元组。 这是在C#中作为ValueTuple返回的。 我们解构元组以获得两个字段,打印结果,然后等待按键。

第4步:构建并运行

  • Visual Studio 2017
  • 命令行/ Visual Studio代码

只需点击F5 ,你的程序就会建立并运行!

结果应该是:

Output
 Init:Zero 0s=1000 1s=0
Init:One  0s=0    1s=1000
Press any key to continue... 

按下键后程序将退出。

第5步:创建叠加

现在我们想操纵这个量子位。 首先,我们将尝试翻转它。 这是通过在我们测试BellTest之前执行一个X门完成的:

Q#
 
    X(qubits[0]);
    let res = M (qubits[0]);

现在结果(在按下F5 )相反:

Output
 Init:Zero 0s=0    1s=1000
Init:One  0s=1000 1s=0 

但是,迄今为止我们所看到的一切都是古典的。 让我们得到一个量子结果。 我们所需要做的就是用H或Hadamard门替换上一次运行中的X门。 我们不会一直将量子比特从0改为1,而只是将其翻转一半。 BellTest的替换行如下所示:

Q#
 
    H(qubits[0]);
    let res = M (qubits[0]);

现在结果变得更有趣了:

Output
 Init:Zero 0s=484  1s=516
Init:One  0s=522  1s=478 

每次我们测量时,我们都要求一个经典值,但量子位在0和1之间,所以我们得到(统计上)0时间的一半和时间的一半。 这被称为叠加 ,给我们第一个真正的量子态。

第6步:创建纠缠

现在我们将做出承诺的贝尔州并炫耀纠缠 我们需要做的第一件事是在BellTest分配2个量子位而不是1个量子位:

Q#
  using (qubits = Qubit[2]) { 

在我们测量BellTestM )之前,这将允许我们添加一个新的门( CNOT ):

Q#
 
                    Set (initial, qubits[0]);
                    Set (Zero, qubits[1]);

                    H(qubits[0]);
                    CNOT(qubits[0],qubits[1]);
                    let res = M (qubits[0]);

我们添加了另一个Set操作来初始化量子位1,以确保它在我们启动时始终处于Zero状态。

我们还需要在释放它之前重置第二个量子位(这也可以用for循环完成)。 我们将在量子位0重置后添加一行:

Q#
 
            Set(Zero, qubits[0]);
            Set(Zero, qubits[1]);

完整的例程现在看起来像这样:

Q#
 
    operation BellTest (count : Int, initial: Result) : (Int,Int) {
        body
        {
            mutable numOnes= 0;
            using (qubits = Qubit[2])
            {
                for (test in 1..count)
                {
                    Set (initial, qubits[0]);
                    Set (Zero, qubits[1]);

                    H(qubits[0]);
                    CNOT(qubits[0],qubits[1]);
                    let res = M (qubits[0]);

                    // Count the number of ones we saw:
                    if (res == One)
                    {
                        set numOnes= numOnes + 1;
                    }
                }

                Set(Zero, qubits[0]);
                Set(Zero, qubits[1]);
            }
            // Return number of times we saw a |0> and number of times we saw a |1>
            return (count-numOnes, numOnes);
        }
    }

如果我们运行这个,我们会得到与之前一样的50-50结果。 然而,我们真正感兴趣的是第二个量子位对第一个量子态的反应。 我们将在BellTest操作的新版本中添加此统计信息:

Q#
 
operation BellTest (count : Int, initial: Result) : (Int,Int,Int) {
        body
        {
            mutable numOnes= 0;
            mutable agree = 0;
            using (qubits = Qubit[2])
            {
                for (test in 1..count)
                {
                    Set (initial, qubits[0]);
                    Set (Zero, qubits[1]);

                    H(qubits[0]);
                    CNOT(qubits[0],qubits[1]);
                    let res = M (qubits[0]);

                    if (M (qubits[1]) == res) 
                    {
                        set agree = agree + 1;
                    }

                    // Count the number of ones we saw:
                    if (res == One)
                    {
                        set numOnes= numOnes + 1;
                    }
                }

                Set(Zero, qubits[0]);
                Set(Zero, qubits[1]);
            }
            // Return number of times we saw a |0> and number of times we saw a |1>
            return (count-numOnes, numOnes, agree);
        }
    }

现在有一个新的返回值( agree ),它将记录每次来自第一个量子位的测量与第二个量子位的测量相匹配。 当然,我们也必须相应地更新Driver.cs文件:

C#
 
            using (var sim = new QuantumSimulator())
            {
                // Try initial values
                Result[] initials = new Result[] { Result.Zero, Result.One };
                foreach (Result initial in initials)
                {
                    var res = BellTest.Run(sim, 1000, initial).Result;
                    var (numZeros, numOnes, agree) = res;
                    System.Console.WriteLine(
                        $"Init:{initial,-4} 0s={numZeros,-4} 1s={numOnes,-4} agree={agree,-4}");
                }
            }
            System.Console.WriteLine("Press any key to continue...");
            System.Console.ReadKey();

现在当我们跑步时,我们得到了一些非常惊人

Output
 Init:Zero 0s=499  1s=501  agree=1000
Init:One  0s=490  1s=510  agree=1000 

我们对第一个量子位的统计没有改变(一个0或1的概率为50-50),但现在当我们测量第二个量子位时,它总是和第一个量子位相同。 我们的CNOT使这两个量子纠缠在一起,所以无论发生在其中一个发生什么,发生在另一个发生。 如果你颠倒了测量(在第一个之前做了第二个量子比特),同样的事情会发生。 第一次测量是随机的,第二次测量将与第一次测量的结果保持一致。




推荐阅读
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 集合的遍历方式及其局限性
    本文介绍了Java中集合的遍历方式,重点介绍了for-each语句的用法和优势。同时指出了for-each语句无法引用数组或集合的索引的局限性。通过示例代码展示了for-each语句的使用方法,并提供了改写为for语句版本的方法。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 本文由编程笔记#小编整理,主要介绍了关于数论相关的知识,包括数论的算法和百度百科的链接。文章还介绍了欧几里得算法、辗转相除法、gcd、lcm和扩展欧几里得算法的使用方法。此外,文章还提到了数论在求解不定方程、模线性方程和乘法逆元方面的应用。摘要长度:184字。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 无损压缩算法专题——LZSS算法实现
    本文介绍了基于无损压缩算法专题的LZSS算法实现。通过Python和C两种语言的代码实现了对任意文件的压缩和解压功能。详细介绍了LZSS算法的原理和实现过程,以及代码中的注释。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • 解决.net项目中未注册“microsoft.ACE.oledb.12.0”提供程序的方法
    在开发.net项目中,通过microsoft.ACE.oledb读取excel文件信息时,报错“未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序”。本文提供了解决这个问题的方法,包括错误描述和代码示例。通过注册提供程序和修改连接字符串,可以成功读取excel文件信息。 ... [详细]
author-avatar
眼睛Blank
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有