diff --git a/Simulation.sln b/Simulation.sln index 2566fb7b457..44e5a43c5b9 100644 --- a/Simulation.sln +++ b/Simulation.sln @@ -57,6 +57,16 @@ 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("{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 +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 @@ -389,6 +399,54 @@ 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 + {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 @@ -416,6 +474,12 @@ 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} + {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} + {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} diff --git a/build/pack.ps1 b/build/pack.ps1 index b4b39f19e2a..617b2a61d12 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/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.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..579385a00e2 --- /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); + } +} 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 new file mode 100644 index 00000000000..38654852ad9 --- /dev/null +++ b/src/Simulation/DecompositionsCore/Common/CircuitUtilities.qs @@ -0,0 +1,137 @@ +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); + 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,...)` + @EnableTestingViaName("Test.Decompositions.MapPauli") + 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) + @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; + 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) + @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); + } + 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); + } + } + } + + @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 + 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); + } + } + } + + @EnableTestingViaName("Test.Decompositions.ApplyGlobalPhaseWithR1") + 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])); + } + } + } + + @EnableTestingViaName("Test.Decompositions.ApplyGlobalPhaseFracWithR1Frac") + 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/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj b/src/Simulation/DecompositionsCore/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj new file mode 100644 index 00000000000..de72236cf51 --- /dev/null +++ b/src/Simulation/DecompositionsCore/Type2/Microsoft.Quantum.Decompositions.Core.Type2.csproj @@ -0,0 +1,45 @@ + + + + + + + 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..6ab982c5b67 --- /dev/null +++ b/src/Simulation/DecompositionsCore/Type2/Type2-Decompositions.qs @@ -0,0 +1,509 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Intrinsic { + open Microsoft.Quantum.Decompositions.Utilities as Utils; + open Microsoft.Quantum.Diagnostics; + + /// # 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. + @EnableTestingViaName("Test.Decompositions.I") + 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); + /// ``` + @EnableTestingViaName("Test.Decompositions.Rx") + 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); + /// ``` + @EnableTestingViaName("Test.Decompositions.Ry") + 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); + /// ``` + @EnableTestingViaName("Test.Decompositions.Rz") + 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); + /// ``` + @EnableTestingViaName("Test.Decompositions.CNOT") + 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); + /// ``` + @EnableTestingViaName("Test.Decompositions.CCNOT") + 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); + /// ``` + @EnableTestingViaName("Test.Decompositions.R1") + 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}$. + @EnableTestingViaName("Test.Decompositions.ResetAll") + 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. + @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"; } + let (newPaulis, newQubits) = Utils.RemovePauliI(paulis, qubits); + + if (Length(newPaulis) != 0) { + 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); + } + } + 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. + @EnableTestingViaName("Test.Decompositions.R1Frac") + operation R1Frac(numerator : Int, power : Int, qubit : Qubit) : Unit is Adj + Ctl { + 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. + @EnableTestingViaName("Test.Decompositions.RFrac") + operation RFrac(pauli : Pauli, numerator : Int, power : Int, qubit : Qubit) : Unit is Adj + Ctl { + if (pauli == PauliI) { + ApplyGlobalPhaseFracWithR1Frac(numerator, power); + } + else { + if (power >= 0) { // when power is negative the operation is exp(i P pi*2^|n|*k) = I + + within { + MapPauli(qubit, PauliZ, pauli); + } + apply { + 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. + @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"; } + + 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) { + 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(...) { + 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. + @EnableTestingViaName("Test.Decompositions.Measure") + operation Measure(bases : Pauli[], qubits : Qubit[]) : Result { + if (Length(bases) == 1) { + 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); + } + } + } + + /// # 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 new file mode 100644 index 00000000000..ebfc9d2a7c1 --- /dev/null +++ b/src/Simulation/DecompositionsCore/Type2/Type2-Internals.qs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Intrinsic { + open Microsoft.Quantum.Decompositions.Utilities as Utils; + open Microsoft.Quantum.Diagnostics; + + @EnableTestingViaName("Test.Decompositions.ApplyGlobalPhase") + 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)); + } + } + } + + @EnableTestingViaName("Test.Decompositions.TS") + internal operation TS(target : Qubit) : Unit is Adj + Ctl { + T(target); + S(target); + } + + @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"; } + if (Length(paulis) == 1) { + rotation(paulis[0], qubits[0]); + } + elif (Length(paulis) == 2) { + within { + MapPauli(qubits[1], paulis[0], paulis[1]); + } + apply { + if (paulis[0] == PauliX) { + IsingXX(theta / 2.0, qubits[0], qubits[1]); + } elif (paulis[0] == PauliY) { + IsingYY(theta / 2.0, qubits[0], qubits[1]); + } elif (paulis[0] == 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 { + ExpNoIdUtil([PauliZ,PauliZ], theta, [qubits[0], qubits[1]], rotation); + } + } + } + } + +} diff --git a/src/Simulation/DecompositionsCore/Type2/Type2-Intrinsic.qs b/src/Simulation/DecompositionsCore/Type2/Type2-Intrinsic.qs new file mode 100644 index 00000000000..b65b85ddbd3 --- /dev/null +++ b/src/Simulation/DecompositionsCore/Type2/Type2-Intrinsic.qs @@ -0,0 +1,271 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Intrinsic { + open Microsoft.Quantum.Diagnostics; + + /// # 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. + @EnableTestingViaName("Test.Decompositions.X") + operation X(qubit : Qubit) : Unit is Adj + Ctl { + body intrinsic; + adjoint self; + } + + /// # 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. + @EnableTestingViaName("Test.Decompositions.Y") + operation Y(qubit : Qubit) : Unit is Adj + Ctl { + body intrinsic; + adjoint self; + } + + /// # 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. + @EnableTestingViaName("Test.Decompositions.Z") + operation Z(qubit : Qubit) : Unit is Adj + Ctl { + body intrinsic; + adjoint self; + } + + /// # 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. + @EnableTestingViaName("Test.Decompositions.H") + 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. + @EnableTestingViaName("Test.Decompositions.S") + 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. + @EnableTestingViaName("Test.Decompositions.T") + 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. + @EnableTestingViaName("Test.Decompositions.SWAP") + 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. + @EnableTestingViaName("Test.Decompositions.R") + 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. + @EnableTestingViaName("Test.Decompositions.M") + 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}$. + @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..708e23b3f4f --- /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); + } +} 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/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..6c83c7b7c3e --- /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 @@ + 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();