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

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

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

先决条件

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

  • 安装了 Python 的 Python 环境。

  • 具有可选扩展 azureqdkPython 库。

    python -m pip install --upgrade qdk[azure]
    

项目工作原理Q#

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

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

设置project_root一个或多个Jupyter NotebookPython文件时,编译器将在project_root文件夹中查找清单文件。

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

定义Q#项目

Q#项目由存在清单文件(命名qsharp.json)和src文件夹定义,这两者都必须位于项目的根文件夹中。 该 src 文件夹包含 Q# 源文件。 对于 Q# 程序和外部项目, Q# 编译器会自动检测项目文件夹。 对于Python程序和Jupyter Notebook文件,您必须通过qsharp.init。 但是,项目的文件夹结构 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/TeleportOperationqsharp.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 CodeQ# 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# 下。 在 Q# 中创建 VS Code项目时, /src 将自动创建该文件夹。

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

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

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

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

    MyMathLib.Multiply(x,y); 
    

仅适用于 Q# 项目

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

如何创建 Q# 项目

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

  1. 在VS Code 文件资源管理器中,导航到您希望用作Q# 项目根文件夹的文件夹。

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

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

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

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

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

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

注意

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

示例项目

此量子传输程序是一个在本地模拟器Q#上运行的项目示例VS Code。 若要在硬件或第三方模拟器上运行程序,请参阅开始使用 程序和 以了解编译程序并连接到工作区的步骤。

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

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

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

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

Q# 源文件

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

    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 并从 PrepareBellPair 文件调用 PrepareStateLib.qs 操作。

    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.qsVS 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_AProject_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中的此操作。 因为 PrepareBellPairProject_B 的本地范围内,因此 Teleport 操作已可访问。

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

项目和隐式命名空间

在 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 

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