diff --git a/Build/manifest.ps1 b/Build/manifest.ps1 index f4b4ab285ed..4fad664f64a 100644 --- a/Build/manifest.ps1 +++ b/Build/manifest.ps1 @@ -12,6 +12,7 @@ ); Assemblies = @( ".\Standard\src\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.Standard.dll", + ".\Visualization\src\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.Standard.Visualization.dll", ".\Numerics\src\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.Numerics.dll", ".\MachineLearning\src\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.MachineLearning.dll", ".\Chemistry\src\DataModel\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.Chemistry.DataModel.dll", diff --git a/Build/pack.ps1 b/Build/pack.ps1 index d32e5108b1b..e4e1af0397a 100644 --- a/Build/pack.ps1 +++ b/Build/pack.ps1 @@ -46,6 +46,9 @@ function Pack-Wheel() { Write-Host "##[info]Pack Standard library" Pack-One '../Standard/src/Standard.csproj' +Write-Host "##[info]Pack Standard visualization library" +Pack-One '../Visualization/src/Visualization.csproj' + Write-Host "##[info]Pack Chemistry library" Pack-One '../Chemistry/src/Runtime/Runtime.csproj' Pack-One '../Chemistry/src/DataModel/DataModel.csproj' diff --git a/Chemistry/src/DataModel/DataModel.csproj b/Chemistry/src/DataModel/DataModel.csproj index 06934c7b3a1..47f07491b0d 100644 --- a/Chemistry/src/DataModel/DataModel.csproj +++ b/Chemistry/src/DataModel/DataModel.csproj @@ -31,9 +31,9 @@ - - - + + + diff --git a/Chemistry/src/DataModel/Logging.cs b/Chemistry/src/DataModel/Logging.cs deleted file mode 100644 index 945491f3b06..00000000000 --- a/Chemistry/src/DataModel/Logging.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using Microsoft.Extensions.Logging; - -namespace Microsoft.Quantum.Chemistry -{ - public static class Logging - { - private static ILoggerFactory loggerFactory = null; - - public static string LogPath - { - get => logPath; - set { - if (loggerFactory != null) - { - loggerFactory.Dispose(); - loggerFactory = null; - } - else - { - logPath = value; - } - } - } - private static string logPath; - - public static ILoggerFactory LoggerFactory - { - get - { - if (loggerFactory == null) - { - loggerFactory = new LoggerFactory() - .AddDebug() - .AddConsole(true); - - if (LogPath != null) - { - System.Console.WriteLine($"Logging to {LogPath}."); - loggerFactory - .AddFile(LogPath); - } - } - - return loggerFactory; - } - } - } -} diff --git a/Chemistry/src/Jupyter/Jupyter.csproj b/Chemistry/src/Jupyter/Jupyter.csproj index 0dbefaadc74..833b8769531 100644 --- a/Chemistry/src/Jupyter/Jupyter.csproj +++ b/Chemistry/src/Jupyter/Jupyter.csproj @@ -25,7 +25,7 @@ - + diff --git a/Chemistry/src/Runtime/Runtime.csproj b/Chemistry/src/Runtime/Runtime.csproj index 19e2bb18e2b..6373a3bff3e 100644 --- a/Chemistry/src/Runtime/Runtime.csproj +++ b/Chemistry/src/Runtime/Runtime.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -36,7 +36,7 @@ - + diff --git a/Chemistry/tests/ChemistryTests/InitialStatePrepTests.qs b/Chemistry/tests/ChemistryTests/InitialStatePrepTests.qs index 1527f1f974d..7ffa9a950cd 100644 --- a/Chemistry/tests/ChemistryTests/InitialStatePrepTests.qs +++ b/Chemistry/tests/ChemistryTests/InitialStatePrepTests.qs @@ -5,7 +5,7 @@ namespace Microsoft.Quantum.Chemistry.Tests { open Microsoft.Quantum.Arithmetic; open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Diagnostics as Diag; open Microsoft.Quantum.Convert; open Microsoft.Quantum.Chemistry.JordanWigner; open Microsoft.Quantum.Arrays; @@ -109,8 +109,7 @@ namespace Microsoft.Quantum.Chemistry.Tests { // Prepare multiple excitations with complex weights - operation PrepareSparseMultiConfigurationalState4Test () : Unit { - + operation PrepareSparseMultiConfigurationalState4Test () : Unit { let nQubits = 1; let intTest = [39, 21, 10]; let phase = 2.453; @@ -118,7 +117,7 @@ namespace Microsoft.Quantum.Chemistry.Tests { using (qubits = Qubit[nQubits]) { PrepareSparseMultiConfigurationalState(NoOp, excitations, qubits); - AssertPhase(-phase / 2.0, qubits[0], 1E-09); + Diag.AssertPhase(-phase / 2.0, qubits[0], 1E-09); ResetAll(qubits); } } diff --git a/Chemistry/tests/ChemistryTests/MajoranaOperatorTests.qs b/Chemistry/tests/ChemistryTests/MajoranaOperatorTests.qs index bf10be29e29..b7c0abd6cf2 100644 --- a/Chemistry/tests/ChemistryTests/MajoranaOperatorTests.qs +++ b/Chemistry/tests/ChemistryTests/MajoranaOperatorTests.qs @@ -5,7 +5,7 @@ namespace Microsoft.Quantum.Chemistry.Tests { open Microsoft.Quantum.Arithmetic; open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Diagnostics as Diag; open Microsoft.Quantum.Convert; open Microsoft.Quantum.Chemistry.JordanWigner; open Microsoft.Quantum.Math; @@ -41,7 +41,7 @@ namespace Microsoft.Quantum.Chemistry.Tests { // |0> -> |0> // |+> -> |-> Message($"Test Z Pauli on qubit {idxTest}"); - AssertProb([PauliZ], [testQubit], Zero, 1.0, $"Error: Test {idxTest} {idxTest} Z Pauli |0>", 1E-10); + Diag.AssertMeasurementProbability([PauliZ], [testQubit], Zero, 1.0, $"Error: Test {idxTest} {idxTest} Z Pauli |0>", 1E-10); } elif (targetIndex == idxTest) { @@ -53,7 +53,7 @@ namespace Microsoft.Quantum.Chemistry.Tests { // |0> -> i|1> // |+> -> -i|-> Message($"Test X or Y Pauli on qubit {idxTest}"); - AssertProb([PauliZ], [testQubit], One, 1.0, $"Error: Test {idxTest} X or Y Pauli |0>", 1E-10); + Diag.AssertMeasurementProbability([PauliZ], [testQubit], One, 1.0, $"Error: Test {idxTest} X or Y Pauli |0>", 1E-10); } else { @@ -61,7 +61,7 @@ namespace Microsoft.Quantum.Chemistry.Tests { // |0> -> |0> // |+> -> |+> Message($"Test ZI Pauli on qubit {idxTest}"); - AssertProb([PauliZ], [testQubit], Zero, 1.0, $"Error: Test {idxTest} I Pauli |0>", 1E-10); + Diag.AssertMeasurementProbability([PauliZ], [testQubit], Zero, 1.0, $"Error: Test {idxTest} I Pauli |0>", 1E-10); } } @@ -109,7 +109,7 @@ namespace Microsoft.Quantum.Chemistry.Tests { // |0> -> |0> // |+> -> |-> Message($"Test Z Pauli on qubit {idxTest}"); - AssertProb([PauliX], [testQubit], One, 1.0, $"Error: Test {idxTest} Z Pauli |+>", 1e-10); + Diag.AssertMeasurementProbability([PauliX], [testQubit], One, 1.0, $"Error: Test {idxTest} Z Pauli |+>", 1e-10); } elif(targetIndex == idxTest){ // Test X Pauli @@ -117,7 +117,7 @@ namespace Microsoft.Quantum.Chemistry.Tests { // |+> -> |+> if(pauliBasis == PauliX){ Message($"Test X Pauli on qubit {idxTest}"); - AssertProb([PauliX], [testQubit], Zero, 1.0, $"Error: Test {idxTest} X Pauli |+>", 1e-10); + Diag.AssertMeasurementProbability([PauliX], [testQubit], Zero, 1.0, $"Error: Test {idxTest} X Pauli |+>", 1e-10); } // Test Y Pauli @@ -125,7 +125,7 @@ namespace Microsoft.Quantum.Chemistry.Tests { // |+> -> -i|-> if(pauliBasis == PauliY){ Message($"Test Y Pauli on qubit {idxTest}"); - AssertProb([PauliX], [testQubit], One, 1.0, $"Error: Test {idxTest} Y Pauli |+>", 1e-10); + Diag.AssertMeasurementProbability([PauliX], [testQubit], One, 1.0, $"Error: Test {idxTest} Y Pauli |+>", 1e-10); } } @@ -134,7 +134,7 @@ namespace Microsoft.Quantum.Chemistry.Tests { // |0> -> |0> // |+> -> |+> Message($"Test I Pauli on qubit {idxTest}"); - AssertProb([PauliX], [testQubit], Zero, 1.0, $"Error: Test {idxTest} I Pauli |+>", 1e-10); + Diag.AssertMeasurementProbability([PauliX], [testQubit], Zero, 1.0, $"Error: Test {idxTest} I Pauli |+>", 1e-10); } } OptimizedBEXY(pauliBasisQubit[0], LittleEndian(indexRegister), targetRegister); @@ -188,8 +188,10 @@ namespace Microsoft.Quantum.Chemistry.Tests { // Test phase of controlled OptimizedBEXY operator. - operation ControlledOptimizedBEOperatorTestHelper (pauliBasis : Pauli, targetRegisterSize : Int, targetIndex : Int) : Unit { - + operation ControlledOptimizedBEOperatorTestHelper( + pauliBasis : Pauli, targetRegisterSize : Int, targetIndex : Int + ) + : Unit { let indexRegisterSize = Ceiling(Lg(IntAsDouble(targetRegisterSize))); using (pauliBasisQubit = Qubit[1]) { @@ -202,37 +204,27 @@ namespace Microsoft.Quantum.Chemistry.Tests { let testQubit = targetRegister[targetIndex]; // Create indexRegister state. - ApplyXorInPlace(targetIndex, LittleEndian(indexRegister)); - - // Initialize control in |+> state. - H(controlRegister[0]); + within { + ApplyXorInPlace(targetIndex, LittleEndian(indexRegister)); - // Choose X or Y operator. - if (pauliBasis == PauliX) { - - // Initialize testQubit state in X +1 eigenstate - H(testQubit); - Controlled OptimizedBEXY(controlRegister, (pauliBasisQubit[0], LittleEndian(indexRegister), targetRegister)); - AssertPhase(0.0, controlRegister[0], 1E-10); - Adjoint Controlled OptimizedBEXY(controlRegister, (pauliBasisQubit[0], LittleEndian(indexRegister), targetRegister)); - H(testQubit); - } - elif (pauliBasis == PauliY) { - X(pauliBasisQubit[0]); - - // Initialize testQubit state Y +1 eigenstate - H(testQubit); - S(testQubit); - Controlled OptimizedBEXY(controlRegister, (pauliBasisQubit[0], LittleEndian(indexRegister), targetRegister)); - AssertPhase(0.0, controlRegister[0], 1E-10); - Adjoint Controlled OptimizedBEXY(controlRegister, (pauliBasisQubit[0], LittleEndian(indexRegister), targetRegister)); - Adjoint S(testQubit); - H(testQubit); - X(pauliBasisQubit[0]); + // Initialize control in |+> state. + H(controlRegister[0]); + + // Choose X or Y operator. + if (pauliBasis == PauliX) { + // Initialize testQubit state in X +1 eigenstate + H(testQubit); + Controlled OptimizedBEXY(controlRegister, (pauliBasisQubit[0], LittleEndian(indexRegister), targetRegister)); + } elif (pauliBasis == PauliY) { + // Initialize testQubit state Y +1 eigenstate + X(pauliBasisQubit[0]); + H(testQubit); + S(testQubit); + Controlled OptimizedBEXY(controlRegister, (pauliBasisQubit[0], LittleEndian(indexRegister), targetRegister)); + } + } apply { + Diag.AssertPhase(0.0, controlRegister[0], 1E-10); } - - H(controlRegister[0]); - Adjoint ApplyXorInPlace(targetIndex, LittleEndian(indexRegister)); } } } @@ -265,7 +257,7 @@ namespace Microsoft.Quantum.Chemistry.Tests { H(targetRegister[idxTest]); ApplyXorInPlace(idxTest, LittleEndian(indexRegister)); SelectZ(LittleEndian(indexRegister), targetRegister); - AssertProb([PauliX], [targetRegister[idxTest]], One, 1.0, $"Error: Test {idxTest} X Pauli |+>", 1E-10); + Diag.AssertMeasurementProbability([PauliX], [targetRegister[idxTest]], One, 1.0, $"Error: Test {idxTest} X Pauli |+>", 1E-10); Z(targetRegister[idxTest]); Adjoint ApplyXorInPlace(idxTest, LittleEndian(indexRegister)); H(targetRegister[idxTest]); diff --git a/Chemistry/tests/ChemistryTests/QSharpTests.csproj b/Chemistry/tests/ChemistryTests/QSharpTests.csproj index c10bdcc9653..258662bd8e1 100644 --- a/Chemistry/tests/ChemistryTests/QSharpTests.csproj +++ b/Chemistry/tests/ChemistryTests/QSharpTests.csproj @@ -1,4 +1,4 @@ - + diff --git a/Chemistry/tests/SystemTests/SystemTests.csproj b/Chemistry/tests/SystemTests/SystemTests.csproj index a856d69a6ff..42c31e5964b 100644 --- a/Chemistry/tests/SystemTests/SystemTests.csproj +++ b/Chemistry/tests/SystemTests/SystemTests.csproj @@ -1,4 +1,4 @@ - + diff --git a/MachineLearning/src/MachineLearning.csproj b/MachineLearning/src/MachineLearning.csproj index 02408133dc4..351e9615ea5 100644 --- a/MachineLearning/src/MachineLearning.csproj +++ b/MachineLearning/src/MachineLearning.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 Microsoft.Quantum.MachineLearning diff --git a/MachineLearning/tests/MachineLearningTests.csproj b/MachineLearning/tests/MachineLearningTests.csproj index a0824b9e216..37a2f9c7fa6 100644 --- a/MachineLearning/tests/MachineLearningTests.csproj +++ b/MachineLearning/tests/MachineLearningTests.csproj @@ -1,4 +1,4 @@ - + diff --git a/Numerics/src/Numerics.csproj b/Numerics/src/Numerics.csproj index 23ce269672f..d18dd45bb63 100644 --- a/Numerics/src/Numerics.csproj +++ b/Numerics/src/Numerics.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -40,7 +40,7 @@ - + diff --git a/Numerics/tests/NumericsTests.csproj b/Numerics/tests/NumericsTests.csproj index ad593aad679..06bf7b26d1d 100644 --- a/Numerics/tests/NumericsTests.csproj +++ b/Numerics/tests/NumericsTests.csproj @@ -1,4 +1,4 @@ - + diff --git a/Standard.sln b/Standard.sln index 6706dfe09f3..b8cbaa04b26 100644 --- a/Standard.sln +++ b/Standard.sln @@ -12,6 +12,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Standard", "Standard\src\St EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Standard.Tests", "Standard\tests\Standard.Tests.csproj", "{DEDA9681-2C11-492F-B1C9-D772BB45730A}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Visualization", "Visualization", "{2C5BF171-7D32-4DEC-BD08-9667A55A36FF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Visualization", "Visualization\src\Visualization.csproj", "{AC5462CC-F249-48E4-BFBE-93FC5AAC0D4E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -26,6 +30,10 @@ Global {DEDA9681-2C11-492F-B1C9-D772BB45730A}.Debug|Any CPU.Build.0 = Debug|Any CPU {DEDA9681-2C11-492F-B1C9-D772BB45730A}.Release|Any CPU.ActiveCfg = Release|Any CPU {DEDA9681-2C11-492F-B1C9-D772BB45730A}.Release|Any CPU.Build.0 = Release|Any CPU + {AC5462CC-F249-48E4-BFBE-93FC5AAC0D4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC5462CC-F249-48E4-BFBE-93FC5AAC0D4E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC5462CC-F249-48E4-BFBE-93FC5AAC0D4E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC5462CC-F249-48E4-BFBE-93FC5AAC0D4E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -39,4 +47,7 @@ Global GlobalSection(Performance) = preSolution HasPerformanceSessions = true EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {AC5462CC-F249-48E4-BFBE-93FC5AAC0D4E} = {2C5BF171-7D32-4DEC-BD08-9667A55A36FF} + EndGlobalSection EndGlobal diff --git a/Standard/src/Diagnostics/Allows.qs b/Standard/src/Diagnostics/Allows.qs new file mode 100644 index 00000000000..201c86114e8 --- /dev/null +++ b/Standard/src/Diagnostics/Allows.qs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Diagnostics { + + /// # Summary + /// Between a call to this operation and its adjoint, asserts that + /// a given operation is called at most a certain number of times. + /// + /// # Input + /// ## nTimes + /// The maximum number of times that `op` may be called. + /// ## op + /// An operation whose calls are to be restricted. + /// ## message + /// A message to be displayed upon failure. + /// + /// # Example + /// The following snippet will fail when executed on machines which + /// support this diagnostic: + /// ```Q# + /// using (register = Qubit[4]) { + /// within { + /// AllowAtMostNCallsCA(3, H, "Too many calls to H."); + /// } apply { + /// // Fails since this calls H four times, rather than the + /// // allowed maximum of three. + /// ApplyToEach(H, register); + /// } + /// } + /// ``` + /// + /// # Remarks + /// This operation may be replaced by a no-op on targets which do not + /// support it. + operation AllowAtMostNCallsCA<'TInput, 'TOutput>( + nTimes : Int, op : ('TInput => 'TOutput is Adj + Ctl), + message : String + ) + : Unit is Adj { + } + + /// # Summary + /// Between a call to this operation and its adjoint, asserts that + /// at most a given number of additional qubits are allocated with + /// using statements. + /// + /// # Input + /// ## nQubits + /// The maximum number of qubits that may be allocated. + /// ## message + /// A message to be displayed upon failure. + /// + /// # Example + /// The following snippet will fail when executed on machines which + /// support this diagnostic: + /// ```Q# + /// within { + /// AllowAtMostNQubits(3, "Too many qubits allocated."); + /// } apply { + /// // Fails since this allocates four qubits. + /// using (register = Qubit[4]) { + /// } + /// } + /// ``` + /// + /// # Remarks + /// This operation may be replaced by a no-op on targets which do not + /// support it. + operation AllowAtMostNQubits(nQubits : Int, message : String) : Unit is Adj { + } + +} diff --git a/Standard/src/Diagnostics/Quantum.qs b/Standard/src/Diagnostics/Asserts.qs similarity index 63% rename from Standard/src/Diagnostics/Quantum.qs rename to Standard/src/Diagnostics/Asserts.qs index c8d76ef5851..acf699987f8 100644 --- a/Standard/src/Diagnostics/Quantum.qs +++ b/Standard/src/Diagnostics/Asserts.qs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace Microsoft.Quantum.Canon { +namespace Microsoft.Quantum.Diagnostics { open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Convert; open Microsoft.Quantum.Math; @@ -9,10 +9,11 @@ namespace Microsoft.Quantum.Canon { /// # Summary /// Asserts that the phase of an equal superposition state has the expected value. /// - /// Specifically, asserts that the phase $\phi$ of a quantum state + /// # Description + /// This operation asserts that the phase $\phi$ of a quantum state /// that may be expressed as /// $\frac{e^{i t}}{\sqrt{2}}(e^{i\phi}\ket{0} + e^{-i\phi}\ket{1})$ - /// for some arbitrary real t has the expected value. + /// for some arbitrary real $t$ has the expected value. /// /// # Input /// ## expected @@ -35,12 +36,12 @@ namespace Microsoft.Quantum.Canon { /// /// `qubit` is in state $\ket{\psi}=e^{-i 2.2}\sqrt{1/2}\ket{0}+e^{i 0.2}\sqrt{1/2}\ket{1}$; /// - `AssertPhase(-1.2,qubit,10e-10);` - operation AssertPhase (expected : Double, qubit : Qubit, tolerance : Double) : Unit { + operation AssertPhase(expected : Double, qubit : Qubit, tolerance : Double) : Unit { let exptectedProbX = Cos(expected) * Cos(expected); let exptectedProbY = Sin(-1.0 * expected + PI() / 4.0) * Sin(-1.0 * expected + PI() / 4.0); - AssertProb([PauliZ], [qubit], Zero, 0.5, $"AssertPhase failed. Was not given a uniform superposition.", tolerance); - AssertProb([PauliY], [qubit], Zero, exptectedProbY, $"AssertPhase failed. PauliY Zero basis did not give probability {exptectedProbY}.", tolerance); - AssertProb([PauliX], [qubit], Zero, exptectedProbX, $"AssertPhase failed. PauliX Zero basis did not give probability {exptectedProbX}.", tolerance); + AssertMeasurementProbability([PauliZ], [qubit], Zero, 0.5, $"AssertPhase failed. Was not given a uniform superposition.", tolerance); + AssertMeasurementProbability([PauliY], [qubit], Zero, exptectedProbY, $"AssertPhase failed. PauliY Zero basis did not give probability {exptectedProbY}.", tolerance); + AssertMeasurementProbability([PauliX], [qubit], Zero, exptectedProbX, $"AssertPhase failed. PauliX Zero basis did not give probability {exptectedProbX}.", tolerance); } } diff --git a/Standard/src/Diagnostics/Deprecated.qs b/Standard/src/Diagnostics/Deprecated.qs new file mode 100644 index 00000000000..7a0b7ae6720 --- /dev/null +++ b/Standard/src/Diagnostics/Deprecated.qs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Canon { + + @Deprecated("Microsoft.Quantum.Diagnostics.AssertPhase") + operation AssertPhase(expected : Double, qubit : Qubit, tolerance : Double) : Unit { + Microsoft.Quantum.Diagnostics.AssertPhase(expected, qubit, tolerance); + } + +} diff --git a/Standard/src/Diagnostics/Dump.qs b/Standard/src/Diagnostics/Dump.qs new file mode 100644 index 00000000000..e84959a6da6 --- /dev/null +++ b/Standard/src/Diagnostics/Dump.qs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Diagnostics { + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Characterization; + open Microsoft.Quantum.Preparation; + + /// # Summary + /// Given an operation, displays diagnostics about + /// the operation that are made available by the current + /// execution target. + /// + /// # Input + /// ## nQubits + /// The number of qubits on which the given operation acts. + /// ## op + /// The operation that is to be diagnosed. + /// + /// # Example + /// When run on the quantum simulator target, the following snippet + /// will output the matrix + /// $\left(\begin{matrix} 0.0 & 0.707 \\\\ 0.707 & 0.0\end{matrix}\right)$: + /// + /// ```Q# + /// open Microsoft.Quantum.Arrays as Arrays; + /// + /// operation ApplyH(register : Qubit[]) : Unit is Adj + Ctl { + /// H(Head(register)); + /// } + /// + /// operation DumpH() : Unit { + /// DumpOperation(1, ApplyH); + /// } + /// ``` + /// + /// # Example + /// When run on the quantum simulator target, the following snippet will + /// output the matrix + /// $$ + /// \begin{aligned} + /// \left(\begin{matrix} + /// 1 & 0 & 0 & 0 \\\\ + /// 0 & 0 & 0 & 1 \\\\ + /// 0 & 0 & 1 & 0 \\\\ + /// 0 & 1 & 0 & 0 + /// \end{matrix}\right) + /// \end{aligned}. + /// $$ + /// + /// ```Q# + /// operation DumpCnot() : Unit { + /// DumpOperation(2, ApplyToFirstTwoQubitsCA(CNOT, _)); + /// } + /// ``` + /// + /// # Remarks + /// Calling this operation has no observable effect from within + /// Q#. The exact diagnostics that are displayed, if any, are + /// dependent on the current execution target and editor environment. + /// For example, when used on the full-state quantum simulator, + /// a unitary matrix used to represent `op` is displayed. + /// + /// Note that, when run on simulators that admit a global phase ambiguity + /// (e.g.: the full-state simulator), returned representations may vary + /// up to a global phase. + /// + /// Similarly, the ordering of rows and columns matrix representations + /// may vary with the conventions used by each simulator supporting this + /// operation. + operation DumpOperation(nQubits : Int, op : (Qubit[] => Unit is Adj)) + : Unit { + using ((reference, target) = (Qubit[nQubits], Qubit[nQubits])) { + // The operation provided could be a partial application of + // another operation, such that there could be an observable + // effect of dumping this operation unless we undo preparing the + // Choi state. + within { + PrepareChoiStateA(op, reference, target); + } apply { + DumpReferenceAndTarget(reference, target); + } + } + } + +} diff --git a/Standard/src/Diagnostics/Emulation/AllowOperationCalls.cs b/Standard/src/Diagnostics/Emulation/AllowOperationCalls.cs new file mode 100644 index 00000000000..f7c0b4509ef --- /dev/null +++ b/Standard/src/Diagnostics/Emulation/AllowOperationCalls.cs @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Quantum.Diagnostics.Emulation; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; + +namespace Microsoft.Quantum.Diagnostics +{ + + public partial class AllowAtMostNCallsCA<__TInput__, __TOutput__> + { + public class Native : AllowAtMostNCallsCA<__TInput__, __TOutput__> + { + private SimulatorBase? Simulator; + + private static Dictionary<(Type, Type), Stack> Handlers = + new Dictionary<(Type, Type), Stack>(); + + private static readonly (Type, Type) Key = (typeof(__TInput__), typeof(__TOutput__)); + + public Native(IOperationFactory m) : base(m) + { + Simulator = m as SimulatorBase; + } + + public override Func<(long, IUnitary, string), QVoid> Body => _args => + { + if (Simulator == null) return QVoid.Instance; + + var (nTimes, op, message) = _args; + var callStack = ImmutableStack.Empty; + var callSites = ImmutableList>.Empty; + + if (!Handlers.ContainsKey(Key)) + { + Handlers[Key] = new Stack(); + } + + bool IsSelf(ICallable callable) => + callable.FullName == "Microsoft.Quantum.Diagnostics.AllowAtMostNCallsCA"; + + // Record whether or not the condition checked by this allow + // has failed, so that we can property unwind in the endOperation + // handler below. + var failed = false; + + Handlers[Key].Push(Simulator.RegisterOperationHandlers( + startOperation: (callable, data) => + { + if (IsSelf(callable)) return; + callStack = callStack.Push(callable.FullName); + if (callable.FullName == op.FullName) + { + callSites = callSites.Add(callStack); + if (callSites.Count > nTimes) + { + Simulator?.MaybeDisplayDiagnostic(new CallSites + { + Sites = callSites, + Subject = op.FullName + }); + failed = true; + throw new ExecutionFailException( + $"Operation {op.FullName} was called more than the allowed {nTimes} times:\n{message}" + ); + } + } + }, + + endOperation: (callable, data) => + { + // Ignore call stack entries that happen after we've + // failed, or that are generated by ending the + // condition itself. + if (failed || IsSelf(callable)) return; + try + { + callStack = callStack.Pop(); + } + catch (InvalidOperationException ex) + { + System.Console.WriteLine($"Call stack was empty when popped:\n{ex}"); + } + } + )); + return QVoid.Instance; + }; + + public override Func<(long, IUnitary, string), QVoid> AdjointBody => _args => + { + if (Simulator == null) return QVoid.Instance; + + Handlers[Key].Pop().Dispose(); + + return QVoid.Instance; + }; + } + + } + +} diff --git a/Standard/src/Diagnostics/Emulation/AllowQubitAllocations.cs b/Standard/src/Diagnostics/Emulation/AllowQubitAllocations.cs new file mode 100644 index 00000000000..910ac6223c0 --- /dev/null +++ b/Standard/src/Diagnostics/Emulation/AllowQubitAllocations.cs @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Quantum.Diagnostics.Emulation; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; + +namespace Microsoft.Quantum.Diagnostics +{ + + public partial class AllowAtMostNQubits + { + public class Native : AllowAtMostNQubits + { + private SimulatorBase? Simulator; + + private static Stack<(IDisposable, IDisposable)> Handlers = + new Stack<(IDisposable, IDisposable)>(); + + public Native(IOperationFactory m) : base(m) + { + Simulator = m as SimulatorBase; + } + + public override Func<(long, string), QVoid> Body => _args => + { + if (Simulator == null) return QVoid.Instance; + + var (nQubitsAllowed, message) = _args; + var nQubitsAllocated = 0L; + + Handlers.Push(( + new ActionDisposer>( + nQubits => + { + nQubitsAllocated += nQubits; + if (nQubitsAllocated > nQubitsAllowed) + { + throw new ExecutionFailException( + $"{nQubitsAllocated} qubit(s) were allocated, but at most {nQubitsAllowed} qubit(s) are allowed:\n{message}." + ); + } + }, + setup: handler => Simulator.OnAllocateQubits += handler, + cleanup: handler => Simulator.OnAllocateQubits -= handler + ), + + new ActionDisposer>>( + register => + { + nQubitsAllocated -= register.Length; + }, + setup: handler => Simulator.OnReleaseQubits += handler, + cleanup: handler => Simulator.OnReleaseQubits -= handler + ) + )); + + return QVoid.Instance; + }; + + public override Func<(long, string), QVoid> AdjointBody => _args => + { + if (Simulator == null) return QVoid.Instance; + + var (start, end) = Handlers.Pop(); + start.Dispose(); + end.Dispose(); + + return QVoid.Instance; + }; + } + + } + +} diff --git a/Standard/src/Diagnostics/Emulation/DataStructures.cs b/Standard/src/Diagnostics/Emulation/DataStructures.cs new file mode 100644 index 00000000000..f0524045fe6 --- /dev/null +++ b/Standard/src/Diagnostics/Emulation/DataStructures.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; +using Newtonsoft.Json; +using static NumSharp.Slice; + +namespace Microsoft.Quantum.Diagnostics.Emulation +{ + /// + /// Represents a unitary operator intended for use as a diagnostic + /// display. + /// + public class DisplayableUnitaryOperator + { + /// + /// The qubits on which the represented operator acts, or + /// null if there is no specific register associated with + /// this operator. + /// + public IList? Qubits { get; set; } + + /// + /// An array of matrix elements for the given unitary operator. + /// + /// + /// For ease of use, this array has a dtype of double, and + /// shape (dim, dim, 2), where dim is the dimension + /// of the represented unitary operator (i.e.: 2^nQubits), and + /// where the last axis represents the real (index 0) and + /// imaginary parts, respectively. + /// + public NumSharp.NDArray? Data { get; set; } + + public override string ToString() => + Data == null + ? "" + : $"Real:\n{Data[Ellipsis, 0]}\nImag:\n{Data[Ellipsis, 1]}"; + } + + /// + /// A diagnostic record of sites where a given operation or function + /// was called. + /// + public struct CallSites + { + /// + /// The name of the operation or function whose calls are + /// represented by this record. + /// + public string Subject { get; set; } + + /// + /// A collection of calls to the given operation or function, each + /// represented as a call stack at the point where the subject was + /// called. + /// + public ImmutableList> Sites { get; set; } + } +} diff --git a/Standard/src/Diagnostics/Emulation/Extensions.cs b/Standard/src/Diagnostics/Emulation/Extensions.cs new file mode 100644 index 00000000000..d02525b5e46 --- /dev/null +++ b/Standard/src/Diagnostics/Emulation/Extensions.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using System; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; + +namespace Microsoft.Quantum.Diagnostics.Emulation +{ + + /// + /// Attaches events to a + /// instance, allowing for events to be removed with a disposable pattern. + /// + internal class SimulatorEventDisposer : IDisposable + { + private SimulatorBase Simulator; + private Action StartOperation, EndOperation; + + public SimulatorEventDisposer( + SimulatorBase simulator, + Action startOperation, + Action endOperation + ) + { + Simulator = simulator; + StartOperation = startOperation; + EndOperation = endOperation; + + Simulator.OnOperationStart += startOperation; + Simulator.OnOperationEnd += endOperation; + } + + public void Dispose() + { + Simulator.OnOperationStart -= StartOperation; + Simulator.OnOperationEnd -= EndOperation; + } + } + + internal class ActionDisposer : IDisposable + { + private Action cleanup; + private TData data; + public ActionDisposer(TData data, Action setup, Action cleanup) + { + this.data = data; + this.cleanup = cleanup; + setup(data); + } + + public void Dispose() => + cleanup(data); + } + + internal static class Extensions + { + /// + /// Attaches given event handlers to a simulator, returning a + /// disposable object that removes handlers when disposed. + /// + internal static SimulatorEventDisposer RegisterOperationHandlers( + this SimulatorBase simulator, + Action startOperation, + Action? endOperation = null + ) => new SimulatorEventDisposer( + simulator, + startOperation, + endOperation ?? ((callable, data) => {}) + ); + } +} diff --git a/Standard/src/Diagnostics/Emulation/Internal.cs b/Standard/src/Diagnostics/Emulation/Internal.cs new file mode 100644 index 00000000000..04f30c91316 --- /dev/null +++ b/Standard/src/Diagnostics/Emulation/Internal.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using System; +using System.Linq; +using Microsoft.Quantum.Diagnostics.Emulation; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; +using NumSharp; +using static NumSharp.Slice; + +namespace Microsoft.Quantum.Diagnostics +{ + internal class ArrayDumper : QuantumSimulator.StateDumper + { + // NB: NumSharp does not yet support complex numbers, so we store data + // as an array with a trailing index of length 2. + internal NumSharp.NDArray? Data = null; + public ArrayDumper(QuantumSimulator sim) : base(sim) + { + } + + public override bool Callback(uint idx, double real, double img) + { + if (Data as object == null) throw new Exception("Expected data buffer to be initialized before callback, but it was null."); + Data[(int)idx, 0] = real; + Data[(int)idx, 1] = img; + return true; + } + + public override bool Dump(IQArray? qubits = null) + { + var count = qubits?.Length ?? Simulator.QubitManager!.GetAllocatedQubitsCount(); + var nQubitsPerRegister = ((int)count / 2); + Data = np.empty(new Shape(1 << ((int)count), 2)); + var result = base.Dump(qubits); + + // At this point, _data should be filled with the full state + // vector, so let's display it, counting on the right display + // encoder to be there to pack it into a table. + var scaleFactor = System.Math.Sqrt(1 << nQubitsPerRegister); + Data = scaleFactor * Data.reshape(1 << nQubitsPerRegister, 1 << nQubitsPerRegister, 2); + + return result; + } + } + + internal partial class DumpReferenceAndTarget + { + public class Native : DumpReferenceAndTarget + { + private SimulatorBase? Simulator; + + public Native(IOperationFactory m) : base(m) + { + Simulator = m as SimulatorBase; + } + + private QVoid DumpUnitaryFromChoiState(QuantumSimulator simulator, IQArray reference, IQArray target) + { + var arrayDumper = new ArrayDumper(simulator); + arrayDumper.Dump(new QArray(reference.Concat(target))); + Simulator?.MaybeDisplayDiagnostic( + new DisplayableUnitaryOperator + { + Data = arrayDumper.Data, + Qubits = target.ToList() + } + ); + return QVoid.Instance; + } + + public override Func<(IQArray, IQArray), QVoid> Body => __in__ => + { + var (reference, target) = __in__; + return Simulator switch + { + QuantumSimulator sim => this.DumpUnitaryFromChoiState(sim, reference, target), + // TODO: Add other simulators here as appropriate. + _ => base.Body(__in__) + }; + }; + + } + } + +} diff --git a/Standard/src/Diagnostics/Facts.qs b/Standard/src/Diagnostics/Facts.qs index 64d7c050650..846d3442565 100644 --- a/Standard/src/Diagnostics/Facts.qs +++ b/Standard/src/Diagnostics/Facts.qs @@ -7,12 +7,6 @@ namespace Microsoft.Quantum.Diagnostics { open Microsoft.Quantum.Arrays; open Microsoft.Quantum.Logical; - /// # Summary - /// Internal function used to generate meaningful error messages. - internal function FormattedExpectation<'T>(actual : 'T, expected : 'T) : String { - return $"Expected: '{expected}'. Actual: '{actual}'"; - } - /// # Summary /// Declares that a classical condition is true. /// @@ -26,7 +20,9 @@ namespace Microsoft.Quantum.Diagnostics { /// # See Also /// - Microsoft.Quantum.Diagnostics.Contradiction function Fact(actual : Bool, message : String) : Unit { - if (not actual) { fail message; } + if (not actual) { + FormattedFailure(actual, true, message); + } } /// # Summary @@ -49,7 +45,9 @@ namespace Microsoft.Quantum.Diagnostics { /// Message("Hello, world."); /// ``` function Contradiction(actual : Bool, message : String) : Unit { - if (actual) { fail message; } + if (actual) { + FormattedFailure(actual, false, message); + } } /// # Summary @@ -67,7 +65,7 @@ namespace Microsoft.Quantum.Diagnostics { function EqualityWithinToleranceFact(actual : Double, expected : Double, tolerance : Double) : Unit { let delta = actual - expected; if (delta > tolerance or delta < -tolerance) { - fail FormattedExpectation(actual, expected); + FormattedFailure(actual, expected, "Values were not equal within tolerance."); } } @@ -102,7 +100,7 @@ namespace Microsoft.Quantum.Diagnostics { // conditions. let ((reA, imA), (reE, imE)) = (actual!, expected!); if (AbsD(reA - reE) >= 1e-10 or AbsD(imA - imE) >= 1e-10) { - fail FormattedExpectation(actual, expected); + FormattedFailure(actual, expected, "Values were not equal within tolerance."); } } @@ -134,7 +132,9 @@ namespace Microsoft.Quantum.Diagnostics { /// ## message /// Failure message string to be used when the assertion is triggered. function EqualityFactI(actual : Int, expected : Int, message : String) : Unit { - Fact(actual == expected, $"{actual} ≠ {expected}: {message}"); + if (actual != expected) { + FormattedFailure(actual, expected, message); + } } /// # Summary @@ -149,7 +149,9 @@ namespace Microsoft.Quantum.Diagnostics { /// ## message /// Failure message string to be used when the assertion is triggered. function EqualityFactL(actual : BigInt, expected : BigInt, message : String) : Unit { - Fact(actual == expected, $"{actual} ≠ {expected}: {message}"); + if (actual != expected) { + FormattedFailure(actual, expected, message); + } } /// # Summary @@ -165,7 +167,9 @@ namespace Microsoft.Quantum.Diagnostics { /// ## message /// Failure message string to be used when the assertion is triggered. function EqualityFactB(actual : Bool, expected : Bool, message : String) : Unit { - Fact(actual == expected, $"{actual} ≠ {expected}: {message}"); + if (actual != expected) { + FormattedFailure(actual, expected, message); + } } /// # Summary @@ -181,7 +185,9 @@ namespace Microsoft.Quantum.Diagnostics { /// ## message /// Failure message string to be used when the assertion is triggered. function EqualityFactR (actual : Result, expected : Result, message : String) : Unit { - Fact(actual == expected, $"{actual} ≠ {expected}: {message}"); + if (actual != expected) { + FormattedFailure(actual, expected, message); + } } /// # Summary @@ -197,7 +203,9 @@ namespace Microsoft.Quantum.Diagnostics { /// ## message /// Failure message string to be used when the assertion is triggered. function EqualityFactC(actual : Complex, expected : Complex, message : String) : Unit { - Fact(EqualC(actual, expected), $"{actual} ≠ {expected}: {message}"); + if (actual::Real != expected::Real or actual::Imag != expected::Imag) { + FormattedFailure(actual, expected, message); + } } /// # Summary @@ -213,7 +221,14 @@ namespace Microsoft.Quantum.Diagnostics { /// ## message /// Failure message string to be used when the assertion is triggered. function EqualityFactCP(actual : ComplexPolar, expected : ComplexPolar, message : String) : Unit { - Fact(EqualCP(actual, expected), $"{actual} ≠ {expected}: {message}"); + let actualCartesian = ComplexPolarAsComplex(actual); + let expectedCartesian = ComplexPolarAsComplex(expected); + if ( + actualCartesian::Real != expectedCartesian::Real or + actualCartesian::Imag != expectedCartesian::Imag + ) { + FormattedFailure(actual, expected, message); + } } /// # Summary @@ -232,7 +247,7 @@ namespace Microsoft.Quantum.Diagnostics { function AllEqualityFactB(actual : Bool[], expected : Bool[], message : String) : Unit { let n = Length(actual); if (n != Length(expected)) { - fail message; + FormattedFailure(actual, expected, message); } Ignore(Mapped(EqualityFactB(_, _, message), Zip(actual, expected))); @@ -254,7 +269,7 @@ namespace Microsoft.Quantum.Diagnostics { function AllEqualityFactI(actual : Int[], expected : Int[], message : String) : Unit { let n = Length(actual); if (n != Length(expected)) { - fail message; + FormattedFailure(actual, expected, message); } Ignore(Mapped(EqualityFactI(_, _, message), Zip(actual, expected))); diff --git a/Standard/src/Diagnostics/Internal.qs b/Standard/src/Diagnostics/Internal.qs new file mode 100644 index 00000000000..80dfaafcce6 --- /dev/null +++ b/Standard/src/Diagnostics/Internal.qs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Diagnostics { + open Microsoft.Quantum.Intrinsic; + + /// # Summary + /// Internal function used to fail with meaningful error messages. + /// + /// # Remarks + /// This function is intended to be emulated by simulation runtimes, so as + /// to allow forwarding formatted contradictions. + internal function FormattedFailure<'T>(actual : 'T, expected : 'T, message : String) : Unit { + fail $"{message}\n\tExpected:\t{expected}\n\tActual:\t{actual}"; + } + + /// # Summary + /// Uses DumpRegister to provide diagnostics on the state of a reference and + /// target register. Written as separate operation to allow overriding and + /// interpreting as separate registers, rather than as a single combined + /// register. + internal operation DumpReferenceAndTarget(reference : Qubit[], target : Qubit[]) : Unit { + DumpRegister((), reference + target); + } + +} diff --git a/Standard/src/Standard.csproj b/Standard/src/Standard.csproj index 634fa6a0eb6..93d78493975 100644 --- a/Standard/src/Standard.csproj +++ b/Standard/src/Standard.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -30,8 +30,9 @@ - + + diff --git a/Standard/tests/AssertTests.qs b/Standard/tests/AssertTests.qs index 1d35be2a51b..30e8eb2ea76 100644 --- a/Standard/tests/AssertTests.qs +++ b/Standard/tests/AssertTests.qs @@ -4,52 +4,50 @@ namespace Microsoft.Quantum.Tests { open Microsoft.Quantum.Arithmetic; open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Diagnostics as Diag; open Microsoft.Quantum.Arrays; // This file contains very simple tests that should trivially pass // with the intent of testing the assert and testing harness mechanisms themselves. - operation EmptyTest () : Unit { } + operation EmptyTest() : Unit { } - operation PreparationTest () : Unit { + operation PreparationTest() : Unit { using (qubit = Qubit()) { - AssertProb([PauliZ], [qubit], Zero, 1.0, $"Freshly prepared qubit was not in |0〉 state.", 1E-10); + Diag.AssertMeasurementProbability([PauliZ], [qubit], Zero, 1.0, $"Freshly prepared qubit was not in |0〉 state.", 1E-10); } } - operation OperationTestShouldFail () : Unit { + operation OperationTestShouldFail() : Unit { fail $"OK"; } - function FunctionTestShouldFail () : Unit { + function FunctionTestShouldFail() : Unit { fail $"OK"; } - function AssertEqualTestShouldFail () : Unit { - NearEqualityFactD(1.0, 0.0); + function AssertEqualTestShouldFail() : Unit { + Diag.NearEqualityFactD(1.0, 0.0); } - function AssertBoolArrayEqualTestShouldFail () : Unit { - - AllEqualityFactB([true, false], [false, true], $"OK"); + function AssertBoolArrayEqualTestShouldFail() : Unit { + Diag.AllEqualityFactB([true, false], [false, true], $"OK"); } - function AssertBoolEqualTestShouldFail () : Unit { - EqualityFactB(true, false, $"OK"); + function AssertBoolEqualTestShouldFail() : Unit { + Diag.EqualityFactB(true, false, $"OK"); } - function EqualityFactRTestShouldFail () : Unit { - EqualityFactR(Zero, One, $"OK"); + function EqualityFactRTestShouldFail() : Unit { + Diag.EqualityFactR(Zero, One, $"OK"); } - function EqualityFactITestShouldFail () : Unit { - - EqualityFactI(12, 42, $"OK"); + function EqualityFactITestShouldFail() : Unit { + Diag.EqualityFactI(12, 42, $"OK"); } @@ -58,9 +56,9 @@ namespace Microsoft.Quantum.Tests { /// These tests are already performed in Solid itself, such that /// this operation tests whether we can reproduce that using our /// operation equality assertions. - operation SelfAdjointOperationsTest () : Unit { + operation SelfAdjointOperationsTest() : Unit { for (op in [I, X, Y, Z, H]) { - AssertOperationsEqualReferenced(3, ApplyToEach(op, _), ApplyToEachA(op, _)); + Diag.AssertOperationsEqualReferenced(3, ApplyToEach(op, _), ApplyToEachA(op, _)); } } @@ -71,17 +69,16 @@ namespace Microsoft.Quantum.Tests { /// /// # Remarks /// Marked as ex-fail due to known issues with Bound. - operation BindSelfAdjointOperationsTestExFail () : Unit { + operation BindSelfAdjointOperationsTestExFail() : Unit { for (op in [I, X, Y, Z, H]) { let arr = [op, Adjoint op]; let bound = BoundCA(arr); - AssertOperationsEqualReferenced(3, ApplyToEachCA(BoundCA(arr), _), ApplyToEachA(I, _)); + Diag.AssertOperationsEqualReferenced(3, ApplyToEachCA(BoundCA(arr), _), ApplyToEachA(I, _)); } } - operation AssertProbIntTest () : Unit { - + operation AssertProbIntTest() : Unit { let theta = 0.123; let prob = 0.015052858190174602; let tolerance = 1E-09; @@ -97,15 +94,14 @@ namespace Microsoft.Quantum.Tests { } - operation AssertPhaseTest () : Unit { - + operation AssertPhaseTest() : Unit { let phase = 0.456; let tolerance = 1E-09; using (qubits = Qubit[1]) { H(qubits[0]); Exp([PauliZ], phase, qubits); - AssertPhase(phase, qubits[0], tolerance); + Diag.AssertPhase(phase, qubits[0], tolerance); ResetAll(qubits); } } diff --git a/Standard/tests/Diagnostics/AllowTests.qs b/Standard/tests/Diagnostics/AllowTests.qs new file mode 100644 index 00000000000..d91648257bd --- /dev/null +++ b/Standard/tests/Diagnostics/AllowTests.qs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Tests { + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Diagnostics as Diag; + + operation CheckAllowNCallsTestShouldFail() : Unit { + within { + Diag.AllowAtMostNCallsCA(3, X, "Too many calls to X."); + } apply { + using (q = Qubit()) { + // Should run four times, one more + // than the three allowed. + for (idx in 0..3) { + X(q); + } + } + } + } + + @Diag.Test("QuantumSimulator") + operation CheckAllowNCalls() : Unit { + within { + Diag.AllowAtMostNCallsCA(4, X, "Too many calls to X."); + } apply { + using (q = Qubit()) { + // Should run four times, exactly as + // many times as allowed. + for (idx in 0..3) { + X(q); + } + } + } + } + + @Diag.Test("ToffoliSimulator") + operation CheckAllowNQubitsWithNestedCalls() : Unit { + // Here, the total number of allocated qubits exceeds our policy, + // but the number of thosse qubits allocated inside the policy + // condition is still OK such that this should pass. + using (outer = Qubit[4]) { + within { + Diag.AllowAtMostNQubits(5, "Too many additional qubit allocations."); + } apply { + using (qs = Qubit[2]) { + using (qs2 = Qubit[1]) { } + using (qs2 = Qubit[2]) { } + } + } + } + } + + operation CheckAllowNQubitsTestShouldFail() : Unit { + within { + Diag.AllowAtMostNQubits(3, "Too many additional qubit allocations."); + } apply { + using (qs = Qubit[2]) { + using (qs2 = Qubit[1]) { } + using (qs2 = Qubit[2]) { } + } + } + } + + @Diag.Test("QuantumSimulator") + operation CheckAllowNQubits() : Unit { + within { + Diag.AllowAtMostNQubits(3, "Too many additional qubit allocations."); + } apply { + using (qs = Qubit[3]) { } + } + } + +} diff --git a/Standard/tests/Diagnostics/DumpTests.cs b/Standard/tests/Diagnostics/DumpTests.cs new file mode 100644 index 00000000000..11827dccae5 --- /dev/null +++ b/Standard/tests/Diagnostics/DumpTests.cs @@ -0,0 +1,124 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.Quantum.Diagnostics.Emulation; +using Microsoft.Quantum.Intrinsic; +using Microsoft.Quantum.Simulation; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; +using Microsoft.Quantum.Standard.Emulation; +using Xunit; +using Assert = Xunit.Assert; + +namespace Microsoft.Quantum.Tests +{ + public class DumpOperation + { + [Fact] + public void DumpSReturnsCorrectMatrix() + { + using var sim = new QuantumSimulator(); + var diagnostics = new List(); + sim.OnDisplayableDiagnostic += diagnostic => + { + diagnostics.Add(diagnostic); + }; + sim.DisableLogToConsole(); + + DumpS.Run(sim).Wait(); + + Assert.Equal(diagnostics.Count, 1); + var diagnostic = diagnostics.Single(); + + Assert.IsType(diagnostic); + var unitary = diagnostic as DisplayableUnitaryOperator; + + Assert.NotNull(unitary); + Assert.Equal(1, unitary.Qubits?.Count); + Assert.NotNull(unitary.Data); + Assert.Equal(3, unitary.Data.ndim); + Assert.Equal(2, unitary.Data.shape[0]); + Assert.Equal(2, unitary.Data.shape[1]); + Assert.Equal(2, unitary.Data.shape[2]); + // Check that the matrix is [1 0; 0 𝑖]. + Assert.Equal(1.0, (double)unitary.Data[0, 0, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[0, 1, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[1, 0, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[1, 1, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[0, 0, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[0, 1, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[1, 0, 1], precision: 6); + Assert.Equal(1.0, (double)unitary.Data[1, 1, 1], precision: 6); + } + + + [Fact] + public void DumpCnotReturnsCorrectMatrix() + { + using var sim = new QuantumSimulator(); + var diagnostics = new List(); + sim.OnDisplayableDiagnostic += diagnostic => + { + diagnostics.Add(diagnostic); + }; + sim.DisableLogToConsole(); + + DumpCnot.Run(sim).Wait(); + + Assert.Equal(diagnostics.Count, 1); + var diagnostic = diagnostics.Single(); + + Assert.IsType(diagnostic); + var unitary = diagnostic as DisplayableUnitaryOperator; + + Assert.NotNull(unitary); + Assert.Equal(2, unitary.Qubits?.Count); + Assert.NotNull(unitary.Data); + Assert.Equal(3, unitary.Data.ndim); + Assert.Equal(4, unitary.Data.shape[0]); + Assert.Equal(4, unitary.Data.shape[1]); + Assert.Equal(2, unitary.Data.shape[2]); + // Check that the matrix is [1 0 0 0; 0 0 0 1; 0 0 1 0; 0 1 0 0]. + // real + Assert.Equal(1.0, (double)unitary.Data[0, 0, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[0, 1, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[0, 2, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[0, 3, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[1, 0, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[1, 1, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[1, 2, 0], precision: 6); + Assert.Equal(1.0, (double)unitary.Data[1, 3, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[2, 0, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[2, 1, 0], precision: 6); + Assert.Equal(1.0, (double)unitary.Data[2, 2, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[2, 3, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[3, 0, 0], precision: 6); + Assert.Equal(1.0, (double)unitary.Data[3, 1, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[3, 2, 0], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[3, 3, 0], precision: 6); + + // imaginary + Assert.Equal(0.0, (double)unitary.Data[0, 0, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[0, 1, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[0, 2, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[0, 3, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[1, 0, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[1, 1, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[1, 2, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[1, 3, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[2, 0, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[2, 1, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[2, 2, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[2, 3, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[3, 0, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[3, 1, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[3, 2, 1], precision: 6); + Assert.Equal(0.0, (double)unitary.Data[3, 3, 1], precision: 6); + } + } +} diff --git a/Standard/tests/Diagnostics/DumpTests.qs b/Standard/tests/Diagnostics/DumpTests.qs new file mode 100644 index 00000000000..29bf24cfa79 --- /dev/null +++ b/Standard/tests/Diagnostics/DumpTests.qs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Tests { + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Diagnostics as Diag; + + operation DumpS() : Unit { + Diag.DumpOperation(1, ApplyToEachCA(S, _)); + } + + operation DumpCnot() : Unit { + Diag.DumpOperation(2, ApplyToFirstTwoQubitsCA(CNOT, _)); + } + +} diff --git a/Standard/tests/MultiplexerTests.qs b/Standard/tests/MultiplexerTests.qs index 0f53c7f43e0..f081bc3b680 100644 --- a/Standard/tests/MultiplexerTests.qs +++ b/Standard/tests/MultiplexerTests.qs @@ -5,11 +5,12 @@ namespace Microsoft.Quantum.Tests { open Microsoft.Quantum.Canon; open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Convert; - open Microsoft.Quantum.Diagnostics; + // Needed to avoid colliding with deprecation stubs in Canon. + open Microsoft.Quantum.Diagnostics as Diag; open Microsoft.Quantum.Measurement; open Microsoft.Quantum.Arrays; - operation MultiplexZTestHelper (coefficients : Double[], multiplexerControl : LittleEndian, additionalControl : Qubit[], target : Qubit, tolerance : Double) : Unit { + operation MultiplexZTestHelper(coefficients : Double[], multiplexerControl : LittleEndian, additionalControl : Qubit[], target : Qubit, tolerance : Double) : Unit { let nCoefficients = Length(coefficients); let nQubits = (Length(multiplexerControl!) + Length(additionalControl)) + 1; @@ -46,7 +47,7 @@ namespace Microsoft.Quantum.Tests { // Case where Identity operation is performed. Message($"Controlled MultiplexZ test. coefficient {multiplexerControlInteger} of {nCoefficients-1}."); - AssertPhase(0.0, target, tolerance); + Diag.AssertPhase(0.0, target, tolerance); } else { mutable coeff = 0.0; @@ -57,13 +58,12 @@ namespace Microsoft.Quantum.Tests { if (Length(additionalControl) == 0) { Message($"MultiplexZ test. Qubits: {nQubits}; coefficient {multiplexerControlInteger} of {nCoefficients-1}."); - AssertPhase(coeff, target, tolerance); + Diag.AssertPhase(coeff, target, tolerance); } else { Message($"Controlled MultiplexZ test. Qubits: {nQubits}; coefficient {multiplexerControlInteger} of {nCoefficients-1}."); - AssertPhase(coeff, target, tolerance); + Diag.AssertPhase(coeff, target, tolerance); } - //AssertPhase(coeff, target, tolerance); } // Note that MeasureInteger has the effect of resetting its target, so @@ -71,8 +71,8 @@ namespace Microsoft.Quantum.Tests { Reset(target); } - - operation MultiplexZTest () : Unit { + @Diag.Test("QuantumSimulator") + operation TestMultiplexZ() : Unit { let maxQubits = 6; @@ -375,7 +375,7 @@ namespace Microsoft.Quantum.Tests { if (result == One) { Message($"MultiplexOperations test. Qubits: {nQubits}; idxTest {idxTest} of idxTarget {idxTarget} result {result}."); - EqualityFactI(idxTarget, idxTest, $"MultiplexOperations failed."); + Diag.EqualityFactI(idxTarget, idxTest, $"MultiplexOperations failed."); } } } @@ -388,28 +388,28 @@ namespace Microsoft.Quantum.Tests { if (result == One) { Message($"Controlled MultiplexOperations test. Qubits: {nQubits}; idxControl = 0; nControls = 1; idxTest {idxTest} of idxTarget {idxTarget} result {result}."); - EqualityFactI(idxTarget, idxTest, $"MultiplexOperations failed."); + Diag.EqualityFactI(idxTarget, idxTest, $"MultiplexOperations failed."); } set result = MultiplexOperationsTestHelper(idxTest, idxTarget, nQubits, 1, 1); if (result == One) { Message($"Controlled MultiplexOperations test. Qubits: {nQubits}; idxControl = 1; nControls = 1; idxTest {idxTest} of idxTarget {idxTarget} result {result}."); - EqualityFactI(idxTarget, idxTest, $"MultiplexOperations failed."); + Diag.EqualityFactI(idxTarget, idxTest, $"MultiplexOperations failed."); } set result = MultiplexOperationsTestHelper(idxTest, idxTarget, nQubits, 1, 2); if (result == One) { Message($"Controlled MultiplexOperations test. Qubits: {nQubits}; idxControl = 1; nControls = 2; idxTest {idxTest} of idxTarget {idxTarget} result {result}."); - EqualityFactI(idxTarget, idxTest, $"MultiplexOperations failed."); + Diag.EqualityFactI(idxTarget, idxTest, $"MultiplexOperations failed."); } set result = MultiplexOperationsTestHelper(idxTest, idxTarget, nQubits, 3, 2); if (result == One) { Message($"Controlled MultiplexOperations test. Qubits: {nQubits}; idxControl = 3; nControls = 2; idxTest {idxTest} of idxTarget {idxTarget} result {result}."); - EqualityFactI(idxTarget, idxTest, $"MultiplexOperations failed."); + Diag.EqualityFactI(idxTarget, idxTest, $"MultiplexOperations failed."); } } } @@ -518,7 +518,7 @@ namespace Microsoft.Quantum.Tests { set result = MultiplexOperationsFromGeneratorTestHelper(idxTest, idxTarget, nQubits, 0, 0); if(result == One){ Message($"MultiplexOperations test. Qubits: {nQubits}; idxTest {idxTest} of idxTarget {idxTarget} result {result}."); - EqualityFactI(idxTarget, idxTest, "MultiplexOperations failed."); + Diag.EqualityFactI(idxTarget, idxTest, "MultiplexOperations failed."); } } } @@ -529,25 +529,25 @@ namespace Microsoft.Quantum.Tests { set result = MultiplexOperationsFromGeneratorTestHelper(idxTest, idxTarget, nQubits, 0, 1); if(result == One){ Message($"Controlled MultiplexOperations test. Qubits: {nQubits}; idxControl = 0; nControls = 1; idxTest {idxTest} of idxTarget {idxTarget} result {result}."); - EqualityFactI(idxTarget, idxTest, "MultiplexOperations failed."); + Diag.EqualityFactI(idxTarget, idxTest, "MultiplexOperations failed."); } set result = MultiplexOperationsFromGeneratorTestHelper(idxTest, idxTarget, nQubits, 1, 1); if(result == One){ Message($"Controlled MultiplexOperations test. Qubits: {nQubits}; idxControl = 1; nControls = 1; idxTest {idxTest} of idxTarget {idxTarget} result {result}."); - EqualityFactI(idxTarget, idxTest, "MultiplexOperations failed."); + Diag.EqualityFactI(idxTarget, idxTest, "MultiplexOperations failed."); } set result = MultiplexOperationsFromGeneratorTestHelper(idxTest, idxTarget, nQubits, 1, 2); if(result == One){ Message($"Controlled MultiplexOperations test. Qubits: {nQubits}; idxControl = 1; nControls = 2; idxTest {idxTest} of idxTarget {idxTarget} result {result}."); - EqualityFactI(idxTarget, idxTest, "MultiplexOperations failed."); + Diag.EqualityFactI(idxTarget, idxTest, "MultiplexOperations failed."); } set result = MultiplexOperationsFromGeneratorTestHelper(idxTest, idxTarget, nQubits, 3, 2); if(result == One){ Message($"Controlled MultiplexOperations test. Qubits: {nQubits}; idxControl = 3; nControls = 2; idxTest {idxTest} of idxTarget {idxTarget} result {result}."); - EqualityFactI(idxTarget, idxTest, "MultiplexOperations failed."); + Diag.EqualityFactI(idxTarget, idxTest, "MultiplexOperations failed."); } } } diff --git a/Standard/tests/QcvvTests.cs b/Standard/tests/QcvvTests.cs index fc36a0857af..f3ce3386779 100644 --- a/Standard/tests/QcvvTests.cs +++ b/Standard/tests/QcvvTests.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; using System.Linq; using System.Runtime.InteropServices; diff --git a/Standard/tests/SimulatorTestTargets.cs b/Standard/tests/SimulatorTestTargets.cs index 0ed1d8d3f5e..c49bea1de67 100644 --- a/Standard/tests/SimulatorTestTargets.cs +++ b/Standard/tests/SimulatorTestTargets.cs @@ -58,7 +58,7 @@ public void QuantumSimulatorTarget(TestOperation opData) opData.TestOperationRunner(sim); } } - catch( System.BadImageFormatException e ) + catch (System.BadImageFormatException e) { throw new System.BadImageFormatException($"Could not load Quantum Simulator. If you are running tests using Visual Studio 2017, " + $"this problem can be fixed by using menu Test > Test Settings > Default Processor Architecture " + diff --git a/Standard/tests/Standard.Tests.csproj b/Standard/tests/Standard.Tests.csproj index 993c86a80fa..56b6a84ad56 100644 --- a/Standard/tests/Standard.Tests.csproj +++ b/Standard/tests/Standard.Tests.csproj @@ -1,4 +1,4 @@ - + diff --git a/Visualization/Common/DelaySign.cs b/Visualization/Common/DelaySign.cs new file mode 100644 index 00000000000..a2e08c82e45 --- /dev/null +++ b/Visualization/Common/DelaySign.cs @@ -0,0 +1,24 @@ +using System.Reflection; + +// Attributes for delay-signing +#if SIGNED +[assembly:AssemblyKeyFile("..\\..\\Build\\267DevDivSNKey2048.snk")] +[assembly:AssemblyDelaySign(true)] +#endif + +internal static class SigningConstants +{ +#if SIGNED + public const string PUBLIC_KEY = ", PublicKey=" + + "002400000c800000140100000602000000240000525341310008000001000100613399aff18ef1" + + "a2c2514a273a42d9042b72321f1757102df9ebada69923e2738406c21e5b801552ab8d200a65a2" + + "35e001ac9adc25f2d811eb09496a4c6a59d4619589c69f5baf0c4179a47311d92555cd006acc8b" + + "5959f2bd6e10e360c34537a1d266da8085856583c85d81da7f3ec01ed9564c58d93d713cd0172c" + + "8e23a10f0239b80c96b07736f5d8b022542a4e74251a5f432824318b3539a5a087f8e53d2f135f" + + "9ca47f3bb2e10aff0af0849504fb7cea3ff192dc8de0edad64c68efde34c56d302ad55fd6e80f3" + + "02d5efcdeae953658d3452561b5f36c542efdbdd9f888538d374cef106acf7d93a4445c3c73cd9" + + "11f0571aaf3d54da12b11ddec375b3"; +#else + public const string PUBLIC_KEY = ""; +#endif +} diff --git a/Visualization/src/CallSiteEncoders.cs b/Visualization/src/CallSiteEncoders.cs new file mode 100644 index 00000000000..35b3f246e36 --- /dev/null +++ b/Visualization/src/CallSiteEncoders.cs @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using System.Text; +using Microsoft.Jupyter.Core; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using static NumSharp.Slice; +using Microsoft.Quantum.IQSharp.Jupyter; +using System.Linq; +using System; + +namespace Microsoft.Quantum.Diagnostics.Emulation +{ + public class CallSitesToTextEncoder : IResultEncoder + { + private ILogger logger; + public string MimeType => MimeTypes.PlainText; + + public CallSitesToTextEncoder(ILogger logger) + { + this.logger = logger; + } + + public EncodedData? Encode(object displayable) => displayable switch + { + CallSites sites => $@"Calls to {sites.Subject}:\n\n{ + String.Join("\n---\n", + sites.Sites.Select( + call => String.Join("\n", + call.Select( + frame => $"- {frame}" + ) + ) + ) + ) + }".ToEncodedData(), + _ => null + }; + } + + public class CallSitesToHtmlEncoder : IResultEncoder + { + private readonly ILogger logger; + private readonly IConfigurationSource configurationSource; + public string MimeType => MimeTypes.Html; + + public CallSitesToHtmlEncoder( + ILogger logger, + IConfigurationSource configurationSource + ) + { + this.logger = logger; + this.configurationSource = configurationSource; + } + + public EncodedData? Encode(object displayable) => displayable switch + { + CallSites sites => $@" +
+ Calls to {sites.Subject}: +
    { + String.Join("\n", + sites.Sites.Select( + call => $@" +
    • { + String.Join("\n", call.Select( + frame => $"
    • {frame}
    • " + )) + }
  • + " + ) + ) + }
+
+ ".ToEncodedData(), + _ => null + }; + + } + +} diff --git a/Visualization/src/DisplayableUnitaryEncoders.cs b/Visualization/src/DisplayableUnitaryEncoders.cs new file mode 100644 index 00000000000..a9652bc6b82 --- /dev/null +++ b/Visualization/src/DisplayableUnitaryEncoders.cs @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using System.Text; +using Microsoft.Jupyter.Core; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using static NumSharp.Slice; +using Microsoft.Quantum.IQSharp.Jupyter; +using System.Linq; +using System; + +namespace Microsoft.Quantum.Diagnostics.Emulation +{ + public class DisplayableUnitaryOperatorToTextEncoder : IResultEncoder + { + private ILogger logger; + public string MimeType => MimeTypes.PlainText; + + public DisplayableUnitaryOperatorToTextEncoder(ILogger logger) + { + this.logger = logger; + } + + public EncodedData? Encode(object displayable) + { + if (displayable is DisplayableUnitaryOperator op) + { + if (op?.Data is null) + { + logger.LogError("Asked to encode a displayable unitary operator, but its data was null. This should not happen."); + return null; + } + return $"Real:\n{op.Data[Ellipsis, 0]}\nImag:\n{op.Data[Ellipsis, 1]}".ToEncodedData(); + } + else return null; + } + + } + + public class DisplayableUnitaryOperatorToHtmlEncoder : IResultEncoder + { + private readonly ILogger logger; + private readonly IConfigurationSource configurationSource; + public string MimeType => MimeTypes.Html; + + public DisplayableUnitaryOperatorToHtmlEncoder( + ILogger logger, + IConfigurationSource configurationSource + ) + { + this.logger = logger; + this.configurationSource = configurationSource; + } + + public EncodedData? Encode(object displayable) + { + bool IsNearZero(double value) => + System.Math.Abs(value) <= configurationSource.TruncationThreshold; + + if (displayable is DisplayableUnitaryOperator op) + { + if (op?.Data is null) + { + logger.LogError("Asked to encode a displayable unitary operator, but its data was null. This should not happen."); + return null; + } + + var precision = configurationSource.GetOptionOrDefault( + "dump.unitaryPrecision", + 3 + ); + + var outputMatrix = String.Join( + " \\\\\n", + op.Data.EnumerateOverAxis().Cast().Select( + row => + String.Join(" & ", + row.EnumerateOverAxis() + .Cast() + .Select(element => + { + var format = $"{{0:G{precision}}}"; + var re = (double)element[0]; + var im = (double)element[1]; + var reFmt = String.Format(format, re); + var imFmt = String.Format(format, System.Math.Abs(im)) + "i"; + if (IsNearZero(re) && IsNearZero(im)) + { + return "0"; + } + else if (IsNearZero(im)) + { + return reFmt; + } + else if (IsNearZero(re)) + { + return im < 0.0 ? $"-{imFmt}" : imFmt; + } + else + { + return $"{reFmt} {(im < 0.0 ? "-" : "+")} {imFmt}"; + } + }) + ) + ) + ); + return $@" + + + + + + + + + +
Qubit IDs{String.Join(", ", op.Qubits.Select(q => q.Id))} +
Unitary representation$$ + \left(\begin{{matrix}} + {outputMatrix} + \end{{matrix}}\right) + $$
+ ".ToEncodedData(); + } + else return null; + } + + } + +} diff --git a/Visualization/src/Extensions.cs b/Visualization/src/Extensions.cs new file mode 100644 index 00000000000..5bacbdd3c3a --- /dev/null +++ b/Visualization/src/Extensions.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using System.Text; +using Microsoft.Jupyter.Core; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using NumSharp; +using static NumSharp.Slice; +using Microsoft.Quantum.IQSharp.Jupyter; +using System.Linq; +using System; +using System.Collections.Generic; + +namespace Microsoft.Quantum.Diagnostics.Emulation +{ + + internal static class Extensions + { + internal static IEnumerable EnumerateOverAxis(this NumSharp.NDArray array, int axis = 0) + { + var prefix = Enumerable.Repeat(All, axis).ToArray(); + foreach (var idx in Enumerable.Range(0, array.Shape[axis])) + { + Slice[] slice = prefix.Concat(new Slice[] { idx, Ellipsis }).ToArray(); + yield return array[slice]; + } + } + } + +} diff --git a/Visualization/src/Visualization.csproj b/Visualization/src/Visualization.csproj new file mode 100644 index 00000000000..c9250f4e0c5 --- /dev/null +++ b/Visualization/src/Visualization.csproj @@ -0,0 +1,45 @@ + + + + netstandard2.1 + Microsoft.Quantum.Standard.Visualization + 1591 + + + + Microsoft + Provides IQ# visualization support for Microsoft's Q# standard libraries. + © Microsoft Corporation. All rights reserved. + See: https://docs.microsoft.com/en-us/quantum/relnotes/ + MIT + https://github.com/Microsoft/Quantum + qdk-nuget-icon.png + Quantum Q# Qsharp + false + true + true + true + snupkg + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb;.xml + + + + + + + + + + + + + + + + + + + + + +