From 055b04248d5c649edb15730c92311c07b2e4b1b5 Mon Sep 17 00:00:00 2001 From: Stefan Wernli Date: Mon, 30 Mar 2020 06:34:21 +0000 Subject: [PATCH 1/6] Creating Type2 Decompositions package This change adds the Type2 decompositions and refactors the Qsharp.Core into Qsharp.Core + Qsharp.Base to allow other core packages to be built on top of Base. --- Simulation.sln | 44 ++ build/pack.ps1 | 2 + .../Common/CircuitUtilities.qs | 130 ++++++ .../Common/Decompositions.qs | 121 +++++ .../DecompositionsCore/Common/Utilities.qs | 139 ++++++ ...t.Quantum.Decompositions.Core.Type2.csproj | 49 ++ .../Type2/Type2-Decompositions.qs | 433 ++++++++++++++++++ .../Type2/Type2-Internals.qs | 109 +++++ .../Type2/Type2-Intrinsic.qs | 263 +++++++++++ src/Simulation/QsharpBase/Assert.qs | 68 +++ .../Bitwise/Bitwise.cs | 0 .../Bitwise/Bitwise.qs | 0 .../Bitwise/Deprecated.qs | 0 .../Convert/Convert.cs | 0 .../Convert/Convert.qs | 0 .../Convert/Deprecated.qs | 0 .../{QsharpCore => QsharpBase}/Core.cs | 0 .../{QsharpCore => QsharpBase}/Core.qs | 0 .../Diagnostics/AssertAllZero.qs | 0 .../Diagnostics/AssertQubit.qs | 0 .../Diagnostics/Dump.qs | 0 .../Diagnostics/UnitTests.qs | 0 .../{QsharpCore => QsharpBase}/Environment.qs | 0 .../Math/Constants.qs | 0 .../Math/Deprecated.qs | 0 .../{QsharpCore => QsharpBase}/Math/Math.cs | 0 .../{QsharpCore => QsharpBase}/Math/Math.qs | 0 .../{QsharpCore => QsharpBase}/Math/Trig.qs | 0 .../{QsharpCore => QsharpBase}/Math/Types.qs | 0 .../Microsoft.Quantum.QSharp.Base.csproj | 40 ++ .../Statements/Allocate.cs | 0 .../Statements/Borrow.cs | 0 .../Statements/Release.cs | 0 .../Statements/Return.cs | 0 src/Simulation/QsharpBase/Utils.qs | 46 ++ src/Simulation/QsharpCore/Intrinsic.qs | 122 +---- .../Microsoft.Quantum.QSharp.Core.csproj | 1 + 37 files changed, 1465 insertions(+), 102 deletions(-) create mode 100644 src/Simulation/DecompositionsCore/Common/CircuitUtilities.qs create mode 100644 src/Simulation/DecompositionsCore/Common/Decompositions.qs create mode 100644 src/Simulation/DecompositionsCore/Common/Utilities.qs create mode 100644 src/Simulation/DecompositionsCore/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj create mode 100644 src/Simulation/DecompositionsCore/Type2/Type2-Decompositions.qs create mode 100644 src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs create mode 100644 src/Simulation/DecompositionsCore/Type2/Type2-Intrinsic.qs create mode 100644 src/Simulation/QsharpBase/Assert.qs rename src/Simulation/{QsharpCore => QsharpBase}/Bitwise/Bitwise.cs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Bitwise/Bitwise.qs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Bitwise/Deprecated.qs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Convert/Convert.cs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Convert/Convert.qs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Convert/Deprecated.qs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Core.cs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Core.qs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Diagnostics/AssertAllZero.qs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Diagnostics/AssertQubit.qs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Diagnostics/Dump.qs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Diagnostics/UnitTests.qs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Environment.qs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Math/Constants.qs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Math/Deprecated.qs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Math/Math.cs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Math/Math.qs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Math/Trig.qs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Math/Types.qs (100%) create mode 100644 src/Simulation/QsharpBase/Microsoft.Quantum.QSharp.Base.csproj rename src/Simulation/{QsharpCore => QsharpBase}/Statements/Allocate.cs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Statements/Borrow.cs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Statements/Release.cs (100%) rename src/Simulation/{QsharpCore => QsharpBase}/Statements/Return.cs (100%) create mode 100644 src/Simulation/QsharpBase/Utils.qs diff --git a/Simulation.sln b/Simulation.sln index 2566fb7b457..2e3b766bdd7 100644 --- a/Simulation.sln +++ b/Simulation.sln @@ -57,6 +57,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Library2", "src\Simulation\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "src\Simulation\Simulators.Tests\TestProjects\UnitTests\UnitTests.csproj", "{46278108-D247-4EFC-AC34-23D4A676F62F}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simulation", "Simulation", "{BDFBF140-34F1-43C2-87C9-25BF6BE20C45}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Quantum.QSharp.Base", "src\Simulation\QsharpBase\Microsoft.Quantum.QSharp.Base.csproj", "{E9E387C0-2881-4F0C-8433-064BB18DB742}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DecompositionsCore", "DecompositionsCore", "{4DF4699D-5A50-4B3F-8232-5B19CAE23950}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Quantum.Decompositions.Type2.Core", "src\Simulation\DecompositionsCore\Type2\Microsoft.Quantum.Decompositions.Core.Type2.csproj", "{C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -389,6 +397,38 @@ Global {46278108-D247-4EFC-AC34-23D4A676F62F}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU {46278108-D247-4EFC-AC34-23D4A676F62F}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU {46278108-D247-4EFC-AC34-23D4A676F62F}.RelWithDebInfo|x64.Build.0 = Release|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.Debug|x64.ActiveCfg = Debug|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.Debug|x64.Build.0 = Debug|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.Release|Any CPU.Build.0 = Release|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.Release|x64.ActiveCfg = Release|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.Release|x64.Build.0 = Release|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU + {E9E387C0-2881-4F0C-8433-064BB18DB742}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.Debug|x64.ActiveCfg = Debug|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.Debug|x64.Build.0 = Debug|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.Release|Any CPU.Build.0 = Release|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.Release|x64.ActiveCfg = Release|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.Release|x64.Build.0 = Release|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -416,6 +456,10 @@ Global {7256B986-6705-42FC-9F57-485D72D9DE51} = {09C842CB-930C-4C7D-AD5F-E30DE4A55820} {A85277B3-4E07-4E15-8F0C-07CC855A3BCB} = {09C842CB-930C-4C7D-AD5F-E30DE4A55820} {46278108-D247-4EFC-AC34-23D4A676F62F} = {09C842CB-930C-4C7D-AD5F-E30DE4A55820} + {BDFBF140-34F1-43C2-87C9-25BF6BE20C45} = {99E234BC-997E-4E63-9F5C-3C3977543404} + {E9E387C0-2881-4F0C-8433-064BB18DB742} = {BDFBF140-34F1-43C2-87C9-25BF6BE20C45} + {4DF4699D-5A50-4B3F-8232-5B19CAE23950} = {BDFBF140-34F1-43C2-87C9-25BF6BE20C45} + {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5} = {4DF4699D-5A50-4B3F-8232-5B19CAE23950} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {929C0464-86D8-4F70-8835-0A5EAF930821} diff --git a/build/pack.ps1 b/build/pack.ps1 index b4b39f19e2a..68c78ca15a5 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -62,7 +62,9 @@ Write-Host "##[info]Using nuget to create packages" Pack-Dotnet '../src/Azure/Azure.Quantum.Client/Microsoft.Azure.Quantum.Client.csproj' Pack-One '../src/Simulation/CsharpGeneration/Microsoft.Quantum.CsharpGeneration.fsproj' '-IncludeReferencedProjects' Pack-Dotnet '../src/Simulation/Core/Microsoft.Quantum.Runtime.Core.csproj' +Pack-Dotnet '../src/Simulation/QsharpBase/Microsoft.Quantum.QSharp.Base.csproj' Pack-Dotnet '../src/Simulation/QsharpCore/Microsoft.Quantum.QSharp.Core.csproj' +Pack-Dotnet '../src/Simulation/Targeting/IonQ/Microsoft.Quantum.IonQ.Core.csproj' Pack-One '../src/Simulation/Simulators/Microsoft.Quantum.Simulators.nuspec' Pack-One '../src/Quantum.Development.Kit/Microsoft.Quantum.Development.Kit.nuspec' Pack-One '../src/Xunit/Microsoft.Quantum.Xunit.csproj' diff --git a/src/Simulation/DecompositionsCore/Common/CircuitUtilities.qs b/src/Simulation/DecompositionsCore/Common/CircuitUtilities.qs new file mode 100644 index 00000000000..a0f317a05e2 --- /dev/null +++ b/src/Simulation/DecompositionsCore/Common/CircuitUtilities.qs @@ -0,0 +1,130 @@ +namespace Microsoft.Quantum.Targeting.Decompositions.Utilities.Circuit { + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Targeting.Decompositions.Utilities as Utils; + + /// Applies a unitary operation such that + /// `SpreadZ(...); Exp([PauliZ],theta,[from]) ; Adjoint SpreadZ(...);` is equivalent to `Exp([PauliZ,..,PauliZ],theta,[from] + to)` + internal operation SpreadZ(from : Qubit, to : Qubit[]) : Unit is Adj { + if (Length(to) > 0) { + CNOT(to[0], from); + if (Length(to) > 1) { + let half = Length(to) / 2; + SpreadZ(to[0], to[half + 1 .. Length(to) - 1]); + SpreadZ(from, to[1 .. half]); + } + } + } + + /// Applies a unitary operation such that + /// `MapPauli(...); R(from,...) ; Adjoint MapPauli(...);` is equivalent to `R(to,...)` + internal operation MapPauli(qubit : Qubit, from : Pauli, to : Pauli) : Unit is Adj { + if (from == to) { + } + elif ((from == PauliZ and to == PauliX) or (from == PauliX and to == PauliZ)) { + H(qubit); + } + elif (from == PauliZ and to == PauliY) { + H(qubit); + S(qubit); + H(qubit); + } + elif (from == PauliY and to == PauliZ) { + H(qubit); + Adjoint S(qubit); + H(qubit); + } + elif (from == PauliY and to == PauliX) { + S(qubit); + } + elif (from == PauliX and to == PauliY) { + Adjoint S(qubit); + } + else { + fail "Unsupported input"; + } + } + + /// Given a multiply-controlled operation that requires k controls + /// applies it using ceiling(k/2) controls and using floor(k/2) temporary qubits + /// almostCCX(c1,c2,t); U(c1,c2) must be equivalent to CCX(c1,c2,t) for some two-qubit unitary operation U(c1,c2) given target qubit starts + /// in zero state, ( for Adjoint it is asserted that target qubit is returned to zero state) + internal operation ApplyWithLessControlsA<'T>(op : ((Qubit[],'T) => Unit is Adj), (controls : Qubit[], arg : 'T), almostCCX : ((Qubit,Qubit,Qubit) => Unit is Adj)) : Unit is Adj { + let numControls = Length(controls); + let numControlPairs = numControls / 2; + using (temps = Qubit[numControlPairs]) { + within { + for (numPair in 0 .. numControlPairs - 1) { // constant depth + almostCCX(controls[2*numPair], controls[2*numPair + 1], temps[numPair]); + } + } apply { + let newControls = numControls % 2 == 0 ? temps | temps + [controls[numControls - 1]]; + op(newControls, arg); + } + } + } + + /// almostCCX(c1,c2,t); U(c1,c2) must be equivalent to CCX(c1,c2,t) for some two-qubit unitary operation U(c1,c2) given target qubit starts + /// in zero state, ( for Adjoint it is asserted that target qubit is returned to zero state) + internal operation ApplyUsingSinglyControlledVersion<'T>(op : ('T => Unit is Adj), controlledOp : ((Qubit,'T) => Unit is Adj), arg : 'T , almostCCX : ((Qubit,Qubit,Qubit) => Unit is Adj)) : Unit is Adj + Ctl { + body(...) { + op(arg); + } + controlled(ctrls, ...) { + let numControls = Length(ctrls); + if (numControls == 0) { op(arg); } + elif (numControls == 1) { controlledOp(ctrls[0], arg); } + else { + let inner = ApplyUsingSinglyControlledVersion(op, controlledOp, _, almostCCX); + ApplyWithLessControlsA(Controlled inner, (ctrls, arg), almostCCX); + } + } + } + + internal operation DispatchR1Frac(numerator : Int, power : Int, qubit : Qubit) : Unit is Adj + Ctl { + if (power >= 0 ) { // when power is negative the operations is (1,exp(i pi*2^|n|*k)) and exp(i pi*2^|n|*k) = 1 + let (kModPositive,n) = Utils.ReducedDyadicFractionPeriodic(numerator,power); // k is odd, or (k,n) are both 0 + if (n == 0) { // kModPositive is 0,1 + if (kModPositive == 1) { Z(qubit); } + elif (kModPositive == 0) {} + else { fail "Something went wrong. This should be unreachable"; } + } + elif (n == 1) { // period is 4, kModPositive is 1,3 + if (kModPositive == 1) { S(qubit); } + elif (kModPositive == 3) { Adjoint S(qubit); } + else { fail "Something went wrong. This should be unreachable"; } + } + elif (n == 2) { // period is 8, kModPositive is 1,3,5,7 + if (kModPositive == 1) { T(qubit); } + elif (kModPositive == 3) { TS(qubit); } + elif (kModPositive == 5) { Adjoint TS(qubit); } + elif (kModPositive == 7) { Adjoint T(qubit); } + else { fail "Something went wrong. This should be unreachable"; } + } + else { + let phi = Utils.DyadicFractionAsDouble(kModPositive, n); + R1(phi,qubit); + } + } + } + + internal operation ApplyGlobalPhaseWithR1(theta : Double) : Unit is Adj + Ctl { + body(...) {} + controlled(ctrls, ... ) { + let numControls = Length(ctrls); + if (numControls > 0) { + Controlled R1(ctrls[1 .. numControls - 1], (theta, ctrls[0])); + } + } + } + + internal operation ApplyGlobalPhaseFracWithR1Frac(numerator : Int, power : Int) : Unit is Adj + Ctl { + body(...) {} + controlled(ctrls, ... ) { + let numControls = Length(ctrls); + if (numControls > 0 ) { + Controlled R1Frac(ctrls[1 .. numControls - 1], (numerator, power, ctrls[0])); + } + } + } + +} \ No newline at end of file diff --git a/src/Simulation/DecompositionsCore/Common/Decompositions.qs b/src/Simulation/DecompositionsCore/Common/Decompositions.qs new file mode 100644 index 00000000000..d1477f6f6fb --- /dev/null +++ b/src/Simulation/DecompositionsCore/Common/Decompositions.qs @@ -0,0 +1,121 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/// Decompositions of some Q# operations into more primitive ones +/// that can be directly executed on various target hardware. +namespace Microsoft.Quantum.Targeting.Decompositions { + open Microsoft.Quantum.Intrinsic; + + /// # Summary + /// Calls "fail message" when the first argument is True + internal function FailIf(failIfTrue : Bool, message : String) : Unit { + if (failIfTrue) { + fail message; + } + } + + /// # Summary + /// Finds a first Pauli not equal to PauliI in the array + internal function FirstIndexOfNonId(array : Pauli[]) : Int { + for (i in 0 .. Length(array) - 1) { + if( array[i] != PauliI ) { + return i; + } + } + return -1; + } + + /// # Summary + /// Applies a Clifford unitary that maps by conjugation Pauli Z + /// to Pauli given by 'basis' argument. The unitary is applied to the qubit given by 'target' argument + internal operation PauliZFlip(basis : Pauli, target : Qubit) : Unit is Adj+Ctl { + + FailIf(basis == PauliI, $"PauliZ cannot be mapped to PauliI using conjugation by Clifford"); + + if (basis == PauliX) { + H(target); + } + elif (basis == PauliY) { + H(target); + S(target); + H(target); + } + else { + FailIf(basis != PauliZ, $"PauliZ must be the only remaining case"); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////// + + internal operation ExpHelper(target : Qubit, paulis : Pauli[], qubits : Qubit[]) : Unit is Adj+Ctl { + for (i in 0 .. Length(paulis) - 1) { + if (paulis[i] != PauliI) { + PauliZFlip(paulis[i], qubits[i]); + CNOT(qubits[i],target); + } + } + } + + /// # Warning + /// Very inefficient implementation in terms of depth + internal operation Exp(paulis : Pauli[], theta : Double, qubits : Qubit[]) : Unit is Adj + Ctl { + FailIf(Length(paulis) == 0, ""); + FailIf(Length(paulis) != Length(qubits), ""); + + let index = FirstIndexOfNonId(paulis); + if (index != -1) { + PauliZFlip(paulis[index], qubits[index]); + ExpHelper(qubits[index], paulis[index + 1 .. Length(paulis) - 1], qubits[index + 1 .. Length(qubits) - 1]); + R(PauliZ, -2.0 * theta, qubits[index]); + Adjoint ExpHelper(qubits[index], paulis[index + 1 .. Length(paulis) - 1], qubits[index + 1 .. Length(qubits) - 1]); + Adjoint PauliZFlip(paulis[index], qubits[index]); + } + else + { + R(PauliI, -2.0 * theta, qubits[0]); + } + } + + internal operation ExpFrac(paulis : Pauli[], numerator : Int, power : Int, qubits : Qubit[]) : Unit { + FailIf(Length(paulis) == 0, ""); + FailIf(Length(paulis) != Length(qubits), ""); + + let index = FirstIndexOfNonId(paulis); + if (index != -1) { + PauliZFlip(paulis[index], qubits[index]); + ExpHelper(qubits[index], paulis[index + 1 .. Length(paulis) - 1], qubits[index + 1 .. Length(qubits) - 1]); + RFrac(PauliZ, numerator, power, qubits[index]); + Adjoint ExpHelper(qubits[index], paulis[index + 1 .. Length(paulis) - 1], qubits[index + 1 .. Length(qubits) - 1]); + Adjoint PauliZFlip(paulis[index], qubits[index]); + } + else + { + RFrac(PauliI, numerator, power, qubits[0]); + } + } + + /// # Warning + /// Very inefficient implementation in terms of depth + internal operation Measure(paulis : Pauli[], qubits : Qubit[]) : Result { + FailIf(Length(paulis) == 0, "" ); + FailIf(Length(paulis) != Length(qubits), ""); + mutable res = Zero; + + let index = FirstIndexOfNonId(paulis); + if (index != -1) { + PauliZFlip(paulis[index], qubits[index]); + ExpHelper(qubits[index], paulis[index+1 .. Length(paulis)-1], qubits[index+1 .. Length(qubits)-1]); + set res = M(qubits[index]); + Adjoint ExpHelper(qubits[index], paulis[index+1 .. Length(paulis)-1], qubits[index+1 .. Length(qubits)-1]); + Adjoint PauliZFlip(paulis[index], qubits[index]); + } + return res; + } + + internal operation MResetZ(target : Qubit) : Result { + let r = M(target); + Reset(target); + + return r; + } +} diff --git a/src/Simulation/DecompositionsCore/Common/Utilities.qs b/src/Simulation/DecompositionsCore/Common/Utilities.qs new file mode 100644 index 00000000000..5dbf0ec3f42 --- /dev/null +++ b/src/Simulation/DecompositionsCore/Common/Utilities.qs @@ -0,0 +1,139 @@ +namespace Microsoft.Quantum.Targeting.Decompositions.Utilities { + + internal operation ApplySinglyControlledCA<'T>(op : ('T => Unit is Adj + Ctl), (control : Qubit, arg : 'T)) : Unit is Adj + Ctl { + Controlled op([control], arg); + } + + internal operation ApplySinglyControlledCAQ2(op : ((Qubit,Qubit) => Unit is Adj + Ctl), (control : Qubit, qubit1 : Qubit, qubit2 : Qubit)) : Unit is Adj + Ctl { + Controlled op([control], (qubit1, qubit2)); + } + + internal function SinglyControlled<'T>(op : ('T => Unit is Adj + Ctl)) : ((Qubit,'T) => Unit is Adj + Ctl) { + return ApplySinglyControlledCA(op, _); + } + + internal function SinglyControlledQ2(op : ((Qubit,Qubit) => Unit is Adj + Ctl)) : ((Qubit,Qubit,Qubit) => Unit is Adj + Ctl) { + return ApplySinglyControlledCAQ2(op, _); + } + + internal function ArrayAsTupleO<'T>(arr : 'T[]) : 'T { + return (arr[0]); + } + + internal function ArrayAsTupleAO<'T>(arr : 'T[]) : ('T[],'T) { + return (arr[1 .. Length(arr) - 1], arr[0]); + } + + internal function ArrayAsTupleOO<'T>(arr : 'T[]) : ('T,'T) { + return (arr[0], arr[1]); + } + + internal function ArrayAsTupleAIOO<'T>(arr : 'T[]) : ('T[],('T,'T)) { + return (arr[2 .. Length(arr) - 1], (arr[0], arr[1])); + } + + internal function ArrayAsTupleOOO<'T>(arr : 'T[]) : ('T,'T,'T) { + return (arr[0], arr[1], arr[2]); + } + + internal function ArrayAsTupleAIOOO<'T>(arr : 'T[]) : ('T[],('T,'T,'T)) { + return (arr[3 .. Length(arr) - 1], (arr[0], arr[1],arr[2])); + } + + internal function ArrayAsTupleAA<'T>(secondArraySize : Int, arr : 'T[]) : ('T[],'T[]) { + return (arr[secondArraySize .. Length(arr) - 1], (arr[0 .. secondArraySize - 1])); + } + + internal function ArrayAsTupleOOIO<'T>( arr : 'T[]) : (('T,'T),'T) { + return ((arr[0], arr[1]), arr[2]); + } + + internal function ArrayAsTupleOIOO<'T>(arr : 'T[]) : ('T,('T,'T)) { + return (arr[0],(arr[1], arr[2])); + } + + internal operation ApplyComposedCA<'U,'V>(op : ('U => Unit is Adj + Ctl), fn : ('V -> 'U), arg : 'V) : Unit is Ctl + Adj { + op(fn(arg)); + } + + internal operation ApplyComposedA<'U,'V>(op : ('U => Unit is Adj), fn : ('V -> 'U), arg : 'V) : Unit is Adj { + op(fn(arg)); + } + + internal operation ApplyComposedC<'U,'V>(op : ('U => Unit is Ctl), fn : ('V -> 'U), arg : 'V) : Unit is Ctl { + op(fn(arg)); + } + + internal operation ApplyComposed<'U,'V>(op : ('U => Unit), fn : ('V -> 'U), arg : 'V) : Unit { + op(fn(arg)); + } + + internal function ArrayFromIndiciesP(values : Pauli[], indicies : Int[]) : Pauli[] { + mutable arr = new Pauli[Length(indicies)]; + for (i in 0 .. Length(indicies) - 1) { + set arr w/= i <- values[indicies[i]]; + } + return arr; + } + + internal function ArrayFromIndiciesQ(values : Qubit[], indicies : Int[]) : Qubit[] { + mutable arr = new Qubit[Length(indicies)]; + for (i in 0 .. Length(indicies) - 1) { + set arr w/= i <- values[indicies[i]]; + } + return arr; + } + + internal function IndicesOfNonIdentity(paulies : Pauli[]) : Int[] { + mutable nonIdPauliCount = 0; + + for (i in 0 .. Length(paulies) - 1) { + if (paulies[i] != PauliI) { set nonIdPauliCount += 1; } + } + + mutable indices = new Int[nonIdPauliCount]; + mutable index = 0; + + for (i in 0 .. Length(paulies) - 1) { + if (paulies[i] != PauliI) { + set indices w/= index <- i; + set index = index + 1; + } + } + + return indices; + } + + internal function ReducedDyadicFraction(numerator : Int, denominatorPowerOfTwo : Int) : (Int,Int) { + if (numerator == 0) { return (0,0); } + mutable num = numerator; + mutable denPow = denominatorPowerOfTwo; + while( num % 2 == 0 ) { + set num /= 2; + set denPow += 1; + } + return (num,denPow); + } + + internal function ReducedDyadicFractionPeriodic(numerator : Int, denominatorPowerOfTwo : Int) : (Int,Int) { + let (k,n) = ReducedDyadicFraction(numerator,denominatorPowerOfTwo); // k is odd, or (k,n) are both 0 + let period = 2*2^n; // \pi k / 2^n is 2\pi periodic, therefore k is 2 * 2^n periodic + let kMod = k % period; // if k was negative, we get kMod in a range [-period + 1, 0] + let kModPositive = kMod >= 0 ? kMod | kMod + period; // kModPositive is in the range [0, period - 1] + return (kModPositive, n); + } + + /// # Summary + /// Returns π×numerator/2^(denominatorPowerOfTwo) as Double + internal function DyadicFractionAsDouble(numerator : Int, denominatorPowerOfTwo : Int) : Double { + let numeratorD = Microsoft.Quantum.Math.PI() * Microsoft.Quantum.Convert.IntAsDouble(numerator); + return numeratorD * Microsoft.Quantum.Math.PowD(2.0, Microsoft.Quantum.Convert.IntAsDouble(-denominatorPowerOfTwo)); + } + + internal function RemovePauliI(paulis : Pauli[], qubits : Qubit[]) : (Pauli[], Qubit[]) { + let indices = IndicesOfNonIdentity(paulis); + let newPaulis = ArrayFromIndiciesP(paulis, indices); + let newQubits = ArrayFromIndiciesQ(qubits, indices); + return (newPaulis, newQubits); + } +} \ No newline at end of file diff --git a/src/Simulation/DecompositionsCore/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj b/src/Simulation/DecompositionsCore/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj new file mode 100644 index 00000000000..5d1acbe14fd --- /dev/null +++ b/src/Simulation/DecompositionsCore/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj @@ -0,0 +1,49 @@ + + + + + + + netstandard2.1 + true + false + false + + + + Microsoft + Support for Type 2 Q# circuit decompositions. + See: https://docs.microsoft.com/en-us/quantum/relnotes/ + MIT + https://github.com/microsoft/qsharp-runtime + https://secure.gravatar.com/avatar/bd1f02955b2853ba0a3b1cdc2434e8ec.png + Quantum Q# Qsharp Decompositions + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Simulation/DecompositionsCore/Type2/Type2-Decompositions.qs b/src/Simulation/DecompositionsCore/Type2/Type2-Decompositions.qs new file mode 100644 index 00000000000..a13497e6470 --- /dev/null +++ b/src/Simulation/DecompositionsCore/Type2/Type2-Decompositions.qs @@ -0,0 +1,433 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Intrinsic { + open Microsoft.Quantum.Targeting.Decompositions as CoreDecomp; + open Microsoft.Quantum.Targeting.Decompositions.Utilities as Utils; + open Microsoft.Quantum.Targeting.Decompositions.Utilities.Circuit as CircuitUtils; + + /// # Summary + /// Performs the identity operation (no-op) on a single qubit. + /// + /// # Remarks + /// This is a no-op. It is provided for completeness and because + /// sometimes it is useful to call the identity in an algorithm or to pass it as a parameter. + operation I(target : Qubit) : Unit + is Adj + Ctl { + body (...) { } + adjoint self; + } + + /// # Summary + /// Applies a rotation about the $x$-axis by a given angle. + /// + /// # Description + /// \begin{align} + /// R_x(\theta) \mathrel{:=} + /// e^{-i \theta \sigma_x / 2} = + /// \begin{bmatrix} + /// \cos \frac{\theta}{2} & -i\sin \frac{\theta}{2} \\\\ + /// -i\sin \frac{\theta}{2} & \cos \frac{\theta}{2} + /// \end{bmatrix}. + /// \end{align} + /// + /// # Input + /// ## theta + /// Angle about which the qubit is to be rotated. + /// ## qubit + /// Qubit to which the gate should be applied. + /// + /// # Remarks + /// Equivalent to: + /// ```qsharp + /// R(PauliX, theta, qubit); + /// ``` + operation Rx(theta : Double, qubit : Qubit) : Unit is Ctl + Adj { + body(...) { + R(PauliX, theta, qubit); + } + adjoint(...) { + R(PauliX, -theta, qubit); + } + } + + /// # Summary + /// Applies a rotation about the $y$-axis by a given angle. + /// + /// # Description + /// \begin{align} + /// R_y(\theta) \mathrel{:=} + /// e^{-i \theta \sigma_y / 2} = + /// \begin{bmatrix} + /// \cos \frac{\theta}{2} & -\sin \frac{\theta}{2} \\\\ + /// \sin \frac{\theta}{2} & \cos \frac{\theta}{2} + /// \end{bmatrix}. + /// \end{align} + /// + /// # Input + /// ## theta + /// Angle about which the qubit is to be rotated. + /// ## qubit + /// Qubit to which the gate should be applied. + /// + /// # Remarks + /// Equivalent to: + /// ```qsharp + /// R(PauliY, theta, qubit); + /// ``` + operation Ry(theta : Double, qubit : Qubit) : Unit is Ctl + Adj { + body(...) { + R(PauliY, theta, qubit); + } + adjoint(...) { + R(PauliY, -theta, qubit); + } + } + + /// # Summary + /// Applies a rotation about the $z$-axis by a given angle. + /// + /// # Description + /// \begin{align} + /// R_z(\theta) \mathrel{:=} + /// e^{-i \theta \sigma_z / 2} = + /// \begin{bmatrix} + /// e^{-i \theta / 2} & 0 \\\\ + /// 0 & e^{i \theta / 2} + /// \end{bmatrix}. + /// \end{align} + /// + /// # Input + /// ## theta + /// Angle about which the qubit is to be rotated. + /// ## qubit + /// Qubit to which the gate should be applied. + /// + /// # Remarks + /// Equivalent to: + /// ```qsharp + /// R(PauliZ, theta, qubit); + /// ``` + operation Rz(theta : Double, qubit : Qubit) : Unit is Ctl + Adj { + body(...) { + R(PauliZ, theta, qubit); + } + adjoint(...) { + R(PauliZ, -theta, qubit); + } + } + + /// # Summary + /// Applies the controlled-NOT (CNOT) gate to a pair of qubits. + /// + /// # Description + /// \begin{align} + /// \operatorname{CNOT} \mathrel{:=} + /// \begin{bmatrix} + /// 1 & 0 & 0 & 0 \\\\ + /// 0 & 1 & 0 & 0 \\\\ + /// 0 & 0 & 0 & 1 \\\\ + /// 0 & 0 & 1 & 0 + /// \end{bmatrix}, + /// \end{align} + /// + /// where rows and columns are ordered as in the quantum concepts guide. + /// + /// # Input + /// ## control + /// Control qubit for the CNOT gate. + /// ## target + /// Target qubit for the CNOT gate. + /// + /// # Remarks + /// Equivalent to: + /// ```qsharp + /// Controlled X([control], target); + /// ``` + operation CNOT(control : Qubit, target : Qubit) : Unit is Adj + Ctl { + body (...) { + Controlled X([control], target); + } + adjoint self; + } + + /// # Summary + /// Applies the doubly controlled–NOT (CCNOT) gate to three qubits. + /// + /// # Input + /// ## control1 + /// First control qubit for the CCNOT gate. + /// ## control2 + /// Second control qubit for the CCNOT gate. + /// ## target + /// Target qubit for the CCNOT gate. + /// + /// # Remarks + /// Equivalent to: + /// ```qsharp + /// Controlled X([control1, control2], target); + /// ``` + operation CCNOT(control1 : Qubit, control2 : Qubit, target : Qubit) : Unit is Adj + Ctl { + body (...) { + Controlled X([control1, control2], target); + } + adjoint self; + } + + /// # Summary + /// Applies a rotation about the $\ket{1}$ state by a given angle. + /// + /// # Description + /// \begin{align} + /// R_1(\theta) \mathrel{:=} + /// \operatorname{diag}(1, e^{i\theta}). + /// \end{align} + /// + /// # Input + /// ## theta + /// Angle about which the qubit is to be rotated. + /// ## qubit + /// Qubit to which the gate should be applied. + /// + /// # Remarks + /// Equivalent to: + /// ```qsharp + /// R(PauliZ, theta, qubit); + /// R(PauliI, -theta, qubit); + /// ``` + operation R1(theta : Double, qubit : Qubit) : Unit is Adj + Ctl { + body(...) { + ApplyGlobalPhase( theta / 2.0 ); + Rz(theta, qubit); + } + adjoint(...) { + R1(-theta, qubit); + } + } + + /// # Summary + /// Given an array of qubits, measure them and ensure they are in the |0⟩ state + /// such that they can be safely released. + /// + /// # Input + /// ## qubits + /// An array of qubits whose states are to be reset to $\ket{0}$. + operation ResetAll(qubits : Qubit[]) : Unit { + for (qubit in qubits) { + Reset(qubit); + } + } + + /// # Summary + /// Applies the exponential of a multi-qubit Pauli operator. + /// + /// # Description + /// \begin{align} + /// e^{i \theta [P_0 \otimes P_1 \cdots P_{N-1}]}, + /// \end{align} + /// where $P_i$ is the $i$th element of `paulis`, and where + /// $N = $`Length(paulis)`. + /// + /// # Input + /// ## paulis + /// Array of single-qubit Pauli values indicating the tensor product + /// factors on each qubit. + /// ## theta + /// Angle about the given multi-qubit Pauli operator by which the + /// target register is to be rotated. + /// ## qubits + /// Register to apply the given rotation to. + operation Exp(paulis : Pauli[], theta : Double, qubits : Qubit[]) : Unit is Adj + Ctl { + body(...) { + if (Length(paulis) != Length(qubits)) { fail "Arrays 'pauli' and 'target' must have the same length"; } + let (newPaulis, newQubits) = Utils.RemovePauliI(paulis, qubits); + + if (Length(newPaulis) != 0) { + ExpNoId(newPaulis, theta , newQubits); + } + else { + ApplyGlobalPhase(theta); + } + } + adjoint(...) { + Exp(paulis, -theta, qubits); + } + } + + /// # Summary + /// Applies a rotation about the $\ket{1}$ state by an angle specified + /// as a dyadic fraction. + /// + /// # Description + /// \begin{align} + /// R_1(n, k) \mathrel{:=} + /// \operatorname{diag}(1, e^{i \pi k / 2^n}). + /// \end{align} + /// + /// > [!WARNING] + /// > This operation uses the **opposite** sign convention from + /// > @"microsoft.quantum.intrinsic.r", and does not include the + /// > factor of $1/ 2$ included by @"microsoft.quantum.intrinsic.r1". + /// + /// # Input + /// ## numerator + /// Numerator in the dyadic fraction representation of the angle + /// by which the qubit is to be rotated. + /// ## power + /// Power of two specifying the denominator of the angle by which + /// the qubit is to be rotated. + /// ## qubit + /// Qubit to which the gate should be applied. + operation R1Frac(numerator : Int, power : Int, qubit : Qubit) : Unit is Adj + Ctl { + CircuitUtils.DispatchR1Frac(numerator, power, qubit); + } + + /// # Summary + /// Applies a rotation about the given Pauli axis by an angle specified + /// as a dyadic fraction. + /// + /// # Description + /// \begin{align} + /// R_{\mu}(n, k) \mathrel{:=} + /// e^{i \pi n \sigma_{\mu} / 2^k}, + /// \end{align} + /// where $\mu \in \{I, X, Y, Z\}$. + /// + /// > [!WARNING] + /// > This operation uses the **opposite** sign convention from + /// > @"microsoft.quantum.intrinsic.r". + /// + /// # Input + /// ## pauli + /// Pauli operator to be exponentiated to form the rotation. + /// ## numerator + /// Numerator in the dyadic fraction representation of the angle + /// by which the qubit is to be rotated. + /// ## power + /// Power of two specifying the denominator of the angle by which + /// the qubit is to be rotated. + /// ## qubit + /// Qubit to which the gate should be applied. + operation RFrac(pauli : Pauli, numerator : Int, power : Int, qubit : Qubit) : Unit is Adj + Ctl { + if (pauli == PauliI) { + CircuitUtils.ApplyGlobalPhaseFracWithR1Frac(numerator, power); + } + else { + if (power >= 0) { // when power is negative the operation is exp(i P pi*2^|n|*k) = I + + within { + CircuitUtils.MapPauli(qubit, PauliZ, pauli); + } + apply { + CircuitUtils.ApplyGlobalPhaseFracWithR1Frac(numerator, power); + R1Frac(-numerator, power - 1, qubit); + } + + //Below is another option for implementing RFrac + //let (kModPositive,n) = Utils.ReducedDyadicFractionPeriodic(numerator,power); // k is odd, in the range [1,2*2^n-1] or (k,n) are both 0 + //let numeratorD = Microsoft.Quantum.Math.PI() * Microsoft.Quantum.Convert.IntAsDouble(kModPositive); + //let phi = numeratorD * Microsoft.Quantum.Math.PowD(2.0, Microsoft.Quantum.Convert.IntAsDouble(-n)); + //R(pauli, -2.0 * phi, qubit); + + } + } + } + + /// # Summary + /// Applies the exponential of a multi-qubit Pauli operator + /// with an argument given by a dyadic fraction. + /// + /// # Description + /// \begin{align} + /// e^{i \pi k [P_0 \otimes P_1 \cdots P_{N-1}] / 2^n}, + /// \end{align} + /// where $P_i$ is the $i$th element of `paulis`, and where + /// $N = $`Length(paulis)`. + /// + /// # Input + /// ## paulis + /// Array of single-qubit Pauli values indicating the tensor product + /// factors on each qubit. + /// ## numerator + /// Numerator ($k$) in the dyadic fraction representation of the angle + /// by which the qubit register is to be rotated. + /// ## power + /// Power of two ($n$) specifying the denominator of the angle by which + /// the qubit register is to be rotated. + /// ## qubits + /// Register to apply the given rotation to. + operation ExpFrac(paulis : Pauli[], numerator : Int, power : Int, qubits : Qubit[]) : Unit is Adj + Ctl { + body(...) { + if (Length(paulis) != Length(qubits)) { fail "Arrays 'pauli' and 'target' must have the same length"; } + + if (Length(paulis) != 0) { + let indices = Utils.IndicesOfNonIdentity(paulis); + let newPaulis = Utils.ArrayFromIndiciesP(paulis, indices); + let newQubits = Utils.ArrayFromIndiciesQ(qubits, indices); + + if (Length(indices) != 0) { ExpNoIdFrac(newPaulis, numerator, power , newQubits); } + else { CircuitUtils.ApplyGlobalPhaseFracWithR1Frac(numerator, power); } + } + } + adjoint(...) { + ExpFrac(paulis, -numerator, power, qubits); + } + } + + /// # Summary + /// Performs a joint measurement of one or more qubits in the + /// specified Pauli bases. + /// + /// # Description + /// The output result is given by the distribution: + /// \begin{align} + /// \Pr(\texttt{Zero} | \ket{\psi}) = + /// \frac12 \braket{ + /// \psi \mid| + /// \left( + /// \boldone + P_0 \otimes P_1 \otimes \cdots \otimes P_{N-1} + /// \right) \mid| + /// \psi + /// }, + /// \end{align} + /// where $P_i$ is the $i$th element of `bases`, and where + /// $N = \texttt{Length}(\texttt{bases})$. + /// That is, measurement returns a `Result` $d$ such that the eigenvalue of the + /// observed measurement effect is $(-1)^d$. + /// + /// # Input + /// ## bases + /// Array of single-qubit Pauli values indicating the tensor product + /// factors on each qubit. + /// ## qubits + /// Register of qubits to be measured. + /// + /// # Output + /// `Zero` if the $+1$ eigenvalue is observed, and `One` if + /// the $-1$ eigenvalue is observed. + /// + /// # Remarks + /// If the basis array and qubit array are different lengths, then the + /// operation will fail. + operation Measure(bases : Pauli[], qubits : Qubit[]) : Result { + if (Length(bases) == 1) { + CircuitUtils.MapPauli(qubits[0], PauliZ, bases[0]); + return M(qubits[0]); + } + else { + using (q = Qubit()) { + within { + H(q); + } + apply { + for (k in 0 .. Length(bases) - 1) { + if (bases[k] == PauliX) { Controlled X([qubits[k]], q); } + if (bases[k] == PauliZ) { Controlled Z([qubits[k]], q); } + if (bases[k] == PauliY) { Controlled Y([qubits[k]], q); } + } + } + return M(q); + } + } + } +} \ No newline at end of file diff --git a/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs b/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs new file mode 100644 index 00000000000..02cff85d9da --- /dev/null +++ b/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Intrinsic { + open Microsoft.Quantum.Targeting.Decompositions as CoreDecomp; + open Microsoft.Quantum.Targeting.Decompositions.Utilities as Utils; + open Microsoft.Quantum.Targeting.Decompositions.Utilities.Circuit as CircuitUtils; + + internal operation ExpNoId(paulis : Pauli[], theta : Double, qubits : Qubit[]) : Unit is Ctl { + if (Length(paulis) != Length(qubits)) { fail "Arrays 'paulis' and 'qubits' must have the same length"; } + if (Length(paulis) == 1) { + R(paulis[0], -2.0 * theta, qubits[0]); + } + elif ( Length(paulis) == 2 ) { + if (paulis[0] != paulis[1] and paulis[0] != PauliI) { fail $"Type 2 Decompositions support only rotation around XX, YY, ZZ given {paulis}"; } + within { + CircuitUtils.MapPauli(qubits[1],paulis[0],paulis[1]); + } + apply { + if (paulis[1] == PauliX) { + ApplyIsingXX(theta, qubits[0], qubits[1]); + } elif (paulis[1] == PauliY) { + ApplyIsingYY(theta, qubits[0], qubits[1]); + } elif (paulis[1] == PauliZ) { + ApplyIsingZZ(theta, qubits[0], qubits[1]); + } else { + fail "Type2 decompositions do not support PauliI"; + } + } + } + else { // Length(paulis) > 2 + within { + for (i in 0 .. Length(paulis) - 1) { + CircuitUtils.MapPauli(qubits[i],PauliZ,paulis[i]); + } + } + apply { + within { + CircuitUtils.SpreadZ(qubits[1], qubits[ 2 .. Length(qubits) - 1]); + } + apply { + ExpNoId([PauliZ,PauliZ], theta, [qubits[0], qubits[1]]); + } + } + } + } + + internal operation ApplyGlobalPhase(theta : Double) : Unit is Ctl + Adj { + body(...) {} + controlled(controls, (...)) { + if (Length(controls) > 0) { + let qubit = controls[0]; //Microsoft.Quantum.Arrays.Head(controls); + let rest = controls[1...]; //Microsoft.Quantum.Arrays.Rest(controls); + // Invoke Controlled R1, which will recursively call back into ApplyGlobalPhase. + // Each time the controls is one shorter, until it is empty and the recursion stops. + Controlled R1(rest, (theta, qubit)); + } + } + } + + internal operation TS(target : Qubit) : Unit is Adj + Ctl { + T(target); + S(target); + } + + internal operation ExpNoIdFrac(paulis : Pauli[], numerator : Int, power : Int, qubits : Qubit[]) : Unit is Ctl { + if (Length(paulis) != Length(qubits)) { fail "Arrays 'paulis' and 'qubits' must have the same length"; } + + let (kModPositive,n) = Utils.ReducedDyadicFractionPeriodic(numerator, power); // k is odd, in the range [1,2*2^n-1] or (k,n) are both 0 + let numeratorD = Microsoft.Quantum.Math.PI() * Microsoft.Quantum.Convert.IntAsDouble(kModPositive); + let theta = numeratorD * Microsoft.Quantum.Math.PowD(2.0, Microsoft.Quantum.Convert.IntAsDouble(-n)); + + if (Length(paulis) == 1 ) { + RFrac(paulis[0], numerator, power, qubits[0]); + } + elif (Length(paulis) == 2) { + within { + CircuitUtils.MapPauli(qubits[1], paulis[0], paulis[1]); + } + apply { + if (paulis[0] == PauliX) { + ApplyIsingXX(theta, qubits[0], qubits[1]); + } elif (paulis[0] == PauliY) { + ApplyIsingYY(theta, qubits[0], qubits[1]); + } elif (paulis[0] == PauliZ) { + ApplyIsingZZ(theta, qubits[0], qubits[1]); + } else { + fail "Type2 decompositions do not support PauliI"; + } + } + } + else { // Length(paulis) > 2 + within { + for (i in 0 .. Length(paulis) - 1) { + CircuitUtils.MapPauli(qubits[i], PauliZ, paulis[i]); + } + } + apply { + within { + CircuitUtils.SpreadZ(qubits[1], qubits[2 .. Length(qubits) - 1]); + } + apply { + ExpNoId([PauliZ,PauliZ], theta, [qubits[0], qubits[1]]); + } + } + } + } + +} \ No newline at end of file diff --git a/src/Simulation/DecompositionsCore/Type2/Type2-Intrinsic.qs b/src/Simulation/DecompositionsCore/Type2/Type2-Intrinsic.qs new file mode 100644 index 00000000000..7ae0df40b26 --- /dev/null +++ b/src/Simulation/DecompositionsCore/Type2/Type2-Intrinsic.qs @@ -0,0 +1,263 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Intrinsic { + + /// # Summary + /// Applies the Pauli $X$ gate. + /// + /// \begin{align} + /// \sigma_x \mathrel{:=} + /// \begin{bmatrix} + /// 0 & 1 \\\\ + /// 1 & 0 + /// \end{bmatrix}. + /// \end{align} + /// + /// # Input + /// ## qubit + /// Qubit to which the gate should be applied. + operation X(qubit : Qubit) : Unit is Adj + Ctl { + body intrinsic; + adjoint self; + } + + /// # Summary + /// Applies the Ising $XX$ gate. + /// + /// TODO - describe XX gate. + /// + /// # Input + /// ## theta + /// The angle about which the qubits are rotated. + /// ## qubit0 + /// The first qubit input to the gate. + /// ## qubit1 + /// The second qubit input to the gate. + /// NOTE: We need to decide if this should be kept internal and moved to Type2-Internals.qs or consider + /// making it public and also adding it to the Qsharp.Core as a language feature (possibly as a decomposition on top of Exp) + /// If made public, consider a more concise name to match other quantum gate equivalent operations. + internal operation ApplyIsingXX(theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit is Ctl { + body intrinsic; + } + + /// # Summary + /// Applies the Pauli $Y$ gate. + /// + /// \begin{align} + /// \sigma_y \mathrel{:=} + /// \begin{bmatrix} + /// 0 & -i \\\\ + /// i & 0 + /// \end{bmatrix}. + /// \end{align} + /// + /// # Input + /// ## qubit + /// Qubit to which the gate should be applied. + operation Y(qubit : Qubit) : Unit is Adj + Ctl { + body intrinsic; + adjoint self; + } + + /// # Summary + /// Applies the Ising $YY$ gate. + /// + /// TODO - describe YY gate. + /// + /// # Input + /// ## theta + /// The angle about which the qubits are rotated. + /// ## qubit0 + /// The first qubit input to the gate. + /// ## qubit1 + /// The second qubit input to the gate. + /// NOTE: We need to decide if this should be kept internal and moved to Type2-Internals.qs or consider + /// making it public and also adding it to the Qsharp.Core as a language feature (possibly as a decomposition on top of Exp) + /// If made public, consider a more concise name to match other quantum gate equivalent operations. + internal operation ApplyIsingYY(theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit is Ctl { + body intrinsic; + } + + /// # Summary + /// Applies the Pauli $Z$ gate. + /// + /// \begin{align} + /// \sigma_z \mathrel{:=} + /// \begin{bmatrix} + /// 1 & 0 \\\\ + /// 0 & -1 + /// \end{bmatrix}. + /// \end{align} + /// + /// # Input + /// ## qubit + /// Qubit to which the gate should be applied. + operation Z(qubit : Qubit) : Unit is Adj + Ctl { + body intrinsic; + adjoint self; + } + + /// # Summary + /// Applies the Ising $ZZ$ gate. + /// + /// TODO - describe ZZ gate. + /// + /// # Input + /// ## theta + /// The angle about which the qubits are rotated. + /// ## qubit0 + /// The first qubit input to the gate. + /// ## qubit1 + /// The second qubit input to the gate. + /// NOTE: We need to decide if this should be kept internal and moved to Type2-Internals.qs or consider + /// making it public and also adding it to the Qsharp.Core as a language feature (possibly as a decomposition on top of Exp) + /// If made public, consider a more concise name to match other quantum gate equivalent operations. + internal operation ApplyIsingZZ(theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit is Ctl { + body intrinsic; + } + + /// # Summary + /// Applies the Hadamard transformation to a single qubit. + /// + /// \begin{align} + /// H \mathrel{:=} + /// \frac{1}{\sqrt{2}} + /// \begin{bmatrix} + /// 1 & 1 \\\\ + /// 1 & -1 + /// \end{bmatrix}. + /// \end{align} + /// + /// # Input + /// ## qubit + /// Qubit to which the gate should be applied. + operation H(qubit : Qubit) : Unit is Adj + Ctl { + body intrinsic; + adjoint self; + } + + /// # Summary + /// Applies the π/4 phase gate to a single qubit. + /// + /// \begin{align} + /// S \mathrel{:=} + /// \begin{bmatrix} + /// 1 & 0 \\\\ + /// 0 & i + /// \end{bmatrix}. + /// \end{align} + /// + /// # Input + /// ## qubit + /// Qubit to which the gate should be applied. + operation S(qubit : Qubit) : Unit is Adj + Ctl { + body intrinsic; + } + + /// # Summary + /// Applies the π/8 gate to a single qubit. + /// + /// # Description + /// \begin{align} + /// T \mathrel{:=} + /// \begin{bmatrix} + /// 1 & 0 \\\\ + /// 0 & e^{i \pi / 4} + /// \end{bmatrix}. + /// \end{align} + /// + /// # Input + /// ## qubit + /// Qubit to which the gate should be applied. + operation T(qubit : Qubit) : Unit is Adj + Ctl { + body intrinsic; + } + + /// # Summary + /// Applies the SWAP gate to a pair of qubits. + /// + /// # Description + /// \begin{align} + /// \operatorname{SWAP} \mathrel{:=} + /// \begin{bmatrix} + /// 1 & 0 & 0 & 0 \\\\ + /// 0 & 0 & 1 & 0 \\\\ + /// 0 & 1 & 0 & 0 \\\\ + /// 0 & 0 & 0 & 1 + /// \end{bmatrix}, + /// \end{align} + /// + /// where rows and columns are ordered as in the quantum concepts guide. + /// + /// # Input + /// ## qubit1 + /// First qubit to be swapped. + /// ## qubit2 + /// Second qubit to be swapped. + operation SWAP(qubit0 : Qubit, qubit1 : Qubit) : Unit is Adj + Ctl { + body intrinsic; + adjoint self; + } + + /// # Summary + /// Applies a rotation about the given Pauli axis. + /// + /// # Description + /// \begin{align} + /// R_{\mu}(\theta) \mathrel{:=} + /// e^{-i \theta \sigma_{\mu} / 2}, + /// \end{align} + /// where $\mu \in \{I, X, Y, Z\}$. + /// + /// # Input + /// ## pauli + /// Pauli operator ($\mu$) to be exponentiated to form the rotation. + /// ## theta + /// Angle about which the qubit is to be rotated. + /// ## qubit + /// Qubit to which the gate should be applied. + /// + /// # Remarks + /// When called with `pauli = PauliI`, this operation applies + /// a *global phase*. This phase can be significant + /// when used with the `Controlled` functor. + operation R(pauli : Pauli, theta : Double, qubit : Qubit) : Unit is Adj + Ctl { + body intrinsic; + } + + /// # Summary + /// Performs a measurement of a single qubit in the + /// Pauli $Z$ basis. + /// + /// # Description + /// The output result is given by + /// the distribution + /// \begin{align} + /// \Pr(\texttt{Zero} | \ket{\psi}) = + /// \braket{\psi | 0} \braket{0 | \psi}. + /// \end{align} + /// + /// # Input + /// ## qubit + /// Qubit to be measured. + /// + /// # Output + /// `Zero` if the $+1$ eigenvalue is observed, and `One` if + /// the $-1$ eigenvalue is observed. + operation M(qubit : Qubit) : Result { + body intrinsic; + } + + /// # Summary + /// Given a single qubit, measures it and ensures it is in the |0⟩ state + /// such that it can be safely released. + /// + /// # Input + /// ## qubit + /// The qubit whose state is to be reset to $\ket{0}$. + operation Reset(qubit : Qubit) : Unit { + body intrinsic; + } + +} \ No newline at end of file diff --git a/src/Simulation/QsharpBase/Assert.qs b/src/Simulation/QsharpBase/Assert.qs new file mode 100644 index 00000000000..6f36ade4d6b --- /dev/null +++ b/src/Simulation/QsharpBase/Assert.qs @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Intrinsic { + /// # Summary + /// Asserts that measuring the given qubits in the given Pauli basis will + /// always have the given result. + /// + /// # Input + /// ## bases + /// A measurement effect to assert the probability of, expressed as a + /// multi-qubit Pauli operator. + /// ## qubits + /// A register on which to make the assertion. + /// ## result + /// The expected result of `Measure(bases, qubits)`. + /// ## msg + /// A message to be reported if the assertion fails. + /// + /// # Remarks + /// Note that the Adjoint and Controlled versions of this operation will not + /// check the condition. + /// + /// # See Also + /// - AssertProb + operation Assert (bases : Pauli[], qubits : Qubit[], result : Result, msg : String) : Unit + is Adj + Ctl { + body intrinsic; + } + + + /// # Summary + /// Asserts that measuring the given qubits in the given Pauli basis will have the given result + /// with the given probability, within some tolerance. + /// + /// # Input + /// ## bases + /// A measurement effect to assert the probability of, expressed as a + /// multi-qubit Pauli operator. + /// ## qubits + /// A register on which to make the assertion. + /// ## result + /// An expected result of `Measure(bases, qubits)`. + /// ## prob + /// The probability with which the given result is expected. + /// ## msg + /// A message to be reported if the assertion fails. + /// + /// # Example + /// ```qsharp + /// using (register = Qubit()) { + /// H(register); + /// AssertProb([PauliZ], [register], One, 0.5, + /// "Measuring in conjugate basis did not give 50/50 results.", 1e-5); + /// } + /// ``` + /// + /// # Remarks + /// Note that the Adjoint and Controlled versions of this operation will not + /// check the condition. + /// + /// # See Also + /// - Assert + operation AssertProb (bases : Pauli[], qubits : Qubit[], result : Result, prob : Double, msg : String, tol : Double) : Unit + is Adj + Ctl { + body intrinsic; + } +} \ No newline at end of file diff --git a/src/Simulation/QsharpCore/Bitwise/Bitwise.cs b/src/Simulation/QsharpBase/Bitwise/Bitwise.cs similarity index 100% rename from src/Simulation/QsharpCore/Bitwise/Bitwise.cs rename to src/Simulation/QsharpBase/Bitwise/Bitwise.cs diff --git a/src/Simulation/QsharpCore/Bitwise/Bitwise.qs b/src/Simulation/QsharpBase/Bitwise/Bitwise.qs similarity index 100% rename from src/Simulation/QsharpCore/Bitwise/Bitwise.qs rename to src/Simulation/QsharpBase/Bitwise/Bitwise.qs diff --git a/src/Simulation/QsharpCore/Bitwise/Deprecated.qs b/src/Simulation/QsharpBase/Bitwise/Deprecated.qs similarity index 100% rename from src/Simulation/QsharpCore/Bitwise/Deprecated.qs rename to src/Simulation/QsharpBase/Bitwise/Deprecated.qs diff --git a/src/Simulation/QsharpCore/Convert/Convert.cs b/src/Simulation/QsharpBase/Convert/Convert.cs similarity index 100% rename from src/Simulation/QsharpCore/Convert/Convert.cs rename to src/Simulation/QsharpBase/Convert/Convert.cs diff --git a/src/Simulation/QsharpCore/Convert/Convert.qs b/src/Simulation/QsharpBase/Convert/Convert.qs similarity index 100% rename from src/Simulation/QsharpCore/Convert/Convert.qs rename to src/Simulation/QsharpBase/Convert/Convert.qs diff --git a/src/Simulation/QsharpCore/Convert/Deprecated.qs b/src/Simulation/QsharpBase/Convert/Deprecated.qs similarity index 100% rename from src/Simulation/QsharpCore/Convert/Deprecated.qs rename to src/Simulation/QsharpBase/Convert/Deprecated.qs diff --git a/src/Simulation/QsharpCore/Core.cs b/src/Simulation/QsharpBase/Core.cs similarity index 100% rename from src/Simulation/QsharpCore/Core.cs rename to src/Simulation/QsharpBase/Core.cs diff --git a/src/Simulation/QsharpCore/Core.qs b/src/Simulation/QsharpBase/Core.qs similarity index 100% rename from src/Simulation/QsharpCore/Core.qs rename to src/Simulation/QsharpBase/Core.qs diff --git a/src/Simulation/QsharpCore/Diagnostics/AssertAllZero.qs b/src/Simulation/QsharpBase/Diagnostics/AssertAllZero.qs similarity index 100% rename from src/Simulation/QsharpCore/Diagnostics/AssertAllZero.qs rename to src/Simulation/QsharpBase/Diagnostics/AssertAllZero.qs diff --git a/src/Simulation/QsharpCore/Diagnostics/AssertQubit.qs b/src/Simulation/QsharpBase/Diagnostics/AssertQubit.qs similarity index 100% rename from src/Simulation/QsharpCore/Diagnostics/AssertQubit.qs rename to src/Simulation/QsharpBase/Diagnostics/AssertQubit.qs diff --git a/src/Simulation/QsharpCore/Diagnostics/Dump.qs b/src/Simulation/QsharpBase/Diagnostics/Dump.qs similarity index 100% rename from src/Simulation/QsharpCore/Diagnostics/Dump.qs rename to src/Simulation/QsharpBase/Diagnostics/Dump.qs diff --git a/src/Simulation/QsharpCore/Diagnostics/UnitTests.qs b/src/Simulation/QsharpBase/Diagnostics/UnitTests.qs similarity index 100% rename from src/Simulation/QsharpCore/Diagnostics/UnitTests.qs rename to src/Simulation/QsharpBase/Diagnostics/UnitTests.qs diff --git a/src/Simulation/QsharpCore/Environment.qs b/src/Simulation/QsharpBase/Environment.qs similarity index 100% rename from src/Simulation/QsharpCore/Environment.qs rename to src/Simulation/QsharpBase/Environment.qs diff --git a/src/Simulation/QsharpCore/Math/Constants.qs b/src/Simulation/QsharpBase/Math/Constants.qs similarity index 100% rename from src/Simulation/QsharpCore/Math/Constants.qs rename to src/Simulation/QsharpBase/Math/Constants.qs diff --git a/src/Simulation/QsharpCore/Math/Deprecated.qs b/src/Simulation/QsharpBase/Math/Deprecated.qs similarity index 100% rename from src/Simulation/QsharpCore/Math/Deprecated.qs rename to src/Simulation/QsharpBase/Math/Deprecated.qs diff --git a/src/Simulation/QsharpCore/Math/Math.cs b/src/Simulation/QsharpBase/Math/Math.cs similarity index 100% rename from src/Simulation/QsharpCore/Math/Math.cs rename to src/Simulation/QsharpBase/Math/Math.cs diff --git a/src/Simulation/QsharpCore/Math/Math.qs b/src/Simulation/QsharpBase/Math/Math.qs similarity index 100% rename from src/Simulation/QsharpCore/Math/Math.qs rename to src/Simulation/QsharpBase/Math/Math.qs diff --git a/src/Simulation/QsharpCore/Math/Trig.qs b/src/Simulation/QsharpBase/Math/Trig.qs similarity index 100% rename from src/Simulation/QsharpCore/Math/Trig.qs rename to src/Simulation/QsharpBase/Math/Trig.qs diff --git a/src/Simulation/QsharpCore/Math/Types.qs b/src/Simulation/QsharpBase/Math/Types.qs similarity index 100% rename from src/Simulation/QsharpCore/Math/Types.qs rename to src/Simulation/QsharpBase/Math/Types.qs diff --git a/src/Simulation/QsharpBase/Microsoft.Quantum.QSharp.Base.csproj b/src/Simulation/QsharpBase/Microsoft.Quantum.QSharp.Base.csproj new file mode 100644 index 00000000000..45d692f4b01 --- /dev/null +++ b/src/Simulation/QsharpBase/Microsoft.Quantum.QSharp.Base.csproj @@ -0,0 +1,40 @@ + + + + + + + netstandard2.1 + true + false + false + + + + Microsoft + Base support for the Q# programming language. + See: https://docs.microsoft.com/en-us/quantum/relnotes/ + MIT + https://github.com/microsoft/qsharp-runtime + https://secure.gravatar.com/avatar/bd1f02955b2853ba0a3b1cdc2434e8ec.png + Quantum Q# Qsharp + true + + + + + + + + + + + + + + + + + + + diff --git a/src/Simulation/QsharpCore/Statements/Allocate.cs b/src/Simulation/QsharpBase/Statements/Allocate.cs similarity index 100% rename from src/Simulation/QsharpCore/Statements/Allocate.cs rename to src/Simulation/QsharpBase/Statements/Allocate.cs diff --git a/src/Simulation/QsharpCore/Statements/Borrow.cs b/src/Simulation/QsharpBase/Statements/Borrow.cs similarity index 100% rename from src/Simulation/QsharpCore/Statements/Borrow.cs rename to src/Simulation/QsharpBase/Statements/Borrow.cs diff --git a/src/Simulation/QsharpCore/Statements/Release.cs b/src/Simulation/QsharpBase/Statements/Release.cs similarity index 100% rename from src/Simulation/QsharpCore/Statements/Release.cs rename to src/Simulation/QsharpBase/Statements/Release.cs diff --git a/src/Simulation/QsharpCore/Statements/Return.cs b/src/Simulation/QsharpBase/Statements/Return.cs similarity index 100% rename from src/Simulation/QsharpCore/Statements/Return.cs rename to src/Simulation/QsharpBase/Statements/Return.cs diff --git a/src/Simulation/QsharpBase/Utils.qs b/src/Simulation/QsharpBase/Utils.qs new file mode 100644 index 00000000000..3c1a2c75612 --- /dev/null +++ b/src/Simulation/QsharpBase/Utils.qs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/// # Summary +/// This namespace includes Q# intrinsic utilies. +/// +/// # Remarks +/// This namespace is opened automatically by the Q# compiler, so all +/// elements of this namespace are always available. +namespace Microsoft.Quantum.Intrinsic { + /// # Summary + /// The random operation takes an array of doubles as input, and returns + /// a randomly-selected index into the array as an `Int`. + /// The probability of selecting a specific index is proportional to the value + /// of the array element at that index. + /// Array elements that are equal to zero are ignored and their indices are never + /// returned. If any array element is less than zero, + /// or if no array element is greater than zero, then the operation fails. + /// + /// # Input + /// ## probs + /// An array of floating-point numbers proportional to the probability of + /// selecting each index. + /// + /// # Output + /// An integer $i$ with probability $\Pr(i) = p_i / \sum_i p_i$, where $p_i$ + /// is the $i$th element of `probs`. + operation Random(probs : Double[]) : Int { + body intrinsic; + } + + /// # Summary + /// Logs a message. + /// + /// # Input + /// ## msg + /// The message to be reported. + /// + /// # Remarks + /// The specific behavior of this function is simulator-dependent, + /// but in most cases the given message will be written to the console. + function Message(msg : String) : Unit { + body intrinsic; + } + +} \ No newline at end of file diff --git a/src/Simulation/QsharpCore/Intrinsic.qs b/src/Simulation/QsharpCore/Intrinsic.qs index 28f0353dd86..00b626aff53 100644 --- a/src/Simulation/QsharpCore/Intrinsic.qs +++ b/src/Simulation/QsharpCore/Intrinsic.qs @@ -4,107 +4,6 @@ namespace Microsoft.Quantum.Intrinsic { open Microsoft.Quantum.Math; open Microsoft.Quantum.Convert; - - /// # Summary - /// The random operation takes an array of doubles as input, and returns - /// a randomly-selected index into the array as an `Int`. - /// The probability of selecting a specific index is proportional to the value - /// of the array element at that index. - /// Array elements that are equal to zero are ignored and their indices are never - /// returned. If any array element is less than zero, - /// or if no array element is greater than zero, then the operation fails. - /// - /// # Input - /// ## probs - /// An array of floating-point numbers proportional to the probability of - /// selecting each index. - /// - /// # Output - /// An integer $i$ with probability $\Pr(i) = p_i / \sum_i p_i$, where $p_i$ - /// is the $i$th element of `probs`. - operation Random (probs : Double[]) : Int { - body intrinsic; - } - - - /// # Summary - /// Asserts that measuring the given qubits in the given Pauli basis will - /// always have the given result. - /// - /// # Input - /// ## bases - /// A measurement effect to assert the probability of, expressed as a - /// multi-qubit Pauli operator. - /// ## qubits - /// A register on which to make the assertion. - /// ## result - /// The expected result of `Measure(bases, qubits)`. - /// ## msg - /// A message to be reported if the assertion fails. - /// - /// # Remarks - /// Note that the Adjoint and Controlled versions of this operation will not - /// check the condition. - /// - /// # See Also - /// - AssertProb - operation Assert (bases : Pauli[], qubits : Qubit[], result : Result, msg : String) : Unit - is Adj + Ctl { - body intrinsic; - } - - - /// # Summary - /// Asserts that measuring the given qubits in the given Pauli basis will have the given result - /// with the given probability, within some tolerance. - /// - /// # Input - /// ## bases - /// A measurement effect to assert the probability of, expressed as a - /// multi-qubit Pauli operator. - /// ## qubits - /// A register on which to make the assertion. - /// ## result - /// An expected result of `Measure(bases, qubits)`. - /// ## prob - /// The probability with which the given result is expected. - /// ## msg - /// A message to be reported if the assertion fails. - /// - /// # Example - /// ```qsharp - /// using (register = Qubit()) { - /// H(register); - /// AssertProb([PauliZ], [register], One, 0.5, - /// "Measuring in conjugate basis did not give 50/50 results.", 1e-5); - /// } - /// ``` - /// - /// # Remarks - /// Note that the Adjoint and Controlled versions of this operation will not - /// check the condition. - /// - /// # See Also - /// - Assert - operation AssertProb (bases : Pauli[], qubits : Qubit[], result : Result, prob : Double, msg : String, tol : Double) : Unit - is Adj + Ctl { - body intrinsic; - } - - - /// # Summary - /// Logs a message. - /// - /// # Input - /// ## msg - /// The message to be reported. - /// - /// # Remarks - /// The specific behavior of this function is simulator-dependent, - /// but in most cases the given message will be written to the console. - function Message (msg : String) : Unit { - body intrinsic; - } //------------------------------------------------- @@ -126,7 +25,8 @@ namespace Microsoft.Quantum.Intrinsic { /// # Summary /// Applies the Pauli $X$ gate. - /// + /// + /// # Description /// \begin{align} /// \sigma_x \mathrel{:=} /// \begin{bmatrix} @@ -147,6 +47,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Summary /// Applies the Pauli $Y$ gate. /// + /// # Description /// \begin{align} /// \sigma_y \mathrel{:=} /// \begin{bmatrix} @@ -168,6 +69,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Summary /// Applies the Pauli $Z$ gate. /// + /// # Description /// \begin{align} /// \sigma_z \mathrel{:=} /// \begin{bmatrix} @@ -189,6 +91,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Summary /// Applies the Hadamard transformation to a single qubit. /// + /// # Description /// \begin{align} /// H \mathrel{:=} /// \frac{1}{\sqrt{2}} @@ -211,6 +114,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Summary /// Applies the π/4 phase gate to a single qubit. /// + /// # Description /// \begin{align} /// S \mathrel{:=} /// \begin{bmatrix} @@ -231,6 +135,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Summary /// Applies the π/8 gate to a single qubit. /// + /// # Description /// \begin{align} /// T \mathrel{:=} /// \begin{bmatrix} @@ -251,6 +156,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Summary /// Applies the controlled-NOT (CNOT) gate to a pair of qubits. /// + /// # Description /// \begin{align} /// \operatorname{CNOT} \mathrel{:=} /// \begin{bmatrix} @@ -316,6 +222,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Summary /// Applies the SWAP gate to a pair of qubits. /// + /// # Description /// \begin{align} /// \operatorname{SWAP} \mathrel{:=} /// \begin{bmatrix} @@ -360,6 +267,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Summary /// Applies a rotation about the given Pauli axis. /// + /// # Description /// \begin{align} /// R_{\mu}(\theta) \mathrel{:=} /// e^{-i \theta \sigma_{\mu} / 2}, @@ -388,6 +296,7 @@ namespace Microsoft.Quantum.Intrinsic { /// Applies a rotation about the given Pauli axis by an angle specified /// as a dyadic fraction. /// + /// # Description /// \begin{align} /// R_{\mu}(n, k) \mathrel{:=} /// e^{i \pi n \sigma_{\mu} / 2^k}, @@ -427,6 +336,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Summary /// Applies a rotation about the $x$-axis by a given angle. /// + /// # Description /// \begin{align} /// R_x(\theta) \mathrel{:=} /// e^{-i \theta \sigma_x / 2} = @@ -464,6 +374,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Summary /// Applies a rotation about the $y$-axis by a given angle. /// + /// # Description /// \begin{align} /// R_y(\theta) \mathrel{:=} /// e^{-i \theta \sigma_y / 2} = @@ -501,6 +412,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Summary /// Applies a rotation about the $z$-axis by a given angle. /// + /// # Description /// \begin{align} /// R_z(\theta) \mathrel{:=} /// e^{-i \theta \sigma_z / 2} = @@ -538,6 +450,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Summary /// Applies a rotation about the $\ket{1}$ state by a given angle. /// + /// # Description /// \begin{align} /// R_1(\theta) \mathrel{:=} /// \operatorname{diag}(1, e^{i\theta}). @@ -567,6 +480,7 @@ namespace Microsoft.Quantum.Intrinsic { /// Applies a rotation about the $\ket{1}$ state by an angle specified /// as a dyadic fraction. /// + /// # Description /// \begin{align} /// R_1(n, k) \mathrel{:=} /// \operatorname{diag}(1, e^{i \pi k / 2^n}). @@ -604,6 +518,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Summary /// Applies the exponential of a multi-qubit Pauli operator. /// + /// # Description /// \begin{align} /// e^{i \theta [P_0 \otimes P_1 \cdots P_{N-1}]}, /// \end{align} @@ -629,6 +544,7 @@ namespace Microsoft.Quantum.Intrinsic { /// Applies the exponential of a multi-qubit Pauli operator /// with an argument given by a dyadic fraction. /// + /// # Description /// \begin{align} /// e^{i \pi k [P_0 \otimes P_1 \cdots P_{N-1}] / 2^n}, /// \end{align} @@ -662,6 +578,7 @@ namespace Microsoft.Quantum.Intrinsic { /// Performs a joint measurement of one or more qubits in the /// specified Pauli bases. /// + /// # Description /// The output result is given by the distribution: /// \begin{align} /// \Pr(\texttt{Zero} | \ket{\psi}) = @@ -701,6 +618,7 @@ namespace Microsoft.Quantum.Intrinsic { /// Performs a measurement of a single qubit in the /// Pauli $Z$ basis. /// + /// # Description /// The output result is given by /// the distribution /// \begin{align} diff --git a/src/Simulation/QsharpCore/Microsoft.Quantum.QSharp.Core.csproj b/src/Simulation/QsharpCore/Microsoft.Quantum.QSharp.Core.csproj index 6fa6d8bc6a1..a07fa279f6b 100644 --- a/src/Simulation/QsharpCore/Microsoft.Quantum.QSharp.Core.csproj +++ b/src/Simulation/QsharpCore/Microsoft.Quantum.QSharp.Core.csproj @@ -27,6 +27,7 @@ + From 5b013550ff1822cb776466422c64dae5deb0454d Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Thu, 28 May 2020 12:37:28 -0700 Subject: [PATCH 2/6] Various fixes to get code/tests to build/pass --- Simulation.sln | 8 +-- build/pack.ps1 | 2 +- ...t.Quantum.Decompositions.Core.Type2.csproj | 2 +- .../Type2/Type2-Internals.qs | 65 +++++++++++++++++-- .../Type2/Type2-Intrinsic.qs | 57 ---------------- .../EntryPointDriver.Tests/Tests.fs | 1 + .../Microsoft.Quantum.QSharp.Base.csproj | 2 +- .../Simulators.Tests/StackTraceTests.cs | 9 ++- 8 files changed, 72 insertions(+), 74 deletions(-) diff --git a/Simulation.sln b/Simulation.sln index 2e3b766bdd7..608c332da07 100644 --- a/Simulation.sln +++ b/Simulation.sln @@ -57,8 +57,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Library2", "src\Simulation\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "src\Simulation\Simulators.Tests\TestProjects\UnitTests\UnitTests.csproj", "{46278108-D247-4EFC-AC34-23D4A676F62F}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simulation", "Simulation", "{BDFBF140-34F1-43C2-87C9-25BF6BE20C45}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Quantum.QSharp.Base", "src\Simulation\QsharpBase\Microsoft.Quantum.QSharp.Base.csproj", "{E9E387C0-2881-4F0C-8433-064BB18DB742}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DecompositionsCore", "DecompositionsCore", "{4DF4699D-5A50-4B3F-8232-5B19CAE23950}" @@ -456,9 +454,9 @@ Global {7256B986-6705-42FC-9F57-485D72D9DE51} = {09C842CB-930C-4C7D-AD5F-E30DE4A55820} {A85277B3-4E07-4E15-8F0C-07CC855A3BCB} = {09C842CB-930C-4C7D-AD5F-E30DE4A55820} {46278108-D247-4EFC-AC34-23D4A676F62F} = {09C842CB-930C-4C7D-AD5F-E30DE4A55820} - {BDFBF140-34F1-43C2-87C9-25BF6BE20C45} = {99E234BC-997E-4E63-9F5C-3C3977543404} - {E9E387C0-2881-4F0C-8433-064BB18DB742} = {BDFBF140-34F1-43C2-87C9-25BF6BE20C45} - {4DF4699D-5A50-4B3F-8232-5B19CAE23950} = {BDFBF140-34F1-43C2-87C9-25BF6BE20C45} + {34D419E9-CCF1-4E48-9FA4-3AD4B86BEEB4} = {99E234BC-997E-4E63-9F5C-3C3977543404} + {E9E387C0-2881-4F0C-8433-064BB18DB742} = {34D419E9-CCF1-4E48-9FA4-3AD4B86BEEB4} + {4DF4699D-5A50-4B3F-8232-5B19CAE23950} = {34D419E9-CCF1-4E48-9FA4-3AD4B86BEEB4} {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5} = {4DF4699D-5A50-4B3F-8232-5B19CAE23950} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/build/pack.ps1 b/build/pack.ps1 index 68c78ca15a5..617b2a61d12 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -64,7 +64,7 @@ Pack-One '../src/Simulation/CsharpGeneration/Microsoft.Quantum.CsharpGeneration. Pack-Dotnet '../src/Simulation/Core/Microsoft.Quantum.Runtime.Core.csproj' Pack-Dotnet '../src/Simulation/QsharpBase/Microsoft.Quantum.QSharp.Base.csproj' Pack-Dotnet '../src/Simulation/QsharpCore/Microsoft.Quantum.QSharp.Core.csproj' -Pack-Dotnet '../src/Simulation/Targeting/IonQ/Microsoft.Quantum.IonQ.Core.csproj' +Pack-Dotnet '../src/Simulation/DecompositionsCore/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj' Pack-One '../src/Simulation/Simulators/Microsoft.Quantum.Simulators.nuspec' Pack-One '../src/Quantum.Development.Kit/Microsoft.Quantum.Development.Kit.nuspec' Pack-One '../src/Xunit/Microsoft.Quantum.Xunit.csproj' diff --git a/src/Simulation/DecompositionsCore/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj b/src/Simulation/DecompositionsCore/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj index 5d1acbe14fd..4f1a4cece7c 100644 --- a/src/Simulation/DecompositionsCore/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj +++ b/src/Simulation/DecompositionsCore/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj @@ -1,4 +1,4 @@ - + diff --git a/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs b/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs index 02cff85d9da..b6052707794 100644 --- a/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs +++ b/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs @@ -12,17 +12,17 @@ namespace Microsoft.Quantum.Intrinsic { R(paulis[0], -2.0 * theta, qubits[0]); } elif ( Length(paulis) == 2 ) { - if (paulis[0] != paulis[1] and paulis[0] != PauliI) { fail $"Type 2 Decompositions support only rotation around XX, YY, ZZ given {paulis}"; } + if (paulis[0] != paulis[1] or paulis[0] == PauliI) { fail $"Type 2 Decompositions support only rotation around XX, YY, ZZ given {paulis}"; } within { CircuitUtils.MapPauli(qubits[1],paulis[0],paulis[1]); } apply { if (paulis[1] == PauliX) { - ApplyIsingXX(theta, qubits[0], qubits[1]); + ApplyIsingXX(theta / 2.0, qubits[0], qubits[1]); } elif (paulis[1] == PauliY) { - ApplyIsingYY(theta, qubits[0], qubits[1]); + ApplyIsingYY(theta / 2.0, qubits[0], qubits[1]); } elif (paulis[1] == PauliZ) { - ApplyIsingZZ(theta, qubits[0], qubits[1]); + ApplyIsingZZ(theta / 2.0, qubits[0], qubits[1]); } else { fail "Type2 decompositions do not support PauliI"; } @@ -79,11 +79,11 @@ namespace Microsoft.Quantum.Intrinsic { } apply { if (paulis[0] == PauliX) { - ApplyIsingXX(theta, qubits[0], qubits[1]); + ApplyIsingXX(theta / 2.0, qubits[0], qubits[1]); } elif (paulis[0] == PauliY) { - ApplyIsingYY(theta, qubits[0], qubits[1]); + ApplyIsingYY(theta / 2.0, qubits[0], qubits[1]); } elif (paulis[0] == PauliZ) { - ApplyIsingZZ(theta, qubits[0], qubits[1]); + ApplyIsingZZ(theta / 2.0, qubits[0], qubits[1]); } else { fail "Type2 decompositions do not support PauliI"; } @@ -106,4 +106,55 @@ namespace Microsoft.Quantum.Intrinsic { } } + /// # Summary + /// Applies the Ising $XX$ gate. + /// + /// TODO - describe XX gate. + /// + /// # Input + /// ## theta + /// The angle about which the qubits are rotated. + /// ## qubit0 + /// The first qubit input to the gate. + /// ## qubit1 + /// The second qubit input to the gate. + /// NOTE: If made public, consider a more concise name to match other quantum gate equivalent operations. + internal operation ApplyIsingXX(theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit is Ctl { + body intrinsic; + } + + /// # Summary + /// Applies the Ising $YY$ gate. + /// + /// TODO - describe YY gate. + /// + /// # Input + /// ## theta + /// The angle about which the qubits are rotated. + /// ## qubit0 + /// The first qubit input to the gate. + /// ## qubit1 + /// The second qubit input to the gate. + /// NOTE: If made public, consider a more concise name to match other quantum gate equivalent operations. + internal operation ApplyIsingYY(theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit is Ctl { + body intrinsic; + } + + /// # Summary + /// Applies the Ising $ZZ$ gate. + /// + /// TODO - describe ZZ gate. + /// + /// # Input + /// ## theta + /// The angle about which the qubits are rotated. + /// ## qubit0 + /// The first qubit input to the gate. + /// ## qubit1 + /// The second qubit input to the gate. + /// NOTE: If made public, consider a more concise name to match other quantum gate equivalent operations. + internal operation ApplyIsingZZ(theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit is Ctl { + body intrinsic; + } + } \ No newline at end of file diff --git a/src/Simulation/DecompositionsCore/Type2/Type2-Intrinsic.qs b/src/Simulation/DecompositionsCore/Type2/Type2-Intrinsic.qs index 7ae0df40b26..50debf158f7 100644 --- a/src/Simulation/DecompositionsCore/Type2/Type2-Intrinsic.qs +++ b/src/Simulation/DecompositionsCore/Type2/Type2-Intrinsic.qs @@ -22,25 +22,6 @@ namespace Microsoft.Quantum.Intrinsic { adjoint self; } - /// # Summary - /// Applies the Ising $XX$ gate. - /// - /// TODO - describe XX gate. - /// - /// # Input - /// ## theta - /// The angle about which the qubits are rotated. - /// ## qubit0 - /// The first qubit input to the gate. - /// ## qubit1 - /// The second qubit input to the gate. - /// NOTE: We need to decide if this should be kept internal and moved to Type2-Internals.qs or consider - /// making it public and also adding it to the Qsharp.Core as a language feature (possibly as a decomposition on top of Exp) - /// If made public, consider a more concise name to match other quantum gate equivalent operations. - internal operation ApplyIsingXX(theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit is Ctl { - body intrinsic; - } - /// # Summary /// Applies the Pauli $Y$ gate. /// @@ -60,25 +41,6 @@ namespace Microsoft.Quantum.Intrinsic { adjoint self; } - /// # Summary - /// Applies the Ising $YY$ gate. - /// - /// TODO - describe YY gate. - /// - /// # Input - /// ## theta - /// The angle about which the qubits are rotated. - /// ## qubit0 - /// The first qubit input to the gate. - /// ## qubit1 - /// The second qubit input to the gate. - /// NOTE: We need to decide if this should be kept internal and moved to Type2-Internals.qs or consider - /// making it public and also adding it to the Qsharp.Core as a language feature (possibly as a decomposition on top of Exp) - /// If made public, consider a more concise name to match other quantum gate equivalent operations. - internal operation ApplyIsingYY(theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit is Ctl { - body intrinsic; - } - /// # Summary /// Applies the Pauli $Z$ gate. /// @@ -98,25 +60,6 @@ namespace Microsoft.Quantum.Intrinsic { adjoint self; } - /// # Summary - /// Applies the Ising $ZZ$ gate. - /// - /// TODO - describe ZZ gate. - /// - /// # Input - /// ## theta - /// The angle about which the qubits are rotated. - /// ## qubit0 - /// The first qubit input to the gate. - /// ## qubit1 - /// The second qubit input to the gate. - /// NOTE: We need to decide if this should be kept internal and moved to Type2-Internals.qs or consider - /// making it public and also adding it to the Qsharp.Core as a language feature (possibly as a decomposition on top of Exp) - /// If made public, consider a more concise name to match other quantum gate equivalent operations. - internal operation ApplyIsingZZ(theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit is Ctl { - body intrinsic; - } - /// # Summary /// Applies the Hadamard transformation to a single qubit. /// diff --git a/src/Simulation/EntryPointDriver.Tests/Tests.fs b/src/Simulation/EntryPointDriver.Tests/Tests.fs index 808b4b6b607..69d00fc41ca 100644 --- a/src/Simulation/EntryPointDriver.Tests/Tests.fs +++ b/src/Simulation/EntryPointDriver.Tests/Tests.fs @@ -96,6 +96,7 @@ let private compileCsharp (sources : string seq) = "System.Runtime.Extensions" "System.Runtime.Numerics" "Microsoft.Quantum.CsharpGeneration.EntryPointDriver" + "Microsoft.Quantum.QSharp.Base" "Microsoft.Quantum.QSharp.Core" "Microsoft.Quantum.QsDataStructures" "Microsoft.Quantum.Runtime.Core" diff --git a/src/Simulation/QsharpBase/Microsoft.Quantum.QSharp.Base.csproj b/src/Simulation/QsharpBase/Microsoft.Quantum.QSharp.Base.csproj index 45d692f4b01..c71dc544274 100644 --- a/src/Simulation/QsharpBase/Microsoft.Quantum.QSharp.Base.csproj +++ b/src/Simulation/QsharpBase/Microsoft.Quantum.QSharp.Base.csproj @@ -1,4 +1,4 @@ - + diff --git a/src/Simulation/Simulators.Tests/StackTraceTests.cs b/src/Simulation/Simulators.Tests/StackTraceTests.cs index 48c142d7e2b..e561ccd55b3 100644 --- a/src/Simulation/Simulators.Tests/StackTraceTests.cs +++ b/src/Simulation/Simulators.Tests/StackTraceTests.cs @@ -97,8 +97,13 @@ public void AlwaysFail4Test() for (int i = 0; i < stackFrames.Length; ++i) { - Assert.StartsWith(@"https://github.com/", stackFrames[i].GetURLFromPDB()); - Assert.EndsWith($"#L{stackFrames[i].FailedLineNumber}", stackFrames[i].GetURLFromPDB()); + Console.WriteLine(stackFrames[i].GetURLFromPDB()); + Assert.StartsWith(@"https://", stackFrames[i].GetURLFromPDB()); + // Only check for correct line numbers if they are included in the trace. + if (stackFrames[i].GetURLFromPDB().Contains("#L")) + { + Assert.EndsWith($"#L{stackFrames[i].FailedLineNumber}", stackFrames[i].GetURLFromPDB()); + } } StringBuilder builder = new StringBuilder(); From 35d9157f258b4b1720b803da78c987e68edcf1da Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 2 Jun 2020 17:08:07 -0700 Subject: [PATCH 3/6] Adding Type2 decomposition tests --- .../Common/DecompositionsTestCommon.csproj | 25 ++ .../Common/IntrinsicExecute.qs | 153 +++++++++ .../Common/IntrinsicTesting-Settings.qs | 49 +++ .../Common/IntrinsicTesting-Utils.qs | 264 ++++++++++++++++ .../Common/IntrinsicTesting.qs | 172 +++++++++++ .../Type2/Type2-Decompositions-Tests.qs | 90 ++++++ .../Type2/Type2DecompositionsTests.csproj | 34 ++ .../Type2/Type2Simulator.cs | 291 ++++++++++++++++++ .../Common/CircuitUtilities.qs | 13 +- .../Common/Decompositions.qs | 121 -------- .../DecompositionsCore/Common/Utilities.qs | 139 --------- ...t.Quantum.Decompositions.Core.Type2.csproj | 8 +- .../Type2/Type2-Decompositions.qs | 86 +++++- .../Type2/Type2-Internals.qs | 84 ++--- .../Type2/Type2-Intrinsic.qs | 65 ++++ ...ft.Quantum.Decompositions.Utilities.csproj | 22 ++ .../DecompositionsCore/Utilities/Utilities.qs | 71 +++++ .../Microsoft.Quantum.QSharp.Base.csproj | 2 +- 18 files changed, 1344 insertions(+), 345 deletions(-) create mode 100644 src/Simulation/DecompositionsCore.Test/Common/DecompositionsTestCommon.csproj create mode 100644 src/Simulation/DecompositionsCore.Test/Common/IntrinsicExecute.qs create mode 100644 src/Simulation/DecompositionsCore.Test/Common/IntrinsicTesting-Settings.qs create mode 100644 src/Simulation/DecompositionsCore.Test/Common/IntrinsicTesting-Utils.qs create mode 100644 src/Simulation/DecompositionsCore.Test/Common/IntrinsicTesting.qs create mode 100644 src/Simulation/DecompositionsCore.Test/Type2/Type2-Decompositions-Tests.qs create mode 100644 src/Simulation/DecompositionsCore.Test/Type2/Type2DecompositionsTests.csproj create mode 100644 src/Simulation/DecompositionsCore.Test/Type2/Type2Simulator.cs delete mode 100644 src/Simulation/DecompositionsCore/Common/Decompositions.qs delete mode 100644 src/Simulation/DecompositionsCore/Common/Utilities.qs create mode 100644 src/Simulation/DecompositionsCore/Utilities/Microsoft.Quantum.Decompositions.Utilities.csproj create mode 100644 src/Simulation/DecompositionsCore/Utilities/Utilities.qs diff --git a/src/Simulation/DecompositionsCore.Test/Common/DecompositionsTestCommon.csproj b/src/Simulation/DecompositionsCore.Test/Common/DecompositionsTestCommon.csproj new file mode 100644 index 00000000000..7d4b8883565 --- /dev/null +++ b/src/Simulation/DecompositionsCore.Test/Common/DecompositionsTestCommon.csproj @@ -0,0 +1,25 @@ + + + + + + netcoreapp3.1 + false + false + false + false + + + + + + + + + + + + + + + diff --git a/src/Simulation/DecompositionsCore.Test/Common/IntrinsicExecute.qs b/src/Simulation/DecompositionsCore.Test/Common/IntrinsicExecute.qs new file mode 100644 index 00000000000..914e68d27f2 --- /dev/null +++ b/src/Simulation/DecompositionsCore.Test/Common/IntrinsicExecute.qs @@ -0,0 +1,153 @@ +// Methods in this file implement executing multiple variants of a given operator +// to exercise its decompositions in all of those cases. For testing convenience +// similar operators are grouped into a few batches. +namespace IntrinsicTesting { + + open Microsoft.Quantum.Decompositions.Utilities as Utils; + open Microsoft.Quantum.Diagnostics; + + //========================================================================= + // Groups of operators that can be tested together + //========================================================================= + operation ExecuteBasicInstrinsics(intrinsics : UnitaryQSharpIntrinsics) : Unit { + let eq = ExecuteWithAdjointAndControlledQ; + eq(intrinsics::X); + eq(intrinsics::Y); + eq(intrinsics::Z); + eq(intrinsics::H); + eq(intrinsics::S); + eq(intrinsics::T); + } + + operation ExecuteSWAPandCNOT(intrinsics : UnitaryQSharpIntrinsics) : Unit { + let eq = ExecuteWithAdjointAndControlledQQ; + eq(intrinsics::CNOT); + eq(intrinsics::SWAP); + } + + operation ExecuteRotations(intrinsics : UnitaryQSharpIntrinsics) : Unit { + let eq = ExecuteWithAdjointAndControlledDQ; + eq(intrinsics::Rx); + eq(intrinsics::Ry); + eq(intrinsics::Rz); + eq(intrinsics::R1); + } + + operation ExecuteCCNOT(intrinsics : UnitaryQSharpIntrinsics) : Unit { + ExecuteWithAdjointAndControlledQQQ(intrinsics::CCNOT); + } + + operation ExecuteR(intrinsics : UnitaryQSharpIntrinsics) : Unit { + ExecuteWithAdjointAndControlledPDQ(intrinsics::R); + } + + operation ExecuteR1Frac(intrinsics : UnitaryQSharpIntrinsics) : Unit { + ExecuteWithAdjointAndControlledIIQ(intrinsics::R1Frac); + } + + operation ExecuteRFrac(intrinsics : UnitaryQSharpIntrinsics) : Unit { + ExecuteWithAdjointAndControlledPIIQ(intrinsics::RFrac); + } + + operation ExecuteExp(intrinsics : UnitaryQSharpIntrinsics) : Unit { + ExecuteWithAdjointAndControlledPADQA(intrinsics::Exp); + } + + operation ExecuteExpFrac(intrinsics : UnitaryQSharpIntrinsics) : Unit { + ExecuteWithAdjointAndControlledPAIIQA(intrinsics::ExpFrac); + } + + //========================================================================= + // Helpers for executing adjoint and controlled variants of an operator. + //========================================================================= + operation ExecuteOnQubitArray(nQubits : Int, op : (Qubit[] => Unit is Adj)) : Unit { + using( qubits = Qubit[nQubits] ) { + op(qubits); + Adjoint op(qubits); + } + } + + operation ExecuteWithAdjointAndControlled<'TupleT>(op : ('TupleT => Unit is Adj + Ctl), tupleMapper : (Qubit[] -> (Qubit[], 'TupleT) ), tupleSize : Int ) : Unit { + let op_composed = ApplyComposedA(Controlled op, tupleMapper, _); + Microsoft.Quantum.Intrinsic.Message($"Executing {op}"); + + for (numQubits in tupleSize .. MaxControls() + tupleSize) { + Microsoft.Quantum.Intrinsic.Message($"Total number of qubits: {numQubits}"); + for (repetition in 1 .. NumberOfTestRepetitions()) { + ExecuteOnQubitArray(numQubits, op_composed); + } + } + } + + operation ExecuteWithAdjointAndControlledQ(op : (Qubit => Unit is Adj + Ctl)) : Unit { + ExecuteWithAdjointAndControlled(op, ArrayAsTupleAO, 1); + } + + operation ExecuteWithAdjointAndControlledAQ(numQubits : Int, op : (Qubit[] => Unit is Adj + Ctl)) : Unit { + ExecuteWithAdjointAndControlled(op, ArrayAsTupleAA(numQubits,_), numQubits); + } + + operation _ExecuteWithAdjointAndControlledAPQ(paulis : Pauli[], op : ((Pauli[],Qubit[]) => Unit is Adj + Ctl)) : Unit { + Microsoft.Quantum.Intrinsic.Message($"Checking for Pauli: {paulis}."); + ExecuteWithAdjointAndControlledAQ(Length(paulis), op(paulis,_)); + } + + operation ExecuteWithAdjointAndControlledAPQ(op : ((Pauli[],Qubit[]) => Unit is Adj + Ctl)) : Unit { + for (numQubits in 1 .. MaxTargets()) { + Microsoft.Quantum.Intrinsic.Message($"Checking with {numQubits} qubits."); + IterateThroughCartesianPowerP(numQubits, PaulisToTest(), _ExecuteWithAdjointAndControlledAPQ(_, op)); + } + } + + operation ExecuteWithAdjointAndControlledPADQA(op : ((Pauli[],Double,Qubit[]) => Unit is Adj + Ctl)) : Unit { + for (angle in AnglesToTest()) { + Microsoft.Quantum.Intrinsic.Message($"Checking angle {angle}"); + ExecuteWithAdjointAndControlledAPQ(op(_,angle,_)); + } + } + + operation ExecuteWithAdjointAndControlledPAIIQA(op : ((Pauli[],Int,Int,Qubit[]) => Unit is Adj + Ctl)) : Unit { + for (fraction in FractionsToTest()) { + Microsoft.Quantum.Intrinsic.Message($"Checking fraction {fraction}"); + let (numerator, denominatorPower) = fraction; + ExecuteWithAdjointAndControlledAPQ(op(_,numerator,denominatorPower,_)); + } + } + + operation ExecuteWithAdjointAndControlledDQ(op : ((Double, Qubit) => Unit is Adj + Ctl)) : Unit { + for (angle in AnglesToTest()) { + Microsoft.Quantum.Intrinsic.Message($"Checking angle {angle}"); + ExecuteWithAdjointAndControlledQ(op(angle,_)); + } + } + + operation ExecuteWithAdjointAndControlledPDQ(op : ((Pauli, Double, Qubit) => Unit is Adj + Ctl)) : Unit { + for (pauli in PaulisToTest()) { + Microsoft.Quantum.Intrinsic.Message($"Checking Pauli: {pauli}"); + ExecuteWithAdjointAndControlledDQ(op(pauli, _,_)); + } + } + + operation ExecuteWithAdjointAndControlledIIQ(op : ((Int, Int, Qubit) => Unit is Adj + Ctl)) : Unit { + for (fraction in FractionsToTest()) { + Microsoft.Quantum.Intrinsic.Message($"Checking fraction {fraction}"); + let (numerator, denominatorPower) = fraction; + ExecuteWithAdjointAndControlledQ(op(numerator,denominatorPower,_)); + } + } + + operation ExecuteWithAdjointAndControlledPIIQ(op : ((Pauli, Int, Int, Qubit) => Unit is Adj + Ctl)) : Unit { + for (pauli in PaulisToTest()) { + Microsoft.Quantum.Intrinsic.Message($"Checking Pauli: {pauli}"); + ExecuteWithAdjointAndControlledIIQ(op(pauli, _,_,_)); + } + } + + operation ExecuteWithAdjointAndControlledQQ(op : ((Qubit,Qubit) => Unit is Adj + Ctl)) : Unit { + ExecuteWithAdjointAndControlled(op, ArrayAsTupleAIOO, 2); + } + + operation ExecuteWithAdjointAndControlledQQQ(op : ((Qubit,Qubit,Qubit) => Unit is Adj + Ctl)) : Unit { + ExecuteWithAdjointAndControlled(op, ArrayAsTupleAIOOO, 3); + } +} \ No newline at end of file diff --git a/src/Simulation/DecompositionsCore.Test/Common/IntrinsicTesting-Settings.qs b/src/Simulation/DecompositionsCore.Test/Common/IntrinsicTesting-Settings.qs new file mode 100644 index 00000000000..e4cb374acff --- /dev/null +++ b/src/Simulation/DecompositionsCore.Test/Common/IntrinsicTesting-Settings.qs @@ -0,0 +1,49 @@ +namespace IntrinsicTesting { + function MaxControls() : Int { + return 3; + } + + function MaxTargets() : Int { + return 3; + } + + function NumberOfTestRepetitions() : Int { + return 1; + } + + function AnglesToTest() : Double[] { + let pi = Microsoft.Quantum.Math.PI(); + return [ + 0.0, + pi/8.0, + pi/4.0, + pi/2.0, + 3.0 * pi/4.0, + pi, + 5.0 * pi/4.0, + 3.0 * pi/2.0, + 2.0 * pi, + 3.0 * pi, + 4.0 * pi, + 0.1984 ]; + } + + function FractionsToTest() : (Int,Int)[] { + return [ + (0,-1), + (1,1), + (-1,1), + (1,2), + (3,2), + (-3,2), + (-1,2), + (1,3), + (-1,3), + (3,4), + (-1,4), + //(1, 9223372036854775807), + (1, 13) + //(1, -13) + ]; + } +} \ No newline at end of file diff --git a/src/Simulation/DecompositionsCore.Test/Common/IntrinsicTesting-Utils.qs b/src/Simulation/DecompositionsCore.Test/Common/IntrinsicTesting-Utils.qs new file mode 100644 index 00000000000..f553aa99e06 --- /dev/null +++ b/src/Simulation/DecompositionsCore.Test/Common/IntrinsicTesting-Utils.qs @@ -0,0 +1,264 @@ +namespace IntrinsicTesting { + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Decompositions.Utilities as Utils; + + internal operation ApplyToFirstQubit (op : (Qubit => Unit), register : Qubit[]) : Unit { + if (Length(register) == 0) { + fail $"Must have at least one qubit to act on."; + } + + op(register[0]); + } + + internal operation ApplyToFirstQubitA (op : (Qubit => Unit is Adj), register : Qubit[]) : Unit { + body (...) { + if (Length(register) == 0) { + fail $"Must have at least one qubit to act on."; + } + + op(register[0]); + } + + adjoint invert; + } + + internal operation ApplyToFirstQubitC (op : (Qubit => Unit is Ctl), register : Qubit[]) : Unit { + body (...) { + if (Length(register) == 0) { + fail $"Must have at least one qubit to act on."; + } + + op(register[0]); + } + + controlled distribute; + } + + internal operation ApplyToFirstQubitCA (op : (Qubit => Unit is Adj + Ctl), register : Qubit[]) : Unit { + body (...) { + if (Length(register) == 0) { + fail $"Must have at least one qubit to act on."; + } + + op(register[0]); + } + + adjoint invert; + controlled distribute; + controlled adjoint distribute; + } + + internal operation ApplyToFirstTwoQubits (op : ((Qubit, Qubit) => Unit), register : Qubit[]) : Unit { + if (Length(register) < 2) { + fail $"Must have at least two qubits to act on."; + } + + op(register[0], register[1]); + } + + internal operation ApplyToFirstTwoQubitsA (op : ((Qubit, Qubit) => Unit is Adj), register : Qubit[]) : Unit { + body (...) { + if (Length(register) < 2) { + fail $"Must have at least two qubits to act on."; + } + + op(register[0], register[1]); + } + + adjoint invert; + } + + operation ApplyToFirstTwoQubitsC (op : ((Qubit, Qubit) => Unit is Ctl), register : Qubit[]) : Unit { + body (...) { + if (Length(register) < 2) { + fail $"Must have at least two qubits to act on."; + } + + op(register[0], register[1]); + } + + controlled distribute; + } + + internal operation ApplyToFirstTwoQubitsCA (op : ((Qubit, Qubit) => Unit is Adj + Ctl), register : Qubit[]) : Unit { + body (...) { + if (Length(register) < 2) { + fail $"Must have at least two qubits to act on."; + } + + op(register[0], register[1]); + } + + adjoint invert; + controlled distribute; + controlled adjoint distribute; + } + + internal operation ApplyToFirstThreeQubits (op : ((Qubit, Qubit, Qubit) => Unit), register : Qubit[]) : Unit { + if (Length(register) < 3) { + fail $"Must have at least three qubits to act on."; + } + + op(register[0], register[1], register[2]); + } + + internal operation ApplyToFirstThreeQubitsA (op : ((Qubit, Qubit, Qubit) => Unit is Adj), register : Qubit[]) : Unit { + body (...) { + if (Length(register) < 3) { + fail $"Must have at least three qubits to act on."; + } + + op(register[0], register[1], register[2]); + } + + adjoint invert; + } + + internal operation ApplyToFirstThreeQubitsC (op : ((Qubit, Qubit, Qubit) => Unit is Ctl), register : Qubit[]) : Unit { + body (...) { + if (Length(register) < 3) { + fail $"Must have at least three qubits to act on."; + } + + op(register[0], register[1], register[2]); + } + + controlled distribute; + } + + internal operation ApplyToFirstThreeQubitsCA (op : ((Qubit, Qubit, Qubit) => Unit is Adj + Ctl), register : Qubit[]) : Unit { + body (...) { + if (Length(register) < 3) { + fail $"Must have at least three qubits to act on."; + } + + op(register[0], register[1], register[2]); + } + + adjoint invert; + controlled distribute; + controlled adjoint distribute; + } + + internal operation AssertOperationsEqualReferencedQ1( actual : ((Qubit) => Unit), expected : ((Qubit) => Unit is Adj) ) : Unit { + AssertOperationsEqualReferenced(2, ApplyToFirstQubit(actual,_), ApplyToFirstQubitA(expected,_)); + } + + internal operation AssertOperationsEqualReferencedQ2( actual : ((Qubit,Qubit) => Unit), expected : ((Qubit,Qubit) => Unit is Adj) ) : Unit { + AssertOperationsEqualReferenced(2, ApplyToFirstTwoQubits(actual,_), ApplyToFirstTwoQubitsA(expected,_)); + } + + internal operation AssertOperationsEqualReferencedQ3( actual : ((Qubit,Qubit,Qubit) => Unit), expected : ((Qubit,Qubit,Qubit) => Unit is Adj) ) : Unit { + AssertOperationsEqualReferenced(3, ApplyToFirstThreeQubits(actual,_), ApplyToFirstThreeQubitsA(expected,_)); + } + + internal operation ApplySinglyControlledCA<'T>(op : ('T => Unit is Adj + Ctl), (control : Qubit, arg : 'T)) : Unit is Adj + Ctl { + Controlled op([control], arg); + } + + internal operation ApplySinglyControlledCAQ2(op : ((Qubit,Qubit) => Unit is Adj + Ctl), (control : Qubit, qubit1 : Qubit, qubit2 : Qubit)) : Unit is Adj + Ctl { + Controlled op([control], (qubit1, qubit2)); + } + + internal function SinglyControlled<'T>(op : ('T => Unit is Adj + Ctl)) : ((Qubit,'T) => Unit is Adj + Ctl) { + return ApplySinglyControlledCA(op, _); + } + + internal function SinglyControlledQ2(op : ((Qubit,Qubit) => Unit is Adj + Ctl)) : ((Qubit,Qubit,Qubit) => Unit is Adj + Ctl) { + return ApplySinglyControlledCAQ2(op, _); + } + + internal function ArrayAsTupleO<'T>(arr : 'T[]) : 'T { + return (arr[0]); + } + + internal function ArrayAsTupleAO<'T>(arr : 'T[]) : ('T[],'T) { + return (arr[1 .. Length(arr) - 1], arr[0]); + } + + internal function ArrayAsTupleOO<'T>(arr : 'T[]) : ('T,'T) { + return (arr[0], arr[1]); + } + + internal function ArrayAsTupleAIOO<'T>(arr : 'T[]) : ('T[],('T,'T)) { + return (arr[2 .. Length(arr) - 1], (arr[0], arr[1])); + } + + internal function ArrayAsTupleOOO<'T>(arr : 'T[]) : ('T,'T,'T) { + return (arr[0], arr[1], arr[2]); + } + + internal function ArrayAsTupleAIOOO<'T>(arr : 'T[]) : ('T[],('T,'T,'T)) { + return (arr[3 .. Length(arr) - 1], (arr[0], arr[1],arr[2])); + } + + internal function ArrayAsTupleAA<'T>(secondArraySize : Int, arr : 'T[]) : ('T[],'T[]) { + return (arr[secondArraySize .. Length(arr) - 1], (arr[0 .. secondArraySize - 1])); + } + + internal function ArrayAsTupleOOIO<'T>( arr : 'T[]) : (('T,'T),'T) { + return ((arr[0], arr[1]), arr[2]); + } + + internal function ArrayAsTupleOIOO<'T>(arr : 'T[]) : ('T,('T,'T)) { + return (arr[0],(arr[1], arr[2])); + } + + internal operation ApplyComposedCA<'U,'V>(op : ('U => Unit is Adj + Ctl), fn : ('V -> 'U), arg : 'V) : Unit is Ctl + Adj { + op(fn(arg)); + } + + internal operation ApplyComposedA<'U,'V>(op : ('U => Unit is Adj), fn : ('V -> 'U), arg : 'V) : Unit is Adj { + op(fn(arg)); + } + + internal operation ApplyComposedC<'U,'V>(op : ('U => Unit is Ctl), fn : ('V -> 'U), arg : 'V) : Unit is Ctl { + op(fn(arg)); + } + + internal operation ApplyComposed<'U,'V>(op : ('U => Unit), fn : ('V -> 'U), arg : 'V) : Unit { + op(fn(arg)); + } + + internal operation IterateThroughCartesianProduct(bounds : Int[], op : (Int[] => Unit)) : Unit { + mutable arr = new Int[Length(bounds)]; + mutable finished = false; + + repeat { + if (not finished) { + op(arr); + } + } + until (finished) + fixup { + //computes the next element in the Cartesian product + set arr w/= 0 <- arr[0] + 1; + + for (i in 0 .. Length(arr) - 2) { + if (arr[i] == bounds[i]) { + set arr w/= i + 1 <- arr[i + 1] + 1; + set arr w/= i <- 0; + } + } + + if (arr[Length(arr) - 1] == bounds[Length(arr) - 1]) { + set finished = true; + } + } + } + + internal operation IterateThroughCartesianPower (power : Int, bound : Int, op : (Int[] => Unit)) : Unit { + mutable arr = new Int[power]; + + for (i in 0 .. power - 1) { + set arr w/= i <- bound; + } + + IterateThroughCartesianProduct(arr, op); + } + + internal operation IterateThroughCartesianPowerP (power : Int, values : Pauli[], op : (Pauli[] => Unit)) : Unit { + let opInt = ApplyComposed(op, Utils.ArrayFromIndiciesP(values,_),_); + IterateThroughCartesianPower(power, Length(values), opInt); + } +} \ No newline at end of file diff --git a/src/Simulation/DecompositionsCore.Test/Common/IntrinsicTesting.qs b/src/Simulation/DecompositionsCore.Test/Common/IntrinsicTesting.qs new file mode 100644 index 00000000000..66657878a1c --- /dev/null +++ b/src/Simulation/DecompositionsCore.Test/Common/IntrinsicTesting.qs @@ -0,0 +1,172 @@ +namespace IntrinsicTesting { + + open Microsoft.Quantum.Decompositions.Utilities as Utils; + open Microsoft.Quantum.Diagnostics; + + newtype UnitaryQSharpIntrinsics = ( + X : (Qubit => Unit is Adj + Ctl), + Y : (Qubit => Unit is Adj + Ctl), + Z : (Qubit => Unit is Adj + Ctl), + H : (Qubit => Unit is Adj + Ctl), + S : (Qubit => Unit is Adj + Ctl), + T : (Qubit => Unit is Adj + Ctl), + CNOT : ((Qubit,Qubit) => Unit is Adj + Ctl), + CCNOT : ((Qubit,Qubit,Qubit) => Unit is Adj + Ctl), + SWAP : ((Qubit,Qubit) => Unit is Adj + Ctl), + R : ((Pauli, Double, Qubit) => Unit is Adj + Ctl), + RFrac : ((Pauli, Int, Int, Qubit) => Unit is Adj + Ctl), + Rx : ((Double, Qubit) => Unit is Adj + Ctl), + Ry : ((Double, Qubit) => Unit is Adj + Ctl), + Rz : ((Double, Qubit) => Unit is Adj + Ctl), + R1 : ((Double, Qubit) => Unit is Adj + Ctl), + R1Frac : ((Int, Int, Qubit) => Unit is Adj + Ctl ), + Exp : ((Pauli[], Double, Qubit[]) => Unit is Adj + Ctl), + ExpFrac : ((Pauli[], Int, Int, Qubit[]) => Unit is Adj + Ctl) + ); + + function StandardIntrinsics() : UnitaryQSharpIntrinsics { + return UnitaryQSharpIntrinsics( + Microsoft.Quantum.Intrinsic.X, + Microsoft.Quantum.Intrinsic.Y, + Microsoft.Quantum.Intrinsic.Z, + Microsoft.Quantum.Intrinsic.H, + Microsoft.Quantum.Intrinsic.S, + Microsoft.Quantum.Intrinsic.T, + Microsoft.Quantum.Intrinsic.CNOT, + Microsoft.Quantum.Intrinsic.CCNOT, + Microsoft.Quantum.Intrinsic.SWAP, + Microsoft.Quantum.Intrinsic.R, + Microsoft.Quantum.Intrinsic.RFrac, + Microsoft.Quantum.Intrinsic.Rx, + Microsoft.Quantum.Intrinsic.Ry, + Microsoft.Quantum.Intrinsic.Rz, + Microsoft.Quantum.Intrinsic.R1, + Microsoft.Quantum.Intrinsic.R1Frac, + Microsoft.Quantum.Intrinsic.Exp, + Microsoft.Quantum.Intrinsic.ExpFrac); + } + + function PaulisToTest() : Pauli[] { + return [PauliI, PauliX, PauliY, PauliZ]; + } + + operation AssertEqualWithAdjointAndControlled<'TupleT>(actual : ('TupleT => Unit is Adj + Ctl), expected : ('TupleT => Unit is Adj + Ctl), tupleMapper : (Qubit[] -> (Qubit[], 'TupleT) ), tupleSize : Int ) : Unit { + let actualOnArr = ApplyComposedA(Controlled actual,tupleMapper,_); + let expectedOnArr = ApplyComposedA(Controlled expected,tupleMapper,_); + Microsoft.Quantum.Intrinsic.Message($"Checking equality of operations {actual} and {expected}"); + for (numQubits in tupleSize .. MaxControls() + tupleSize) { + Microsoft.Quantum.Intrinsic.Message($"Total number of qubits: {numQubits}"); + for (repetition in 1 .. NumberOfTestRepetitions()) { + AssertOperationsEqualReferenced(numQubits, actualOnArr, expectedOnArr); + AssertOperationsEqualReferenced(numQubits, Adjoint actualOnArr, Adjoint expectedOnArr); + } + } + Microsoft.Quantum.Intrinsic.Message($"Operations {actual} and {expected} are equal as well as their adjoint and controlled versions up to {MaxControls()} controls"); + } + + operation AssertEqualWithAdjointAndControlledQ(actual : (Qubit => Unit is Adj + Ctl), expected : (Qubit => Unit is Adj + Ctl)) : Unit { + AssertEqualWithAdjointAndControlled(actual, expected, ArrayAsTupleAO, 1); + } + + operation AssertEqualWithAdjointAndControlledAQ(numQubits : Int, actual : (Qubit[] => Unit is Adj + Ctl), expected : (Qubit[] => Unit is Adj + Ctl)) : Unit { + AssertEqualWithAdjointAndControlled(actual, expected, ArrayAsTupleAA(numQubits,_), numQubits); + } + + operation _AssertEqualWithAdjointAndControlledAPQ(paulis : Pauli[], actual : ((Pauli[],Qubit[]) => Unit is Adj + Ctl), expected : ((Pauli[],Qubit[]) => Unit is Adj + Ctl)) : Unit { + Microsoft.Quantum.Intrinsic.Message($"Checking for Pauli: {paulis}."); + AssertEqualWithAdjointAndControlledAQ(Length(paulis), actual(paulis,_), expected(paulis,_)); + } + + operation AssertEqualWithAdjointAndControlledAPQ(actual : ((Pauli[],Qubit[]) => Unit is Adj + Ctl), expected : ((Pauli[],Qubit[]) => Unit is Adj + Ctl)) : Unit { + for (numQubits in 1 .. MaxTargets()) { + Microsoft.Quantum.Intrinsic.Message($"Checking with {numQubits} qubits."); + IterateThroughCartesianPowerP( numQubits, PaulisToTest(), _AssertEqualWithAdjointAndControlledAPQ(_,actual,expected)); + } + } + + operation AssertEqualWithAdjointAndControlledPADQA(actual : ((Pauli[],Double,Qubit[]) => Unit is Adj + Ctl), expected : ((Pauli[],Double,Qubit[]) => Unit is Adj + Ctl)) : Unit { + for (angle in AnglesToTest()) { + Microsoft.Quantum.Intrinsic.Message($"Checking angle {angle}"); + AssertEqualWithAdjointAndControlledAPQ(actual(_,angle,_), expected(_,angle,_)); + } + } + + operation AssertEqualWithAdjointAndControlledPAIIQA(actual : ((Pauli[],Int,Int,Qubit[]) => Unit is Adj + Ctl), expected : ((Pauli[],Int,Int,Qubit[]) => Unit is Adj + Ctl)) : Unit { + for (fraction in FractionsToTest()) { + Microsoft.Quantum.Intrinsic.Message($"Checking fraction {fraction}"); + let (numerator, denominatorPower) = fraction; + AssertEqualWithAdjointAndControlledAPQ(actual(_,numerator,denominatorPower,_), expected(_,numerator,denominatorPower,_)); + } + } + + operation AssertEqualWithAdjointAndControlledDQ(actual : ((Double, Qubit) => Unit is Adj + Ctl), expected : ((Double, Qubit) => Unit is Adj + Ctl)) : Unit { + for (angle in AnglesToTest()) { + Microsoft.Quantum.Intrinsic.Message($"Checking angle {angle}"); + AssertEqualWithAdjointAndControlledQ(actual(angle,_), expected(angle,_)); + } + } + + operation AssertEqualWithAdjointAndControlledPDQ(actual : ((Pauli, Double, Qubit) => Unit is Adj + Ctl), expected : ((Pauli, Double, Qubit) => Unit is Adj + Ctl)) : Unit { + for (pauli in PaulisToTest()) { + Microsoft.Quantum.Intrinsic.Message($"Checking Pauli: {pauli}"); + AssertEqualWithAdjointAndControlledDQ(actual(pauli, _,_), expected(pauli, _,_)); + } + } + + operation AssertEqualWithAdjointAndControlledIIQ(actual : ((Int, Int, Qubit) => Unit is Adj + Ctl), expected : ((Int, Int, Qubit) => Unit is Adj + Ctl)) : Unit { + for (fraction in FractionsToTest()) { + Microsoft.Quantum.Intrinsic.Message($"Checking fraction {fraction}"); + let (numerator, denominatorPower) = fraction; + AssertEqualWithAdjointAndControlledQ(actual(numerator,denominatorPower,_), expected(numerator,denominatorPower,_)); + } + } + + operation AssertEqualWithAdjointAndControlledPIIQ(actual : ((Pauli, Int, Int, Qubit) => Unit is Adj + Ctl), expected : ((Pauli, Int, Int, Qubit) => Unit is Adj + Ctl)) : Unit { + for (pauli in PaulisToTest()) { + Microsoft.Quantum.Intrinsic.Message($"Checking Pauli: {pauli}"); + AssertEqualWithAdjointAndControlledIIQ(actual(pauli, _,_,_), expected(pauli, _,_,_)); + } + } + + operation AssertEqualWithAdjointAndControlledQQ(actual : ((Qubit,Qubit) => Unit is Adj + Ctl), expected : ((Qubit,Qubit) => Unit is Adj + Ctl)) : Unit { + AssertEqualWithAdjointAndControlled(actual, expected, ArrayAsTupleAIOO, 2); + } + + operation AssertEqualWithAdjointAndControlledQQQ(actual : ((Qubit,Qubit,Qubit) => Unit is Adj + Ctl), expected : ((Qubit,Qubit,Qubit) => Unit is Adj + Ctl)) : Unit { + AssertEqualWithAdjointAndControlled(actual, expected, ArrayAsTupleAIOOO, 3); + } + + operation TestInstrinsics( actualIntrinsics : UnitaryQSharpIntrinsics, expectedIntrinsics : UnitaryQSharpIntrinsics ) : Unit { + if (true) { + let eq = AssertEqualWithAdjointAndControlledQ; + eq(actualIntrinsics::X, expectedIntrinsics::X); + eq(actualIntrinsics::Y, expectedIntrinsics::Y); + eq(actualIntrinsics::Z, expectedIntrinsics::Z); + eq(actualIntrinsics::H, expectedIntrinsics::H); + eq(actualIntrinsics::S, expectedIntrinsics::S); + eq(actualIntrinsics::T, expectedIntrinsics::T); + } + + if (true) { + let eq = AssertEqualWithAdjointAndControlledQQ; + eq(actualIntrinsics::CNOT, expectedIntrinsics::CNOT); + eq(actualIntrinsics::SWAP, expectedIntrinsics::SWAP); + } + + AssertEqualWithAdjointAndControlledQQQ(actualIntrinsics::CCNOT, expectedIntrinsics::CCNOT); + AssertEqualWithAdjointAndControlledPDQ(actualIntrinsics::R, expectedIntrinsics::R); + + if (true) { + let eq = AssertEqualWithAdjointAndControlledDQ; + eq(actualIntrinsics::Rx, expectedIntrinsics::Rx); + eq(actualIntrinsics::Ry, expectedIntrinsics::Ry); + eq(actualIntrinsics::Rz, expectedIntrinsics::Rz); + eq(actualIntrinsics::R1, expectedIntrinsics::R1); + } + + AssertEqualWithAdjointAndControlledIIQ(actualIntrinsics::R1Frac, expectedIntrinsics::R1Frac); + AssertEqualWithAdjointAndControlledPIIQ(actualIntrinsics::RFrac, expectedIntrinsics::RFrac); + AssertEqualWithAdjointAndControlledPADQA(actualIntrinsics::Exp, expectedIntrinsics::Exp); + AssertEqualWithAdjointAndControlledPAIIQA(actualIntrinsics::ExpFrac, expectedIntrinsics::ExpFrac); + } +} \ No newline at end of file diff --git a/src/Simulation/DecompositionsCore.Test/Type2/Type2-Decompositions-Tests.qs b/src/Simulation/DecompositionsCore.Test/Type2/Type2-Decompositions-Tests.qs new file mode 100644 index 00000000000..0dbc110fdee --- /dev/null +++ b/src/Simulation/DecompositionsCore.Test/Type2/Type2-Decompositions-Tests.qs @@ -0,0 +1,90 @@ +namespace Type2.Decompositions.Tests { + + function Type2Decompositions() : IntrinsicTesting.UnitaryQSharpIntrinsics { + return IntrinsicTesting.UnitaryQSharpIntrinsics( + Test.Decompositions.X, + Test.Decompositions.Y, + Test.Decompositions.Z, + Test.Decompositions.H, + Test.Decompositions.S, + Test.Decompositions.T, + Test.Decompositions.CNOT, + Test.Decompositions.CCNOT, + Test.Decompositions.SWAP, + Test.Decompositions.R, + Test.Decompositions.RFrac, + Test.Decompositions.Rx, + Test.Decompositions.Ry, + Test.Decompositions.Rz, + Test.Decompositions.R1, + Test.Decompositions.R1Frac, + Test.Decompositions.Exp, + Test.Decompositions.ExpFrac); + } + + @Microsoft.Quantum.Diagnostics.Test("Test.Decompositions.Type2Simulator") + operation UnitaryIntrinsicTest() : Unit { + let standardIntrinsic = IntrinsicTesting.StandardIntrinsics(); + let decompositions = Type2Decompositions(); + IntrinsicTesting.TestInstrinsics(decompositions, standardIntrinsic); + } + + // For running against Type2Processor simulator. + // Here the meaning of oracle and test target switch places: Type2 decompositions + // from this project become the reference, while the standard intrinsics implemented + // by the Type2Processor become the test target. + operation BasicGates_Decompostion() : Unit { + IntrinsicTesting.ExecuteBasicInstrinsics(IntrinsicTesting.StandardIntrinsics()); + } + operation BasicGates_Decomposition_Type2Reference() : Unit { + IntrinsicTesting.ExecuteBasicInstrinsics(Type2Decompositions()); + } + operation SWAPandCNOT_Decompostion() : Unit { + IntrinsicTesting.ExecuteSWAPandCNOT(IntrinsicTesting.StandardIntrinsics()); + } + operation SWAPandCNOT_Decomposition_Type2Reference() : Unit { + IntrinsicTesting.ExecuteSWAPandCNOT(Type2Decompositions()); + } + operation Rotations_Decompostion() : Unit { + IntrinsicTesting.ExecuteSWAPandCNOT(IntrinsicTesting.StandardIntrinsics()); + } + operation Rotations_Decomposition_Type2Reference() : Unit { + IntrinsicTesting.ExecuteSWAPandCNOT(Type2Decompositions()); + } + operation CCNOT_Decompostion() : Unit { + IntrinsicTesting.ExecuteCCNOT(IntrinsicTesting.StandardIntrinsics()); + } + operation CCNOT_Decomposition_Type2Reference() : Unit { + IntrinsicTesting.ExecuteCCNOT(Type2Decompositions()); + } + operation R_Decompostion() : Unit { + IntrinsicTesting.ExecuteR(IntrinsicTesting.StandardIntrinsics()); + } + operation R_Decomposition_Type2Reference() : Unit { + IntrinsicTesting.ExecuteR(Type2Decompositions()); + } + operation R1Frac_Decompostion() : Unit { + IntrinsicTesting.ExecuteR1Frac(IntrinsicTesting.StandardIntrinsics()); + } + operation R1Frac_Decomposition_Type2Reference() : Unit { + IntrinsicTesting.ExecuteR1Frac(Type2Decompositions()); + } + operation RFrac_Decompostion() : Unit { + IntrinsicTesting.ExecuteRFrac(IntrinsicTesting.StandardIntrinsics()); + } + operation RFrac_Decomposition_Type2Reference() : Unit { + IntrinsicTesting.ExecuteRFrac(Type2Decompositions()); + } + operation Exp_Decompostion() : Unit { + IntrinsicTesting.ExecuteExp(IntrinsicTesting.StandardIntrinsics()); + } + operation Exp_Decomposition_Type2Reference() : Unit { + IntrinsicTesting.ExecuteExp(Type2Decompositions()); + } + operation ExpFrac_Decompostion() : Unit { + IntrinsicTesting.ExecuteExpFrac(IntrinsicTesting.StandardIntrinsics()); + } + operation ExpFrac_Decomposition_Type2Reference() : Unit { + IntrinsicTesting.ExecuteExpFrac(Type2Decompositions()); + } +} \ No newline at end of file diff --git a/src/Simulation/DecompositionsCore.Test/Type2/Type2DecompositionsTests.csproj b/src/Simulation/DecompositionsCore.Test/Type2/Type2DecompositionsTests.csproj new file mode 100644 index 00000000000..64c98813abb --- /dev/null +++ b/src/Simulation/DecompositionsCore.Test/Type2/Type2DecompositionsTests.csproj @@ -0,0 +1,34 @@ + + + + + + + netcoreapp3.1 + false + false + false + false + true + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Simulation/DecompositionsCore.Test/Type2/Type2Simulator.cs b/src/Simulation/DecompositionsCore.Test/Type2/Type2Simulator.cs new file mode 100644 index 00000000000..029490253c3 --- /dev/null +++ b/src/Simulation/DecompositionsCore.Test/Type2/Type2Simulator.cs @@ -0,0 +1,291 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; + +namespace Test.Decompositions +{ + public class Type2Simulator : QuantumSimulator + { + public Type2Simulator() + : base() + { } + + public class Type2SimulatorX : X + { + private QuantumSimulator.QSimX Simulator { get; } + + public Type2SimulatorX(QuantumSimulator m) : base(m) + { + Simulator = new QuantumSimulator.QSimX(m); + } + + public override Func Body => Simulator.Body; + + public override Func<(IQArray, Qubit), QVoid> ControlledBody => Simulator.ControlledBody; + } + + public class Type2SimulatorY : Y + { + private QuantumSimulator.QSimY Simulator { get; } + + public Type2SimulatorY(QuantumSimulator m) : base(m) + { + Simulator = new QuantumSimulator.QSimY(m); + } + + public override Func Body => Simulator.Body; + + public override Func<(IQArray, Qubit), QVoid> ControlledBody => Simulator.ControlledBody; + } + + public class Type2SimulatorZ : Z + { + private QuantumSimulator.QSimZ Simulator { get; } + + public Type2SimulatorZ(QuantumSimulator m) : base(m) + { + Simulator = new QuantumSimulator.QSimZ(m); + } + + public override Func Body => Simulator.Body; + + public override Func<(IQArray, Qubit), QVoid> ControlledBody => Simulator.ControlledBody; + } + + public class Type2SimulatorH : H + { + private QuantumSimulator.QSimH Simulator { get; } + + public Type2SimulatorH(QuantumSimulator m) : base(m) + { + Simulator = new QuantumSimulator.QSimH(m); + } + + public override Func Body => Simulator.Body; + + public override Func<(IQArray, Qubit), QVoid> ControlledBody => Simulator.ControlledBody; + } + + public class Type2SimulatorS : S + { + private QuantumSimulator.QSimS Simulator { get; } + + public Type2SimulatorS(QuantumSimulator m) : base(m) + { + Simulator = new QuantumSimulator.QSimS(m); + } + + public override Func Body => Simulator.Body; + + public override Func<(IQArray, Qubit), QVoid> ControlledBody => Simulator.ControlledBody; + + public override Func AdjointBody => Simulator.AdjointBody; + + public override Func<(IQArray, Qubit), QVoid> ControlledAdjointBody => Simulator.ControlledAdjointBody; + } + + public class Type2SimulatorT : T + { + private QuantumSimulator.QSimT Simulator { get; } + + public Type2SimulatorT(QuantumSimulator m) : base(m) + { + Simulator = new QuantumSimulator.QSimT(m); + } + + public override Func Body => Simulator.Body; + + public override Func<(IQArray, Qubit), QVoid> ControlledBody => Simulator.ControlledBody; + + public override Func AdjointBody => Simulator.AdjointBody; + + public override Func<(IQArray, Qubit), QVoid> ControlledAdjointBody => Simulator.ControlledAdjointBody; + } + + public class Type2SimulatorSWAP : SWAP + { + private QuantumSimulator.QSimX Simulator { get; } + + public Type2SimulatorSWAP(QuantumSimulator m) : base(m) + { + Simulator = new QuantumSimulator.QSimX(m); + } + + public override Func<(Qubit, Qubit), QVoid> Body => (args) => + { + (Qubit q1, Qubit q2) = args; + Simulator.ControlledBody((new QArray(q1), q2)); + Simulator.ControlledBody((new QArray(q2), q1)); + Simulator.ControlledBody((new QArray(q1), q2)); + return QVoid.Instance; + }; + + public override Func<(IQArray, (Qubit, Qubit)), QVoid> ControlledBody => (args) => + { + (IQArray ctrls, (Qubit q1, Qubit q2)) = args; + + if ((ctrls == null) || (ctrls.Count == 0)) + { + Body((q1, q2)); + } + else + { + Simulator.ControlledBody((QArray.Add(ctrls, new QArray(q1)), q2)); + Simulator.ControlledBody((QArray.Add(ctrls, new QArray(q2)), q1)); + Simulator.ControlledBody((QArray.Add(ctrls, new QArray(q1)), q2)); + } + + return QVoid.Instance; + }; + } + + public class Type2SimulatorR : R + { + private QuantumSimulator.QSimR Simulator { get; } + + public Type2SimulatorR(QuantumSimulator m) : base(m) + { + Simulator = new QuantumSimulator.QSimR(m); + } + + public override Func<(Pauli, double, Qubit), QVoid> Body => Simulator.Body; + + public override Func<(Pauli, double, Qubit), QVoid> AdjointBody => Simulator.AdjointBody; + + public override Func<(IQArray, (Pauli, double, Qubit)), QVoid> ControlledBody => Simulator.ControlledBody; + + public override Func<(IQArray, (Pauli, double, Qubit)), QVoid> ControlledAdjointBody => Simulator.ControlledAdjointBody; + } + + public class Type2SimulatorExp : Exp + { + private QuantumSimulator.QSimExp Simulator { get; } + + public Type2SimulatorExp(QuantumSimulator m) : base(m) + { + Simulator = new QuantumSimulator.QSimExp(m); + } + + public override Func<(IQArray, double, IQArray), QVoid> Body => Simulator.Body; + + public override Func<(IQArray, double, IQArray), QVoid> AdjointBody => Simulator.AdjointBody; + + public override Func<(IQArray, (IQArray, double, IQArray)), QVoid> ControlledBody => Simulator.ControlledBody; + + public override Func<(IQArray, (IQArray, double, IQArray)), QVoid> ControlledAdjointBody => Simulator.ControlledAdjointBody; + } + + public class Type2SimulatorM : M + { + private QuantumSimulator.QSimM Simulator { get; } + + public Type2SimulatorM(QuantumSimulator m) : base(m) + { + Simulator = new QuantumSimulator.QSimM(m); + } + + public override Func Body => Simulator.Body; + } + + public class Type2SimulatorApplyIsingXX : IsingXX + { + private QuantumSimulator.QSimExp Simulator { get; } + + public Type2SimulatorApplyIsingXX(QuantumSimulator m) : base(m) + { + Simulator = new QuantumSimulator.QSimExp(m); + } + + public override Func<(double, Qubit, Qubit), QVoid> Body => (args) => + { + (double theta, Qubit q1, Qubit q2) = (args); + Simulator.Body(((new QArray(Pauli.PauliX, Pauli.PauliX)), (theta * 2.0), (new QArray(q1, q2)))); + return QVoid.Instance; + }; + + public override Func<(IQArray, (double, Qubit, Qubit)), QVoid> ControlledBody => (args) => + { + (IQArray ctrls, (double theta, Qubit q1, Qubit q2)) = args; + + if ((ctrls == null) || (ctrls.Count == 0)) + { + Body((theta, q1, q2)); + } + else + { + Simulator.ControlledBody((ctrls, ((new QArray(Pauli.PauliX, Pauli.PauliX)), (theta * 2.0), (new QArray(q1, q2))))); + } + + return QVoid.Instance; + }; + } + + public class Type2SimulatorApplyIsingYY : IsingYY + { + private QuantumSimulator.QSimExp Simulator { get; } + + public Type2SimulatorApplyIsingYY(QuantumSimulator m) : base(m) + { + Simulator = new QuantumSimulator.QSimExp(m); + } + + public override Func<(double, Qubit, Qubit), QVoid> Body => (args) => + { + (double theta, Qubit q1, Qubit q2) = (args); + Simulator.Body(((new QArray(Pauli.PauliY, Pauli.PauliY)), (theta * 2.0), (new QArray(q1, q2)))); + return QVoid.Instance; + }; + + public override Func<(IQArray, (double, Qubit, Qubit)), QVoid> ControlledBody => (args) => + { + (IQArray ctrls, (double theta, Qubit q1, Qubit q2)) = args; + + if ((ctrls == null) || (ctrls.Count == 0)) + { + Body((theta, q1, q2)); + } + else + { + Simulator.ControlledBody((ctrls, ((new QArray(Pauli.PauliY, Pauli.PauliY)), (theta * 2.0), (new QArray(q1, q2))))); + } + + return QVoid.Instance; + }; + } + public class Type2SimulatorApplyIsingZZ : IsingZZ + { + private QuantumSimulator.QSimExp Simulator { get; } + + public Type2SimulatorApplyIsingZZ(QuantumSimulator m) : base(m) + { + Simulator = new QuantumSimulator.QSimExp(m); + } + + public override Func<(double, Qubit, Qubit), QVoid> Body => (args) => + { + (double theta, Qubit q1, Qubit q2) = (args); + Simulator.Body(((new QArray(Pauli.PauliZ, Pauli.PauliZ)), (theta * 2.0), (new QArray(q1, q2)))); + return QVoid.Instance; + }; + + public override Func<(IQArray, (double, Qubit, Qubit)), QVoid> ControlledBody => (args) => + { + (IQArray ctrls, (double theta, Qubit q1, Qubit q2)) = args; + + if ((ctrls == null) || (ctrls.Count == 0)) + { + Body((theta, q1, q2)); + } + else + { + Simulator.ControlledBody((ctrls, ((new QArray(Pauli.PauliZ, Pauli.PauliZ)), (theta * 2.0), (new QArray(q1, q2))))); + } + + return QVoid.Instance; + }; + } + } +} diff --git a/src/Simulation/DecompositionsCore/Common/CircuitUtilities.qs b/src/Simulation/DecompositionsCore/Common/CircuitUtilities.qs index a0f317a05e2..38654852ad9 100644 --- a/src/Simulation/DecompositionsCore/Common/CircuitUtilities.qs +++ b/src/Simulation/DecompositionsCore/Common/CircuitUtilities.qs @@ -1,9 +1,10 @@ -namespace Microsoft.Quantum.Targeting.Decompositions.Utilities.Circuit { - open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.Targeting.Decompositions.Utilities as Utils; +namespace Microsoft.Quantum.Intrinsic { + open Microsoft.Quantum.Decompositions.Utilities as Utils; + open Microsoft.Quantum.Diagnostics; /// Applies a unitary operation such that /// `SpreadZ(...); Exp([PauliZ],theta,[from]) ; Adjoint SpreadZ(...);` is equivalent to `Exp([PauliZ,..,PauliZ],theta,[from] + to)` + @EnableTestingViaName("Test.Decompositions.SpreadZ") internal operation SpreadZ(from : Qubit, to : Qubit[]) : Unit is Adj { if (Length(to) > 0) { CNOT(to[0], from); @@ -17,6 +18,7 @@ namespace Microsoft.Quantum.Targeting.Decompositions.Utilities.Circuit { /// Applies a unitary operation such that /// `MapPauli(...); R(from,...) ; Adjoint MapPauli(...);` is equivalent to `R(to,...)` + @EnableTestingViaName("Test.Decompositions.MapPauli") internal operation MapPauli(qubit : Qubit, from : Pauli, to : Pauli) : Unit is Adj { if (from == to) { } @@ -48,6 +50,7 @@ namespace Microsoft.Quantum.Targeting.Decompositions.Utilities.Circuit { /// applies it using ceiling(k/2) controls and using floor(k/2) temporary qubits /// almostCCX(c1,c2,t); U(c1,c2) must be equivalent to CCX(c1,c2,t) for some two-qubit unitary operation U(c1,c2) given target qubit starts /// in zero state, ( for Adjoint it is asserted that target qubit is returned to zero state) + @EnableTestingViaName("Test.Decompositions.ApplyWithLessControlsA") internal operation ApplyWithLessControlsA<'T>(op : ((Qubit[],'T) => Unit is Adj), (controls : Qubit[], arg : 'T), almostCCX : ((Qubit,Qubit,Qubit) => Unit is Adj)) : Unit is Adj { let numControls = Length(controls); let numControlPairs = numControls / 2; @@ -65,6 +68,7 @@ namespace Microsoft.Quantum.Targeting.Decompositions.Utilities.Circuit { /// almostCCX(c1,c2,t); U(c1,c2) must be equivalent to CCX(c1,c2,t) for some two-qubit unitary operation U(c1,c2) given target qubit starts /// in zero state, ( for Adjoint it is asserted that target qubit is returned to zero state) + @EnableTestingViaName("Test.Decompositions.ApplyUsingSinglyControlledVersion") internal operation ApplyUsingSinglyControlledVersion<'T>(op : ('T => Unit is Adj), controlledOp : ((Qubit,'T) => Unit is Adj), arg : 'T , almostCCX : ((Qubit,Qubit,Qubit) => Unit is Adj)) : Unit is Adj + Ctl { body(...) { op(arg); @@ -80,6 +84,7 @@ namespace Microsoft.Quantum.Targeting.Decompositions.Utilities.Circuit { } } + @EnableTestingViaName("Test.Decompositions.DispatchR1Frac") internal operation DispatchR1Frac(numerator : Int, power : Int, qubit : Qubit) : Unit is Adj + Ctl { if (power >= 0 ) { // when power is negative the operations is (1,exp(i pi*2^|n|*k)) and exp(i pi*2^|n|*k) = 1 let (kModPositive,n) = Utils.ReducedDyadicFractionPeriodic(numerator,power); // k is odd, or (k,n) are both 0 @@ -107,6 +112,7 @@ namespace Microsoft.Quantum.Targeting.Decompositions.Utilities.Circuit { } } + @EnableTestingViaName("Test.Decompositions.ApplyGlobalPhaseWithR1") internal operation ApplyGlobalPhaseWithR1(theta : Double) : Unit is Adj + Ctl { body(...) {} controlled(ctrls, ... ) { @@ -117,6 +123,7 @@ namespace Microsoft.Quantum.Targeting.Decompositions.Utilities.Circuit { } } + @EnableTestingViaName("Test.Decompositions.ApplyGlobalPhaseFracWithR1Frac") internal operation ApplyGlobalPhaseFracWithR1Frac(numerator : Int, power : Int) : Unit is Adj + Ctl { body(...) {} controlled(ctrls, ... ) { diff --git a/src/Simulation/DecompositionsCore/Common/Decompositions.qs b/src/Simulation/DecompositionsCore/Common/Decompositions.qs deleted file mode 100644 index d1477f6f6fb..00000000000 --- a/src/Simulation/DecompositionsCore/Common/Decompositions.qs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -/// Decompositions of some Q# operations into more primitive ones -/// that can be directly executed on various target hardware. -namespace Microsoft.Quantum.Targeting.Decompositions { - open Microsoft.Quantum.Intrinsic; - - /// # Summary - /// Calls "fail message" when the first argument is True - internal function FailIf(failIfTrue : Bool, message : String) : Unit { - if (failIfTrue) { - fail message; - } - } - - /// # Summary - /// Finds a first Pauli not equal to PauliI in the array - internal function FirstIndexOfNonId(array : Pauli[]) : Int { - for (i in 0 .. Length(array) - 1) { - if( array[i] != PauliI ) { - return i; - } - } - return -1; - } - - /// # Summary - /// Applies a Clifford unitary that maps by conjugation Pauli Z - /// to Pauli given by 'basis' argument. The unitary is applied to the qubit given by 'target' argument - internal operation PauliZFlip(basis : Pauli, target : Qubit) : Unit is Adj+Ctl { - - FailIf(basis == PauliI, $"PauliZ cannot be mapped to PauliI using conjugation by Clifford"); - - if (basis == PauliX) { - H(target); - } - elif (basis == PauliY) { - H(target); - S(target); - H(target); - } - else { - FailIf(basis != PauliZ, $"PauliZ must be the only remaining case"); - } - } - - ///////////////////////////////////////////////////////////////////////////////////////////////////////// - - internal operation ExpHelper(target : Qubit, paulis : Pauli[], qubits : Qubit[]) : Unit is Adj+Ctl { - for (i in 0 .. Length(paulis) - 1) { - if (paulis[i] != PauliI) { - PauliZFlip(paulis[i], qubits[i]); - CNOT(qubits[i],target); - } - } - } - - /// # Warning - /// Very inefficient implementation in terms of depth - internal operation Exp(paulis : Pauli[], theta : Double, qubits : Qubit[]) : Unit is Adj + Ctl { - FailIf(Length(paulis) == 0, ""); - FailIf(Length(paulis) != Length(qubits), ""); - - let index = FirstIndexOfNonId(paulis); - if (index != -1) { - PauliZFlip(paulis[index], qubits[index]); - ExpHelper(qubits[index], paulis[index + 1 .. Length(paulis) - 1], qubits[index + 1 .. Length(qubits) - 1]); - R(PauliZ, -2.0 * theta, qubits[index]); - Adjoint ExpHelper(qubits[index], paulis[index + 1 .. Length(paulis) - 1], qubits[index + 1 .. Length(qubits) - 1]); - Adjoint PauliZFlip(paulis[index], qubits[index]); - } - else - { - R(PauliI, -2.0 * theta, qubits[0]); - } - } - - internal operation ExpFrac(paulis : Pauli[], numerator : Int, power : Int, qubits : Qubit[]) : Unit { - FailIf(Length(paulis) == 0, ""); - FailIf(Length(paulis) != Length(qubits), ""); - - let index = FirstIndexOfNonId(paulis); - if (index != -1) { - PauliZFlip(paulis[index], qubits[index]); - ExpHelper(qubits[index], paulis[index + 1 .. Length(paulis) - 1], qubits[index + 1 .. Length(qubits) - 1]); - RFrac(PauliZ, numerator, power, qubits[index]); - Adjoint ExpHelper(qubits[index], paulis[index + 1 .. Length(paulis) - 1], qubits[index + 1 .. Length(qubits) - 1]); - Adjoint PauliZFlip(paulis[index], qubits[index]); - } - else - { - RFrac(PauliI, numerator, power, qubits[0]); - } - } - - /// # Warning - /// Very inefficient implementation in terms of depth - internal operation Measure(paulis : Pauli[], qubits : Qubit[]) : Result { - FailIf(Length(paulis) == 0, "" ); - FailIf(Length(paulis) != Length(qubits), ""); - mutable res = Zero; - - let index = FirstIndexOfNonId(paulis); - if (index != -1) { - PauliZFlip(paulis[index], qubits[index]); - ExpHelper(qubits[index], paulis[index+1 .. Length(paulis)-1], qubits[index+1 .. Length(qubits)-1]); - set res = M(qubits[index]); - Adjoint ExpHelper(qubits[index], paulis[index+1 .. Length(paulis)-1], qubits[index+1 .. Length(qubits)-1]); - Adjoint PauliZFlip(paulis[index], qubits[index]); - } - return res; - } - - internal operation MResetZ(target : Qubit) : Result { - let r = M(target); - Reset(target); - - return r; - } -} diff --git a/src/Simulation/DecompositionsCore/Common/Utilities.qs b/src/Simulation/DecompositionsCore/Common/Utilities.qs deleted file mode 100644 index 5dbf0ec3f42..00000000000 --- a/src/Simulation/DecompositionsCore/Common/Utilities.qs +++ /dev/null @@ -1,139 +0,0 @@ -namespace Microsoft.Quantum.Targeting.Decompositions.Utilities { - - internal operation ApplySinglyControlledCA<'T>(op : ('T => Unit is Adj + Ctl), (control : Qubit, arg : 'T)) : Unit is Adj + Ctl { - Controlled op([control], arg); - } - - internal operation ApplySinglyControlledCAQ2(op : ((Qubit,Qubit) => Unit is Adj + Ctl), (control : Qubit, qubit1 : Qubit, qubit2 : Qubit)) : Unit is Adj + Ctl { - Controlled op([control], (qubit1, qubit2)); - } - - internal function SinglyControlled<'T>(op : ('T => Unit is Adj + Ctl)) : ((Qubit,'T) => Unit is Adj + Ctl) { - return ApplySinglyControlledCA(op, _); - } - - internal function SinglyControlledQ2(op : ((Qubit,Qubit) => Unit is Adj + Ctl)) : ((Qubit,Qubit,Qubit) => Unit is Adj + Ctl) { - return ApplySinglyControlledCAQ2(op, _); - } - - internal function ArrayAsTupleO<'T>(arr : 'T[]) : 'T { - return (arr[0]); - } - - internal function ArrayAsTupleAO<'T>(arr : 'T[]) : ('T[],'T) { - return (arr[1 .. Length(arr) - 1], arr[0]); - } - - internal function ArrayAsTupleOO<'T>(arr : 'T[]) : ('T,'T) { - return (arr[0], arr[1]); - } - - internal function ArrayAsTupleAIOO<'T>(arr : 'T[]) : ('T[],('T,'T)) { - return (arr[2 .. Length(arr) - 1], (arr[0], arr[1])); - } - - internal function ArrayAsTupleOOO<'T>(arr : 'T[]) : ('T,'T,'T) { - return (arr[0], arr[1], arr[2]); - } - - internal function ArrayAsTupleAIOOO<'T>(arr : 'T[]) : ('T[],('T,'T,'T)) { - return (arr[3 .. Length(arr) - 1], (arr[0], arr[1],arr[2])); - } - - internal function ArrayAsTupleAA<'T>(secondArraySize : Int, arr : 'T[]) : ('T[],'T[]) { - return (arr[secondArraySize .. Length(arr) - 1], (arr[0 .. secondArraySize - 1])); - } - - internal function ArrayAsTupleOOIO<'T>( arr : 'T[]) : (('T,'T),'T) { - return ((arr[0], arr[1]), arr[2]); - } - - internal function ArrayAsTupleOIOO<'T>(arr : 'T[]) : ('T,('T,'T)) { - return (arr[0],(arr[1], arr[2])); - } - - internal operation ApplyComposedCA<'U,'V>(op : ('U => Unit is Adj + Ctl), fn : ('V -> 'U), arg : 'V) : Unit is Ctl + Adj { - op(fn(arg)); - } - - internal operation ApplyComposedA<'U,'V>(op : ('U => Unit is Adj), fn : ('V -> 'U), arg : 'V) : Unit is Adj { - op(fn(arg)); - } - - internal operation ApplyComposedC<'U,'V>(op : ('U => Unit is Ctl), fn : ('V -> 'U), arg : 'V) : Unit is Ctl { - op(fn(arg)); - } - - internal operation ApplyComposed<'U,'V>(op : ('U => Unit), fn : ('V -> 'U), arg : 'V) : Unit { - op(fn(arg)); - } - - internal function ArrayFromIndiciesP(values : Pauli[], indicies : Int[]) : Pauli[] { - mutable arr = new Pauli[Length(indicies)]; - for (i in 0 .. Length(indicies) - 1) { - set arr w/= i <- values[indicies[i]]; - } - return arr; - } - - internal function ArrayFromIndiciesQ(values : Qubit[], indicies : Int[]) : Qubit[] { - mutable arr = new Qubit[Length(indicies)]; - for (i in 0 .. Length(indicies) - 1) { - set arr w/= i <- values[indicies[i]]; - } - return arr; - } - - internal function IndicesOfNonIdentity(paulies : Pauli[]) : Int[] { - mutable nonIdPauliCount = 0; - - for (i in 0 .. Length(paulies) - 1) { - if (paulies[i] != PauliI) { set nonIdPauliCount += 1; } - } - - mutable indices = new Int[nonIdPauliCount]; - mutable index = 0; - - for (i in 0 .. Length(paulies) - 1) { - if (paulies[i] != PauliI) { - set indices w/= index <- i; - set index = index + 1; - } - } - - return indices; - } - - internal function ReducedDyadicFraction(numerator : Int, denominatorPowerOfTwo : Int) : (Int,Int) { - if (numerator == 0) { return (0,0); } - mutable num = numerator; - mutable denPow = denominatorPowerOfTwo; - while( num % 2 == 0 ) { - set num /= 2; - set denPow += 1; - } - return (num,denPow); - } - - internal function ReducedDyadicFractionPeriodic(numerator : Int, denominatorPowerOfTwo : Int) : (Int,Int) { - let (k,n) = ReducedDyadicFraction(numerator,denominatorPowerOfTwo); // k is odd, or (k,n) are both 0 - let period = 2*2^n; // \pi k / 2^n is 2\pi periodic, therefore k is 2 * 2^n periodic - let kMod = k % period; // if k was negative, we get kMod in a range [-period + 1, 0] - let kModPositive = kMod >= 0 ? kMod | kMod + period; // kModPositive is in the range [0, period - 1] - return (kModPositive, n); - } - - /// # Summary - /// Returns π×numerator/2^(denominatorPowerOfTwo) as Double - internal function DyadicFractionAsDouble(numerator : Int, denominatorPowerOfTwo : Int) : Double { - let numeratorD = Microsoft.Quantum.Math.PI() * Microsoft.Quantum.Convert.IntAsDouble(numerator); - return numeratorD * Microsoft.Quantum.Math.PowD(2.0, Microsoft.Quantum.Convert.IntAsDouble(-denominatorPowerOfTwo)); - } - - internal function RemovePauliI(paulis : Pauli[], qubits : Qubit[]) : (Pauli[], Qubit[]) { - let indices = IndicesOfNonIdentity(paulis); - let newPaulis = ArrayFromIndiciesP(paulis, indices); - let newQubits = ArrayFromIndiciesQ(qubits, indices); - return (newPaulis, newQubits); - } -} \ No newline at end of file diff --git a/src/Simulation/DecompositionsCore/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj b/src/Simulation/DecompositionsCore/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj index 4f1a4cece7c..de72236cf51 100644 --- a/src/Simulation/DecompositionsCore/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj +++ b/src/Simulation/DecompositionsCore/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj @@ -1,4 +1,4 @@ - + @@ -26,15 +26,11 @@ - - - - - + diff --git a/src/Simulation/DecompositionsCore/Type2/Type2-Decompositions.qs b/src/Simulation/DecompositionsCore/Type2/Type2-Decompositions.qs index a13497e6470..131668b0bbe 100644 --- a/src/Simulation/DecompositionsCore/Type2/Type2-Decompositions.qs +++ b/src/Simulation/DecompositionsCore/Type2/Type2-Decompositions.qs @@ -2,9 +2,8 @@ // Licensed under the MIT License. namespace Microsoft.Quantum.Intrinsic { - open Microsoft.Quantum.Targeting.Decompositions as CoreDecomp; - open Microsoft.Quantum.Targeting.Decompositions.Utilities as Utils; - open Microsoft.Quantum.Targeting.Decompositions.Utilities.Circuit as CircuitUtils; + open Microsoft.Quantum.Decompositions.Utilities as Utils; + open Microsoft.Quantum.Diagnostics; /// # Summary /// Performs the identity operation (no-op) on a single qubit. @@ -12,6 +11,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Remarks /// This is a no-op. It is provided for completeness and because /// sometimes it is useful to call the identity in an algorithm or to pass it as a parameter. + @EnableTestingViaName("Test.Decompositions.I") operation I(target : Qubit) : Unit is Adj + Ctl { body (...) { } @@ -42,6 +42,7 @@ namespace Microsoft.Quantum.Intrinsic { /// ```qsharp /// R(PauliX, theta, qubit); /// ``` + @EnableTestingViaName("Test.Decompositions.Rx") operation Rx(theta : Double, qubit : Qubit) : Unit is Ctl + Adj { body(...) { R(PauliX, theta, qubit); @@ -75,6 +76,7 @@ namespace Microsoft.Quantum.Intrinsic { /// ```qsharp /// R(PauliY, theta, qubit); /// ``` + @EnableTestingViaName("Test.Decompositions.Ry") operation Ry(theta : Double, qubit : Qubit) : Unit is Ctl + Adj { body(...) { R(PauliY, theta, qubit); @@ -108,6 +110,7 @@ namespace Microsoft.Quantum.Intrinsic { /// ```qsharp /// R(PauliZ, theta, qubit); /// ``` + @EnableTestingViaName("Test.Decompositions.Rz") operation Rz(theta : Double, qubit : Qubit) : Unit is Ctl + Adj { body(...) { R(PauliZ, theta, qubit); @@ -144,6 +147,7 @@ namespace Microsoft.Quantum.Intrinsic { /// ```qsharp /// Controlled X([control], target); /// ``` + @EnableTestingViaName("Test.Decompositions.CNOT") operation CNOT(control : Qubit, target : Qubit) : Unit is Adj + Ctl { body (...) { Controlled X([control], target); @@ -167,6 +171,7 @@ namespace Microsoft.Quantum.Intrinsic { /// ```qsharp /// Controlled X([control1, control2], target); /// ``` + @EnableTestingViaName("Test.Decompositions.CCNOT") operation CCNOT(control1 : Qubit, control2 : Qubit, target : Qubit) : Unit is Adj + Ctl { body (...) { Controlled X([control1, control2], target); @@ -195,6 +200,7 @@ namespace Microsoft.Quantum.Intrinsic { /// R(PauliZ, theta, qubit); /// R(PauliI, -theta, qubit); /// ``` + @EnableTestingViaName("Test.Decompositions.R1") operation R1(theta : Double, qubit : Qubit) : Unit is Adj + Ctl { body(...) { ApplyGlobalPhase( theta / 2.0 ); @@ -212,6 +218,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Input /// ## qubits /// An array of qubits whose states are to be reset to $\ket{0}$. + @EnableTestingViaName("Test.Decompositions.ResetAll") operation ResetAll(qubits : Qubit[]) : Unit { for (qubit in qubits) { Reset(qubit); @@ -237,6 +244,7 @@ namespace Microsoft.Quantum.Intrinsic { /// target register is to be rotated. /// ## qubits /// Register to apply the given rotation to. + @EnableTestingViaName("Test.Decompositions.Exp") operation Exp(paulis : Pauli[], theta : Double, qubits : Qubit[]) : Unit is Adj + Ctl { body(...) { if (Length(paulis) != Length(qubits)) { fail "Arrays 'pauli' and 'target' must have the same length"; } @@ -278,8 +286,9 @@ namespace Microsoft.Quantum.Intrinsic { /// the qubit is to be rotated. /// ## qubit /// Qubit to which the gate should be applied. + @EnableTestingViaName("Test.Decompositions.R1Frac") operation R1Frac(numerator : Int, power : Int, qubit : Qubit) : Unit is Adj + Ctl { - CircuitUtils.DispatchR1Frac(numerator, power, qubit); + DispatchR1Frac(numerator, power, qubit); } /// # Summary @@ -308,18 +317,19 @@ namespace Microsoft.Quantum.Intrinsic { /// the qubit is to be rotated. /// ## qubit /// Qubit to which the gate should be applied. + @EnableTestingViaName("Test.Decompositions.RFrac") operation RFrac(pauli : Pauli, numerator : Int, power : Int, qubit : Qubit) : Unit is Adj + Ctl { if (pauli == PauliI) { - CircuitUtils.ApplyGlobalPhaseFracWithR1Frac(numerator, power); + ApplyGlobalPhaseFracWithR1Frac(numerator, power); } else { if (power >= 0) { // when power is negative the operation is exp(i P pi*2^|n|*k) = I within { - CircuitUtils.MapPauli(qubit, PauliZ, pauli); + MapPauli(qubit, PauliZ, pauli); } apply { - CircuitUtils.ApplyGlobalPhaseFracWithR1Frac(numerator, power); + ApplyGlobalPhaseFracWithR1Frac(numerator, power); R1Frac(-numerator, power - 1, qubit); } @@ -356,6 +366,7 @@ namespace Microsoft.Quantum.Intrinsic { /// the qubit register is to be rotated. /// ## qubits /// Register to apply the given rotation to. + @EnableTestingViaName("Test.Decompositions.ExpFrac") operation ExpFrac(paulis : Pauli[], numerator : Int, power : Int, qubits : Qubit[]) : Unit is Adj + Ctl { body(...) { if (Length(paulis) != Length(qubits)) { fail "Arrays 'pauli' and 'target' must have the same length"; } @@ -366,7 +377,7 @@ namespace Microsoft.Quantum.Intrinsic { let newQubits = Utils.ArrayFromIndiciesQ(qubits, indices); if (Length(indices) != 0) { ExpNoIdFrac(newPaulis, numerator, power , newQubits); } - else { CircuitUtils.ApplyGlobalPhaseFracWithR1Frac(numerator, power); } + else { ApplyGlobalPhaseFracWithR1Frac(numerator, power); } } } adjoint(...) { @@ -409,9 +420,10 @@ namespace Microsoft.Quantum.Intrinsic { /// # Remarks /// If the basis array and qubit array are different lengths, then the /// operation will fail. + @EnableTestingViaName("Test.Decompositions.Measure") operation Measure(bases : Pauli[], qubits : Qubit[]) : Result { if (Length(bases) == 1) { - CircuitUtils.MapPauli(qubits[0], PauliZ, bases[0]); + MapPauli(qubits[0], PauliZ, bases[0]); return M(qubits[0]); } else { @@ -430,4 +442,60 @@ namespace Microsoft.Quantum.Intrinsic { } } } + + /// # Summary + /// Measures a single qubit in the Z basis. + /// + /// # Description + /// Performs a single-qubit measurement in the $Z$-basis. + /// + /// # Input + /// ## target + /// A single qubit to be measured. + /// + /// # Output + /// The result of measuring `target` in the Pauli $Z$ basis. + operation MResetZ (target : Qubit) : Result { + let r = M(target); + Reset(target); + return r; + } + + + /// # Summary + /// Measures a single qubit in the X basis. + /// + /// # Description + /// Performs a single-qubit measurement in the $X$-basis. + /// + /// # Input + /// ## target + /// A single qubit to be measured. + /// + /// # Output + /// The result of measuring `target` in the Pauli $X$ basis. + operation MResetX (target : Qubit) : Result { + let r = Measure([PauliX], [target]); + Reset(target); + return r; + } + + + /// # Summary + /// Measures a single qubit in the Y basis. + /// + /// # Description + /// Performs a single-qubit measurement in the $Y$-basis. + /// + /// # Input + /// ## target + /// A single qubit to be measured. + /// + /// # Output + /// The result of measuring `target` in the Pauli $Y$ basis. + operation MResetY (target : Qubit) : Result { + let r = Measure([PauliY], [target]); + Reset(target); + return r; + } } \ No newline at end of file diff --git a/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs b/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs index b6052707794..ee2db0b5079 100644 --- a/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs +++ b/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs @@ -2,10 +2,10 @@ // Licensed under the MIT License. namespace Microsoft.Quantum.Intrinsic { - open Microsoft.Quantum.Targeting.Decompositions as CoreDecomp; - open Microsoft.Quantum.Targeting.Decompositions.Utilities as Utils; - open Microsoft.Quantum.Targeting.Decompositions.Utilities.Circuit as CircuitUtils; + open Microsoft.Quantum.Decompositions.Utilities as Utils; + open Microsoft.Quantum.Diagnostics; + @EnableTestingViaName("Test.Decompositions.ExpNoId") internal operation ExpNoId(paulis : Pauli[], theta : Double, qubits : Qubit[]) : Unit is Ctl { if (Length(paulis) != Length(qubits)) { fail "Arrays 'paulis' and 'qubits' must have the same length"; } if (Length(paulis) == 1) { @@ -14,15 +14,15 @@ namespace Microsoft.Quantum.Intrinsic { elif ( Length(paulis) == 2 ) { if (paulis[0] != paulis[1] or paulis[0] == PauliI) { fail $"Type 2 Decompositions support only rotation around XX, YY, ZZ given {paulis}"; } within { - CircuitUtils.MapPauli(qubits[1],paulis[0],paulis[1]); + MapPauli(qubits[1],paulis[0],paulis[1]); } apply { if (paulis[1] == PauliX) { - ApplyIsingXX(theta / 2.0, qubits[0], qubits[1]); + IsingXX(theta / 2.0, qubits[0], qubits[1]); } elif (paulis[1] == PauliY) { - ApplyIsingYY(theta / 2.0, qubits[0], qubits[1]); + IsingYY(theta / 2.0, qubits[0], qubits[1]); } elif (paulis[1] == PauliZ) { - ApplyIsingZZ(theta / 2.0, qubits[0], qubits[1]); + IsingZZ(theta / 2.0, qubits[0], qubits[1]); } else { fail "Type2 decompositions do not support PauliI"; } @@ -31,12 +31,12 @@ namespace Microsoft.Quantum.Intrinsic { else { // Length(paulis) > 2 within { for (i in 0 .. Length(paulis) - 1) { - CircuitUtils.MapPauli(qubits[i],PauliZ,paulis[i]); + MapPauli(qubits[i],PauliZ,paulis[i]); } } apply { within { - CircuitUtils.SpreadZ(qubits[1], qubits[ 2 .. Length(qubits) - 1]); + SpreadZ(qubits[1], qubits[ 2 .. Length(qubits) - 1]); } apply { ExpNoId([PauliZ,PauliZ], theta, [qubits[0], qubits[1]]); @@ -45,6 +45,7 @@ namespace Microsoft.Quantum.Intrinsic { } } + @EnableTestingViaName("Test.Decompositions.ApplyGlobalPhase") internal operation ApplyGlobalPhase(theta : Double) : Unit is Ctl + Adj { body(...) {} controlled(controls, (...)) { @@ -58,11 +59,13 @@ namespace Microsoft.Quantum.Intrinsic { } } + @EnableTestingViaName("Test.Decompositions.TS") internal operation TS(target : Qubit) : Unit is Adj + Ctl { T(target); S(target); } + @EnableTestingViaName("Test.Decompositions.ExpNoIdFrac") internal operation ExpNoIdFrac(paulis : Pauli[], numerator : Int, power : Int, qubits : Qubit[]) : Unit is Ctl { if (Length(paulis) != Length(qubits)) { fail "Arrays 'paulis' and 'qubits' must have the same length"; } @@ -75,15 +78,15 @@ namespace Microsoft.Quantum.Intrinsic { } elif (Length(paulis) == 2) { within { - CircuitUtils.MapPauli(qubits[1], paulis[0], paulis[1]); + MapPauli(qubits[1], paulis[0], paulis[1]); } apply { if (paulis[0] == PauliX) { - ApplyIsingXX(theta / 2.0, qubits[0], qubits[1]); + IsingXX(theta / 2.0, qubits[0], qubits[1]); } elif (paulis[0] == PauliY) { - ApplyIsingYY(theta / 2.0, qubits[0], qubits[1]); + IsingYY(theta / 2.0, qubits[0], qubits[1]); } elif (paulis[0] == PauliZ) { - ApplyIsingZZ(theta / 2.0, qubits[0], qubits[1]); + IsingZZ(theta / 2.0, qubits[0], qubits[1]); } else { fail "Type2 decompositions do not support PauliI"; } @@ -92,12 +95,12 @@ namespace Microsoft.Quantum.Intrinsic { else { // Length(paulis) > 2 within { for (i in 0 .. Length(paulis) - 1) { - CircuitUtils.MapPauli(qubits[i], PauliZ, paulis[i]); + MapPauli(qubits[i], PauliZ, paulis[i]); } } apply { within { - CircuitUtils.SpreadZ(qubits[1], qubits[2 .. Length(qubits) - 1]); + SpreadZ(qubits[1], qubits[2 .. Length(qubits) - 1]); } apply { ExpNoId([PauliZ,PauliZ], theta, [qubits[0], qubits[1]]); @@ -106,55 +109,4 @@ namespace Microsoft.Quantum.Intrinsic { } } - /// # Summary - /// Applies the Ising $XX$ gate. - /// - /// TODO - describe XX gate. - /// - /// # Input - /// ## theta - /// The angle about which the qubits are rotated. - /// ## qubit0 - /// The first qubit input to the gate. - /// ## qubit1 - /// The second qubit input to the gate. - /// NOTE: If made public, consider a more concise name to match other quantum gate equivalent operations. - internal operation ApplyIsingXX(theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit is Ctl { - body intrinsic; - } - - /// # Summary - /// Applies the Ising $YY$ gate. - /// - /// TODO - describe YY gate. - /// - /// # Input - /// ## theta - /// The angle about which the qubits are rotated. - /// ## qubit0 - /// The first qubit input to the gate. - /// ## qubit1 - /// The second qubit input to the gate. - /// NOTE: If made public, consider a more concise name to match other quantum gate equivalent operations. - internal operation ApplyIsingYY(theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit is Ctl { - body intrinsic; - } - - /// # Summary - /// Applies the Ising $ZZ$ gate. - /// - /// TODO - describe ZZ gate. - /// - /// # Input - /// ## theta - /// The angle about which the qubits are rotated. - /// ## qubit0 - /// The first qubit input to the gate. - /// ## qubit1 - /// The second qubit input to the gate. - /// NOTE: If made public, consider a more concise name to match other quantum gate equivalent operations. - internal operation ApplyIsingZZ(theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit is Ctl { - body intrinsic; - } - } \ No newline at end of file diff --git a/src/Simulation/DecompositionsCore/Type2/Type2-Intrinsic.qs b/src/Simulation/DecompositionsCore/Type2/Type2-Intrinsic.qs index 50debf158f7..b65b85ddbd3 100644 --- a/src/Simulation/DecompositionsCore/Type2/Type2-Intrinsic.qs +++ b/src/Simulation/DecompositionsCore/Type2/Type2-Intrinsic.qs @@ -2,6 +2,7 @@ // Licensed under the MIT License. namespace Microsoft.Quantum.Intrinsic { + open Microsoft.Quantum.Diagnostics; /// # Summary /// Applies the Pauli $X$ gate. @@ -17,6 +18,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Input /// ## qubit /// Qubit to which the gate should be applied. + @EnableTestingViaName("Test.Decompositions.X") operation X(qubit : Qubit) : Unit is Adj + Ctl { body intrinsic; adjoint self; @@ -36,6 +38,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Input /// ## qubit /// Qubit to which the gate should be applied. + @EnableTestingViaName("Test.Decompositions.Y") operation Y(qubit : Qubit) : Unit is Adj + Ctl { body intrinsic; adjoint self; @@ -55,6 +58,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Input /// ## qubit /// Qubit to which the gate should be applied. + @EnableTestingViaName("Test.Decompositions.Z") operation Z(qubit : Qubit) : Unit is Adj + Ctl { body intrinsic; adjoint self; @@ -75,6 +79,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Input /// ## qubit /// Qubit to which the gate should be applied. + @EnableTestingViaName("Test.Decompositions.H") operation H(qubit : Qubit) : Unit is Adj + Ctl { body intrinsic; adjoint self; @@ -94,6 +99,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Input /// ## qubit /// Qubit to which the gate should be applied. + @EnableTestingViaName("Test.Decompositions.S") operation S(qubit : Qubit) : Unit is Adj + Ctl { body intrinsic; } @@ -113,6 +119,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Input /// ## qubit /// Qubit to which the gate should be applied. + @EnableTestingViaName("Test.Decompositions.T") operation T(qubit : Qubit) : Unit is Adj + Ctl { body intrinsic; } @@ -138,6 +145,7 @@ namespace Microsoft.Quantum.Intrinsic { /// First qubit to be swapped. /// ## qubit2 /// Second qubit to be swapped. + @EnableTestingViaName("Test.Decompositions.SWAP") operation SWAP(qubit0 : Qubit, qubit1 : Qubit) : Unit is Adj + Ctl { body intrinsic; adjoint self; @@ -165,6 +173,7 @@ namespace Microsoft.Quantum.Intrinsic { /// When called with `pauli = PauliI`, this operation applies /// a *global phase*. This phase can be significant /// when used with the `Controlled` functor. + @EnableTestingViaName("Test.Decompositions.R") operation R(pauli : Pauli, theta : Double, qubit : Qubit) : Unit is Adj + Ctl { body intrinsic; } @@ -188,6 +197,7 @@ namespace Microsoft.Quantum.Intrinsic { /// # Output /// `Zero` if the $+1$ eigenvalue is observed, and `One` if /// the $-1$ eigenvalue is observed. + @EnableTestingViaName("Test.Decompositions.M") operation M(qubit : Qubit) : Result { body intrinsic; } @@ -199,8 +209,63 @@ namespace Microsoft.Quantum.Intrinsic { /// # Input /// ## qubit /// The qubit whose state is to be reset to $\ket{0}$. + @EnableTestingViaName("Test.Decompositions.Reset") operation Reset(qubit : Qubit) : Unit { body intrinsic; } + /// # Summary + /// Applies the Ising $XX$ gate. + /// + /// TODO - describe XX gate. + /// + /// # Input + /// ## theta + /// The angle about which the qubits are rotated. + /// ## qubit0 + /// The first qubit input to the gate. + /// ## qubit1 + /// The second qubit input to the gate. + /// NOTE: If made public, consider a more concise name to match other quantum gate equivalent operations. + @EnableTestingViaName("Test.Decompositions.IsingXX") + operation IsingXX(theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit is Ctl { + body intrinsic; + } + + /// # Summary + /// Applies the Ising $YY$ gate. + /// + /// TODO - describe YY gate. + /// + /// # Input + /// ## theta + /// The angle about which the qubits are rotated. + /// ## qubit0 + /// The first qubit input to the gate. + /// ## qubit1 + /// The second qubit input to the gate. + /// NOTE: If made public, consider a more concise name to match other quantum gate equivalent operations. + @EnableTestingViaName("Test.Decompositions.IsingYY") + operation IsingYY(theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit is Ctl { + body intrinsic; + } + + /// # Summary + /// Applies the Ising $ZZ$ gate. + /// + /// TODO - describe ZZ gate. + /// + /// # Input + /// ## theta + /// The angle about which the qubits are rotated. + /// ## qubit0 + /// The first qubit input to the gate. + /// ## qubit1 + /// The second qubit input to the gate. + /// NOTE: If made public, consider a more concise name to match other quantum gate equivalent operations. + @EnableTestingViaName("Test.Decompositions.IsingZZ") + operation IsingZZ(theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit is Ctl { + body intrinsic; + } + } \ No newline at end of file diff --git a/src/Simulation/DecompositionsCore/Utilities/Microsoft.Quantum.Decompositions.Utilities.csproj b/src/Simulation/DecompositionsCore/Utilities/Microsoft.Quantum.Decompositions.Utilities.csproj new file mode 100644 index 00000000000..7be8560156f --- /dev/null +++ b/src/Simulation/DecompositionsCore/Utilities/Microsoft.Quantum.Decompositions.Utilities.csproj @@ -0,0 +1,22 @@ + + + + netstandard2.1 + true + false + false + + + + + + + + + + + + + + + diff --git a/src/Simulation/DecompositionsCore/Utilities/Utilities.qs b/src/Simulation/DecompositionsCore/Utilities/Utilities.qs new file mode 100644 index 00000000000..887986ad033 --- /dev/null +++ b/src/Simulation/DecompositionsCore/Utilities/Utilities.qs @@ -0,0 +1,71 @@ +namespace Microsoft.Quantum.Decompositions.Utilities { + + function ArrayFromIndiciesP(values : Pauli[], indicies : Int[]) : Pauli[] { + mutable arr = new Pauli[Length(indicies)]; + for (i in 0 .. Length(indicies) - 1) { + set arr w/= i <- values[indicies[i]]; + } + return arr; + } + + function ArrayFromIndiciesQ(values : Qubit[], indicies : Int[]) : Qubit[] { + mutable arr = new Qubit[Length(indicies)]; + for (i in 0 .. Length(indicies) - 1) { + set arr w/= i <- values[indicies[i]]; + } + return arr; + } + + function IndicesOfNonIdentity(paulies : Pauli[]) : Int[] { + mutable nonIdPauliCount = 0; + + for (i in 0 .. Length(paulies) - 1) { + if (paulies[i] != PauliI) { set nonIdPauliCount += 1; } + } + + mutable indices = new Int[nonIdPauliCount]; + mutable index = 0; + + for (i in 0 .. Length(paulies) - 1) { + if (paulies[i] != PauliI) { + set indices w/= index <- i; + set index = index + 1; + } + } + + return indices; + } + + function ReducedDyadicFraction(numerator : Int, denominatorPowerOfTwo : Int) : (Int,Int) { + if (numerator == 0) { return (0,0); } + mutable num = numerator; + mutable denPow = denominatorPowerOfTwo; + while( num % 2 == 0 ) { + set num /= 2; + set denPow += 1; + } + return (num,denPow); + } + + function ReducedDyadicFractionPeriodic(numerator : Int, denominatorPowerOfTwo : Int) : (Int,Int) { + let (k,n) = ReducedDyadicFraction(numerator,denominatorPowerOfTwo); // k is odd, or (k,n) are both 0 + let period = 2*2^n; // \pi k / 2^n is 2\pi periodic, therefore k is 2 * 2^n periodic + let kMod = k % period; // if k was negative, we get kMod in a range [-period + 1, 0] + let kModPositive = kMod >= 0 ? kMod | kMod + period; // kModPositive is in the range [0, period - 1] + return (kModPositive, n); + } + + /// # Summary + /// Returns π×numerator/2^(denominatorPowerOfTwo) as Double + function DyadicFractionAsDouble(numerator : Int, denominatorPowerOfTwo : Int) : Double { + let numeratorD = Microsoft.Quantum.Math.PI() * Microsoft.Quantum.Convert.IntAsDouble(numerator); + return numeratorD * Microsoft.Quantum.Math.PowD(2.0, Microsoft.Quantum.Convert.IntAsDouble(-denominatorPowerOfTwo)); + } + + function RemovePauliI(paulis : Pauli[], qubits : Qubit[]) : (Pauli[], Qubit[]) { + let indices = IndicesOfNonIdentity(paulis); + let newPaulis = ArrayFromIndiciesP(paulis, indices); + let newQubits = ArrayFromIndiciesQ(qubits, indices); + return (newPaulis, newQubits); + } +} \ No newline at end of file diff --git a/src/Simulation/QsharpBase/Microsoft.Quantum.QSharp.Base.csproj b/src/Simulation/QsharpBase/Microsoft.Quantum.QSharp.Base.csproj index c71dc544274..6c83c7b7c3e 100644 --- a/src/Simulation/QsharpBase/Microsoft.Quantum.QSharp.Base.csproj +++ b/src/Simulation/QsharpBase/Microsoft.Quantum.QSharp.Base.csproj @@ -1,4 +1,4 @@ - + From 377a3d82cf23ba8a9a030f157d276d0f29a1c65f Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Mon, 8 Jun 2020 08:15:13 -0700 Subject: [PATCH 4/6] Adding Type2 Decomp to solution and test script --- Simulation.sln | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Simulation.sln b/Simulation.sln index 608c332da07..44e5a43c5b9 100644 --- a/Simulation.sln +++ b/Simulation.sln @@ -63,6 +63,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DecompositionsCore", "Decom EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Quantum.Decompositions.Type2.Core", "src\Simulation\DecompositionsCore\Type2\Microsoft.Quantum.Decompositions.Core.Type2.csproj", "{C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DecompositionsCore.Test", "DecompositionsCore.Test", "{2593CE5E-AFD0-4AF0-B816-0E263201E726}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Type2DecompositionsTests", "src\Simulation\DecompositionsCore.Test\Type2\Type2DecompositionsTests.csproj", "{05C78D61-BAB4-459D-8E7E-72071BFC5FBB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -427,6 +431,22 @@ Global {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.Debug|x64.ActiveCfg = Debug|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.Debug|x64.Build.0 = Debug|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.Release|Any CPU.Build.0 = Release|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.Release|x64.ActiveCfg = Release|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.Release|x64.Build.0 = Release|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -458,6 +478,8 @@ Global {E9E387C0-2881-4F0C-8433-064BB18DB742} = {34D419E9-CCF1-4E48-9FA4-3AD4B86BEEB4} {4DF4699D-5A50-4B3F-8232-5B19CAE23950} = {34D419E9-CCF1-4E48-9FA4-3AD4B86BEEB4} {C548F9AA-DD12-4C39-BC75-8458CBCDB1D5} = {4DF4699D-5A50-4B3F-8232-5B19CAE23950} + {2593CE5E-AFD0-4AF0-B816-0E263201E726} = {34D419E9-CCF1-4E48-9FA4-3AD4B86BEEB4} + {05C78D61-BAB4-459D-8E7E-72071BFC5FBB} = {2593CE5E-AFD0-4AF0-B816-0E263201E726} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {929C0464-86D8-4F70-8835-0A5EAF930821} From eac0f06cbd43bd2dd25741d5e2ffc2d599b0c8e0 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Wed, 10 Jun 2020 08:20:13 -0700 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Scott Carda <55811729+ScottCarda-MS@users.noreply.github.com> --- .../DecompositionsCore.Test/Common/IntrinsicExecute.qs | 4 ++-- .../DecompositionsCore/Type2/Type2-Internals.qs | 8 ++++---- src/Simulation/DecompositionsCore/Utilities/Utilities.qs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Simulation/DecompositionsCore.Test/Common/IntrinsicExecute.qs b/src/Simulation/DecompositionsCore.Test/Common/IntrinsicExecute.qs index 914e68d27f2..579385a00e2 100644 --- a/src/Simulation/DecompositionsCore.Test/Common/IntrinsicExecute.qs +++ b/src/Simulation/DecompositionsCore.Test/Common/IntrinsicExecute.qs @@ -67,7 +67,7 @@ namespace IntrinsicTesting { } } - operation ExecuteWithAdjointAndControlled<'TupleT>(op : ('TupleT => Unit is Adj + Ctl), tupleMapper : (Qubit[] -> (Qubit[], 'TupleT) ), tupleSize : Int ) : Unit { + operation ExecuteWithAdjointAndControlled<'TupleT>(op : ('TupleT => Unit is Adj + Ctl), tupleMapper : (Qubit[] -> (Qubit[], 'TupleT)), tupleSize : Int) : Unit { let op_composed = ApplyComposedA(Controlled op, tupleMapper, _); Microsoft.Quantum.Intrinsic.Message($"Executing {op}"); @@ -150,4 +150,4 @@ namespace IntrinsicTesting { operation ExecuteWithAdjointAndControlledQQQ(op : ((Qubit,Qubit,Qubit) => Unit is Adj + Ctl)) : Unit { ExecuteWithAdjointAndControlled(op, ArrayAsTupleAIOOO, 3); } -} \ No newline at end of file +} diff --git a/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs b/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs index ee2db0b5079..36ebccaad03 100644 --- a/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs +++ b/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs @@ -14,7 +14,7 @@ namespace Microsoft.Quantum.Intrinsic { elif ( Length(paulis) == 2 ) { if (paulis[0] != paulis[1] or paulis[0] == PauliI) { fail $"Type 2 Decompositions support only rotation around XX, YY, ZZ given {paulis}"; } within { - MapPauli(qubits[1],paulis[0],paulis[1]); + MapPauli(qubits[1], paulis[0], paulis[1]); } apply { if (paulis[1] == PauliX) { @@ -31,12 +31,12 @@ namespace Microsoft.Quantum.Intrinsic { else { // Length(paulis) > 2 within { for (i in 0 .. Length(paulis) - 1) { - MapPauli(qubits[i],PauliZ,paulis[i]); + MapPauli(qubits[i], PauliZ, paulis[i]); } } apply { within { - SpreadZ(qubits[1], qubits[ 2 .. Length(qubits) - 1]); + SpreadZ(qubits[1], qubits[2 .. Length(qubits) - 1]); } apply { ExpNoId([PauliZ,PauliZ], theta, [qubits[0], qubits[1]]); @@ -109,4 +109,4 @@ namespace Microsoft.Quantum.Intrinsic { } } -} \ No newline at end of file +} diff --git a/src/Simulation/DecompositionsCore/Utilities/Utilities.qs b/src/Simulation/DecompositionsCore/Utilities/Utilities.qs index 887986ad033..708e23b3f4f 100644 --- a/src/Simulation/DecompositionsCore/Utilities/Utilities.qs +++ b/src/Simulation/DecompositionsCore/Utilities/Utilities.qs @@ -40,7 +40,7 @@ namespace Microsoft.Quantum.Decompositions.Utilities { if (numerator == 0) { return (0,0); } mutable num = numerator; mutable denPow = denominatorPowerOfTwo; - while( num % 2 == 0 ) { + while(num % 2 == 0) { set num /= 2; set denPow += 1; } @@ -68,4 +68,4 @@ namespace Microsoft.Quantum.Decompositions.Utilities { let newQubits = ArrayFromIndiciesQ(qubits, indices); return (newPaulis, newQubits); } -} \ No newline at end of file +} From 38458d0322b77f7076ea8f06da5c75990d109072 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Wed, 10 Jun 2020 09:05:02 -0700 Subject: [PATCH 6/6] Updated Exp internals to unify implementation --- .../Type2/Type2-Decompositions.qs | 14 ++++- .../Type2/Type2-Internals.qs | 55 ++----------------- 2 files changed, 16 insertions(+), 53 deletions(-) diff --git a/src/Simulation/DecompositionsCore/Type2/Type2-Decompositions.qs b/src/Simulation/DecompositionsCore/Type2/Type2-Decompositions.qs index 131668b0bbe..6ab982c5b67 100644 --- a/src/Simulation/DecompositionsCore/Type2/Type2-Decompositions.qs +++ b/src/Simulation/DecompositionsCore/Type2/Type2-Decompositions.qs @@ -251,7 +251,8 @@ namespace Microsoft.Quantum.Intrinsic { let (newPaulis, newQubits) = Utils.RemovePauliI(paulis, qubits); if (Length(newPaulis) != 0) { - ExpNoId(newPaulis, theta , newQubits); + if (Length(newPaulis) == 2 and newPaulis[0] != newPaulis[1]) { fail $"Type 2 Decompositions support only rotation around XX, YY, ZZ given {paulis}"; } + ExpNoIdUtil(newPaulis, theta , newQubits, R(_, -2.0 * theta, _)); } else { ApplyGlobalPhase(theta); @@ -376,8 +377,15 @@ namespace Microsoft.Quantum.Intrinsic { let newPaulis = Utils.ArrayFromIndiciesP(paulis, indices); let newQubits = Utils.ArrayFromIndiciesQ(qubits, indices); - if (Length(indices) != 0) { ExpNoIdFrac(newPaulis, numerator, power , newQubits); } - else { ApplyGlobalPhaseFracWithR1Frac(numerator, power); } + if (Length(indices) != 0) { + let (kModPositive,n) = Utils.ReducedDyadicFractionPeriodic(numerator, power); // k is odd, in the range [1,2*2^n-1] or (k,n) are both 0 + let numeratorD = Microsoft.Quantum.Math.PI() * Microsoft.Quantum.Convert.IntAsDouble(kModPositive); + let theta = numeratorD * Microsoft.Quantum.Math.PowD(2.0, Microsoft.Quantum.Convert.IntAsDouble(-n)); + ExpNoIdUtil(newPaulis, theta, newQubits, RFrac(_, numerator, power, _)); + } + else { + ApplyGlobalPhaseFracWithR1Frac(numerator, power); + } } } adjoint(...) { diff --git a/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs b/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs index 36ebccaad03..ebfc9d2a7c1 100644 --- a/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs +++ b/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs @@ -5,46 +5,6 @@ namespace Microsoft.Quantum.Intrinsic { open Microsoft.Quantum.Decompositions.Utilities as Utils; open Microsoft.Quantum.Diagnostics; - @EnableTestingViaName("Test.Decompositions.ExpNoId") - internal operation ExpNoId(paulis : Pauli[], theta : Double, qubits : Qubit[]) : Unit is Ctl { - if (Length(paulis) != Length(qubits)) { fail "Arrays 'paulis' and 'qubits' must have the same length"; } - if (Length(paulis) == 1) { - R(paulis[0], -2.0 * theta, qubits[0]); - } - elif ( Length(paulis) == 2 ) { - if (paulis[0] != paulis[1] or paulis[0] == PauliI) { fail $"Type 2 Decompositions support only rotation around XX, YY, ZZ given {paulis}"; } - within { - MapPauli(qubits[1], paulis[0], paulis[1]); - } - apply { - if (paulis[1] == PauliX) { - IsingXX(theta / 2.0, qubits[0], qubits[1]); - } elif (paulis[1] == PauliY) { - IsingYY(theta / 2.0, qubits[0], qubits[1]); - } elif (paulis[1] == PauliZ) { - IsingZZ(theta / 2.0, qubits[0], qubits[1]); - } else { - fail "Type2 decompositions do not support PauliI"; - } - } - } - else { // Length(paulis) > 2 - within { - for (i in 0 .. Length(paulis) - 1) { - MapPauli(qubits[i], PauliZ, paulis[i]); - } - } - apply { - within { - SpreadZ(qubits[1], qubits[2 .. Length(qubits) - 1]); - } - apply { - ExpNoId([PauliZ,PauliZ], theta, [qubits[0], qubits[1]]); - } - } - } - } - @EnableTestingViaName("Test.Decompositions.ApplyGlobalPhase") internal operation ApplyGlobalPhase(theta : Double) : Unit is Ctl + Adj { body(...) {} @@ -65,16 +25,11 @@ namespace Microsoft.Quantum.Intrinsic { S(target); } - @EnableTestingViaName("Test.Decompositions.ExpNoIdFrac") - internal operation ExpNoIdFrac(paulis : Pauli[], numerator : Int, power : Int, qubits : Qubit[]) : Unit is Ctl { + @EnableTestingViaName("Test.Decompositions.ExpNoIdUtil") + internal operation ExpNoIdUtil(paulis : Pauli[], theta : Double, qubits : Qubit[], rotation : ((Pauli, Qubit) => Unit is Adj + Ctl)) : Unit is Ctl { if (Length(paulis) != Length(qubits)) { fail "Arrays 'paulis' and 'qubits' must have the same length"; } - - let (kModPositive,n) = Utils.ReducedDyadicFractionPeriodic(numerator, power); // k is odd, in the range [1,2*2^n-1] or (k,n) are both 0 - let numeratorD = Microsoft.Quantum.Math.PI() * Microsoft.Quantum.Convert.IntAsDouble(kModPositive); - let theta = numeratorD * Microsoft.Quantum.Math.PowD(2.0, Microsoft.Quantum.Convert.IntAsDouble(-n)); - - if (Length(paulis) == 1 ) { - RFrac(paulis[0], numerator, power, qubits[0]); + if (Length(paulis) == 1) { + rotation(paulis[0], qubits[0]); } elif (Length(paulis) == 2) { within { @@ -103,7 +58,7 @@ namespace Microsoft.Quantum.Intrinsic { SpreadZ(qubits[1], qubits[2 .. Length(qubits) - 1]); } apply { - ExpNoId([PauliZ,PauliZ], theta, [qubits[0], qubits[1]]); + ExpNoIdUtil([PauliZ,PauliZ], theta, [qubits[0], qubits[1]], rotation); } } }