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);
};
}
}