次の方法で共有


特殊化宣言

callable 宣言に関するセクションで説明したように、現在、関数の特殊化を明示的に宣言する理由はありません。 このトピックは演算に適用され、特定のファンクタをサポートするために必要な特殊化を宣言する方法について詳しく説明します。

指定された変換の随伴行列が必要になることは、量子コンピューティングでは非常に一般的な問題です。 多くの量子アルゴリズムでは、計算を実行するために、演算とその随伴行列の両方が必要です。 Q# では、特定の本体実装に対応する随伴行列実装を自動的に生成できるシンボリック計算が採用されています。 この生成は、古典計算と量子計算が制限なく混在している実装でも可能です。 ただし、この場合はいくつかの制限が適用されます。 たとえば、実装で変更可能な変数が使用される場合、パフォーマンス上の理由から自動生成はサポートされません。 さらに、本体内で呼び出される各演算は、対応する随伴行列を生成するため、Adjoint ファンクタ自体をサポートする必要があります。

マルチ量子ビットの場合、測定を元に戻すことは簡単ではありませんが、適用された変換がユニタリになるように測定を組み合わせることは可能です。 この場合は、それ自体ではAdjoint ファンクタをサポートしない測定が本体実装に含まれていても、本体全体が随伴行列対応になります。 それにもかかわらず、この場合、随伴行列実装の自動生成は失敗します。 この理由から、実装を手動で指定することが可能です。 コンパイラは、共役などの一般的なパターンに対して最適化された実装を自動的に生成します。 それにもかかわらず、より最適化された実装を手動で定義するには、明示的な特殊化が望ましい場合があります。 任意の 1 つの実装と任意の数の実装を明示的に指定することが可能です。

Note

手動で指定されたこのような実装の正確性は、コンパイラによって検証されません。

次の例では、2 つの量子ビット q1q2 の状態を交換する演算 SWAP の宣言は、その adjoint バージョンとその controlled バージョンに対して明示的な特殊化を宣言します。 そのため、Adjoint SWAPControlled SWAP の実装はユーザー定義ですが、コンパイラはそれでも、両方のファンクタの組み合わせ (Controlled Adjoint SWAP、これは Adjoint Controlled SWAP と同じです) の実装を生成する必要があります。

    operation SWAP (q1 : Qubit, q2 : Qubit) : Unit
    is Adj + Ctl { 

        body ... {
            CNOT(q1, q2);
            CNOT(q2, q1);
            CNOT(q1, q2);
        }

        adjoint ... { 
            SWAP(q1, q2);
        }

        controlled (cs, ...) { 
            CNOT(q1, q2);
            Controlled CNOT(cs, (q2, q1));
            CNOT(q1, q2);            
        } 
    }

自動生成ディレクティブ

特定の特殊化を生成する方法を決定するときに、コンパイラは、ユーザー定義の実装に優先順位を付けます。 つまり、adjoint 特殊化がユーザー定義であり、controlled 特殊化が自動生成である場合、controlled adjoint 特殊化は、ユーザー定義の adjoint に基づいて生成されます。その逆も同様です。 この場合は、どちらの特殊化もユーザー定義です。 随伴行列実装の自動生成には、より多くの制限が適用されるため、controlled adjoint 特殊化では、既定で、adjoint 特殊化の明示的に定義された実装の controlled 特殊化が生成されます。

SWAP 実装の場合、よりよいオプションは、controlled 特殊化に adjoint を適用することです。これにより、制御量子ビットの状態に対する最初と最後の CNOT の実行に不必要な条件付けをしないようにします。 適切な生成ディレクティブを指定する controlled adjoint バージョンの明示的な宣言を追加すると、コンパイラは、代わりに controlled バージョンの手動で指定された実装に基づいて controlled adjoint 特殊化を生成するように強制されます。 コンパイラによって生成される特殊化のこのような明示的な宣言は、次の形式になります。

    controlled adjoint invert;

さらに、SWAP の宣言の内部に挿入されます。 一方で、次の行を挿入します。

    controlled adjoint distribute;

この場合、コンパイラは、定義された (または生成された) adjoint 特殊化に基づいて特殊化を生成するように強制されます。 詳細については、この部分的特殊化推論の提案を参照してください。

演算 SWAP の場合は、よりよいオプションがあります。 SWAP は "自己随伴的" です。つまり、それ自体の逆演算です。随伴行列のユーザー定義実装は、SWAP の本体を呼び出すだけです。 これを表現するには、次のディレクティブを使用します。

    adjoint self;

この方法で adjoint 特殊化を宣言すると、コンパイラによって自動的に挿入される controlled adjoint 特殊化は、controlled 特殊化を呼び出すだけになります。

次の生成ディレクティブが存在し、有効です。

特殊化 ディレクティブ
body 特殊化: -
adjoint 特殊化: self, invert
controlled 特殊化: distribute
controlled adjoint 特殊化: self, invert, distribute

controlled adjoint 特殊化に対してすべての生成ディレクティブが有効であるのは、偶然ではありません。ファンクタが可換である限り、ファンクタの 1 つの組み合わせに対して特殊化を実装するための有効な生成ディレクティブの集合は、常に、それぞれの有効なジェネレーターの集合の和集合であるためです。

上記のディレクティブに加えて、 ディレクティブ auto は を除く bodyすべての特殊化に対して有効です。これは、コンパイラが適切な生成ディレクティブを自動的に選択する必要があることを示します。 次の宣言

    operation DoNothing() : Unit {
        body ... { }
        adjoint auto;
        controlled auto;
        controlled adjoint auto;
    }

上記の式は、次の式と同じです。

    operation DoNothing() : Unit 
    is Adj + Ctl { }

この例の注釈 is Adj + Ctl は、特定の演算でサポートされるファンクタに関する情報が含まれている、"演算特性" を指定します。

読みやすくするために、各演算にその特性の詳細な説明を注釈として付けることをお勧めしますが、コンパイラは、明示的に宣言された特殊化に基づいて注釈を自動的に挿入または完了します。 逆に、コンパイラは、明示的に宣言されていないが、注釈された特性に基づいて存在する必要がある特殊化も生成します。 このことを、指定された注釈によってこれらの特殊化が "暗黙的に宣言された" と表現します。 コンパイラは、可能であれば、適切なディレクティブを選択して、必要な特殊化を自動的に生成します。 そのため、Q# は、(部分的な) 注釈と明示的に定義された特殊化に基づいて、演算特性と既存の特殊化の両方の推論をサポートします。

ある意味では、特殊化は、同じ callable の個々のオーバーロードに似ています。ただし、どのオーバーロードを宣言できるかについて、特定の制限が適用される点に注意してください。