ひとりAdvent Calendar 10日目です。
この資料などにも書いている内容なのですが、Quantum Development Kitで作成するQ#のプロジェクト構造についてまとめてみたいと思います。
www.slideshare.net
対象バージョンはVersion 0.3.1811.2802
ですが、内容としては0.3.n全体で変更はないと思います。直近ではSDK本体の変更ではありませんが、VS Code拡張でevent streamパッケージの脆弱性に基づく対応でSDK全体が更新されています。
Quantum Development Kit preview release notes | Microsoft Docs
Q# のコンパイラはどのパスにインストールされているか
Q#で書かれたコードが実行されるということは、なにかしらのコンパイラーが動いていることが想像されます。.NET Coreのdotnet
コマンドや、MSBuild
コマンドを実行するバイナリのように、Q#をコンパイルするバイナリがインストールフォルダにインストールされている気もするかもしれませんが、インストール手順を見ると、.NET Coreのテンプレートを追加していることがわかります。
$ dotnet new -i "Microsoft.Quantum.ProjectTemplates::0.3.1811.2802-preview"
Installing the Q# development environment for VS Code | Microsoft Docs
つまりこの手順ではコンパイラーに相当するものはまだマシンに追加されていません。Q#の開発は、次にQ#のプロジェクトを作るとすぐに始めることができます。というわけでプロジェクトを作成してみます。
$ dotnet new Console -lang Q# --output QSharp
このコマンドも.NET Coreでプロジェクトを作成する標準のコマンドです。作成されたプロジェクトファイル({プロジェクト名}.csproj}を見てみましょう。
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp2.0</TargetFramework> <PlatformTarget>x64</PlatformTarget> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.Quantum.Canon" Version="0.3.1811.2802-preview" /> <PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.3.1811.2802-preview" /> </ItemGroup> </Project>
<ItemGroup>
を除けば、netcoreapp2.0 をターゲットフレームワークとする、プロジェクト単体で実行可能な.NET Core Consoleプロジェクトです。<ItemGroup>
についても、NuGetからライブラリを追加する場合の標準的な書き方です。つまり、Q#のプロジェクトというのは、プロジェクトとしては.NET Coreのプロジェクトそのものであり、Q#のコンパイラーやそのほかのライブラリも含めNuGet経由でプロジェクトに追加しているという形態になっています。
そのため、Quantum Development Kitのアップグレードの際には、.NET Coreのプロジェクトテンプレートを更新することでこれから新規に作成するプロジェクトに追加するライブラリのバージョンを上げる作業のほかに、既存のプロジェクトで参照しているNuGetライブラリのバージョンを上げる作業が必要になります。
QDK 0.3 Language Review and Migration Guide | Microsoft Docs
Q#コードのコンパイルの仕組み
.qs
ファイルに書くQ#コードは、dotnet build
でコンパイルされ、dotnet run
で実行することができます。プロジェクトが.NET Coreプロジェクトですし、C#コードからQ#のoperation
を参照できるので、ILなどにコンパイルされているような気がしますが、実はC#コードにコンパイル*1されています。
dotnet build
したあとに、プロジェクトフォルダの下にobj/qsharp/Operations.g.cs
というファイルが生成されているのがわかります。
空のOperation一つのQ#コードだと次のようなC#コードが生成されています。このコードを見ると、C#から実行するときにoperationの名前(この場合はHelloQ)がついたクラスのRun
メソッドを実行することでQ#コードが実行されることや、Q#コードを実行する場所に渡すためのDriverはIOperationFactory
を実装していることがわかります。
#pragma warning disable 1591 using System; using Microsoft.Quantum.Core; using Microsoft.Quantum.Primitive; using Microsoft.Quantum.Simulation.Core; [assembly: Microsoft.Quantum.QsCompiler.Attributes.CallableDeclaration("{\"Kind\":{\"Case\":\"Operation\"},\"QualifiedName\":{\"Namespace\":\"QSharp\",\"Name\":\"HelloQ\"},\"SourceFile\":\"C:/Users/TanakaTakayoshi/source/BellTest/QSharp/Operations.qs\",\"Position\":{\"Item1\":5,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":11},\"Item2\":{\"Line\":1,\"Column\":17}},\"ArgumentTuple\":{\"Case\":\"QsTuple\",\"Fields\":[[]]},\"Signature\":{\"TypeParameters\":[],\"ArgumentType\":{\"Case\":\"UnitType\"},\"ReturnType\":{\"Case\":\"UnitType\"},\"SupportedFunctors\":[]},\"Documentation\":[]}")] [assembly: Microsoft.Quantum.QsCompiler.Attributes.SpecializationDeclaration("{\"Kind\":{\"Case\":\"QsBody\"},\"Parent\":{\"Namespace\":\"QSharp\",\"Name\":\"HelloQ\"},\"SourceFile\":\"C:/Users/TanakaTakayoshi/source/BellTest/QSharp/Operations.qs\",\"Position\":{\"Item1\":5,\"Item2\":4},\"HeaderRange\":{\"Item1\":{\"Line\":1,\"Column\":11},\"Item2\":{\"Line\":1,\"Column\":17}},\"Documentation\":[]}")] #line hidden namespace QSharp { public class HelloQ : Operation<QVoid, QVoid>, ICallable { public HelloQ(IOperationFactory m) : base(m) { } String ICallable.Name => "HelloQ"; String ICallable.FullName => "QSharp.HelloQ"; protected ICallable<String, QVoid> Message { get; set; } public override Func<QVoid, QVoid> Body => (__in) => { #line 7 "C:\\Users\\TanakaTakayoshi\\source\\BellTest\\QSharp\\Operations.qs" Message.Apply("Hello quantum world!"); #line hidden return QVoid.Instance; } ; public override void Init() { this.Message = this.Factory.Get<ICallable<String, QVoid>>(typeof(Microsoft.Quantum.Primitive.Message)); } public override IApplyData __dataIn(QVoid data) => data; public override IApplyData __dataOut(QVoid data) => data; public static System.Threading.Tasks.Task<QVoid> Run(IOperationFactory __m__) { return __m__.Run<HelloQ, QVoid, QVoid>(QVoid.Instance); } } }
また、qsc-command.txt
とqsc-ok.txt
といういかにもそれっぽい名前のファイルが生成されており、前者がコンパイルした際の入力ファイル、参照ライブラリ、実行コマンドを記録しており、後者が結果を記録しているように見えます。
:: files :: Operations.qs :: qsim :: :: references :: C:\Users\User\.nuget\packages\microsoft.quantum.canon\0.3.1811.2802-preview\lib\netstandard2.0\Microsoft.Quantum.Canon.dll C:\Users\User\.nuget\packages\microsoft.quantum.development.kit\0.3.1811.2802-preview\lib\netstandard2.0\Microsoft.Quantum.Primitives.dll C:\Users\User\.nuget\packages\microsoft.quantum.development.kit\0.3.1811.2802-preview\lib\netstandard2.0\Microsoft.Quantum.QsCompilerCommon.dll C:\Users\User\.nuget\packages\microsoft.quantum.development.kit\0.3.1811.2802-preview\lib\netstandard2.0\Microsoft.Quantum.Simulation.Common.dll C:\Users\User\.nuget\packages\microsoft.quantum.development.kit\0.3.1811.2802-preview\lib\netstandard2.0\Microsoft.Quantum.Simulation.Core.dll C:\Users\User\.nuget\packages\microsoft.quantum.development.kit\0.3.1811.2802-preview\lib\netstandard2.0\Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime.dll C:\Users\User\.nuget\packages\microsoft.quantum.development.kit\0.3.1811.2802-preview\lib\netstandard2.0\Microsoft.Quantum.Simulation.Simulators.dll :: command :: dotnet "C:\Users\User\.nuget\packages\microsoft.quantum.development.kit\0.3.1811.2802-preview\build\../tools/qsc/qsc.dll" build --format MsBuild --input "Operations.qs" --references "C:\Users\User\.nuget\packages\microsoft.quantum.canon\0.3.1811.2802-preview\lib\netstandard2.0\Microsoft.Quantum.Canon.dll" "C:\Users\User\.nuget\packages\microsoft.quantum.development.kit\0.3.1811.2802-preview\lib\netstandard2.0\Microsoft.Quantum.Primitives.dll" "C:\Users\User\.nuget\packages\microsoft.quantum.development.kit\0.3.1811.2802-preview\lib\netstandard2.0\Microsoft.Quantum.QsCompilerCommon.dll" "C:\Users\User\.nuget\packages\microsoft.quantum.development.kit\0.3.1811.2802-preview\lib\netstandard2.0\Microsoft.Quantum.Simulation.Common.dll" "C:\Users\User\.nuget\packages\microsoft.quantum.development.kit\0.3.1811.2802-preview\lib\netstandard2.0\Microsoft.Quantum.Simulation.Core.dll" "C:\Users\User\.nuget\packages\microsoft.quantum.development.kit\0.3.1811.2802-preview\lib\netstandard2.0\Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime.dll" "C:\Users\User\.nuget\packages\microsoft.quantum.development.kit\0.3.1811.2802-preview\lib\netstandard2.0\Microsoft.Quantum.Simulation.Simulators.dll" --output obj\qsharp\src\
qsc completed succesfully!
dotnet コマンドでビルドしているので、おそらくqsc.dll
というバイナリの中に以下の記事で紹介されているようなMSBuild Taskが定義されていて、dotnet build
によりトリガーされて実行されているのではないかと想像されます。qsc.dllを含んだQuantum Development Kitのコードは今のところ公開されていないので、今調べることができるのはここまでということになります。
まとめ
この記事で書いた内容は特に仕様とかで決まっているものではないので、今後のリリースで変更される可能性は十分にあります。ただ今のところ、.NET Coreで用意されているテンプレートやMSBuild Taskの拡張など標準的な技術で提供されているのは興味深いです。Q#の言語紹介にも書いてある通り、ドメイン特化していて、古典的なコンピューターから実行されるサブルーチン的な処理を行うための言語とあるので、C#をはじめとした言語から実行される仕組みというのは、当面変わらないだろうと予想しています。
*1:トランスパイルのほうがより正確かもしれませんが、今回はコンパイルという表現に統一しておきます