銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

Microsoft Q# Coding Contest – Summer 2020の復習(1) ユニタリーの識別

1か月以上前ですが、Microsoft Q# Coding Contest がSummer 2020として今年も行われました。復習をまとめておきたいと思います。まずは、warmupのA問題、ユニタリーの識別です。公式の解説記事はこちらです。

codeforces.com

A1からA5の問題は与えられたユニタリーゲートが与えられたあるゲートと同じであるかどうかを判定する問題です。これらの問題はユニタリーの識別としてはQuantum Katasにも追加されたので、今から解く場合はこちらを使うのが手っ取り早いでしょう。

QuantumKatas/DistinguishUnitaries at master · microsoft/QuantumKatas · GitHub

A1

最初の問題A1はIゲートもしくはXゲートのどちらかが unitary として与えられるので、次の操作を実装してIゲートなら0をXゲートなら1を返すようにするものです。

operation DistinguishIfromX (unitary : (Qubit => Unit is Adj+Ctl)) : Int {
    return -1;
}

今回の問題の共通の考え方として解説に説明されていますが、量子プログラムでは「測定」が情報を得る唯一の方法です。その測定とは量子ビット(Qubit)の状態を計測した結果です。ゲートは量子ビットの状態を変える操作ですので、既知の状態の量子ビットを与えられたゲートで変化させてその測定結果で判定する必要があります。すなわち、どのような状態の量子ビットを与えて、ゲートで操作した後、測定した結果でどちらか判定する必要があります。 一般に量子ビットの測定は確率的であるため、任意の2つの状態を測定で区別することはできません。しかし、直交する2つの状態であれば区別することができます。したがって、IゲートとXゲートを適用すると直交する状態になるような量子ビットの状態を見つける必要があります。

Iゲートは|0>と|1>の状態をそのままにし、Xゲートはそれぞれ反転させることを考えれば、|0>の量子ビットを用意しゲートを操作した後測定して、|0>ならIゲート、|1>ならXゲートと考えることができます。したがって答えは次のようになります。

operation DistinguishIfromX (unitary : (Qubit => Unit is Adj+Ctl)) : Int {
    using (q = Qubit()) {
        unitary(q);
        let res = M(q);
        Reset(q);
        return res == Zero ? 0 | 1;
    }       
}

usingを抜けるときにQubitは初期化しておかないといけないので、Reset操作をしてから測定結果を比較して値を返しています。この計測してResetする操作をまとめておこなうMResetZという操作がMicrosoft.Quantum.Measurement名前空間にあるので次のように書くことができます。

open Microsoft.Quantum.Measurement;

operation DistinguishIfromX (unitary : (Qubit => Unit is Adj+Ctl)) : Int {
    using (q = Qubit()) {
        unitary(q);
        return MResetZ(q) == Zero ? 0 | 1;
    }       
}

A2

今度は与えられたunitaryゲートがIゲートなら0をPauliZゲートなら1を返す問題です。PauliZゲートは|0>はそのままに、|1>は位相をひっくり返して-|1>にするためこの2つの状態では区別できません。しかし、|+>と|->の2つの直交する状態を考えると、PauliZは|+>を|->に変化させるので区別することができます。H|0>=|+>、H|+>=|0> とH|->=|1> を利用すると次のように書けます。

operation DistinguishIfromZ (unitary : (Qubit => Unit is Adj+Ctl)) : Int {
    using(q = Qubit()) {
        H(q);
        unitary(q);
        H(q);
        let res = M(q);
        Reset(q);
        return res == Zero ? 0 | 1;
    }
}

また、|+>と|->はPauliXに沿った測定で計測できることと、MResetX という操作があるので次のように短く書けます。

operation DistinguishIfromZ (unitary : (Qubit => Unit is Adj+Ctl)) : Int {
    using(q = Qubit()) {
        H(q);
        unitary(q);
        return MResetX(q) == Zero ? 0 | 1;
    }
}

A3

今度はZゲートなら0をSゲートなら1を返す関数です。ヒントは問題文の条件がexactly onceからat most twiceに変わっていることです。いままでは1回しかunitary操作ができなかったところを、今回は2回できます。Zゲートを2回適用するとIゲートになり、Sゲートを2回適用するとZゲートになります。つまりA2と同じ問題になります。

ration DistinguishZfromS (unitary : (Qubit => Unit is Adj+Ctl)) : Int {
    using (q = Qubit()) {
        H(q);
        unitary(q);
        unitary(q);
        return MResetX(q) == Zero ? 0 | 1;
    }
}

A4

なぜか本来の問題の順番と、解説およびQuantumKatasではA4とA5の順番が逆になっています。おそらく単一Qubutに対するunitary操作の識別を最初に並べて、最後に2つのQubitの操作にしたのでしょう。本来の問題通り、I⊗XとCNOTの識別をA4として解きます。

I⊗Xは2番目のQubitのみにXゲートを適用させる操作で、CNOTは1番目のゲートを制御Qubitとして2番目のQubitにNOT操作(つまりXゲート)を適用する操作です。|0>と|1>の直交状態のQubit2つからなる4つの状態において、それぞれの操作でどのように変化するか考えてみましょう。

State I⊗X CNOT
|00> |01> |00>
|01> |00> |01>
|10> |11> |11>
|11> |10> |10>

2つのQubitはentangle状態にはなっていないので、|00>という状態のQubitに対してunitary操作をしたあとの2番目のQubitの状態が|0>か|1>か計測すれば識別できることになります。したがって答えは次のようになります。

operation DistinguishIXfromCNOT (unitary : (Qubit[] => Unit is Adj+Ctl)) : Int {
    using (qs = Qubit[2]) {
        unitary(qs);
        return MResetZ(qs[1]) == One ? 0 | 1;
    }
}

A5

今度はZゲートなら0を-Zゲートなら1を返す関数です。Zゲートと-Zゲートはグローバル位相しか違わないので直交する2つの状態で識別できなさそうな気がしてきます。そこでここまで問題文に与えられているのに使っていない条件に着目します。unitary操作がControlledという条件です。ひとつ前の問題でCNOTという操作を出しているのもこの条件に気付いてほしかったのではと、解説を読んで気付きました。制御Qubitが必要なので、2つQubitを用意し、1番目のQubitを制御Qubitとし、2番目のQubitにControlled ZもしくはControlled -Z を適用した場合の状態変化を考えます。

State Controlled Z Controlled -Z
|00> |00> |00>
|01> |01> |01>
|10> |10> -|10>
|11> -|11> |11>

1番目のQubitが|0>であればどちらの操作でも状態は変化しません。1番目のQubitが|1>のときだけ-1の位相のずれが発生します。そこで|00>と|10>が均等に混じった状態、つまり (|0> + |1>) ⊗ |0>に着目します。この状態にControlled Zを適用しても状態は変化しません。Controlled -Zを適用すると、|1> ⊗ |0> が位相-1だけずれ、 (|0> - |1>) ⊗ |0>の状態になります。1番目のQubitが (|0> + |1>) と (|0> - |1>) の状態を識別すればよいので、A2と同じ問題であることがわかります。というわけで答えは次のようになります。Q#のControlled functorはQubitの配列に対して適用できるので、単一Qubitを要素数1の配列として渡します。

peration DistinguishZfromMinusZ (unitary : (Qubit => Unit is Adj+Ctl)) : Int {
    using (qs = Qubit[2]) {
        H(qs[0]);
        Controlled unitary(qs[0..0], qs[1]);
        return MResetX(qs[0]) == Zero ? 0 | 1;
    }
}

以上でwarmupラウンドのA問題のまとめになります。

Microsoft MVPを再受賞しました

Microsoft MVPを再受賞しました。最近は新規受賞者は毎月審査して発表、継続受賞者は1年に一度7月1日に発表となっています。

たしかここ2年ほどAzureカテゴリでの受賞だったのですが、今年はAzureとDeveloper Technologiesの2つのカテゴリで受賞となりました。

f:id:tanaka733:20200702165848p:plain

今回で8度目の受賞らしいのですが、最初の受賞からしばらくはC#(とそこから名前が変わった.NETなど)カテゴリでの受賞でした。2年ほど前からAzureカテゴリでの活動も増えたのでAzureカテゴリのみの受賞になったのですが、今年は両方のカテゴリで受賞することができました。正直、片方ずつだと活動量たりないかも?と思っていたので非常に驚いています。

今年も昨年に引き続き、Azure、C#、Q#あたりの分野で活動していく予定です。特にQ#はオンライン化が進む中であまり活動できていないので、さっそくなにかやろうと考えています。

活動もますますオンライン化が進む1年になりそうですが、引き続きよろしくお願いします。

Q#プロジェクトでC#のクラスが不要になりました

しばらくQ#の記事を書いていなかったのですが、最近のQ#の更新でC#のクラスが不要になっていたので久しぶりに記事を書きたいと思います。

少し古い記事ですが、Q#プロジェクトというのは、プロジェクトとしては.NET Coreのプロジェクトで、エントリポイントとなるMainメソッドを含むC#クラスが存在し、そこから呼び出されるQ#のソースが存在する構造でした。

tech.tanaka733.net

ですが、今年4月にリリースされたVersion 0.11.2004.2825からこのC#クラスが不要になりました。

例えば最新のQ# SDKでプロジェクトを作るとこのような構造のProgram.qsファイルとcsprojファイルのみのプロジェクトが作られます。

namespace Quantum.QSharpCompilerExtensionLab {

    open Microsoft.Quantum.Canon;
    open Microsoft.Quantum.Intrinsic;

    
    @EntryPoint()
    operation HelloQ () : Unit {
        Message("Hello quantum world!");
    }
}

この@EntryPoint()というのがC#のMainメソッドに相当するものになります。これがどのように動くのかが気になるので、コンパイル(トランスパイル)結果を見てみましょう。最新のバージョンでもQ#コードはいったんC#にトランスパイルされてからコンパイルされます。C#としての出力は、/obj/qsharp/src以下にあります。

f:id:tanaka733:20200607212741p:plain

このProgram.g.csのほうが以前と同じQ#コードをC#コードにトランスパイルしたもののようです。もう一つのProgram.EntryPoint.g.csのほうがMainメソッドを含むエントリーポイントのためのコンパイル結果です。実際にはこのようになっています。

//------------------------------------------------------------------------------
// <auto-generated>                                                             
//     This code was generated by a tool.                                       
//     Changes to this file may cause incorrect behavior and will be lost if    
//     the code is regenerated.                                                 
// </auto-generated>                                                            
//------------------------------------------------------------------------------
namespace Quantum.QSharpCompilerExtensionLab
{
    using System;
    using Microsoft.Quantum.Core;
    using Microsoft.Quantum.Intrinsic;
    using Microsoft.Quantum.Simulation.Core;

    internal class __QsEntryPoint__ : Microsoft.Quantum.CsharpGeneration.EntryPointDriver.IEntryPoint<QVoid, QVoid>
    {
        public string Summary => "";
        public System.Collections.Generic.IEnumerable<System.CommandLine.Option> Options => new System.CommandLine.Option[] { };
        public string DefaultSimulator => "QuantumSimulator";
        public EntryPointInfo<QVoid, QVoid> Info => Quantum.QSharpCompilerExtensionLab.HelloQ.Info;
        public IOperationFactory CreateDefaultCustomSimulator() => throw new InvalidOperationException();
        public QVoid CreateArgument(System.CommandLine.Parsing.ParseResult parseResult) => QVoid.Instance;
        private static async System.Threading.Tasks.Task<int> Main(string[] args) => await new Microsoft.Quantum.CsharpGeneration.EntryPointDriver.Driver<Quantum.QSharpCompilerExtensionLab.HelloQ, QVoid, QVoid>(new __QsEntryPoint__()).Run(args);
    }
}

MainメソッドはDriver.Runというメソッドを呼び出しており、このDriverクラスが以前のQ#プロジェクトで書いていたC#クラス内の処理を行っているように見えます。また、SummaryやDefaultSimulatorみたいな項目もありますが、現状@EntryPointにパラメーターを渡せないで、この辺はまだ変更できなさそうです。もしかすると今後のバージョンアップで機能追加されるかもしれません。