你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

如何创建和管理 Q# 项目和自定义库

本文介绍如何创建、管理和共享 Q# 项目。 Q#项目是一个文件夹结构,其中包含多个Q#文件,可以访问彼此的作和函数。 项目有助于在逻辑上组织源代码。 还可以将项目用作可从外部源访问的自定义库。

先决条件

若要运行 Python 程序,还需要:

  • 安装了 Python 和 Pip 的 Python 环境。
  • Azure Quantum qsharpazure-quantum 包。

项目工作原理Q#

Q#项目包含一个名为qsharp.json的清单文件和一个或多个.qs文件,这些文件位于指定的Q#文件夹结构中。 可以在 VS Code 中手动或直接创建 Q# 项目。

在 VS Code 中打开 .qs 文件时,编译器会在周围的文件夹层级中搜索清单文件,并确定项目的范围。 如果未找到清单文件,编译器将在单个文件模式下运行。

在 Jupyter Notebook 或 Python 文件中设置 project_root 时,编译器将在文件夹中查找清单文件 project_root

外部 Q# 项目是驻留在另一个目录或公共 GitHub 存储库中的标准 Q# 项目,充当自定义库。 外部项目使用 export 语句定义外部程序可访问的函数和作。 程序将外部项目定义为其清单文件中的依赖项,并使用 import 语句访问外部项目中的项,例如作、函数、结构和命名空间。 有关详细信息,请参阅 将项目用作外部依赖项

定义Q#项目

Q#项目由存在清单文件(命名qsharp.json)和src文件夹定义,这两者都必须位于项目的根文件夹中。 该 src 文件夹包含 Q# 源文件。 对于 Q# 程序和外部项目, Q# 编译器会自动检测项目文件夹。 对于 Python 程序和 Jupyter Notebook 文件,必须使用一个调用语句来指定项目文件夹。 但是,项目的文件夹结构 Q# 对于所有类型的程序都是相同的。

项目的文件夹结构和层次结构 Q# 。

定义项目文件夹(Q# 程序)

在 VS Code 中打开 .qs 文件时, Q# 编译器会在文件夹结构中向上搜索清单文件。 如果编译器找到清单文件,编译器将包括目录中的所有 Q# 文件 /src 及其所有子目录。 每个文件中定义的项可供项目中的所有其他文件使用。

例如,请考虑以下文件夹结构:

  • Teleportation_project
    • qsharp.json
    • src
      • Main.qs
      • TeleportOperations
        • TeleportLib.qs
        • PrepareState
          • PrepareStateLib.qs

打开文件 /src/TeleportOperation/PrepareState/PrepareStateLib.qs 时,Q# 编译器执行以下操作:

  1. 检查 /src/TeleportOperation/PrepareState/qsharp.json
  2. 检查 /src/TeleportOperation 以检查 qsharp.json
  3. 检查 /srcqsharp.json
  4. 检查 /* 是否有 qsharp.json
  5. /根据清单文件的设置,建立为项目的根目录,并在项目根目录下包括所有.qs文件。

创建清单文件

清单文件是一个 JSON qsharp.json 文件,可选择性地包括 作者许可证lints 字段。 最小可行清单文件是字符串 {}。 在 VS Code 中创建 Q# 项目时,会为你创建一个最小的清单文件。

{}

清单文件示例

以下示例显示如何通过清单文件来定义你的项目 Q# 的范围。

  • 在此示例中, 作者 是唯一指定的字段,因此此目录中的所有 .qs 文件及其子目录都包含在 Q# 项目中。

    {
        "author":"Microsoft",
        "license": "MIT"
    }
    
  • 在一个Q#项目中,还可以使用清单文件来微调 VS Code Q# Linter 设置。 默认情况下,三个 Linter 规则为:

    • needlessParens:默认 = allow

    • divisionByZero:默认 = warn

    • redundantSemicolons:默认 = warn

      可以将清单文件中的每个规则设置为 allowwarnerror。 例如:

      {
          "author":"Microsoft",
          "lints": [
              {
                "lint": "needlessParens",
                "level": "allow"
              },
              {
                "lint": "redundantSemicolons",
                "level": "warn"
              },
              {
                "lint": "divisionByZero",
                "level": "error"
              }
            ]
      }
      
  • 还可以使用清单文件将外部 Q# 项目定义为依赖项,并远程访问该外部项目中的操作和函数。 有关详细信息,请参阅 将项目用作外部依赖项

Q# 项目要求和属性

以下要求和配置适用于所有 Q# 项目。

  • 要包含在项目中的所有 .qs 文件都必须位于名为 src的文件夹下,该文件夹必须位于项目的根文件夹 Q# 下。 在 VS Code 中创建 Q# 项目时, /src 将自动创建该文件夹。

  • 清单文件应与 src 文件夹位于同一级别。 在 VS Code 中创建 Q# 项目时,会自动创建最小文件。

  • 使用 import 语句引用项目中其他文件中的操作和函数。

    import MyMathLib.*;  //imports all the callables in the MyMathLib namespace
    ...
        Multiply(x,y);
    

    或者,使用命名空间单独引用它们。

    MyMathLib.Multiply(x,y); 
    

仅适用于 Q# 项目

  • 您只能在项目中的一个.qs文件中定义入口点操作,该操作是默认的Main()操作。
  • 必须将具有入口点定义的文件放在 .qs 清单文件下面的项目目录级别。
  • 项目中所有的操作和函数,Q# 显示中的内容会被缓存到 .qs,并且在 VS Code 的预测文本中展示。
  • 如果尚未导入所选作或函数的命名空间,则 VS Code 会自动添加必要的 import 语句。

如何创建 Q# 项目

若要创建 Q# 项目,请执行以下步骤:

  1. 在 VS Code 文件资源管理器中,转到要用作项目的根文件夹的文件夹 Q# 。

  2. 打开 “视图 ”菜单,然后选择 “命令面板”。

  3. 输入 QDK:创建 Q# 项目 ,然后按 Enter。 VS Code 在文件夹中创建最小清单文件,并添加包含/srcMain.qs模板文件的文件夹。

  4. 编辑项目的Manifest文件。 请参阅清单文件示例

  5. 将您的Q#源文件添加并组织在/src文件夹下。

  6. 如果要从 Python 程序或 Jupyter Notebook 访问Q#项目,请使用 设置qsharp.init。 假设此示例程序位于 /src 项目的 Q# 文件夹中:

    qsharp.init(project_root = '../Teleportation_project')
    
  7. 如果仅在 Q# VS Code 中使用文件,则编译器在打开 Q# 文件时搜索清单文件,确定项目的根文件夹,然后扫描子文件夹查找 .qs 文件。

注意

还可以手动创建清单文件和 /src 文件夹。

示例项目

此量子传送程序是在 VS Code 中的本地模拟器上运行的项目示例 Q# 。 若要在 Azure Quantum 硬件或第三方模拟器上运行程序,请参阅程序和 VS Code 入门Q#,了解编译程序并连接到 Azure Quantum 工作区的步骤。

此示例具有以下目录结构:

  • Teleportation_project
    • qsharp.json
    • src
      • Main.qs
      • TeleportOperations
        • TeleportLib.qs
        • PrepareState
          • PrepareStateLib.qs

清单文件包含 作者许可证 字段:

{
    "author":"Microsoft",
    "license":"MIT"
}

Q# 源文件

名为Main.qs的主文件包含入口点,并引用TeleportLib.qs中的TeleportOperations.TeleportLib命名空间。


    import TeleportOperations.TeleportLib.Teleport; // references the Teleport operation from TeleportLib.qs

    operation Main() : Unit {
        use msg = Qubit();
        use target = Qubit();

        H(msg);
        Teleport(msg, target); // calls the Teleport() operation from TeleportLib.qs
        H(target);

        if M(target) == Zero {
            Message("Teleported successfully!");
        
        Reset(msg);
        Reset(target);
        }
    }

这个文件 TeleportLib.qs 定义操作 Teleport 并从 PrepareStateLib.qs 文件调用 PrepareBellPair 操作。


    import TeleportOperations.PrepareState.PrepareStateLib.*; // references the namespace in PrepareStateLib.qs
 
    operation Teleport(msg : Qubit, target : Qubit) : Unit {
        use here = Qubit();

        PrepareBellPair(here, target); // calls the PrepareBellPair() operation from PrepareStateLib.qs
        Adjoint PrepareBellPair(msg, here);

        if M(msg) == One { Z(target); }
        if M(here) == One { X(target); }

        Reset(here);
    }

PrepareStateLib.qs 文件包含用于创建贝尔对的标准可重用操作。

    
    operation PrepareBellPair(left : Qubit, right : Qubit) : Unit is Adj + Ctl {
        H(left);
        CNOT(left, right);
    }

运行程序

选择用于运行程序的环境的选项卡。

若要运行此程序,请在 Main.qs VS Code 中打开该文件,然后选择“ 运行”。

将 Q# 项目配置为外部依赖项

可以将 Q# 配置为其他项目的外部依赖项,类似于一个库。 外部 Q# 项目中的函数和作可用于多个 Q# 项目。 外部依赖项可以驻留在驱动器共享上,也可以发布到公共 GitHub 存储库。

若要将 Q# 项目用作外部依赖项,必须:

  • 将外部项目添加为调用项目的清单文件中的依赖项。
  • 如果外部项目发布到 GitHub,则将 文件 属性添加到外部项目的清单文件中。
  • 向外部项目添加 export 语句。
  • 向调用项目添加 import 语句。

配置Manifest文件

外部 Q# 项目可以驻留在本地或网络驱动器共享上,也可以发布到公共 GitHub 存储库。

调用项目清单文件

若要将依赖项添加到驱动器共享上的外部项目,请在调用项目的清单文件中定义依赖项。

{
    "author": "Microsoft",
    "license": "MIT",
    "dependencies": {
        "MyDependency": {
            "path": "/path/to/project/folder/on/disk"
        }
    }
}

在前面的清单文件中, MyDependency 是一个用户定义的字符串,用于在调用作时标识命名空间。 例如,如果创建名为 MyMathFunctions 的依赖项,则可以使用该依赖项 MyMathFunctions.MyFunction()调用函数。

若要将依赖项添加到发布到公共 GitHub 存储库的项目,请使用以下示例清单文件:

{
    "author": "Microsoft",
    "dependencies": {
        "MyDependency": {
            "github": {
                "owner": "GitHubUser",
                "repo": "GitHubRepoName",
                "ref": "CommitHash",
                "path": "/path/to/dependency"
            }
        }
    }
}

注意

对于 GitHub 依赖项, ref 引用 GitHub refspec。 Microsoft建议您始终使用提交哈希,以确保您可以依赖依赖项的特定版本。

外部项目清单文件

如果外部 Q# 项目发布到公共 GitHub 存储库,则必须将 文件 属性添加到外部项目的清单文件中,包括项目中使用的所有文件。

{
    "author": "Microsoft",
    "license": "MIT",
    "files": [ "src/MyMathFunctions.qs", "src/Strings/MyStringFunctions.qs" ]
}

文件属性对于通过"path"(即基于本地 filepath 的导入)导入的外部项目是可选的。 文件属性仅适用于发布到 GitHub 的项目。

使用 export 语句

若要使外部项目中的函数和操作可供调用的项目访问,请使用export语句。 可以导出文件中的任何或所有可调用对象。 不支持通配符语法,因此必须指定要导出的每个可调用项。

operation Operation_A() : Unit {
...
}
operation Operation_B() : Unit  {
...
}

// makes just Operation_A available to calling programs
export Operation_A;

// makes Operation_A and Operation_B available to calling programs 
export Operation_A, Operation_B, etc.; 

// makes Operation_A available as 'OpA'
export Operation_A as OpA;

使用 import 语句

若要使外部依赖项中的项可用,请使用 import 调用程序中的语句。 该 import 语句使用为清单文件中的依赖项定义的命名空间。

例如,请考虑以下清单文件中的依赖项:

{
    "author": "Microsoft",
    "license": "MIT",
    "dependencies": {
        "MyMathFunctions": {
            "path": "/path/to/project/folder/on/disk"
        }
    }
}

使用以下代码导入可调用项:

import MyMathFunctions.MyFunction;  // imports "MyFunction()" from the namespace
...

import 语句还支持通配符语法和别名。

// imports all items from the "MyMathFunctions" namespace
import MyMathFunctions.*; 

// imports the namespace as "Math", all items are accessible via "Math.<callable>"
import MyMathFunctions as Math;

// imports a single item, available in the local scope as "Add"
import MyMathFunctions.MyFunction as Add;

// imports can be combined on one line
import MyMathFunctions.MyFunction, MyMathFunctions.AnotherFunction as Multiply; 

注意

当前使用的 open 语句 Q#(用于引用库和命名空间)仍受支持,但最终将弃用。 同时,可以选择更新当前文件以使用 import 语句。 例如,可以使用 open Std.Diagnostics; 替换 import Std.Diagnostics.*;

示例外部项目

对于此示例,你将使用与前面的示例相同的转接程序,但将呼叫程序和可调用者分开到不同的项目中。

  1. 在本地驱动器上创建两个文件夹,例如“Project_A”和“Project_B”。

  2. Q#按照“如何创建项目”中的步骤在每个文件夹中创建项目Q#

  3. Project_A 中,调用程序将以下代码复制到清单文件中,根据需要编辑Project_B的路径

    {
      "author": "Microsoft",
      "license": "MIT",
      "dependencies": {
        "MyTeleportLib": {
          "path": "/Project_B" 
          }
        }
      }    
    
  4. 在Project_A中,将以下代码复制到 Main.qs 中

    import MyTeleportLib.Teleport; // imports the Teleport operation from the MyTeleportLib namespace defined in the manifest file
    
    operation Main() : Unit {
        use msg = Qubit();
        use target = Qubit();
    
        H(msg);
        Teleport(msg, target); // calls the Teleport() operation from the MyTeleportLib namespace
        H(target);
    
        if M(target) == Zero {
            Message("Teleported successfully!");
    
        Reset(msg);
        Reset(target);
        }
    }   
    
  5. Project_B中,将以下代码复制到 Main.qs

    
        operation Teleport(msg : Qubit, target : Qubit) : Unit {
            use here = Qubit();
    
            PrepareBellPair(here, target); 
            Adjoint PrepareBellPair(msg, here);
    
            if M(msg) == One { Z(target); }
            if M(here) == One { X(target); }
    
            Reset(here);
        }
    
        operation PrepareBellPair(left : Qubit, right : Qubit) : Unit is Adj + Ctl {
            H(left);
            CNOT(left, right);
        }
    
        export Teleport;       //  makes the Teleport operation available to external programs
    

    注意

    请注意,PrepareBellPair 操作不需要导出,因为它不会直接在您的Project_A程序中调用。 由于它位于Project_B的本地范围内,Teleport操作已可访问该元素。

  6. 若要运行程序,请在 VS Code 中打开 /Project_A/Main.qs 并选择 “运行”。

项目和隐式命名空间

在 Q# 项目中,如果未在 .qs 程序中指定命名空间,编译器将使用文件名作为命名空间。 引用一个来自外部依赖项的调用项,然后使用语法 <dependencyName>.<namespace>.<callable>。 但是,如果文件已命名 Main.qs,编译器将假定命名空间和调用语法, <dependencyName>.<callable>如前面的示例 import MyTeleportLib.Teleport所示。

由于可能有多个项目文件,因此在引用可调用项时需要考虑正确的语法。 例如,请考虑具有以下文件结构的项目:

  • /src
    • Main.qs
    • MathFunctions.qs

以下代码调用外部依赖项:

import MyTeleportLib.MyFunction;        // "Main" namespace is implied

import MyTeleportLib.MathFunctions.MyFunction;   // "Math" namespace must be explicit 

有关命名空间行为的详细信息,请参阅 用户命名空间