diff --git a/src/Simulation/Common/IQuantumExecutor.cs b/src/Simulation/Common/IQuantumExecutor.cs new file mode 100644 index 00000000000..3c9d1236250 --- /dev/null +++ b/src/Simulation/Common/IQuantumExecutor.cs @@ -0,0 +1,583 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + /// + /// An interface for implementing QDK target machines that work on a quantum circuit level. + /// It is intended to be used with . + /// + /// + /// Simulators implemented using interface do not manage qubits on their own. + /// Instead they are notified when qubits are allocated, released, borrowed and returned. + /// + public interface IQuantumExecutor + { + /// + /// Called when Microsoft.Quantum.Intrinsic.X is called in Q#. + /// In Q# the operation applies X gate to . The gate is given by matrix X=((0,1),(1,0)). + /// + /// + /// When adjoint of X is called in Q#, is called because X is self-adjoint. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// Qubit to which the gate should be applied. + void X(Qubit qubit); + + /// + /// Called when controlled Microsoft.Quantum.Intrinsic.X is called in Q#. + /// In Q# the operation applies X gate to controlled on . The gate is given by matrix X=((0,1),(1,0)). + /// + /// + /// When adjoint of Controlled X is called in Q#, is called because X is self-adjoint. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// The array of qubits on which the operation is controlled. + /// Qubit to which the gate should be applied. + void ControlledX(IQArray controls, Qubit qubit); + + /// + /// Called when Microsoft.Quantum.Intrinsic.Y is called in Q#. + /// In Q# the operation applies Y gate to . The gate is given by matrix Y=((0,-𝑖),(𝑖,0)). + /// + /// + /// When adjoint of Y is called in Q#, is called because Y is self-adjoint. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// Qubit to which the gate should be applied. + void Y(Qubit qubit); + + /// + /// Called when controlled Microsoft.Quantum.Intrinsic.Y is called in Q#. + /// In Q# the operation applies X gate to controlled on . The gate is given by matrix Y=((0,-𝑖),(𝑖,0)). + /// + /// + /// When adjoint of Controlled Y is called in Q#, is called because Y is self-adjoint. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// The array of qubits on which the operation is controlled. + /// Qubit to which the gate should be applied. + void ControlledY(IQArray controls, Qubit qubit); + + /// + /// Called when Microsoft.Quantum.Intrinsic.Z is called in Q#. + /// In Q# the operation applies Z gate to . The gate is given by matrix Z=((1,0),(0,-1)). + /// + /// + /// When adjoint of Z is called in Q#, is called because Z is self-adjoint. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// Qubit to which the gate should be applied. + void Z(Qubit qubit); + + /// + /// Called when controlled Microsoft.Quantum.Intrinsic.Z is called in Q#. + /// In Q# the operation applies Z gate to controlled on . The gate is given by matrix Z=((1,0),(0,-1)). + /// + /// + /// When adjoint of Controlled Z is called in Q#, is called because Z is self-adjoint. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// The array of qubits on which the operation is controlled. + /// Qubit to which the gate should be applied. + void ControlledZ(IQArray controls, Qubit qubit); + + /// + /// Called when Microsoft.Quantum.Intrinsic.SWAP is called in Q#. + /// In Q# the operation applies gate given by rule |ψ⟩⊗|ϕ⟩ ↦ |ϕ⟩⊗|ψ⟩ where |ϕ⟩,|ψ⟩ arbitrary one qubit states. + /// + /// + /// When adjoint of SWAP is called in Q#, is called because SWAP is self-adjoint. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// First qubit to be swapped. + /// Second qubit to be swapped. + void SWAP(Qubit qubit1, Qubit qubit2); + + /// + /// Called when controlled Microsoft.Quantum.Intrinsic.SWAP is called in Q#. + /// In Q# the operation applies gate given by rule |ψ⟩⊗|ϕ⟩ ↦ |ϕ⟩⊗|ψ⟩ where |ϕ⟩,|ψ⟩ arbitrary one qubit states controlled on . + /// + /// + /// When adjoint of Controlled SWAP is called in Q#, is called because SWAP is self-adjoint. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// The array of qubits on which the operation is controlled. + /// First qubit to be swapped. + /// Second qubit to be swapped. + void ControlledSWAP(IQArray controls, Qubit qubit1, Qubit qubit2); + + /// + /// Called when Microsoft.Quantum.Intrinsic.H is called in Q#. + /// In Q# the operation applies Hadamard gate to . The gate is given by matrix H=((1,1),(1,-1))/√2. + /// + /// + /// When adjoint of H is called in Q#, is called because Hadamard is self-adjoint. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// Qubit to which the gate should be applied. + void H(Qubit qubit); + + /// + /// Called when controlled Microsoft.Quantum.Intrinsic.H is called in Q#. + /// In Q# the operation applies Hadamard gate to controlled on . The gate is given by matrix H=((1,1),(1,-1))/√2. + /// + /// + /// When adjoint of Controlled H is called in Q#, is called because Hadamard is self-adjoint. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// The array of qubits on which the operation is controlled. + /// Qubit to which the gate should be applied. + void ControlledH(IQArray controls, Qubit qubit); + + /// + /// Called when Microsoft.Quantum.Intrinsic.S is called in Q#. + /// In Q# the operation applies S gate to . The gate is given by matrix S=((1,0),(0,𝑖)). + /// + /// + /// When adjoint of S is called in Q#, is called. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// Qubit to which the gate should be applied. + void S(Qubit qubit); + + /// + /// Called when controlled Microsoft.Quantum.Intrinsic.S is called in Q#. + /// In Q# the operation applies S gate to controlled on . The gate is given by matrix S=((1,0),(0,𝑖)). + /// + /// + /// When adjoint of Controlled S is called in Q#, is called. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// The array of qubits on which the operation is controlled. + /// Qubit to which the gate should be applied. + void ControlledS(IQArray controls, Qubit qubit); + + /// + /// Called when adjoint Microsoft.Quantum.Intrinsic.S is called in Q#. + /// In Q# the operation applies S† gate to . The gate is given by matrix S†=((1,0),(0,-𝑖)). + /// + /// + /// When adjoint of Adjoint S is called in Q#, is called. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// Qubit to which the gate should be applied. + void SAdj(Qubit qubit); + + /// + /// Called when controlled adjoint Microsoft.Quantum.Intrinsic.S is called in Q#. + /// In Q# the operation applies S† gate to controlled on . The gate is given by matrix S†=((1,0),(0,𝑖)). + /// + /// + /// When adjoint of Controlled S† is called in Q#, is called. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// The array of qubits on which the operation is controlled. + /// Qubit to which the gate should be applied. + void ControlledSAdj(IQArray controls, Qubit qubit); + + /// + /// Called when Microsoft.Quantum.Intrinsic.T is called in Q#. + /// In Q# the operation applies T gate to . The gate is given by matrix T=((1,0),(0,𝑒𝑥𝑝(𝑖⋅π/4))). + /// + /// + /// When adjoint of T is called in Q#, is called. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// Qubit to which the gate should be applied. + void T(Qubit qubit); + + /// + /// Called when controlled Microsoft.Quantum.Intrinsic.T is called in Q#. + /// In Q# the operation applies T gate to controlled on . The gate is given by matrix T=((1,0),(0,𝑒𝑥𝑝(𝑖⋅π/4))). + /// + /// + /// When adjoint of Controlled T is called in Q#, is called. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// The array of qubits on which the operation is controlled. + /// Qubit to which the gate should be applied. + void ControlledT(IQArray controls, Qubit qubit); + + /// + /// Called when adjoint Microsoft.Quantum.Intrinsic.T is called in Q#. + /// In Q# the operation applies T† gate to . The gate is given by matrix T†=((1,0),(0,𝑒𝑥𝑝(-𝑖⋅π/4))). + /// + /// + /// When adjoint of Adjoint T is called in Q#, is called. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// Qubit to which the gate should be applied. + void TAdj(Qubit qubit); + + /// + /// Called when controlled adjoint Microsoft.Quantum.Intrinsic.T is called in Q#. + /// In Q# the operation applies T† gate to controlled on . The gate is given by matrix T†=((1,0),(0,𝑒𝑥𝑝(-𝑖⋅π/4))). + /// + /// + /// When adjoint of Controlled T† is called in Q#, is called. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// The array of qubits on which the operation is controlled. + /// Qubit to which the gate should be applied. + void ControlledTAdj(IQArray controls, Qubit qubit); + + /// + /// Called when Microsoft.Quantum.Intrinsic.R is called in Q#. + /// In Q# the operation applies 𝑒𝑥𝑝(-𝑖⋅/2) to . + /// + /// + /// When adjoint of R is called in Q#, is called with replaced by -. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// Pauli operator to be exponentiated to form the rotation. + /// Angle about which the qubit is to be rotated. + /// Qubit to which the gate should be applied. + void R(Pauli axis, double theta, Qubit qubit); + + /// + /// Called when controlled Microsoft.Quantum.Intrinsic.R is called in Q#. + /// In Q# the operation applies 𝑒𝑥𝑝(-𝑖⋅/2) to controlled on . + /// + /// + /// When adjoint of Controlled R is called in Q#, is called with replaced by -. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// The array of qubits on which the operation is controlled. + /// Pauli operator to be exponentiated to form the rotation. + /// Angle about which the qubit is to be rotated. + /// Qubit to which the gate should be applied. + void ControlledR(IQArray controls, Pauli axis, double theta, Qubit qubit); + + /// + /// Called when Microsoft.Quantum.Intrinsic.RFrac is called in Q#. + /// In Q# the operation applies 𝑒𝑥𝑝(𝑖⋅π⋅/2^) to . + /// + /// + /// When adjoint of RFrac is called in Q#, is called with replaced by -. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// Pauli operator to be exponentiated to form the rotation. + /// Numerator in the dyadic fraction representation of the angle by which the qubit is to be rotated. + /// Power of two specifying the denominator of the angle by which the qubit is to be rotated. + /// Qubit to which the gate should be applied. + void RFrac(Pauli axis, long numerator, long power, Qubit qubit); + + /// + /// Called when a controlled Microsoft.Quantum.Intrinsic.RFrac is called in Q#. + /// In Q# the operation applies 𝑒𝑥𝑝(𝑖⋅π⋅/2^) to controlled on . + /// + /// + /// When adjoint of Controlled RFrac is called in Q#, is called with replaced by -. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// The array of qubits on which the operation is controlled. + /// Pauli operator to be exponentiated to form the rotation. + /// Numerator in the dyadic fraction representation of the angle by which the qubit is to be rotated. + /// Power of two specifying the denominator of the angle by which the qubit is to be rotated. + /// Qubit to which the gate should be applied. + void ControlledRFrac(IQArray controls, Pauli axis, long numerator, long power, Qubit qubit); + + /// + /// Called when Microsoft.Quantum.Intrinsic.R1 is called in Q#. + /// In Q# the operation applies gate given by matrix ((1,0),(0,𝑒𝑥𝑝(𝑖⋅))) to . + /// + /// + /// When adjoint of R1 is called in Q#, is called with replaced by -. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// Angle about which the qubit is to be rotated. + /// Qubit to which the gate should be applied. + void R1(double theta, Qubit qubit); + + /// + /// Called when controlled Microsoft.Quantum.Intrinsic.R is called in Q#. + /// In Q# the operation applies gate given by matrix ((1,0),(0,𝑒𝑥𝑝(𝑖⋅))) to controlled on . + /// + /// + /// When adjoint of Controlled R1 is called in Q#, is called with replaced by -. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// The array of qubits on which the operation is controlled. + /// Angle about which the qubit is to be rotated. + /// Qubit to which the gate should be applied. + void ControlledR1(IQArray controls, double theta, Qubit qubit); + + /// + /// Called when Microsoft.Quantum.Intrinsic.R1Frac is called in Q#. + /// In Q# the operation applies gate given by matrix ((1,0),(0,𝑒𝑥𝑝(𝑖⋅π⋅/2^))) to . + /// + /// + /// When adjoint of R1Frac is called in Q#, is called with replaced by -. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// Numerator in the dyadic fraction representation of the angle by which the qubit is to be rotated. + /// Power of two specifying the denominator of the angle by which the qubit is to be rotated. + /// Qubit to which the gate should be applied. + void R1Frac(long numerator, long power, Qubit qubit); + + /// + /// Called when a controlled Microsoft.Quantum.Intrinsic.R1Frac is called in Q#. + /// In Q# the operation applies gate given by matrix ((1,0),(0,𝑒𝑥𝑝(𝑖⋅π⋅/2^))) to controlled on . + /// + /// + /// When adjoint of Controlled RFrac is called in Q#, is called with replaced by -. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// The array of qubits on which the operation is controlled. + /// Numerator in the dyadic fraction representation of the angle by which the qubit is to be rotated. + /// Power of two specifying the denominator of the angle by which the qubit is to be rotated. + /// Qubit to which the gate should be applied. + void ControlledR1Frac(IQArray controls, long numerator, long power, Qubit qubit); + + /// + /// Called when Microsoft.Quantum.Intrinsic.Exp is called in Q#. + /// In Q# the operation applies 𝑒𝑥𝑝(𝑖⋅) to . + /// + /// + /// When adjoint of Exp is called in Q#, is called with replaced by -. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// Array of single-qubit Pauli values representing a multi-qubit Pauli to be applied. + /// Angle about the given multi-qubit Pauli operator by which the target register is to be rotated. + /// Register to apply the exponent to. + void Exp(IQArray paulis, double theta, IQArray qubits); + + /// + /// Called when a controlled Microsoft.Quantum.Intrinsic.Exp is called in Q#. + /// In Q# the operation applies 𝑒𝑥𝑝(𝑖⋅) to controlled on . + /// + /// + /// When adjoint of Controlled Exp is called in Q#, is called with replaced by -. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// The array of qubits on which the operation is controlled. + /// Array of single-qubit Pauli values representing a multi-qubit Pauli to be applied. + /// Angle about the given multi-qubit Pauli operator by which the target register is to be rotated. + /// Register to apply the exponent to. + void ControlledExp(IQArray controls, IQArray paulis, double theta, IQArray qubits); + + /// + /// Called when Microsoft.Quantum.Intrinsic.ExpFrac is called in Q#. + /// In Q# the operation applies 𝑒𝑥𝑝(𝑖⋅π⋅/2^) to . + /// + /// + /// When adjoint of ExpFrac is called in Q#, is called with replaced by -. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// Array of single-qubit Pauli values representing a multi-qubit Pauli to be applied. + /// Numerator in the dyadic fraction representation of the angle by which the qubit register is to be rotated. + /// Power of two specifying the denominator of the angle by which the qubit register is to be rotated. + /// Register to apply the exponent to. + void ExpFrac(IQArray paulis, long numerator, long power, IQArray qubits); + + /// + /// Called when controlled Microsoft.Quantum.Intrinsic.ExpFrac is called in Q#. + /// In Q# the operation applies 𝑒𝑥𝑝(𝑖⋅π⋅/2^) to controlled on . + /// + /// + /// When adjoint of Controlled ExpFrac is called in Q#, is called with replaced by -. + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// The array of qubits on which the operation is controlled. + /// Array of single-qubit Pauli values representing a multi-qubit Pauli to be applied. + /// Numerator in the dyadic fraction representation of the angle by which the qubit register is to be rotated. + /// Power of two specifying the denominator of the angle by which the qubit register is to be rotated. + /// Register to apply the exponent to. + void ControlledExpFrac(IQArray controls, IQArray paulis, long numerator, long power, IQArray qubits); + + /// + /// Called when Microsoft.Quantum.Intrinsic.M is called in Q#. + /// In Q# the operation measures in Z basis, in other words in the computational basis. + /// + /// + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// Class implementing interface can return any class derived from . + /// + /// Qubit to which the gate should be applied. + /// Zero if the +1 eigenvalue is observed, and One if the -1 eigenvalue is observed. + Result M(Qubit qubit); + + /// + /// Called when Microsoft.Quantum.Intrinsic.Measure is called in Q#. + /// In Q# the operation measures multi-qubit Pauli observable given by on . + /// + /// + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// Class implementing interface can return any class derived from . + /// + /// Qubits to which the gate should be applied. + /// Array of single-qubit Pauli values describing multi-qubit Pauli observable. + /// Zero if the +1 eigenvalue is observed, and One if the -1 eigenvalue is observed. + Result Measure(IQArray bases, IQArray qubits); + + /// + /// Called when Microsoft.Quantum.Intrinsic.Reset is called in Q#. + /// In Q# the operation, measures and ensures it is in the |0⟩ state such that it can be safely released. + /// + /// + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// Qubit to which the gate should be applied. + void Reset(Qubit qubit); + + /// + /// Called when Microsoft.Quantum.Intrinsic.Assert is called in Q#. + /// + /// + /// The names and the order of the parameters are the same as for the corresponding Q# operation. + /// + /// A multi-qubit Pauli operator, for which the measurement outcome is asserted. + /// A register on which to make the assertion. + /// The expected result of Measure(bases, qubits) + /// A message to be reported if the assertion fails. + void Assert(IQArray bases, IQArray qubits, Result result, string msg); + + /// + /// Called when Microsoft.Quantum.Intrinsic.Assert is called in Q#. + /// + /// + /// The names and the order of the parameters is similar to the corresponding Q# operation./ + /// + /// A multi-qubit Pauli operator, for which the measurement outcome is asserted. + /// A register on which to make the assertion. + /// The probability with which result Zero is expected. + /// A message to be reported if the assertion fails. + /// The precision with which the probability of Zero outcome is specified. + void AssertProb(IQArray bases, IQArray qubits, double probabilityOfZero, string msg, double tol); + + /// + /// Called before a call to any Q# operation. + /// + /// Information about operation being called. + /// Information about the arguments passed to the operation. + /// + /// To get the fully qualified Q# name of operation being called use . + /// For the variant of operation, that is to find if Adjoint, Controlled or Controlled Adjoint being called use . + /// To get a sequence of all qubits passed to the operation use . + /// + void OnOperationStart(ICallable operation, IApplyData arguments); + + /// + /// Called when returning from a call to any Q# operation. + /// + /// Information about operation being called. + /// Information about the arguments passed to the operation. + /// + /// To get the fully qualified Q# name of operation being called use . + /// For the variant of operation, that is to find if Adjoint, Controlled or Controlled Adjoint being called use . + /// To get a sequence of all qubits passed to the operation use . + /// + void OnOperationEnd(ICallable operation, IApplyData arguments); + + /// + /// Called in the end of the call to any Q# operation. + /// + /// Information about exception that was raised. + /// + /// + void OnFail(System.Runtime.ExceptionServices.ExceptionDispatchInfo exceptionDispatchInfo); + + /// + /// Intended for a limited support of branching upon measurement results on a simulator level. + /// + /// The result of the measurement upon which branching is to be performed. + /// Corresponds to quantum program that must be executed if result is + /// Corresponds to quantum program that must be executed if result is + /// + /// Calling onZero() will result in the execution of quantum program that Q# user intends to execute if result is . + /// The program is executed with the same instance of interface. + /// + void ClassicallyControlled(Result measurementResult, Action onZero, Action onOne); + + /// + /// Intended for a limited support of branching upon measurement results on a simulator level. + /// + /// The actual results of the measurements of a number of qubits upon which branching is to be performed. + /// The expected values of results of the measurements of these qubits. + /// Corresponds to quantum program that must be executed if all values are equal to corresponding + /// Corresponds to quantum program that must be executed if at least one of the values is not equal to a corresponding + /// + /// Calling onZero() will result in the execution of quantum program that Q# user intends to execute if result is . + /// The program is executed with the same instance of interface. + /// + void ClassicallyControlled(IQArray measurementResults, IQArray resultsValues, Action equalOp, Action nonEqualOp); + + /// + /// Called when qubits are allocated by Q# using block. + /// + /// Qubits that are allocated. + /// + /// Every qubit in simulation framework has a unique identifier . + /// All newly allocated qubits are in |0⟩ state. + /// + void OnAllocateQubits(IQArray qubits); + + /// + /// Called when qubits are released in Q# in the end of using block. + /// + /// Qubits that are released. + /// + /// Every qubit in simulation framework has a unique identifier . + /// All qubits are expected to be released in |0⟩ state. + /// + void OnReleaseQubits(IQArray qubits); + + /// + /// Called when qubits are borrowed by Q# borrowing block. + /// + /// Qubits that are borrowed. + /// + /// Every qubit in simulation framework has a unique identifier . + /// Borrowed qubits can be in any state. + /// + void OnBorrowQubits(IQArray qubits); + + /// + /// Called when qubits are returned in the end of Q# borrowing block. + /// + /// Qubits that has been allocated. + /// + /// Every qubit in simulation framework has a unique identifier . + /// Borrowed qubits are expected to be returned in the same state as the state they have been borrowed in. + /// + void OnReturnQubits(IQArray qubits); + + /// + /// Called when + /// Microsoft.Quantum.Diagnostics.DumpMachine + /// is called in Q#. + /// + /// + /// The names and the order of the parameters are similar to corresponding Q# operation. + /// + /// + /// Provides information on where to generate the DumpMachine state. + void OnDumpMachine(T location); + + /// + /// Called when + /// Microsoft.Quantum.Diagnostics.DumpRegister + /// is called in Q#. + /// + /// + /// The names and the order of the parameters are similar to corresponding Q# operation. + /// + /// + /// Provides information on where to generate the DumpRegister state. + /// The list of qubits to report. + void OnDumpRegister(T location, IQArray qubits); + + /// + /// Called when Microsoft.Quantum.Intrinsic.Message is called in Q#. + /// + /// + /// The names and the order of the parameters are the same as corresponding Q# operations. + /// + /// The message to be reported. + void OnMessage(string msg); + } +} diff --git a/src/Simulation/Common/Microsoft.Quantum.Simulation.Common.csproj b/src/Simulation/Common/Microsoft.Quantum.Simulation.Common.csproj index 369209ad295..54886046904 100644 --- a/src/Simulation/Common/Microsoft.Quantum.Simulation.Common.csproj +++ b/src/Simulation/Common/Microsoft.Quantum.Simulation.Common.csproj @@ -8,6 +8,10 @@ x64 + + + + diff --git a/src/Simulation/Common/PortablePDBReader.cs b/src/Simulation/Common/PortablePDBReader.cs new file mode 100644 index 00000000000..4f094820a06 --- /dev/null +++ b/src/Simulation/Common/PortablePDBReader.cs @@ -0,0 +1,279 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Quantum.Simulation.Common +{ + using System.Collections.Generic; + using System.Reflection.Metadata; + using System; + using System.IO; + using System.IO.Compression; + using System.Text; + using Newtonsoft.Json.Linq; + using System.Text.RegularExpressions; + using Microsoft.Quantum.Simulation.Core; + + // Based on https://github.com/microsoft/BPerf/blob/master/WebViewer/Microsoft.BPerf.SymbolicInformation.ProgramDatabase/PortablePdbSymbolReader.cs + class PortablePdbSymbolReader + { + private static readonly Guid SourceLink = new Guid("CC110556-A091-4D38-9FEC-25AB9A351A6A"); + + private static readonly Guid EmbeddedSource = new Guid("0E8A571B-6926-466E-B4AD-8AB04611F5FE"); + + public static Dictionary GetEmbeddedFiles(string pdbFilePath) + { + Dictionary embeddedFiles = new Dictionary(); + + using (FileStream stream = File.OpenRead(pdbFilePath)) + using (MetadataReaderProvider metadataReaderProvider = MetadataReaderProvider.FromPortablePdbStream(stream)) + { + MetadataReader metadataReader = metadataReaderProvider.GetMetadataReader(); + CustomDebugInformationHandleCollection customDebugInformationHandles = metadataReader.CustomDebugInformation; + foreach (var customDebugInformationHandle in customDebugInformationHandles) + { + CustomDebugInformation customDebugInformation = metadataReader.GetCustomDebugInformation(customDebugInformationHandle); + if (metadataReader.GetGuid(customDebugInformation.Kind) == EmbeddedSource) + { + byte[] embeddedSource = metadataReader.GetBlobBytes(customDebugInformation.Value); + Int32 uncompressedSourceFileSize = BitConverter.ToInt32(embeddedSource, 0); + if (uncompressedSourceFileSize != 0) + { + Document document = metadataReader.GetDocument((DocumentHandle)customDebugInformation.Parent); + string sourceFileName = System.IO.Path.GetFullPath(metadataReader.GetString(document.Name)); + + // See https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#embedded-source-c-and-vb-compilers + // Decompress embedded source + MemoryStream memoryStream = + new MemoryStream(embeddedSource, sizeof(Int32), embeddedSource.Length - sizeof(Int32)); + + using (DeflateStream decompressionStream = new DeflateStream(memoryStream, CompressionMode.Decompress)) + { + MemoryStream decompressed = new MemoryStream(new byte[uncompressedSourceFileSize], true); + decompressionStream.CopyTo(decompressed); + embeddedFiles.Add(sourceFileName, Encoding.UTF8.GetString(decompressed.ToArray())); + } + } + } + } + } + return embeddedFiles; + } + + public static string GetSourceLinkString(string pdbFilePath) + { + using (FileStream stream = File.OpenRead(pdbFilePath)) + { + using (MetadataReaderProvider metadataReaderProvider = MetadataReaderProvider.FromPortablePdbStream(stream)) + { + MetadataReader metadataReader = metadataReaderProvider.GetMetadataReader(); + CustomDebugInformationHandleCollection customDebugInformationHandles = metadataReader.CustomDebugInformation; + foreach (var customDebugInformationHandle in customDebugInformationHandles) + { + CustomDebugInformation customDebugInformation = metadataReader.GetCustomDebugInformation(customDebugInformationHandle); + + if (metadataReader.GetGuid(customDebugInformation.Kind) == SourceLink) + { + return Encoding.UTF8.GetString(metadataReader.GetBlobBytes(customDebugInformation.Value)); + } + } + } + } + return null; + } + + public static Tuple[] ParseSourceLinkString(string SourceLinkString) + { + if (SourceLinkString == null) return null; + List> pairs = new List>(); + JObject jsonResponse = JObject.Parse(SourceLinkString); + JObject document = JObject.Parse(jsonResponse["documents"].ToString()); + foreach (JProperty property in document.Properties()) + { + string fullPath = System.IO.Path.GetFullPath(property.Name.Replace("*", "")); + pairs.Add(new Tuple(fullPath, property.Value.ToObject().Replace("*", ""))); + } + return pairs.ToArray(); + } + + public static string TryFormatGitHubUrl(Tuple rawUrl, int lineNumber) + { + if (rawUrl == null) + return null; + + string result = null; + if (rawUrl.Item1.StartsWith(@"https://raw.githubusercontent.com")) + { + result = Regex.Replace(rawUrl.Item1, "[a-f0-9]+" + Regex.Escape("/") + "$", (Match m) => { return @"blob/" + m.Value; }); + result = result.Replace(@"https://raw.githubusercontent.com", @"https://github.com"); + result += rawUrl.Item2; + result += $"#L{lineNumber}"; + } + else + { + result = rawUrl.Item1 + rawUrl.Item2; + } + return result; + } + + public static string GetPDBLocation(ICallable callable) + { + try + { + string filename = System.IO.Path.ChangeExtension(callable.UnwrapCallable().GetType().Assembly.Location, ".pdb"); + if( File.Exists(filename) ) + { + return filename; + } + else + { + return null; + } + } + catch(NotSupportedException) + { + return null; + } + } + } + + public static class PortablePDBPathRemappingCache + { + [ThreadStatic] + private static Dictionary[]> pdbLocationToPathRemapping = null; + + public static Tuple[] GetRemappingInfromation(string pdbLocation) + { + if (pdbLocationToPathRemapping == null) + { + pdbLocationToPathRemapping = new Dictionary[]>(); + } + + Tuple[] remappings; + if (pdbLocationToPathRemapping.TryGetValue(pdbLocation, out remappings)) + { + return remappings; + } + else + { + try + { + string sourceLinkString = PortablePdbSymbolReader.GetSourceLinkString(pdbLocation); + remappings = PortablePdbSymbolReader.ParseSourceLinkString(sourceLinkString); + } + finally + { + pdbLocationToPathRemapping.Add(pdbLocation, remappings); + } + return remappings; + } + } + + /// + /// Tuple of strings such that full URL consists of their concatenation. + /// First part of URL is URL root for all files in PDB and second part is relative path to given file. + /// + public static Tuple TryGetFileUrl(string pdbLocation, string fileName ) + { + if (fileName == null) return null; + + string prefix = null; + string rest = null; + + Tuple[] fileCorrespondence = GetRemappingInfromation(pdbLocation); + if (fileCorrespondence != null) + { + foreach (Tuple replacement in fileCorrespondence) + { + if (fileName.StartsWith(replacement.Item1)) + { + rest = fileName.Replace(replacement.Item1, ""); + prefix = replacement.Item2; + if (prefix.Contains(@"/") && rest.Contains(@"\")) + { + rest = rest.Replace(@"\", @"/"); + } + break; + } + } + } + return new Tuple(prefix,rest); + } + } + + public static class PortablePDBEmbeddedFilesCache + { + public const string lineMarkPrefix = ">>>"; + public const int lineNumberPaddingWidth = 6; + + [ThreadStatic] + private static Dictionary> pdbLocationToEmbeddedFiles = null; + + public static Dictionary GetEmbeddedFiles(string pdbLocation) + { + if (pdbLocationToEmbeddedFiles == null) + { + pdbLocationToEmbeddedFiles = new Dictionary>(); + } + + Dictionary embeddedFilesFromPath = null; + if (pdbLocationToEmbeddedFiles.TryGetValue(pdbLocation, out embeddedFilesFromPath)) + { + return embeddedFilesFromPath; + } + else + { + try + { + embeddedFilesFromPath = PortablePdbSymbolReader.GetEmbeddedFiles(pdbLocation); + } + finally + { + pdbLocationToEmbeddedFiles.Add(pdbLocation, embeddedFilesFromPath); + } + return embeddedFilesFromPath; + } + } + + public static string GetEmbeddedFileRange( string pdbLocation, string fullName, int lineStart, int lineEnd, bool showLineNumbers = false, int markedLine = -1, string markPrefix = lineMarkPrefix) + { + Dictionary fileNameToFileSourceText = GetEmbeddedFiles(pdbLocation); + if (fileNameToFileSourceText == null) return null; + string source = fileNameToFileSourceText.GetValueOrDefault(fullName); + if (source == null) return null; + + StringBuilder builder = new StringBuilder(); + using (StringReader reader = new StringReader(source)) + { + int lineNumber = 0; + + // first go through text source till we reach lineStart + while ( reader.Peek() != -1 ) + { + lineNumber++; + if (lineNumber == lineStart) + break; + reader.ReadLine(); + } + + while (reader.Peek() != -1) + { + string currentLine = reader.ReadLine(); + if (showLineNumbers) + { + builder.Append($"{lineNumber} ".PadLeft(lineNumberPaddingWidth)); + } + if (lineNumber == markedLine) + { + builder.Append(markPrefix); + } + builder.AppendLine(currentLine); + + lineNumber++; + if (lineNumber == lineEnd) + break; + } + } + return builder.ToString(); + } + } +} \ No newline at end of file diff --git a/src/Simulation/Common/SimulatorBase.cs b/src/Simulation/Common/SimulatorBase.cs index 3bc17a26e4e..b0dc7dcba3d 100644 --- a/src/Simulation/Common/SimulatorBase.cs +++ b/src/Simulation/Common/SimulatorBase.cs @@ -98,14 +98,34 @@ public override AbstractCallable CreateInstance(Type t) return result; } + public virtual O RunSync(I args) where T : AbstractCallable, ICallable + { + O res = default(O); + var op = Get(); + try + { + res = op.Apply(args); + } + catch (Exception e) // Dumps q# call-stack in case of exception if CallStack tracking was enabled + { + StackFrame[] qsharpStackFrames = this.CallStack; + if (qsharpStackFrames != null) + { + OnLog?.Invoke($"Unhandled Exception: {e.GetType().FullName}: {e.Message}"); + foreach (StackFrame sf in qsharpStackFrames) + { + OnLog?.Invoke(sf.ToStringWithBestSourceLocation()); + } + } + OnLog?.Invoke(""); + throw; + } + return res; + } public virtual Task Run(I args) where T : AbstractCallable, ICallable { - return Task.Run(() => - { - var op = Get(); - return op.Apply(args); - }); + return Task.Run(() => RunSync(args)); } /// @@ -346,5 +366,16 @@ public virtual void Fail(System.Runtime.ExceptionServices.ExceptionDispatchInfo { OnFail?.Invoke(exceptionInfo); } + + #region Stack trace collection support + private StackTraceCollector stackTraceCollector = null; + + public void EnableStackTrace() + { + stackTraceCollector = new StackTraceCollector(this); + } + + public StackFrame[] CallStack => stackTraceCollector?.CallStack; + #endregion } } diff --git a/src/Simulation/Common/StackTrace.cs b/src/Simulation/Common/StackTrace.cs new file mode 100644 index 00000000000..5d01999ca76 --- /dev/null +++ b/src/Simulation/Common/StackTrace.cs @@ -0,0 +1,238 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Quantum.Simulation.Core; +using System.Diagnostics; +using System.Linq; + +namespace Microsoft.Quantum.Simulation.Common +{ + [Serializable] + public class StackFrame + { + public readonly ICallable operation; + public readonly IApplyData argument; + + /// + /// The path to the source where operation is defined + /// + public string sourceFile; + + /// + /// One based line number in the operation that resulted in failure. Note that for automatically derived Adjoint and Controlled + /// variants of the operation, the line always points to the operation declaration + /// + public int failedLineNumber = -2; + + /// + /// One based line number where the declaration starts. + /// + public int declarationStartLineNumber; + + /// + /// One based line number of the first line after the declaration. + /// The value -1, if the declaration ends on the last line of the file. + /// + public int declarationEndLineNumber; + + public StackFrame(ICallable _operation, IApplyData _argument) + { + operation = _operation; + argument = _argument; + } + + /// + /// Uses PortablePDBs and SourceLink to get the source of failed operation. + /// + public string GetOperationSourceFromPDB() + { + string pdbFileLocation = PortablePdbSymbolReader.GetPDBLocation(operation); + return PortablePDBEmbeddedFilesCache.GetEmbeddedFileRange( + pdbFileLocation, + sourceFile, + declarationStartLineNumber, + declarationEndLineNumber, + showLineNumbers: true, markedLine: failedLineNumber); + } + + /// + /// Uses PortablePDBs and SourceLink to get URL for file and line number. + /// + /// + public string GetURLFromPDB() + { + string pdbFileLocation = PortablePdbSymbolReader.GetPDBLocation(operation); + Tuple result = PortablePDBPathRemappingCache.TryGetFileUrl(pdbFileLocation, sourceFile); + return PortablePdbSymbolReader.TryFormatGitHubUrl(result, failedLineNumber); + } + + private const string messageFormat = "in {0} on {1}"; + + public override string ToString() + { + return string.Format(messageFormat, operation.FullName, $"{sourceFile}:line {failedLineNumber}"); + } + + /// + /// The same as , but tries to point to best source location. + /// If the source is not available on local machine, source location will be replaced + /// by URL pointing to GitHub repository. + /// This is more costly than because it checks if source file exists on disk. + /// If the file does not exist it calls to get the URL + /// which is also more costly than . + /// + public virtual string ToStringWithBestSourceLocation() + { + string message = ToString(); + if (System.IO.File.Exists(sourceFile)) + { + return message; + } + else + { + string url = GetURLFromPDB(); + if (url == null) + { + return message; + } + else + { + return string.Format(messageFormat, operation.FullName, url); + } + } + } + } + + /// + /// Tracks Q# call-stack till the first failure resulting in + /// event invocation. + /// + public class StackTraceCollector + { + private readonly Stack callStack; + private System.Diagnostics.StackFrame[] frames = null; + StackFrame[] stackFramesWithLocations = null; + bool hasFailed = false; + + public StackTraceCollector(SimulatorBase sim) + { + sim.OnOperationStart += this.OnOperationStart; + sim.OnOperationEnd += this.OnOperationEnd; + sim.OnFail += this.OnFail; + callStack = new Stack(); + } + + void OnOperationStart(ICallable callable, IApplyData arg) + { + if (!hasFailed) + { + callStack.Push(new StackFrame(callable, arg)); + } + } + + void OnOperationEnd(ICallable callable, IApplyData arg) + { + if (!hasFailed) + { + callStack.Pop(); + } + } + + void OnFail(System.Runtime.ExceptionServices.ExceptionDispatchInfo exceptionInfo) + { + if (!hasFailed) + { + hasFailed = true; + } + + System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(exceptionInfo.SourceException, 0, true); + System.Diagnostics.StackFrame[] currentFrames = stackTrace.GetFrames(); + + if (frames == null) + { + frames = currentFrames; + } + else + { + // When the exception is thrown, OnFail can be called multiple times. + // With every next call we see bigger part of the call stack, so we save the biggest call stack + if (currentFrames.Length > frames.Length) + { + Debug.Assert((frames.Length == 0) || (frames[0].ToString() == currentFrames[0].ToString())); + frames = currentFrames; + } + } + } + + static StackFrame[] PopulateSourceLocations(Stack qsharpCallStack, System.Diagnostics.StackFrame[] csharpCallStack) + { + foreach (StackFrame currentFrame in qsharpCallStack) + { + ICallable op = currentFrame.operation.UnwrapCallable(); + object[] locations = op.GetType().GetCustomAttributes(typeof(SourceLocationAttribute), true); + foreach (object location in locations) + { + SourceLocationAttribute sourceLocation = (location as SourceLocationAttribute); + if (sourceLocation != null && sourceLocation.SpecializationKind == op.Variant) + { + currentFrame.sourceFile = System.IO.Path.GetFullPath(sourceLocation.SourceFile); + currentFrame.declarationStartLineNumber = sourceLocation.StartLine; + currentFrame.declarationEndLineNumber = sourceLocation.EndLine; + } + } + } + + StackFrame[] qsharpStackFrames = qsharpCallStack.ToArray(); + int qsharpStackFrameId = 0; + for (int csharpStackFrameId = 0; csharpStackFrameId < csharpCallStack.Length; ++csharpStackFrameId) + { + string fileName = csharpCallStack[csharpStackFrameId].GetFileName(); + if ( fileName != null ) + { + fileName = System.IO.Path.GetFullPath(fileName); + int failedLineNumber = csharpCallStack[csharpStackFrameId].GetFileLineNumber(); + StackFrame currentQsharpStackFrame = qsharpStackFrames[qsharpStackFrameId]; + if (fileName == currentQsharpStackFrame.sourceFile && + currentQsharpStackFrame.declarationStartLineNumber <= failedLineNumber && + ( + (failedLineNumber < currentQsharpStackFrame.declarationEndLineNumber) || + (currentQsharpStackFrame.declarationEndLineNumber == -1) + ) + ) + { + currentQsharpStackFrame.failedLineNumber = failedLineNumber; + qsharpStackFrameId++; + } + } + } + return qsharpStackFrames; + } + + /// + /// If failure has happened returns the call-stack at time of failure. + /// Returns null if the failure has not happened. + /// + public StackFrame[] CallStack + { + get + { + if (hasFailed) + { + if( stackFramesWithLocations == null ) + { + stackFramesWithLocations = PopulateSourceLocations(callStack, frames); + } + return stackFramesWithLocations; + } + else + { + return null; + } + } + } + } +} diff --git a/src/Simulation/Common/Utils.cs b/src/Simulation/Common/Utils.cs new file mode 100644 index 00000000000..04df6315d59 --- /dev/null +++ b/src/Simulation/Common/Utils.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Quantum.Simulation.Core; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Microsoft.Quantum.Simulation.Common +{ + public class CommonUtils + { + /// + /// Removes PauliI terms from observable and corresponding qubits from qubits. + /// Returns the observable description that is equivalent to the original one, but has no PauliI terms + /// + public static void PruneObservable(IQArray observable, IQArray qubits, out QArray prunnedObservable, out QArray prunnedQubits) + { + Debug.Assert(observable != null); + Debug.Assert(qubits != null); + Debug.Assert(observable.Length == qubits.Length); + prunnedObservable = new QArray(PrunnedSequence(observable, Pauli.PauliI, observable)); + prunnedQubits = new QArray(PrunnedSequence(observable, Pauli.PauliI, qubits)); + } + + /// + /// Returns IEnumerable that contains sub-sequence of [i], such that [i] is not equal to . + /// + public static IEnumerable PrunnedSequence(IQArray sequence, U value, IQArray sequenceToPrune ) + { + for (uint i = 0; i < sequence.Length; ++i) + { + if (!sequence[i].Equals(value)) + { + yield return sequenceToPrune[i]; + } + } + } + + /// + /// Converts numbers of the form /2^ into canonical form where is odd or zero. + /// If is zero, must also be zero in the canonical form. + /// + public static (long, long) Reduce(long numerator, long denominatorPower) + { + if (numerator == 0) + { + return (0, 0); + } + + if (numerator % 2 != 0) + { + return (numerator, denominatorPower); + } + + long numNew = numerator; + long denomPowerNew = denominatorPower; + + while (numNew % 2 == 0) + { + numNew /= 2; + denomPowerNew -= 1; + } + + return (numNew, denomPowerNew); + } + } +} diff --git a/src/Simulation/Core/Generics/Adjoint.cs b/src/Simulation/Core/Generics/Adjoint.cs index b00116bb7c8..45577250ebd 100644 --- a/src/Simulation/Core/Generics/Adjoint.cs +++ b/src/Simulation/Core/Generics/Adjoint.cs @@ -23,7 +23,7 @@ public interface IAdjointable : ICallable /// input Type is not resolved until it gets Applied at runtime. /// [DebuggerTypeProxy(typeof(GenericAdjoint.DebuggerProxy))] - public class GenericAdjoint : GenericCallable, IApplyData + public class GenericAdjoint : GenericCallable, IApplyData, IOperationWrapper { public GenericAdjoint(GenericCallable baseOp) : base(baseOp.Factory, null) { @@ -31,6 +31,7 @@ public GenericAdjoint(GenericCallable baseOp) : base(baseOp.Factory, null) } public GenericCallable BaseOp { get; } + ICallable IOperationWrapper.BaseOperation => BaseOp; IEnumerable IApplyData.Qubits => ((IApplyData)this.BaseOp)?.Qubits; diff --git a/src/Simulation/Core/Generics/Controlled.cs b/src/Simulation/Core/Generics/Controlled.cs index 65d5235c0bb..cc9b05fb994 100644 --- a/src/Simulation/Core/Generics/Controlled.cs +++ b/src/Simulation/Core/Generics/Controlled.cs @@ -25,7 +25,7 @@ public partial interface IControllable : ICallable /// input Type is not resolved until it gets Applied at runtime. /// [DebuggerTypeProxy(typeof(GenericControlled.DebuggerProxy))] - public class GenericControlled : GenericCallable, IApplyData + public class GenericControlled : GenericCallable, IApplyData, IOperationWrapper { public GenericControlled(GenericCallable baseOp) : base(baseOp.Factory, null) { @@ -33,6 +33,7 @@ public GenericControlled(GenericCallable baseOp) : base(baseOp.Factory, null) } public GenericCallable BaseOp { get; } + ICallable IOperationWrapper.BaseOperation => BaseOp; IEnumerable IApplyData.Qubits => ((IApplyData)this.BaseOp)?.Qubits; diff --git a/src/Simulation/Core/Generics/GenericPartial.cs b/src/Simulation/Core/Generics/GenericPartial.cs index 295502d7c8f..8db1263639e 100644 --- a/src/Simulation/Core/Generics/GenericPartial.cs +++ b/src/Simulation/Core/Generics/GenericPartial.cs @@ -14,7 +14,7 @@ namespace Microsoft.Quantum.Simulation.Core /// input Type is not resolved until it gets Applied at runtime. /// [DebuggerTypeProxy(typeof(GenericPartial.DebuggerProxy))] - public class GenericPartial : GenericCallable, IApplyData + public class GenericPartial : GenericCallable, IApplyData, IOperationWrapper { private Lazy __qubits = null; @@ -29,6 +29,7 @@ public GenericPartial(GenericCallable baseOp, object partialValues) : base(baseO } public GenericCallable BaseOp { get; } + ICallable IOperationWrapper.BaseOperation => BaseOp; public override string Name => this.BaseOp.Name; public override string FullName => this.BaseOp.FullName; diff --git a/src/Simulation/Core/Operations/Adjoint.cs b/src/Simulation/Core/Operations/Adjoint.cs index 428858f4c66..6db9f4a9f81 100644 --- a/src/Simulation/Core/Operations/Adjoint.cs +++ b/src/Simulation/Core/Operations/Adjoint.cs @@ -39,7 +39,7 @@ public Adjointable(IOperationFactory m) : base(m) /// Class used to represents an operation that has been adjointed. /// [DebuggerTypeProxy(typeof(AdjointedOperation<,>.DebuggerProxy))] - public class AdjointedOperation : Unitary, IApplyData, ICallable + public class AdjointedOperation : Unitary, IApplyData, ICallable, IOperationWrapper { public AdjointedOperation(Operation op) : base(op.Factory) { @@ -50,6 +50,7 @@ public AdjointedOperation(Operation op) : base(op.Factory) } public Operation BaseOp { get; } + ICallable IOperationWrapper.BaseOperation => BaseOp; public override void Init() { } diff --git a/src/Simulation/Core/Operations/Controlled.cs b/src/Simulation/Core/Operations/Controlled.cs index 2c9f1e183cc..8df3a8eb579 100644 --- a/src/Simulation/Core/Operations/Controlled.cs +++ b/src/Simulation/Core/Operations/Controlled.cs @@ -38,7 +38,7 @@ public Controllable(IOperationFactory m) : base(m) { } /// This class is used to represents an operation that has been controlled. /// [DebuggerTypeProxy(typeof(ControlledOperation<,>.DebuggerProxy))] - public class ControlledOperation : Unitary<(IQArray, I)>, IApplyData, ICallable + public class ControlledOperation : Unitary<(IQArray, I)>, IApplyData, ICallable, IOperationWrapper { public class In : IApplyData { @@ -66,6 +66,7 @@ public ControlledOperation(Operation op) : base(op.Factory) } public Operation BaseOp { get; } + ICallable IOperationWrapper.BaseOperation => BaseOp; public override void Init() { } diff --git a/src/Simulation/Core/Operations/Operation.cs b/src/Simulation/Core/Operations/Operation.cs index 7963654f389..e81248f2528 100644 --- a/src/Simulation/Core/Operations/Operation.cs +++ b/src/Simulation/Core/Operations/Operation.cs @@ -17,7 +17,15 @@ public partial interface ICallable : ICallable ICallable Partial

(Func mapper); } - + ///

+ /// An operation that wraps another operation, for example + /// , , + /// , + /// + public interface IOperationWrapper + { + ICallable BaseOperation { get; } + } /// /// The base class for all ClosedType quantum operations. diff --git a/src/Simulation/Core/Operations/OperationPartial.cs b/src/Simulation/Core/Operations/OperationPartial.cs index 4b059962132..12e03f01913 100644 --- a/src/Simulation/Core/Operations/OperationPartial.cs +++ b/src/Simulation/Core/Operations/OperationPartial.cs @@ -17,7 +17,7 @@ namespace Microsoft.Quantum.Simulation.Core /// Optionally it can receive a Mapper to do the same. /// [DebuggerTypeProxy(typeof(OperationPartial<,,>.DebuggerProxy))] - public class OperationPartial : Operation, IUnitary

+ public class OperationPartial : Operation, IUnitary

, IOperationWrapper { private Lazy __qubits = null; @@ -59,6 +59,7 @@ public OperationPartial(Operation op, object partialTuple) : base(op.Facto public override void Init() { } public Operation BaseOp { get; } + ICallable IOperationWrapper.BaseOperation => BaseOp; public Func Mapper { get; } @@ -137,6 +138,7 @@ ICallable ICallable.Partial(Func mapper) IUnitary

IUnitary

.Adjoint => base.Adjoint; IUnitary<(IQArray, P)> IUnitary

.Controlled => base.Controlled; + IUnitary IUnitary

.Partial(Func mapper) => new OperationPartial(this, mapper); public override string ToString() => $"{this.BaseOp}{{_}}"; diff --git a/src/Simulation/Core/TypeExtensions.cs b/src/Simulation/Core/TypeExtensions.cs index 9822b37f373..94bc5ebabf6 100644 --- a/src/Simulation/Core/TypeExtensions.cs +++ b/src/Simulation/Core/TypeExtensions.cs @@ -368,5 +368,15 @@ public static string QSharpType(this Type t) return t.Name; } + + public static ICallable UnwrapCallable(this ICallable op) + { + ICallable res = op; + while (res as IOperationWrapper != null) + { + res = (res as IOperationWrapper).BaseOperation; + } + return res; + } } } diff --git a/src/Simulation/CsharpGeneration/Microsoft.Quantum.CsharpGeneration.fsproj b/src/Simulation/CsharpGeneration/Microsoft.Quantum.CsharpGeneration.fsproj index 749698bd81e..296cb60026c 100644 --- a/src/Simulation/CsharpGeneration/Microsoft.Quantum.CsharpGeneration.fsproj +++ b/src/Simulation/CsharpGeneration/Microsoft.Quantum.CsharpGeneration.fsproj @@ -18,7 +18,7 @@ - + diff --git a/src/Simulation/Simulators.Tests/Circuits/Fail.qs b/src/Simulation/Simulators.Tests/Circuits/Fail.qs index 3fb9302255c..4d20fdea39c 100644 --- a/src/Simulation/Simulators.Tests/Circuits/Fail.qs +++ b/src/Simulation/Simulators.Tests/Circuits/Fail.qs @@ -3,10 +3,75 @@ namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits { - operation AlwaysFail() : Unit { - fail "Always fail"; + operation AlwaysFail() : Unit is Adj + Ctl{ + Fail(); } -} + operation AlwaysFail1() : Unit is Adj + Ctl{ + AlwaysFail(); + } + operation AlwaysFail2() : Unit is Adj + Ctl { + Controlled AlwaysFail1(new Qubit[0],()); + } + operation AlwaysFail3() : Unit is Adj + Ctl { + Adjoint AlwaysFail2(); + } + operation AlwaysFail4() : Unit is Adj + Ctl { + Adjoint AlwaysFail3(); + } + operation GenericFail<'T,'U>( a : 'T, b : 'U ) : Unit is Adj + Ctl { + AlwaysFail(); + } + + operation GenericFail1() : Unit is Adj + Ctl { + GenericFail(5,6); + } + + operation PartialFail( a : Int, b : Int ) : Unit is Adj + Ctl { + AlwaysFail(); + } + + operation PartialFail1() : Unit is Adj + Ctl { + let op = PartialFail(0,_); + op(2); + } + + operation PartialAdjFail1() : Unit is Adj + Ctl { + let op = PartialFail(0,_); + Adjoint op(2); + } + + operation PartialCtlFail1() : Unit is Adj + Ctl { + let op = PartialFail(0,_); + Controlled op(new Qubit[0], 2); + } + + operation GenericAdjFail1() : Unit is Adj + Ctl { + Adjoint GenericFail(5,6); + } + + operation GenericCtlFail1() : Unit is Adj + Ctl { + Controlled GenericFail( new Qubit[0], (5,6)); + } + + function Fail() : Unit { + fail "Always fail"; + } + + operation RecursionFail( a : Int) : Unit is Adj { + if ( a >= 1 ) + { + RecursionFail(a-1); + } + else + { + Fail(); + } + } + + operation RecursionFail1() : Unit { + RecursionFail(2); + } +} \ No newline at end of file diff --git a/src/Simulation/Simulators.Tests/StackTraceTests.cs b/src/Simulation/Simulators.Tests/StackTraceTests.cs new file mode 100644 index 00000000000..602b9fff854 --- /dev/null +++ b/src/Simulation/Simulators.Tests/StackTraceTests.cs @@ -0,0 +1,314 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Xunit; + +using System; +using System.Threading.Tasks; + +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Simulators.Tests.Circuits; +using Microsoft.Quantum.Simulation.Simulators.Exceptions; +using Xunit.Abstractions; +using System.Text; + +namespace Microsoft.Quantum.Simulation.Simulators.Tests +{ + public class StackTraceTests + { + const string namespacePrefix = "Microsoft.Quantum.Simulation.Simulators.Tests.Circuits."; + + private readonly ITestOutputHelper output; + public StackTraceTests(ITestOutputHelper output) + { + this.output = output; + } + + [Fact] + public void AlwaysFail4Test() + { + ToffoliSimulator sim = new ToffoliSimulator(); + StackTraceCollector sc = new StackTraceCollector(sim); + ICallable op = sim.Get(); + try + { + QVoid res = op.Apply(QVoid.Instance); + } + catch (ExecutionFailException) + { + StackFrame[] stackFrames = sc.CallStack; + + Assert.Equal(5, stackFrames.Length); + + Assert.Equal(namespacePrefix + "AlwaysFail", stackFrames[0].operation.FullName); + Assert.Equal(namespacePrefix + "AlwaysFail1", stackFrames[1].operation.FullName); + Assert.Equal(namespacePrefix + "AlwaysFail2", stackFrames[2].operation.FullName); + Assert.Equal(namespacePrefix + "AlwaysFail3", stackFrames[3].operation.FullName); + Assert.Equal(namespacePrefix + "AlwaysFail4", stackFrames[4].operation.FullName); + + Assert.Equal(OperationFunctor.Controlled, stackFrames[0].operation.Variant); + Assert.Equal(OperationFunctor.Controlled, stackFrames[1].operation.Variant); + Assert.Equal(OperationFunctor.Body, stackFrames[2].operation.Variant); + Assert.Equal(OperationFunctor.Adjoint, stackFrames[3].operation.Variant); + Assert.Equal(OperationFunctor.Body, stackFrames[4].operation.Variant); + + Assert.Equal(14, stackFrames[2].failedLineNumber); + Assert.Equal(21, stackFrames[4].failedLineNumber); + + // For Adjoint and Controlled we expect failedLineNumber to be equal to declarationStartLineNumber + Assert.Equal(stackFrames[0].declarationStartLineNumber, stackFrames[0].failedLineNumber); + Assert.Equal(stackFrames[1].declarationStartLineNumber, stackFrames[1].failedLineNumber); + Assert.Equal(stackFrames[3].declarationStartLineNumber, stackFrames[3].failedLineNumber); + + 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()); + } + + StringBuilder builder = new StringBuilder(); + builder.Append("13 ".PadLeft(PortablePDBEmbeddedFilesCache.lineNumberPaddingWidth)); + builder.AppendLine(" operation AlwaysFail2() : Unit is Adj + Ctl {"); + builder.Append("14 ".PadLeft(PortablePDBEmbeddedFilesCache.lineNumberPaddingWidth) + PortablePDBEmbeddedFilesCache.lineMarkPrefix); + builder.AppendLine(" Controlled AlwaysFail1(new Qubit[0],());"); + builder.Append("15 ".PadLeft(PortablePDBEmbeddedFilesCache.lineNumberPaddingWidth)); + builder.AppendLine(" }"); + Assert.Equal(builder.ToString(), stackFrames[2].GetOperationSourceFromPDB()); + + for( int i = 0; i < stackFrames.Length; ++i ) + { + output.WriteLine($"operation:{stackFrames[i].operation.FullName}"); + output.WriteLine(stackFrames[i].GetOperationSourceFromPDB()); + } + } + } + + [Fact] + public void GenericFail1Test() + { + ToffoliSimulator sim = new ToffoliSimulator(); + + { + StackTraceCollector sc = new StackTraceCollector(sim); + ICallable op = sim.Get(); + try + { + QVoid res = op.Apply(QVoid.Instance); + } + catch (ExecutionFailException) + { + StackFrame[] stackFrames = sc.CallStack; + + Assert.Equal(3, stackFrames.Length); + + Assert.Equal(namespacePrefix + "AlwaysFail", stackFrames[0].operation.FullName); + Assert.Equal(namespacePrefix + "GenericFail", stackFrames[1].operation.FullName); + Assert.Equal(namespacePrefix + "GenericFail1", stackFrames[2].operation.FullName); + + Assert.Equal(OperationFunctor.Body, stackFrames[0].operation.Variant); + Assert.Equal(OperationFunctor.Body, stackFrames[1].operation.Variant); + Assert.Equal(OperationFunctor.Body, stackFrames[2].operation.Variant); + + Assert.Equal(7, stackFrames[0].failedLineNumber); + Assert.Equal(25, stackFrames[1].failedLineNumber); + Assert.Equal(29, stackFrames[2].failedLineNumber); + } + } + + { + StackTraceCollector sc = new StackTraceCollector(sim); + ICallable op = sim.Get(); + try + { + QVoid res = op.Apply(QVoid.Instance); + } + catch (ExecutionFailException) + { + StackFrame[] stackFrames = sc.CallStack; + + Assert.Equal(3, stackFrames.Length); + + Assert.Equal(namespacePrefix + "AlwaysFail", stackFrames[0].operation.FullName); + Assert.Equal(namespacePrefix + "GenericFail", stackFrames[1].operation.FullName); + Assert.Equal(namespacePrefix + "GenericAdjFail1", stackFrames[2].operation.FullName); + + Assert.Equal(OperationFunctor.Adjoint, stackFrames[0].operation.Variant); + Assert.Equal(OperationFunctor.Adjoint, stackFrames[1].operation.Variant); + Assert.Equal(OperationFunctor.Body, stackFrames[2].operation.Variant); + + Assert.Equal(6, stackFrames[0].failedLineNumber); + Assert.Equal(24, stackFrames[1].failedLineNumber); + Assert.Equal(52, stackFrames[2].failedLineNumber); + } + } + + { + StackTraceCollector sc = new StackTraceCollector(sim); + ICallable op = sim.Get(); + try + { + QVoid res = op.Apply(QVoid.Instance); + } + catch (ExecutionFailException) + { + StackFrame[] stackFrames = sc.CallStack; + + Assert.Equal(3, stackFrames.Length); + + Assert.Equal(namespacePrefix + "AlwaysFail", stackFrames[0].operation.FullName); + Assert.Equal(namespacePrefix + "GenericFail", stackFrames[1].operation.FullName); + Assert.Equal(namespacePrefix + "GenericCtlFail1", stackFrames[2].operation.FullName); + + Assert.Equal(OperationFunctor.Controlled, stackFrames[0].operation.Variant); + Assert.Equal(OperationFunctor.Controlled, stackFrames[1].operation.Variant); + Assert.Equal(OperationFunctor.Body, stackFrames[2].operation.Variant); + + Assert.Equal(6, stackFrames[0].failedLineNumber); + Assert.Equal(24, stackFrames[1].failedLineNumber); + Assert.Equal(56, stackFrames[2].failedLineNumber); + } + } + } + + [Fact] + public void PartialFail1Test() + { + ToffoliSimulator sim = new ToffoliSimulator(); + + { + StackTraceCollector sc = new StackTraceCollector(sim); + ICallable op = sim.Get(); + try + { + QVoid res = op.Apply(QVoid.Instance); + } + catch (ExecutionFailException) + { + StackFrame[] stackFrames = sc.CallStack; + + Assert.Equal(3, stackFrames.Length); + + Assert.Equal(namespacePrefix + "AlwaysFail", stackFrames[0].operation.FullName); + Assert.Equal(namespacePrefix + "PartialFail", stackFrames[1].operation.FullName); + Assert.Equal(namespacePrefix + "PartialFail1", stackFrames[2].operation.FullName); + + Assert.Equal(OperationFunctor.Body, stackFrames[0].operation.Variant); + Assert.Equal(OperationFunctor.Body, stackFrames[1].operation.Variant); + Assert.Equal(OperationFunctor.Body, stackFrames[2].operation.Variant); + + Assert.Equal(7, stackFrames[0].failedLineNumber); + Assert.Equal(33, stackFrames[1].failedLineNumber); + Assert.Equal(38, stackFrames[2].failedLineNumber); + } + } + + { + StackTraceCollector sc = new StackTraceCollector(sim); + ICallable op = sim.Get(); + try + { + QVoid res = op.Apply(QVoid.Instance); + } + catch (ExecutionFailException) + { + StackFrame[] stackFrames = sc.CallStack; + + Assert.Equal(3, stackFrames.Length); + + Assert.Equal(namespacePrefix + "AlwaysFail", stackFrames[0].operation.FullName); + Assert.Equal(namespacePrefix + "PartialFail", stackFrames[1].operation.FullName); + Assert.Equal(namespacePrefix + "PartialAdjFail1", stackFrames[2].operation.FullName); + + Assert.Equal(OperationFunctor.Adjoint, stackFrames[0].operation.Variant); + Assert.Equal(OperationFunctor.Adjoint, stackFrames[1].operation.Variant); + Assert.Equal(OperationFunctor.Body, stackFrames[2].operation.Variant); + + Assert.Equal(6, stackFrames[0].failedLineNumber); + Assert.Equal(32, stackFrames[1].failedLineNumber); + Assert.Equal(43, stackFrames[2].failedLineNumber); + } + } + + { + StackTraceCollector sc = new StackTraceCollector(sim); + ICallable op = sim.Get(); + try + { + QVoid res = op.Apply(QVoid.Instance); + } + catch (ExecutionFailException) + { + StackFrame[] stackFrames = sc.CallStack; + + Assert.Equal(3, stackFrames.Length); + + Assert.Equal(namespacePrefix + "AlwaysFail", stackFrames[0].operation.FullName); + Assert.Equal(namespacePrefix + "PartialFail", stackFrames[1].operation.FullName); + Assert.Equal(namespacePrefix + "PartialCtlFail1", stackFrames[2].operation.FullName); + + Assert.Equal(OperationFunctor.Controlled, stackFrames[0].operation.Variant); + Assert.Equal(OperationFunctor.Controlled, stackFrames[1].operation.Variant); + Assert.Equal(OperationFunctor.Body, stackFrames[2].operation.Variant); + + Assert.Equal(6, stackFrames[0].failedLineNumber); + Assert.Equal(32, stackFrames[1].failedLineNumber); + Assert.Equal(48, stackFrames[2].failedLineNumber); + } + } + } + + [Fact] + public void RecursionFail1Test() + { + ToffoliSimulator sim = new ToffoliSimulator(); + + { + StackTraceCollector sc = new StackTraceCollector(sim); + ICallable op = sim.Get(); + try + { + QVoid res = op.Apply(QVoid.Instance); + } + catch (ExecutionFailException) + { + StackFrame[] stackFrames = sc.CallStack; + + Assert.Equal(4, stackFrames.Length); + + Assert.Equal(namespacePrefix + "RecursionFail", stackFrames[0].operation.FullName); + Assert.Equal(namespacePrefix + "RecursionFail", stackFrames[1].operation.FullName); + Assert.Equal(namespacePrefix + "RecursionFail", stackFrames[2].operation.FullName); + Assert.Equal(namespacePrefix + "RecursionFail1", stackFrames[3].operation.FullName); + + Assert.Equal(OperationFunctor.Body, stackFrames[0].operation.Variant); + Assert.Equal(OperationFunctor.Body, stackFrames[1].operation.Variant); + Assert.Equal(OperationFunctor.Body, stackFrames[2].operation.Variant); + Assert.Equal(OperationFunctor.Body, stackFrames[3].operation.Variant); + + Assert.Equal(70, stackFrames[0].failedLineNumber); + Assert.Equal(66, stackFrames[1].failedLineNumber); + Assert.Equal(66, stackFrames[2].failedLineNumber); + Assert.Equal(75, stackFrames[3].failedLineNumber); + } + } + } + + [Fact] + public void ErrorLogTest() + { + ToffoliSimulator sim = new ToffoliSimulator(); + sim.EnableStackTrace(); + StringBuilder stringBuilder = new StringBuilder(); + sim.OnLog += (msg) => stringBuilder.AppendLine(msg); + try + { + QVoid res = sim.RunSync(QVoid.Instance); + } + catch (ExecutionFailException) + { + } + output.WriteLine(stringBuilder.ToString()); + } + } +} \ No newline at end of file diff --git a/src/Simulation/Simulators.Tests/Tests.Microsoft.Quantum.Simulation.Simulators.csproj b/src/Simulation/Simulators.Tests/Tests.Microsoft.Quantum.Simulation.Simulators.csproj index 64d3e4bb24e..565987988c9 100644 --- a/src/Simulation/Simulators.Tests/Tests.Microsoft.Quantum.Simulation.Simulators.csproj +++ b/src/Simulation/Simulators.Tests/Tests.Microsoft.Quantum.Simulation.Simulators.csproj @@ -1,6 +1,7 @@  + netcoreapp3.0 diff --git a/src/Simulation/Simulators/QuantumExecutor/Allocate.cs b/src/Simulation/Simulators/QuantumExecutor/Allocate.cs new file mode 100644 index 00000000000..04ceba85113 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/Allocate.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimAllocate : Intrinsic.Allocate + { + private readonly QuantumExecutorSimulator sim; + public QuantumExecutorSimAllocate(QuantumExecutorSimulator m) : base(m){ + sim = m; + } + + public override Qubit Apply() + { + IQArray qubits = sim.QubitManager.Allocate(1); + sim.QuantumExecutor.OnAllocateQubits(qubits); + return qubits[0]; + } + + public override IQArray Apply( long count ) + { + IQArray qubits = sim.QubitManager.Allocate(count); + sim.QuantumExecutor.OnAllocateQubits(qubits); + return qubits; + } + } + } +} \ No newline at end of file diff --git a/src/Simulation/Simulators/QuantumExecutor/Assert.cs b/src/Simulation/Simulators/QuantumExecutor/Assert.cs new file mode 100644 index 00000000000..64450284bb3 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/Assert.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimAssert : Quantum.Intrinsic.Assert + { + private QuantumExecutorSimulator Simulator { get; } + + public QuantumExecutorSimAssert(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func<(IQArray, IQArray, Result, string), QVoid> Body => (_args) => + { + var (paulis, qubits, result, msg) = _args; + if (paulis.Length != qubits.Length) + { + IgnorableAssert.Assert((paulis.Length != qubits.Length), "Arrays length mismatch"); + throw new InvalidOperationException($"Both input arrays for {this.GetType().Name} (paulis,qubits), must be of same size."); + } + + CommonUtils.PruneObservable(paulis, qubits, out QArray newPaulis, out QArray newQubits); + Simulator.QuantumExecutor.Assert(newPaulis, newQubits, result, msg); + + return QVoid.Instance; + }; + + public override Func<(IQArray, IQArray, Result, string), QVoid> AdjointBody => (_args) => { return QVoid.Instance; }; + + public override Func<(IQArray, (IQArray, IQArray, Result, string)), QVoid> ControlledBody => (_args) => { return QVoid.Instance; }; + + public override Func<(IQArray, (IQArray, IQArray, Result, string)), QVoid> ControlledAdjointBody => (_args) => { return QVoid.Instance; }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/AssertProb.cs b/src/Simulation/Simulators/QuantumExecutor/AssertProb.cs new file mode 100644 index 00000000000..1b283a44804 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/AssertProb.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimAssertProb : Quantum.Intrinsic.AssertProb + { + + private QuantumExecutorSimulator Simulator { get; } + + + public QuantumExecutorSimAssertProb(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func<(IQArray, IQArray, Result, double, string, double), QVoid> Body => (_args) => + { + var (paulis, qubits, result, expectedPr, msg, tol) = _args; + if (paulis.Length != qubits.Length) + { + IgnorableAssert.Assert((paulis.Length != qubits.Length), "Arrays length mismatch"); + throw new InvalidOperationException($"Both input arrays for {this.GetType().Name} (paulis,qubits), must be of same size."); + } + + double probabilityOfZero = result == Result.Zero ? expectedPr : 1.0 - expectedPr; + CommonUtils.PruneObservable(paulis, qubits, out QArray newPaulis, out QArray newQubits); + Simulator.QuantumExecutor.AssertProb(newPaulis, newQubits, probabilityOfZero, msg, tol ); + return QVoid.Instance; + }; + + public override Func<(IQArray, IQArray, Result, double, string, double), QVoid> AdjointBody => (_args) => { return QVoid.Instance; }; + + public override Func<(IQArray, (IQArray, IQArray, Result, double, string, double)), QVoid> ControlledBody => (_args) => { return QVoid.Instance; }; + + public override Func<(IQArray, (IQArray, IQArray, Result, double, string, double)), QVoid> ControlledAdjointBody => (_args) => { return QVoid.Instance; }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/Borrow.cs b/src/Simulation/Simulators/QuantumExecutor/Borrow.cs new file mode 100644 index 00000000000..0dbfe80e838 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/Borrow.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimBorrow : Intrinsic.Borrow + { + private readonly QuantumExecutorSimulator sim; + public QuantumExecutorSimBorrow(QuantumExecutorSimulator m) : base(m){ + sim = m; + } + + public override Qubit Apply() + { + IQArray qubits = sim.QubitManager.Borrow(1); + sim.QuantumExecutor.OnBorrowQubits(qubits); + return qubits[0]; + } + + public override IQArray Apply( long count ) + { + IQArray qubits = sim.QubitManager.Borrow(1); + sim.QuantumExecutor.OnBorrowQubits(qubits); + return qubits; + } + } + } +} \ No newline at end of file diff --git a/src/Simulation/Simulators/QuantumExecutor/ClassicalControl.cs b/src/Simulation/Simulators/QuantumExecutor/ClassicalControl.cs new file mode 100644 index 00000000000..4320d0995b5 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/ClassicalControl.cs @@ -0,0 +1,232 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class SimApplyIfElse : Extensions.ApplyIfElseIntrinsic + { + private QuantumExecutorSimulator Simulator { get; } + + public SimApplyIfElse(QuantumExecutorSimulator m) : base(m) { this.Simulator = m; } + + public override Func<(Result, ICallable, ICallable), QVoid> Body => (q) => + { + var (measurementResult, onZero, onOne) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResult, + () => onZero.Apply(QVoid.Instance), + () => onOne.Apply(QVoid.Instance)); + return QVoid.Instance; + }; + } + + public class SimApplyIfElseA : Extensions.ApplyIfElseIntrinsicA + { + private QuantumExecutorSimulator Simulator { get; } + + public SimApplyIfElseA(QuantumExecutorSimulator m) : base(m) { this.Simulator = m; } + + public override Func<(Result, IAdjointable, IAdjointable), QVoid> Body => (q) => + { + var (measurementResult, onZero, onOne) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResult, + () => onZero.Apply(QVoid.Instance), + () => onOne.Apply(QVoid.Instance)); + return QVoid.Instance; + }; + + public override Func<(Result, IAdjointable, IAdjointable), QVoid> AdjointBody => (q) => + { + var (measurementResult, onZero, onOne) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResult, + () => onZero.Adjoint.Apply(QVoid.Instance), + () => onOne.Adjoint.Apply(QVoid.Instance)); + return QVoid.Instance; + }; + } + + public class SimApplyIfElseC : Extensions.ApplyIfElseIntrinsicC + { + private QuantumExecutorSimulator Simulator { get; } + + public SimApplyIfElseC(QuantumExecutorSimulator m) : base(m) { this.Simulator = m; } + + public override Func<(Result, IControllable, IControllable), QVoid> Body => (q) => + { + var (measurementResult, onZero, onOne) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResult, + () => onZero.Apply(QVoid.Instance), + () => onOne.Apply(QVoid.Instance)); + return QVoid.Instance; + }; + + public override Func<(IQArray, (Result, IControllable, IControllable)), QVoid> ControlledBody => (q) => + { + var (ctrls, (measurementResult, onZero, onOne)) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResult, + () => onZero.Controlled.Apply(ctrls), + () => onOne.Controlled.Apply(ctrls)); + return QVoid.Instance; + }; + } + + public class SimApplyIfElseCA : Extensions.ApplyIfElseIntrinsicCA + { + private QuantumExecutorSimulator Simulator { get; } + + public SimApplyIfElseCA(QuantumExecutorSimulator m) : base(m) { this.Simulator = m; } + + public override Func<(Result, IUnitary, IUnitary), QVoid> Body => (q) => + { + var (measurementResult, onZero, onOne) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResult, + () => onZero.Apply(QVoid.Instance), + () => onOne.Apply(QVoid.Instance)); + return QVoid.Instance; + }; + + public override Func<(Result, IUnitary, IUnitary), QVoid> AdjointBody => (q) => + { + var (measurementResult, onZero, onOne) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResult, + () => onZero.Adjoint.Apply(QVoid.Instance), + () => onOne.Adjoint.Apply(QVoid.Instance)); + return QVoid.Instance; + }; + + public override Func<(IQArray, (Result, IUnitary, IUnitary)), QVoid> ControlledBody => (q) => + { + var (ctrls, (measurementResult, onZero, onOne)) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResult, + () => onZero.Controlled.Apply(ctrls), + () => onOne.Controlled.Apply(ctrls)); + return QVoid.Instance; + }; + + public override Func<(IQArray, (Result, IUnitary, IUnitary)), QVoid> ControlledAdjointBody => (q) => + { + var (ctrls, (measurementResult, onZero, onOne)) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResult, + () => onZero.Controlled.Adjoint.Apply(ctrls), + () => onOne.Controlled.Adjoint.Apply(ctrls)); + return QVoid.Instance; + }; + } + + + + public class SimApplyConditionally : Extensions.ApplyConditionallyIntrinsic + { + private QuantumExecutorSimulator Simulator { get; } + + public SimApplyConditionally(QuantumExecutorSimulator m) : base(m) { this.Simulator = m; } + + public override Func<(IQArray, IQArray, ICallable, ICallable), QVoid> Body => (q) => + { + var (measurementResults, resultsValues, onEqualOp, onNonEqualOp) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResults, resultsValues, + () => onEqualOp.Apply(QVoid.Instance), + () => onNonEqualOp.Apply(QVoid.Instance)); + return QVoid.Instance; + }; + } + + public class SimApplyConditionallyA : Extensions.ApplyConditionallyIntrinsicA + { + private QuantumExecutorSimulator Simulator { get; } + + public SimApplyConditionallyA(QuantumExecutorSimulator m) : base(m) { this.Simulator = m; } + + public override Func<(IQArray, IQArray, IAdjointable, IAdjointable), QVoid> Body => (q) => + { + var (measurementResults, resultsValues, onEqualOp, onNonEqualOp) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResults, resultsValues, + () => onEqualOp.Apply(QVoid.Instance), + () => onNonEqualOp.Apply(QVoid.Instance)); + return QVoid.Instance; + }; + + public override Func<(IQArray, IQArray, IAdjointable, IAdjointable), QVoid> AdjointBody => (q) => + { + var (measurementResults, resultsValues, onEqualOp, onNonEqualOp) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResults, resultsValues, + () => onEqualOp.Adjoint.Apply(QVoid.Instance), + () => onNonEqualOp.Adjoint.Apply(QVoid.Instance)); + return QVoid.Instance; + }; + } + + public class SimApplyConditionallyC : Extensions.ApplyConditionallyIntrinsicC + { + private QuantumExecutorSimulator Simulator { get; } + + public SimApplyConditionallyC(QuantumExecutorSimulator m) : base(m) { this.Simulator = m; } + + public override Func<(IQArray, IQArray, IControllable, IControllable), QVoid> Body => (q) => + { + var (measurementResults, resultsValues, onEqualOp, onNonEqualOp) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResults, resultsValues, + () => onEqualOp.Apply(QVoid.Instance), + () => onNonEqualOp.Apply(QVoid.Instance)); + return QVoid.Instance; + }; + + public override Func<(IQArray, (IQArray, IQArray, IControllable, IControllable)), QVoid> ControlledBody => (q) => + { + var (ctrls, (measurementResults, resultsValues, onEqualOp, onNonEqualOp)) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResults, resultsValues, + () => onEqualOp.Controlled.Apply(ctrls), + () => onNonEqualOp.Controlled.Apply(ctrls)); + return QVoid.Instance; + }; + } + + public class SimApplyConditionallyCA : Extensions.ApplyConditionallyIntrinsicCA + { + private QuantumExecutorSimulator Simulator { get; } + + public SimApplyConditionallyCA(QuantumExecutorSimulator m) : base(m) { this.Simulator = m; } + + public override Func<(IQArray, IQArray, IUnitary, IUnitary), QVoid> Body => (q) => + { + var (measurementResults, resultsValues, onEqualOp, onNonEqualOp) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResults, resultsValues, + () => onEqualOp.Apply(QVoid.Instance), + () => onNonEqualOp.Apply(QVoid.Instance)); + return QVoid.Instance; + }; + + public override Func<(IQArray, IQArray, IUnitary, IUnitary), QVoid> AdjointBody => (q) => + { + var (measurementResults, resultsValues, onEqualOp, onNonEqualOp) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResults, resultsValues, + () => onEqualOp.Adjoint.Apply(QVoid.Instance), + () => onNonEqualOp.Adjoint.Apply(QVoid.Instance)); + return QVoid.Instance; + }; + + public override Func<(IQArray, (IQArray, IQArray, IUnitary, IUnitary)), QVoid> ControlledBody => (q) => + { + var (ctrls, (measurementResults, resultsValues, onEqualOp, onNonEqualOp)) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResults, resultsValues, + () => onEqualOp.Controlled.Apply(ctrls), + () => onNonEqualOp.Controlled.Apply(ctrls)); + return QVoid.Instance; + }; + + public override Func<(IQArray, (IQArray, IQArray, IUnitary, IUnitary)), QVoid> ControlledAdjointBody => (q) => + { + var (ctrls, (measurementResults, resultsValues, onEqualOp, onNonEqualOp)) = q; + Simulator.QuantumExecutor.ClassicallyControlled(measurementResults, resultsValues, + () => onEqualOp.Controlled.Adjoint.Apply(ctrls), + () => onNonEqualOp.Controlled.Adjoint.Apply(ctrls)); + return QVoid.Instance; + }; + } + + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/ClassicalControl.qs b/src/Simulation/Simulators/QuantumExecutor/ClassicalControl.qs new file mode 100644 index 00000000000..f98b6e23c3a --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/ClassicalControl.qs @@ -0,0 +1,171 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Simulation.QuantumExecutor.Extensions +{ + open Microsoft.Quantum.Intrinsic; + + operation NoOp() : Unit is Ctl + Adj {} + + + operation Delay<'T>(op : ('T => Unit), arg : 'T, aux : Unit) : Unit { + op(arg); + } + + operation DelayC<'T>(op : ('T => Unit is Ctl), arg : 'T, aux : Unit) : Unit is Ctl { + op(arg); + } + + operation DelayA<'T>(op : ('T => Unit is Adj), arg : 'T, aux : Unit) : Unit is Adj { + op(arg); + } + + operation DelayCA<'T>(op : ('T => Unit is Ctl + Adj), arg : 'T, aux : Unit) : Unit is Ctl + Adj { + op(arg); + } + + + operation ApplyIfElseIntrinsic(measurementResult : Result, onResultZeroOp : (Unit => Unit) , onResultOneOp : (Unit => Unit)) : Unit { + body intrinsic; + } + + operation ApplyIfElseIntrinsicA(measurementResult : Result, onResultZeroOp : (Unit => Unit is Adj) , onResultOneOp : (Unit => Unit is Adj)) : Unit { + body intrinsic; + adjoint intrinsic; + } + + operation ApplyIfElseIntrinsicC(measurementResult : Result, onResultZeroOp : (Unit => Unit is Ctl) , onResultOneOp : (Unit => Unit is Ctl)) : Unit { + body intrinsic; + controlled intrinsic; + } + + operation ApplyIfElseIntrinsicCA(measurementResult : Result, onResultZeroOp : (Unit => Unit is Ctl + Adj) , onResultOneOp : (Unit => Unit is Ctl + Adj)) : Unit { + body intrinsic; + adjoint intrinsic; + controlled intrinsic; + controlled adjoint intrinsic; + } + + + operation ApplyConditionallyIntrinsic(measurementResults : Result[], resultsValues : Result[], onEqualOp : (Unit => Unit) , onNonEqualOp : (Unit => Unit)) : Unit { + body intrinsic; + } + + operation ApplyConditionallyIntrinsicA(measurementResults : Result[], resultsValues : Result[], onEqualOp : (Unit => Unit is Adj) , onNonEqualOp : (Unit => Unit is Adj)) : Unit is Adj { + body intrinsic; + adjoint intrinsic; + } + + operation ApplyConditionallyIntrinsicC(measurementResults : Result[], resultsValues : Result[], onEqualOp : (Unit => Unit is Ctl) , onNonEqualOp : (Unit => Unit is Ctl)) : Unit is Ctl { + body intrinsic; + controlled intrinsic; + } + + operation ApplyConditionallyIntrinsicCA(measurementResults : Result[], resultsValues : Result[], onEqualOp : (Unit => Unit is Ctl + Adj) , onNonEqualOp : (Unit => Unit is Ctl + Adj)) : Unit is Ctl + Adj { + body intrinsic; + adjoint intrinsic; + controlled intrinsic; + controlled adjoint intrinsic; + } + + + operation ApplyIfElseR<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit), zeroArg : 'T) , (onResultOneOp : ('U => Unit), oneArg : 'U)) : Unit { + let zeroOp = Delay(onResultZeroOp, zeroArg, _); + let oneOp = Delay(onResultOneOp, oneArg, _); + ApplyIfElseIntrinsic(measurementResult, zeroOp, oneOp); + } + + operation ApplyIfElseRA<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Adj), zeroArg : 'T) , (onResultOneOp : ('U => Unit is Adj), oneArg : 'U)) : Unit is Adj { + let zeroOp = DelayA(onResultZeroOp, zeroArg, _); + let oneOp = DelayA(onResultOneOp, oneArg, _); + ApplyIfElseIntrinsicA(measurementResult, zeroOp, oneOp); + } + + operation ApplyIfElseRC<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Ctl), zeroArg : 'T) , (onResultOneOp : ('U => Unit is Ctl), oneArg : 'U)) : Unit is Ctl { + let zeroOp = DelayC(onResultZeroOp, zeroArg, _); + let oneOp = DelayC(onResultOneOp, oneArg, _); + ApplyIfElseIntrinsicC(measurementResult, zeroOp, oneOp); + } + + operation ApplyIfElseCA<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Adj + Ctl), zeroArg : 'T) , (onResultOneOp : ('U => Unit is Adj + Ctl), oneArg : 'U)) : Unit is Ctl + Adj { + let zeroOp = DelayCA(onResultZeroOp, zeroArg, _); + let oneOp = DelayCA(onResultOneOp, oneArg, _); + ApplyIfElseIntrinsicCA(measurementResult, zeroOp, oneOp); + } + + + operation ApplyIfZero<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit), zeroArg : 'T)) : Unit { + let zeroOp = Delay(onResultZeroOp, zeroArg, _); + let oneOp = Delay(NoOp, (), _); + ApplyIfElseIntrinsic(measurementResult, zeroOp, oneOp); + } + + operation ApplyIfZeroA<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Adj), zeroArg : 'T)) : Unit is Adj{ + let zeroOp = DelayA(onResultZeroOp, zeroArg, _); + let oneOp = DelayA(NoOp, (), _); + ApplyIfElseIntrinsicA(measurementResult, zeroOp, oneOp); + } + + operation ApplyIfZeroC<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Ctl), zeroArg : 'T)) : Unit is Ctl { + let zeroOp = DelayC(onResultZeroOp, zeroArg, _); + let oneOp = DelayC(NoOp, (), _); + ApplyIfElseIntrinsicC(measurementResult, zeroOp, oneOp); + } + + operation ApplyIfZeroCA<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Ctl + Adj), zeroArg : 'T)) : Unit is Ctl + Adj { + let zeroOp = DelayCA(onResultZeroOp, zeroArg, _); + let oneOp = DelayCA(NoOp, (), _); + ApplyIfElseIntrinsicCA(measurementResult, zeroOp, oneOp); + } + + + operation ApplyIfOne<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit), oneArg : 'T)) : Unit { + let oneOp = Delay(onResultOneOp, oneArg, _); + let zeroOp = Delay(NoOp, (), _); + ApplyIfElseIntrinsic(measurementResult, zeroOp, oneOp); + } + + operation ApplyIfOneA<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit is Adj), oneArg : 'T)) : Unit is Adj { + let oneOp = DelayA(onResultOneOp, oneArg, _); + let zeroOp = DelayA(NoOp, (), _); + ApplyIfElseIntrinsicA(measurementResult, zeroOp, oneOp); + } + + operation ApplyIfOneC<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit is Ctl), oneArg : 'T)) : Unit is Ctl { + let oneOp = DelayC(onResultOneOp, oneArg, _); + let zeroOp = DelayC(NoOp, (), _); + ApplyIfElseIntrinsicC(measurementResult, zeroOp, oneOp); + } + + operation ApplyIfOneCA<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit is Ctl + Adj), oneArg : 'T)) : Unit is Ctl + Adj { + let oneOp = DelayCA(onResultOneOp, oneArg, _); + let zeroOp = DelayCA(NoOp, (), _); + ApplyIfElseIntrinsicCA(measurementResult, zeroOp, oneOp); + } + + + operation ApplyConditionally<'T,'U>(measurementResults : Result[], resultsValues : Result[], (onEqualOp : ('T => Unit), equalArg : 'T) , (onNonEqualOp : ('U => Unit), nonEqualArg : 'U)) : Unit { + let equalOp = Delay(onEqualOp,equalArg,_); + let nonEqualOp = Delay(onNonEqualOp,nonEqualArg,_); + ApplyConditionallyIntrinsic(measurementResults, resultsValues, equalOp, nonEqualOp); + } + + operation ApplyConditionallyA<'T,'U>(measurementResults : Result[], resultsValues : Result[], (onEqualOp : ('T => Unit is Adj), equalArg : 'T) , (onNonEqualOp : ('U => Unit is Adj), nonEqualArg : 'U)) : Unit is Adj { + let equalOp = DelayA(onEqualOp, equalArg, _); + let nonEqualOp = DelayA(onNonEqualOp, nonEqualArg, _); + ApplyConditionallyIntrinsicA(measurementResults, resultsValues, equalOp, nonEqualOp); + } + + operation ApplyConditionallyC<'T,'U>(measurementResults : Result[], resultsValues : Result[], (onEqualOp : ('T => Unit is Ctl), equalArg : 'T) , (onNonEqualOp : ('U => Unit is Ctl), nonEqualArg : 'U)) : Unit is Ctl { + let equalOp = DelayC(onEqualOp, equalArg, _); + let nonEqualOp = DelayC(onNonEqualOp, nonEqualArg, _); + ApplyConditionallyIntrinsicC(measurementResults, resultsValues, equalOp, nonEqualOp); + } + + operation ApplyConditionallyCA<'T,'U>(measurementResults : Result[], resultsValues : Result[], (onEqualOp : ('T => Unit is Ctl + Adj), equalArg : 'T) , (onNonEqualOp : ('U => Unit is Ctl + Adj), nonEqualArg : 'U)) : Unit is Ctl + Adj { + let equalOp = DelayCA(onEqualOp, equalArg, _); + let nonEqualOp = DelayCA(onNonEqualOp, nonEqualArg, _); + ApplyConditionallyIntrinsicCA(measurementResults, resultsValues, equalOp, nonEqualOp); + } + +} diff --git a/src/Simulation/Simulators/QuantumExecutor/Dump.cs b/src/Simulation/Simulators/QuantumExecutor/Dump.cs new file mode 100644 index 00000000000..749631d9f90 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/Dump.cs @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + ///

+ /// Dumps the wave function for the given qubits into the given target. + /// If the target is QVoid or an empty string, it dumps it to the console + /// using the `Message` function, otherwise it dumps the content into a file + /// with the given name. + /// DumpMachine dumps the entire wave function, + /// DumpRegister attempts to create the wave function or the resulting subsystem; if it fails + /// because the qubits are entangled with some external qubit, it just generates a message. + /// + protected virtual QVoid DumpMachine(T target) + { + QuantumExecutor.OnDumpMachine(target); + return QVoid.Instance; + } + + protected virtual QVoid DumpRegister(T target, IQArray qubits) + { + QuantumExecutor.OnDumpRegister(target, qubits); + return QVoid.Instance; + } + + public class QuantumExecutorSimDumpMachine : Quantum.Diagnostics.DumpMachine + { + private QuantumExecutorSimulator Simulator { get; } + + public QuantumExecutorSimDumpMachine(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func Body => (location) => + { + if (location == null) { throw new ArgumentNullException(nameof(location)); } + + return Simulator.DumpMachine(location); + }; + } + + public class QuantumExecutorSimDumpRegister : Quantum.Diagnostics.DumpRegister + { + private QuantumExecutorSimulator Simulator { get; } + + + public QuantumExecutorSimDumpRegister(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func<(T, IQArray), QVoid> Body => (__in) => + { + var (location, qubits) = __in; + + if (location == null) { throw new ArgumentNullException(nameof(location)); } + return Simulator.DumpRegister(location, qubits); + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/EmptyQuantumExecutor.cs b/src/Simulation/Simulators/QuantumExecutor/EmptyQuantumExecutor.cs new file mode 100644 index 00000000000..8b897d4d072 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/EmptyQuantumExecutor.cs @@ -0,0 +1,238 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + /// + /// A class that implements IQuantumExecutor that does not do any logic, but is convenient to inherit from. + /// It throws for most APIs. + /// + class EmptyQuantumExecutor : IQuantumExecutor + { + public virtual void Assert(IQArray bases, IQArray qubits, Result result, string msg) + { + } + + public virtual void AssertProb(IQArray bases, IQArray qubits, double probabilityOfZero, string msg, double tol) + { + } + + public virtual void ClassicallyControlled(Result measurementResult, Action onZero, Action onOne) + { + throw new NotImplementedException(); + } + + public virtual void ClassicallyControlled(IQArray measurementResults, IQArray resultsValues, Action equalOp, Action nonEqualOp) + { + throw new NotImplementedException(); + } + + public virtual void ControlledExp(IQArray controls, IQArray paulis, double theta, IQArray qubits) + { + throw new NotImplementedException(); + } + + public virtual void ControlledExpFrac(IQArray controls, IQArray paulis, long numerator, long power, IQArray qubits) + { + throw new NotImplementedException(); + } + + public virtual void ControlledH(IQArray controls, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void ControlledR(IQArray controls, Pauli axis, double theta, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void ControlledR1(IQArray controls, double theta, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void ControlledR1Frac(IQArray controls, long numerator, long power, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void ControlledRFrac(IQArray controls, Pauli axis, long numerator, long power, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void ControlledS(IQArray controls, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void ControlledSAdj(IQArray controls, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void ControlledSWAP(IQArray controls, Qubit qubit1, Qubit qubit2) + { + throw new NotImplementedException(); + } + + public virtual void ControlledT(IQArray controls, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void ControlledTAdj(IQArray controls, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void ControlledX(IQArray controls, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void ControlledY(IQArray controls, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void ControlledZ(IQArray controls, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void Exp(IQArray paulis, double theta, IQArray qubits) + { + throw new NotImplementedException(); + } + + public virtual void ExpFrac(IQArray paulis, long numerator, long power, IQArray qubits) + { + throw new NotImplementedException(); + } + + public virtual void H(Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual Result M(Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual Result Measure(IQArray bases, IQArray qubits) + { + throw new NotImplementedException(); + } + + public virtual void OnAllocateQubits(IQArray qubits) + { + } + + public virtual void OnBorrowQubits(IQArray qubits) + { + } + + public virtual void OnDumpMachine(T location) + { + } + + public virtual void OnDumpRegister(T location, IQArray qubits) + { + } + + public virtual void OnMessage(string msg) + { + } + + public virtual void OnOperationEnd(ICallable operation, IApplyData arguments) + { + } + + public virtual void OnOperationStart(ICallable operation, IApplyData arguments) + { + } + + public virtual void OnFail(System.Runtime.ExceptionServices.ExceptionDispatchInfo exceptionDispatchInfo) + { + } + + public virtual void OnReleaseQubits(IQArray qubits) + { + } + + public virtual void OnReturnQubits(IQArray qubits) + { + } + + public virtual void R(Pauli axis, double theta, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void R1(double theta, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void R1Frac(long numerator, long power, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void Reset(Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void RFrac(Pauli axis, long numerator, long power, Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void S(Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void SAdj(Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void SWAP(Qubit qubit1, Qubit qubit2) + { + throw new NotImplementedException(); + } + + public virtual void T(Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void TAdj(Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void X(Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void Y(Qubit qubit) + { + throw new NotImplementedException(); + } + + public virtual void Z(Qubit qubit) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/Exp.cs b/src/Simulation/Simulators/QuantumExecutor/Exp.cs new file mode 100644 index 00000000000..6d8469dd46b --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/Exp.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimExp : Quantum.Intrinsic.Exp + { + private QuantumExecutorSimulator Simulator { get; } + + public QuantumExecutorSimExp(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func<(IQArray, double, IQArray), QVoid> Body => (_args) => + { + + var (paulis, theta, qubits) = _args; + + if (paulis.Length != qubits.Length) + { + throw new InvalidOperationException($"Both input arrays for {this.GetType().Name} (paulis,qubits), must be of same size."); + } + + CommonUtils.PruneObservable(paulis, qubits, out QArray newPaulis, out QArray newQubits); + Simulator.QuantumExecutor.Exp(newPaulis, theta, newQubits); + + return QVoid.Instance; + }; + + public override Func<(IQArray, double, IQArray), QVoid> AdjointBody => (_args) => + { + var (paulis, angle, qubits) = _args; + return this.Body.Invoke((paulis, -angle, qubits)); + }; + + public override Func<(IQArray, (IQArray, double, IQArray)), QVoid> ControlledBody => (_args) => + { + + var (ctrls, (paulis, theta, qubits)) = _args; + + CommonUtils.PruneObservable(paulis, qubits, out QArray newPaulis, out QArray newQubits); + Simulator.QuantumExecutor.ControlledExp(ctrls, newPaulis, theta, newQubits); + + return QVoid.Instance; + }; + + public override Func<(IQArray, (IQArray, double, IQArray)), QVoid> ControlledAdjointBody => (_args) => + { + var (ctrls, (paulis, angle, qubits)) = _args; + + return this.ControlledBody.Invoke((ctrls, (paulis, -angle, qubits))); + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/ExpFrac.cs b/src/Simulation/Simulators/QuantumExecutor/ExpFrac.cs new file mode 100644 index 00000000000..865f4b3bc7b --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/ExpFrac.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; +using System; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimExpFrac : Quantum.Intrinsic.ExpFrac + { + private QuantumExecutorSimulator Simulator { get; } + + public QuantumExecutorSimExpFrac(QuantumExecutorSimulator m) : base(m) { this.Simulator = m; } + + public override Func<(IQArray, long, long, IQArray), QVoid> Body => (_args) => + { + + var (paulis, nom, den, qubits) = _args; + + if (paulis.Length != qubits.Length) + { + throw new InvalidOperationException( + $"Both input arrays for {this.GetType().Name} (paulis,qubits), must be of same size."); + } + + CommonUtils.PruneObservable(paulis, qubits, out QArray newPaulis, out QArray newQubits); + + Simulator.QuantumExecutor.ExpFrac(newPaulis, nom, den, newQubits); + return QVoid.Instance; + }; + + public override Func<(IQArray, long, long, IQArray), QVoid> AdjointBody => (_args) => + { + var (paulis, nom, den, qubits) = _args; + return this.Body.Invoke((paulis, -nom, den, qubits)); + }; + + public override Func<(IQArray, (IQArray, long, long, IQArray)), QVoid> + ControlledBody => (_args) => + { + + var (ctrls, (paulis, nom, den, qubits)) = _args; + + if (paulis.Length != qubits.Length) + { + throw new InvalidOperationException( + $"Both input arrays for {this.GetType().Name} (paulis,qubits), must be of same size."); + } + CommonUtils.PruneObservable(paulis, qubits, out QArray newPaulis, out QArray newQubits); + Simulator.QuantumExecutor.ControlledExpFrac(ctrls, newPaulis, nom, den, newQubits); + + return QVoid.Instance; + }; + + public override Func<(IQArray, (IQArray, long, long, IQArray)), QVoid> + ControlledAdjointBody => (_args) => + { + var (ctrls, (paulis, nom, den, qubits)) = _args; + + return this.ControlledBody.Invoke((ctrls, (paulis, -nom, den, qubits))); + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/H.cs b/src/Simulation/Simulators/QuantumExecutor/H.cs new file mode 100644 index 00000000000..8cbd721019c --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/H.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimH : Quantum.Intrinsic.H + { + private QuantumExecutorSimulator Simulator { get; } + + + public QuantumExecutorSimH(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func Body => (q1) => + { + + Simulator.QuantumExecutor.H(q1); + return QVoid.Instance; + }; + + + public override Func<(IQArray, Qubit), QVoid> ControlledBody => (args) => + { + + var (ctrls, q1) = args; + Simulator.QuantumExecutor.ControlledH(ctrls,q1); + return QVoid.Instance; + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/M.cs b/src/Simulation/Simulators/QuantumExecutor/M.cs new file mode 100644 index 00000000000..49a613b9a47 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/M.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimM : Quantum.Intrinsic.M + { + private QuantumExecutorSimulator Simulator { get; } + + public QuantumExecutorSimM(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func Body => (q) => + { + return Simulator.QuantumExecutor.M(q); + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/Measure.cs b/src/Simulation/Simulators/QuantumExecutor/Measure.cs new file mode 100644 index 00000000000..98f6a89ce4f --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/Measure.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimMeasure : Quantum.Intrinsic.Measure + { + private QuantumExecutorSimulator Simulator { get; } + + + public QuantumExecutorSimMeasure(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func<(IQArray, IQArray), Result> Body => (_args) => + { + var (paulis, qubits) = _args; + + if (paulis.Length != qubits.Length) + { + throw new InvalidOperationException($"Both input arrays for {this.GetType().Name} (paulis,qubits), must be of same size"); + } + + CommonUtils.PruneObservable(paulis, qubits, out QArray newPaulis, out QArray newQubits); + return Simulator.QuantumExecutor.Measure( newPaulis, newQubits); + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/QuantumExecutorSimulator.cs b/src/Simulation/Simulators/QuantumExecutor/QuantumExecutorSimulator.cs new file mode 100644 index 00000000000..79270e6acf8 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/QuantumExecutorSimulator.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Common; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + /// + /// Simulator that redirects all the calls to a class implementing interface. + /// + public partial class QuantumExecutorSimulator : SimulatorBase + { + private const int PreallocatedQubitCount = 1000; + /// + /// Random number generator used for Microsoft.Quantum.Intrinsic.Random + /// + public readonly System.Random random; + + public override string Name => "QuantumExecutorSimulator"; + + /// + /// An instance of a class implementing interface that this simulator wraps. + /// + public IQuantumExecutor QuantumExecutor + { + get; + private set; + } + + /// + /// + /// + /// An instance of a class implementing interface to be wrapped. If the parameter is null is used. + /// An instance of a class implementing interface. If the parameter is null is used. + /// A seed to be used by Q# Microsoft.Quantum.Intrinsic.Random operation. + public QuantumExecutorSimulator(IQuantumExecutor quantumExecutor = null, IQubitManager qubitManager = null, int? randomSeed = null) + : base(qubitManager ?? new QubitManagerTrackingScope(PreallocatedQubitCount, mayExtendCapacity:true, disableBorrowing:false)) + { + random = new System.Random(randomSeed == null ? DateTime.Now.Millisecond : randomSeed.Value); + QuantumExecutor = quantumExecutor ?? new EmptyQuantumExecutor(); + OnOperationStart += QuantumExecutor.OnOperationStart; + OnOperationEnd += QuantumExecutor.OnOperationEnd; + OnLog += QuantumExecutor.OnMessage; + } + + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/R.cs b/src/Simulation/Simulators/QuantumExecutor/R.cs new file mode 100644 index 00000000000..93a91a07221 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/R.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimR : Quantum.Intrinsic.R + { + + private QuantumExecutorSimulator Simulator { get; } + + + public QuantumExecutorSimR(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func<(Pauli, double, Qubit), QVoid> Body => (_args) => + { + + var (basis, angle, q1) = _args; + if (basis != Pauli.PauliI) + { + Simulator.QuantumExecutor.R(basis, angle,q1); + } + return QVoid.Instance; + }; + + public override Func<(Pauli, double, Qubit), QVoid> AdjointBody => (_args) => + { + var (basis, angle, q1) = _args; + return this.Body.Invoke((basis, -angle, q1)); + }; + + public override Func<(IQArray, (Pauli, double, Qubit)), QVoid> ControlledBody => (_args) => + { + + var (ctrls, (basis, angle, q1)) = _args; + Simulator.QuantumExecutor.ControlledR(ctrls, basis, angle, q1); + return QVoid.Instance; + }; + + + public override Func<(IQArray, (Pauli, double, Qubit)), QVoid> ControlledAdjointBody => (_args) => + { + var (ctrls, (basis, angle, q1)) = _args; + return this.ControlledBody.Invoke((ctrls, (basis, -angle, q1))); + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/R1.cs b/src/Simulation/Simulators/QuantumExecutor/R1.cs new file mode 100644 index 00000000000..1ae4d440caa --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/R1.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimR1 : Quantum.Intrinsic.R1 + { + + private QuantumExecutorSimulator Simulator { get; } + + + public QuantumExecutorSimR1(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func<(double, Qubit), QVoid> Body => (_args) => + { + + var (angle, q1) = _args; + Simulator.QuantumExecutor.R1(angle, q1); + return QVoid.Instance; + }; + + public override Func<(double, Qubit), QVoid> AdjointBody => (_args) => + { + var (angle, q1) = _args; + return this.Body.Invoke((-angle, q1)); + }; + + public override Func<(IQArray, ( double, Qubit)), QVoid> ControlledBody => (_args) => + { + + var (ctrls, (angle, q1)) = _args; + Simulator.QuantumExecutor.ControlledR1(ctrls, angle, q1); + return QVoid.Instance; + }; + + + public override Func<(IQArray, (double, Qubit)), QVoid> ControlledAdjointBody => (_args) => + { + var (ctrls, (angle, q1)) = _args; + return this.ControlledBody.Invoke((ctrls, (-angle, q1))); + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/R1Frac.cs b/src/Simulation/Simulators/QuantumExecutor/R1Frac.cs new file mode 100644 index 00000000000..54b26df4b22 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/R1Frac.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimR1Frac : Quantum.Intrinsic.R1Frac + { + + private QuantumExecutorSimulator Simulator { get; } + + + public QuantumExecutorSimR1Frac(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func<(long, long, Qubit), QVoid> Body => (_args) => + { + + var (num, denom , q1) = _args; + var (numNew, denomNew) = CommonUtils.Reduce(num, denom); + Simulator.QuantumExecutor.R1Frac(numNew, denomNew, q1); + return QVoid.Instance; + }; + + public override Func<(long, long, Qubit), QVoid> AdjointBody => (_args) => + { + var (num, denom, q1) = _args; + return this.Body.Invoke((-num, denom, q1)); + }; + + public override Func<(IQArray, (long, long, Qubit)), QVoid> ControlledBody => (_args) => + { + + var (ctrls, (num, denom, q1)) = _args; + var (numNew, denomNew) = CommonUtils.Reduce(num, denom); + Simulator.QuantumExecutor.ControlledR1Frac(ctrls, numNew, denomNew, q1); + return QVoid.Instance; + }; + + public override Func<(IQArray, (long, long, Qubit)), QVoid> ControlledAdjointBody => (_args) => + { + var (ctrls, (num, denom, q1)) = _args; + return this.ControlledBody.Invoke((ctrls, (-num, denom, q1))); + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/RFrac.cs b/src/Simulation/Simulators/QuantumExecutor/RFrac.cs new file mode 100644 index 00000000000..cc7fef265d2 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/RFrac.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimRFrac : Quantum.Intrinsic.RFrac + { + + private QuantumExecutorSimulator Simulator { get; } + + + public QuantumExecutorSimRFrac(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func<(Pauli, long, long, Qubit), QVoid> Body => (_args) => + { + + var (basis, num, denom , q1) = _args; + if (basis != Pauli.PauliI) + { + var (numNew, denomNew) = CommonUtils.Reduce(num, denom); + Simulator.QuantumExecutor.RFrac(basis, numNew, denomNew, q1); + } + return QVoid.Instance; + }; + + public override Func<(Pauli, long, long, Qubit), QVoid> AdjointBody => (_args) => + { + var (basis, num, denom, q1) = _args; + return this.Body.Invoke((basis, -num, denom, q1)); + }; + + public override Func<(IQArray, (Pauli, long, long, Qubit)), QVoid> ControlledBody => (_args) => + { + + var (ctrls, (basis, num, denom, q1)) = _args; + var (numNew, denomNew) = CommonUtils.Reduce(num, denom); + Simulator.QuantumExecutor.ControlledRFrac(ctrls, basis, numNew, denomNew, q1); + return QVoid.Instance; + }; + + public override Func<(IQArray, (Pauli, long, long, Qubit)), QVoid> ControlledAdjointBody => (_args) => + { + var (ctrls, (basis, num, denom, q1)) = _args; + return this.ControlledBody.Invoke((ctrls, (basis, -num, denom, q1))); + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/Release.cs b/src/Simulation/Simulators/QuantumExecutor/Release.cs new file mode 100644 index 00000000000..4aa94ffa091 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/Release.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimRelease : Intrinsic.Release + { + private readonly QuantumExecutorSimulator sim; + public QuantumExecutorSimRelease(QuantumExecutorSimulator m) : base(m){ + sim = m; + } + + public override void Apply(Qubit q) + { + sim.QuantumExecutor.OnReleaseQubits(new QArray(q)); + sim.QubitManager.Release(q); + } + + public override void Apply(IQArray qubits) + { + sim.QuantumExecutor.OnReleaseQubits(qubits); + sim.QubitManager.Release(qubits); + } + } + } +} \ No newline at end of file diff --git a/src/Simulation/Simulators/QuantumExecutor/Reset.cs b/src/Simulation/Simulators/QuantumExecutor/Reset.cs new file mode 100644 index 00000000000..3daec9ebb82 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/Reset.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimReset : Quantum.Intrinsic.Reset + { + private QuantumExecutorSimulator Simulator { get; } + + + public QuantumExecutorSimReset(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func Body => (q1) => + { + + Simulator.QuantumExecutor.Reset(q1); + return QVoid.Instance; + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/Return.cs b/src/Simulation/Simulators/QuantumExecutor/Return.cs new file mode 100644 index 00000000000..743ddfeb8d8 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/Return.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + using Microsoft.Quantum.Simulation.Core; + + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimReturn : Intrinsic.Return + { + private readonly QuantumExecutorSimulator sim; + public QuantumExecutorSimReturn(QuantumExecutorSimulator m) : base(m){ + sim = m; + } + + public override void Apply(Qubit q) + { + sim.QuantumExecutor.OnReturnQubits(new QArray(q)); + sim.QubitManager.Return(q); + } + + public override void Apply(IQArray qubits) + { + sim.QuantumExecutor.OnReturnQubits(qubits); + sim.QubitManager.Return(qubits); + } + } + } +} \ No newline at end of file diff --git a/src/Simulation/Simulators/QuantumExecutor/S.cs b/src/Simulation/Simulators/QuantumExecutor/S.cs new file mode 100644 index 00000000000..1d8812df8d7 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/S.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimS : Quantum.Intrinsic.S + { + private QuantumExecutorSimulator Simulator { get; } + + public QuantumExecutorSimS(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func Body => (q1) => + { + + Simulator.QuantumExecutor.S(q1); + return QVoid.Instance; + }; + + public override Func<(IQArray, Qubit), QVoid> ControlledBody => (_args) => + { + + (IQArray ctrls, Qubit q1) = _args; + Simulator.QuantumExecutor.ControlledS(ctrls, q1); + return QVoid.Instance; + }; + + public override Func AdjointBody => (q1) => + { + + Simulator.QuantumExecutor.SAdj(q1); + return QVoid.Instance; + }; + + public override Func<(IQArray, Qubit), QVoid> ControlledAdjointBody => (_args) => + { + + (IQArray ctrls, Qubit q1) = _args; + Simulator.QuantumExecutor.ControlledSAdj(ctrls, q1); + return QVoid.Instance; + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/SWAP.cs b/src/Simulation/Simulators/QuantumExecutor/SWAP.cs new file mode 100644 index 00000000000..7b96ecf6e73 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/SWAP.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimSWAP : Quantum.Intrinsic.SWAP + { + private QuantumExecutorSimulator Simulator { get; } + + + public QuantumExecutorSimSWAP(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func<(Qubit,Qubit), QVoid> Body => (q1) => + { + + Simulator.QuantumExecutor.SWAP(q1.Item1, q1.Item2); + return QVoid.Instance; + }; + + public override Func<(IQArray, (Qubit, Qubit)), QVoid> ControlledBody => (args) => + { + + var (ctrls, q1) = args; + Simulator.QuantumExecutor.ControlledSWAP(ctrls, q1.Item1, q1.Item2); + return QVoid.Instance; + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/T.cs b/src/Simulation/Simulators/QuantumExecutor/T.cs new file mode 100644 index 00000000000..55ecdfde021 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/T.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimT : Quantum.Intrinsic.T + { + private QuantumExecutorSimulator Simulator { get; } + + + public QuantumExecutorSimT(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func Body => (q1) => + { + + Simulator.QuantumExecutor.T(q1); + return QVoid.Instance; + }; + + public override Func<(IQArray, Qubit), QVoid> ControlledBody => (_args) => + { + + (IQArray ctrls, Qubit q1) = _args; + + Simulator.QuantumExecutor.ControlledT(ctrls, q1); + + return QVoid.Instance; + }; + + public override Func AdjointBody => (q1) => + { + + Simulator.QuantumExecutor.TAdj(q1); + return QVoid.Instance; + }; + + public override Func<(IQArray, Qubit), QVoid> ControlledAdjointBody => (_args) => + { + + (IQArray ctrls, Qubit q1) = _args; + Simulator.QuantumExecutor.ControlledTAdj(ctrls, q1); + return QVoid.Instance; + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/X.cs b/src/Simulation/Simulators/QuantumExecutor/X.cs new file mode 100644 index 00000000000..a65e19d62b0 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/X.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimX : Quantum.Intrinsic.X + { + private QuantumExecutorSimulator Simulator { get; } + + + public QuantumExecutorSimX(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func Body => (q1) => + { + + Simulator.QuantumExecutor.X(q1); + return QVoid.Instance; + }; + + public override Func<(IQArray, Qubit), QVoid> ControlledBody => (args) => + { + + var (ctrls, q1) = args; + Simulator.QuantumExecutor.ControlledX(ctrls, q1); + return QVoid.Instance; + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/Y.cs b/src/Simulation/Simulators/QuantumExecutor/Y.cs new file mode 100644 index 00000000000..bc64eb27500 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/Y.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimY : Quantum.Intrinsic.Y + { + private QuantumExecutorSimulator Simulator { get; } + + + public QuantumExecutorSimY(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func Body => (q1) => + { + + Simulator.QuantumExecutor.Y(q1); + return QVoid.Instance; + }; + + public override Func<(IQArray, Qubit), QVoid> ControlledBody => (_args) => + { + + (IQArray ctrls, Qubit q1) = _args; + Simulator.QuantumExecutor.ControlledY(ctrls, q1); + return QVoid.Instance; + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/Z.cs b/src/Simulation/Simulators/QuantumExecutor/Z.cs new file mode 100644 index 00000000000..2e7c3646e71 --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/Z.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public class QuantumExecutorSimZ : Quantum.Intrinsic.Z + { + private QuantumExecutorSimulator Simulator { get; } + + public QuantumExecutorSimZ(QuantumExecutorSimulator m) : base(m) + { + this.Simulator = m; + } + + public override Func Body => (q1) => + { + + Simulator.QuantumExecutor.Z(q1); + return QVoid.Instance; + }; + + public override Func<(IQArray, Qubit), QVoid> ControlledBody => (_args) => + { + + (IQArray ctrls, Qubit q1) = _args; + Simulator.QuantumExecutor.ControlledZ(ctrls, q1); + return QVoid.Instance; + }; + } + } +} diff --git a/src/Simulation/Simulators/QuantumExecutor/random.cs b/src/Simulation/Simulators/QuantumExecutor/random.cs new file mode 100644 index 00000000000..38350175bad --- /dev/null +++ b/src/Simulation/Simulators/QuantumExecutor/random.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Quantum.Simulation.Core; +using System; + +namespace Microsoft.Quantum.Simulation.QuantumExecutor +{ + public partial class QuantumExecutorSimulator + { + public static long SampleDistribution(IQArray unnormalizedDistribution, double uniformZeroOneSample) + { + double total = 0.0; + foreach (double prob in unnormalizedDistribution) + { + if (prob < 0) + { + throw new ExecutionFailException("Random expects array of non-negative doubles."); + } + total += prob; + } + + if (total == 0) + { + throw new ExecutionFailException("Random expects array of non-negative doubles with positive sum."); + } + + double sample = uniformZeroOneSample * total; + double sum = unnormalizedDistribution[0]; + for (int i = 0; i < unnormalizedDistribution.Length - 1; ++i) + { + if (sum >= sample) + { + return i; + } + sum += unnormalizedDistribution[i]; + } + return unnormalizedDistribution.Length; + } + + public class QuantumExecutorSimRandom : Quantum.Intrinsic.Random + { + private QuantumExecutorSimulator Simulator { get; } + public QuantumExecutorSimRandom(QuantumExecutorSimulator m) : base(m) + { + Simulator = m; + } + + public override Func, Int64> Body => (p) => + { + return SampleDistribution(p, Simulator.random.NextDouble()); + }; + } + } +}