diff --git a/src/Simulation/Core/Extensions.cs b/src/Simulation/Core/Extensions.cs index ab524f68f8e..cb1dea84a8a 100644 --- a/src/Simulation/Core/Extensions.cs +++ b/src/Simulation/Core/Extensions.cs @@ -24,6 +24,13 @@ public static Result ToResult(this bool b) : Result.Zero; } + public static Result ToResult(this bool b, Qubit qubit) + { + return b + ? new ResultMeasured(ResultValue.One, qubit) + : new ResultMeasured(ResultValue.Zero, qubit); + } + public static Result ToResult(this uint b) { Debug.Assert(b == 0 || b == 1, $"Unexpected result value: {b}"); @@ -33,6 +40,15 @@ public static Result ToResult(this uint b) : Result.One; } + public static Result ToResult(this uint b, Qubit qubit) + { + Debug.Assert(b == 0 || b == 1, $"Unexpected result value: {b}"); + + return b == 0 + ? new ResultMeasured(ResultValue.Zero, qubit) + : new ResultMeasured(ResultValue.One, qubit); + } + public static double Pow(this double x, double y) { return System.Math.Pow(x, y); diff --git a/src/Simulation/Core/RuntimeMetadata.cs b/src/Simulation/Core/RuntimeMetadata.cs index d61f63050f9..86a8067c5c3 100644 --- a/src/Simulation/Core/RuntimeMetadata.cs +++ b/src/Simulation/Core/RuntimeMetadata.cs @@ -42,12 +42,16 @@ public class RuntimeMetadata /// /// True if operation is composed of multiple operations. /// - /// /// /// This is used in composite operations, such as ApplyToEach. /// public bool IsComposite { get; set; } + /// + /// True if operation is a classically-controlled conditional operation. + /// + public bool IsConditional { get; set; } + /// /// Group of operations for each classical branch (true and false). /// @@ -83,7 +87,8 @@ public override bool Equals(object? obj) if (this.Label != other.Label || this.FormattedNonQubitArgs != other.FormattedNonQubitArgs || this.IsAdjoint != other.IsAdjoint || this.IsControlled != other.IsControlled || - this.IsMeasurement != other.IsMeasurement || this.IsComposite != other.IsComposite) + this.IsMeasurement != other.IsMeasurement || this.IsComposite != other.IsComposite || + this.IsConditional != other.IsConditional) return false; if (!ListEquals(this.Controls, other.Controls)) return false; @@ -122,7 +127,7 @@ public override int GetHashCode() // Combine all other properties and get the resulting hashcode var otherHash = HashCode.Combine(this.Label, this.FormattedNonQubitArgs, this.IsAdjoint, this.IsControlled, - this.IsMeasurement, this.IsComposite); + this.IsMeasurement, this.IsComposite, this.IsConditional); // Combine them all together to get the final hashcode return HashCode.Combine(controlsHash, targetsHash, childrenHash, otherHash); diff --git a/src/Simulation/Core/Types.cs b/src/Simulation/Core/Types.cs index 4fac9c5e969..840668ac775 100644 --- a/src/Simulation/Core/Types.cs +++ b/src/Simulation/Core/Types.cs @@ -49,6 +49,24 @@ public override ResultValue GetValue() } } + public class ResultMeasured : Result + { + private ResultValue Value; + + public Qubit Qubit { get; set; } + + public ResultMeasured(ResultValue value, Qubit qubit) + { + Value = value; + Qubit = qubit; + } + + public override ResultValue GetValue() + { + return Value; + } + } + /// /// Represents the Result of a Measurement. Corresponds to Q# type Result. /// diff --git a/src/Simulation/QsharpCore/ClassicalControl.cs b/src/Simulation/QsharpCore/ClassicalControl.cs new file mode 100644 index 00000000000..4645786fa92 --- /dev/null +++ b/src/Simulation/QsharpCore/ClassicalControl.cs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Simulation.QuantumProcessor.Extensions +{ + /// + /// Provides interface to access base `GetRuntimeMetadata` method. + /// + public interface IApplyIfElse : ICallable + { + RuntimeMetadata? GetBaseRuntimeMetadata(IApplyData args); + } + + /// + /// Provides static `GetRuntimeMetadata` method for ApplyIfElseR and its variants + /// to avoid code duplication. + /// + public class ApplyIfElseUtils<__C__, __T__, __U__> + { + public static RuntimeMetadata? GetRuntimeMetadata(IApplyIfElse op, IApplyData args) + { + Debug.Assert(args.Value is ValueTuple, + $"Failed to retrieve runtime metadata for {op.ToString()}."); + + if (args.Value is ValueTuple ifArgs) + { + var (result, (onZeroOp, zeroArg), (onOneOp, oneArg)) = ifArgs; + var metadata = op.GetBaseRuntimeMetadata(args); + + if (metadata == null) return null; + + if (result is ResultMeasured measured) + { + metadata.IsConditional = true; + metadata.Controls = new List() { measured.Qubit }; + } + else + { + metadata.IsComposite = true; + } + + return metadata; + } + + return null; + } + } + + public partial class ApplyIfElseR<__T__, __U__> : IApplyIfElse + { + public RuntimeMetadata? GetBaseRuntimeMetadata(IApplyData args) => + base.GetRuntimeMetadata(args); + + /// + public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) => + ApplyIfElseUtils.GetRuntimeMetadata(this, args); + } + + public partial class ApplyIfElseRA<__T__, __U__> : IApplyIfElse + { + public RuntimeMetadata? GetBaseRuntimeMetadata(IApplyData args) => + base.GetRuntimeMetadata(args); + + /// + public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) => + ApplyIfElseUtils.GetRuntimeMetadata(this, args); + } + + public partial class ApplyIfElseRC<__T__, __U__> : IApplyIfElse + { + public RuntimeMetadata? GetBaseRuntimeMetadata(IApplyData args) => + base.GetRuntimeMetadata(args); + + /// + public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) => + ApplyIfElseUtils.GetRuntimeMetadata(this, args); + } + + public partial class ApplyIfElseRCA<__T__, __U__> : IApplyIfElse + { + public RuntimeMetadata? GetBaseRuntimeMetadata(IApplyData args) => + base.GetRuntimeMetadata(args); + + /// + public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) => + ApplyIfElseUtils.GetRuntimeMetadata(this, args); + } +} diff --git a/src/Simulation/Simulators.Tests/RuntimeMetadataTests.cs b/src/Simulation/Simulators.Tests/RuntimeMetadataTests.cs index 8ba1e3cd373..2264a1838ee 100644 --- a/src/Simulation/Simulators.Tests/RuntimeMetadataTests.cs +++ b/src/Simulation/Simulators.Tests/RuntimeMetadataTests.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.QuantumProcessor.Extensions; using Xunit; namespace Microsoft.Quantum.Simulation.Simulators.Tests @@ -41,6 +42,7 @@ public void CheckEquality() IsControlled = false, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { }, @@ -53,6 +55,7 @@ public void CheckEquality() IsControlled = false, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { }, @@ -89,6 +92,11 @@ public void CheckEquality() Assert.NotEqual(a, b); Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); b.IsComposite = false; + + b.IsConditional = true; + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + b.IsConditional = false; } [Fact] @@ -198,6 +206,7 @@ public void CNOT() IsControlled = true, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { control }, Targets = new List() { target }, @@ -222,6 +231,7 @@ public void CCNOT() IsControlled = true, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { control1, control2 }, Targets = new List() { target }, @@ -245,6 +255,7 @@ public void Swap() IsControlled = false, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { q1, q2 }, @@ -267,6 +278,7 @@ public void Ry() IsControlled = false, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { target }, @@ -289,6 +301,7 @@ public void M() IsControlled = false, IsMeasurement = true, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { measureQubit }, @@ -311,6 +324,7 @@ public void Reset() IsControlled = false, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { target }, @@ -358,6 +372,7 @@ public void MResetX() IsControlled = false, IsMeasurement = true, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { measureQubit }, @@ -380,6 +395,7 @@ public void MResetY() IsControlled = false, IsMeasurement = true, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { measureQubit }, @@ -402,6 +418,7 @@ public void MResetZ() IsControlled = false, IsMeasurement = true, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { measureQubit }, @@ -427,6 +444,7 @@ public void EmptyOperation() IsControlled = false, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { }, @@ -450,6 +468,7 @@ public void OperationAsArgument() IsControlled = false, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { q }, @@ -471,6 +490,7 @@ public void NestedOperation() IsControlled = false, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { }, @@ -493,6 +513,7 @@ public void DuplicateQubitArgs() IsControlled = false, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { q }, @@ -540,6 +561,7 @@ public void FooUDTOp() IsControlled = false, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { target }, @@ -566,6 +588,7 @@ public void ControlledH() IsControlled = true, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = controls, Targets = new List() { target }, @@ -589,6 +612,7 @@ public void ControlledX() IsControlled = true, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = controls, Targets = new List() { target }, @@ -613,6 +637,7 @@ public void ControlledCNOT() IsControlled = true, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = controls.Append(control), Targets = new List() { target }, @@ -639,6 +664,7 @@ public void ControlledCCNOT() IsControlled = true, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { control1, control2, control3 }, Targets = new List() { target }, @@ -664,6 +690,7 @@ public void AdjointH() IsControlled = false, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { target }, @@ -686,6 +713,7 @@ public void AdjointX() IsControlled = false, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { target }, @@ -708,6 +736,7 @@ public void AdjointAdjointH() IsControlled = false, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { target }, @@ -732,6 +761,7 @@ public void ControlledAdjointH() IsControlled = true, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = controls, Targets = new List() { target }, @@ -758,6 +788,7 @@ public void ControlledAdjointAdjointH() IsControlled = true, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = controls, Targets = new List() { target }, @@ -787,6 +818,7 @@ public void PartialRy() IsControlled = false, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { target }, @@ -810,6 +842,7 @@ public void PartialUDT() IsControlled = false, IsMeasurement = false, IsComposite = false, + IsConditional = false, Children = null, Controls = new List() { }, Targets = new List() { }, @@ -818,4 +851,119 @@ public void PartialUDT() Assert.Equal(op.GetRuntimeMetadata(args), expected); } } + + public class ClassicallyControlledTests + { + [Fact] + public void ApplyIfElseR() + { + var qsim = new QuantumSimulator(); + var control = new FreeQubit(0); + var target = new FreeQubit(1); + var res = new ResultMeasured(ResultValue.Zero, control); + var zeroOp = qsim.Get(); + var oneOp = qsim.Get(); + var op = qsim.Get>(); + var args = op.__dataIn((res, (zeroOp, target), (oneOp, target))); + var expected = new RuntimeMetadata() + { + Label = "ApplyIfElseR", + FormattedNonQubitArgs = "(Zero, (Z), (X))", + IsAdjoint = false, + IsControlled = false, + IsMeasurement = false, + IsComposite = false, + IsConditional = true, + Children = null, + Controls = new List() { control }, + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void ApplyIfElseRA() + { + var qsim = new QuantumSimulator(); + var control = new FreeQubit(0); + var target = new FreeQubit(1); + var res = new ResultMeasured(ResultValue.Zero, control); + var zeroOp = qsim.Get().Adjoint; + var oneOp = qsim.Get().Adjoint; + var op = qsim.Get>(); + var args = op.__dataIn((res, (zeroOp, target), (oneOp, target))); + var expected = new RuntimeMetadata() + { + Label = "ApplyIfElseRA", + FormattedNonQubitArgs = "(Zero, (Z), (X))", + IsAdjoint = false, + IsControlled = false, + IsMeasurement = false, + IsComposite = false, + IsConditional = true, + Children = null, + Controls = new List() { control }, + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void ApplyIfElseRC() + { + var qsim = new QuantumSimulator(); + var control = new FreeQubit(0); + var target = new FreeQubit(1); + var res = new ResultMeasured(ResultValue.Zero, control); + var zeroOp = qsim.Get().Controlled; + var oneOp = qsim.Get().Controlled; + var op = qsim.Get>(); + var args = op.__dataIn((res, (zeroOp, target), (oneOp, target))); + var expected = new RuntimeMetadata() + { + Label = "ApplyIfElseRC", + FormattedNonQubitArgs = "(Zero, (Z), (X))", + IsAdjoint = false, + IsControlled = false, + IsMeasurement = false, + IsComposite = false, + IsConditional = true, + Children = null, + Controls = new List() { control }, + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void ApplyIfElseRCA() + { + var qsim = new QuantumSimulator(); + var control = new FreeQubit(0); + var target = new FreeQubit(1); + var res = new ResultMeasured(ResultValue.Zero, control); + var zeroOp = qsim.Get().Controlled.Adjoint; + var oneOp = qsim.Get().Controlled.Adjoint; + var op = qsim.Get>(); + var args = op.__dataIn((res, (zeroOp, target), (oneOp, target))); + var expected = new RuntimeMetadata() + { + Label = "ApplyIfElseRCA", + FormattedNonQubitArgs = "(Zero, (Z), (X))", + IsAdjoint = false, + IsControlled = false, + IsMeasurement = false, + IsComposite = false, + IsConditional = true, + Children = null, + Controls = new List() { control }, + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + } } diff --git a/src/Simulation/Simulators/QuantumSimulator/M.cs b/src/Simulation/Simulators/QuantumSimulator/M.cs index 2b4fee0874d..a8b8974c72d 100644 --- a/src/Simulation/Simulators/QuantumSimulator/M.cs +++ b/src/Simulation/Simulators/QuantumSimulator/M.cs @@ -29,7 +29,7 @@ public QSimM(QuantumSimulator m) : base(m) Simulator.CheckQubit(q); //setting qubit as measured to allow for release q.IsMeasured = true; - return M(Simulator.Id, (uint)q.Id).ToResult(); + return M(Simulator.Id, (uint)q.Id).ToResult(q); }; } }