diff --git a/src/ExecutionPathTracer/ExecutionPath.cs b/src/ExecutionPathTracer/ExecutionPath.cs
index cdba2f657e..9e5b25d7dd 100644
--- a/src/ExecutionPathTracer/ExecutionPath.cs
+++ b/src/ExecutionPathTracer/ExecutionPath.cs
@@ -98,6 +98,19 @@ public QubitDeclaration(int id, int numChildren = 0)
public bool ShouldSerializeNumChildren() => NumChildren > 0;
}
+ ///
+ /// Conditions on when to render the given operation.
+ ///
+ public enum ConditionalRender
+ {
+ /** Always rendered. */
+ Always,
+ /** Render classically-controlled operation when measurement is a zero. */
+ OnZero,
+ /** Render classically-controlled operation when measurement is a one. */
+ OnOne,
+ }
+
///
/// Represents an operation used in an execution path.
///
@@ -128,6 +141,12 @@ public class Operation
[JsonProperty("isMeasurement")]
public bool IsMeasurement { get; set; }
+ ///
+ /// True if operation is a classically-controlled operations.
+ ///
+ [JsonProperty("isConditional")]
+ public bool IsConditional { get; set; }
+
///
/// True if operation is a controlled operations.
///
@@ -152,6 +171,12 @@ public class Operation
[JsonProperty("targets")]
public IEnumerable Targets { get; set; } = new List();
+ ///
+ /// Specify conditions on when to render operation.
+ ///
+ [JsonProperty("conditionalRender")]
+ public ConditionalRender? ConditionalRender;
+
///
/// Dictionary of data attributes to add to rendered gate element.
///
diff --git a/src/ExecutionPathTracer/ExecutionPathTracer.cs b/src/ExecutionPathTracer/ExecutionPathTracer.cs
index d4b6f1ef8e..92ef6c6622 100644
--- a/src/ExecutionPathTracer/ExecutionPathTracer.cs
+++ b/src/ExecutionPathTracer/ExecutionPathTracer.cs
@@ -75,6 +75,17 @@ public void OnOperationEndHandler(ICallable operation, IApplyData result)
// Add operation to parent operation's children
parentOp.Children = (parentOp.Children ?? ImmutableList.Empty).Add(currentOperation);
+ // If parent op is a conditional statement, the first child is rendered onZero and the second onOne
+ if (parentOp.IsConditional)
+ {
+ if (parentOp.Children.Count() == 1) currentOperation.ConditionalRender = ConditionalRender.OnZero;
+ else currentOperation.ConditionalRender = ConditionalRender.OnOne;
+ } else if (parentOp.ConditionalRender != null)
+ {
+ // Inherit parent's render condition
+ currentOperation.ConditionalRender = parentOp.ConditionalRender;
+ }
+
// Add target qubits to parent
parentOp.Targets = parentOp.Targets
.Concat(currentOperation.Targets.Where(reg => reg is QubitRegister))
@@ -144,6 +155,7 @@ private ClassicalRegister GetClassicalRegister(Qubit controlQubit)
{
Gate = metadata.Label,
DisplayArgs = displayArgs,
+ IsConditional = metadata.IsConditional,
IsControlled = metadata.IsControlled,
IsAdjoint = metadata.IsAdjoint,
Controls = this.GetQubitRegisters(metadata.Controls),
diff --git a/src/ExecutionPathTracer/Extensions.cs b/src/ExecutionPathTracer/Extensions.cs
index 103bbbf7e6..b28b17452d 100644
--- a/src/ExecutionPathTracer/Extensions.cs
+++ b/src/ExecutionPathTracer/Extensions.cs
@@ -3,11 +3,41 @@
#nullable enable
+using System;
using System.Collections.Generic;
using Microsoft.Quantum.Simulation.Common;
+using Microsoft.Quantum.Simulation.Core;
+using Microsoft.Quantum.Simulation.QuantumProcessor.Extensions;
namespace Microsoft.Quantum.IQSharp.ExecutionPathTracer
{
+ ///
+ /// Custom ApplyIfElse used by Tracer to overrides the default behaviour and executes both branches
+ /// of the conditional statement.
+ ///
+ public class TracerApplyIfElse : ApplyIfElseR
+ {
+ private SimulatorBase Simulator { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public TracerApplyIfElse(SimulatorBase m) : base(m)
+ {
+ this.Simulator = m;
+ }
+
+ ///
+ public override Func<(Result, (ICallable, Qubit), (ICallable, Qubit)), QVoid> Body => (q) =>
+ {
+ (Result measurementResult, (ICallable onZero, Qubit one), (ICallable onOne, Qubit two)) = q;
+ onZero.Apply(one);
+ onOne.Apply(two);
+
+ return QVoid.Instance;
+ };
+ }
+
///
/// Extension methods to be used with and by .
///
@@ -22,6 +52,10 @@ public static T WithExecutionPathTracer(this T sim, ExecutionPathTracer trace
{
sim.OnOperationStart += tracer.OnOperationStartHandler;
sim.OnOperationEnd += tracer.OnOperationEndHandler;
+ sim.Register(
+ typeof(ApplyIfElseR),
+ typeof(TracerApplyIfElse)
+ );
return sim;
}
diff --git a/src/Tests/ExecutionPathTracerTests.cs b/src/Tests/ExecutionPathTracerTests.cs
index 2a657bcb9a..9e1a23fbcc 100644
--- a/src/Tests/ExecutionPathTracerTests.cs
+++ b/src/Tests/ExecutionPathTracerTests.cs
@@ -577,6 +577,53 @@ public void BigTest()
var expected = new ExecutionPath(qubits, operations);
AssertExecutionPathsEqual(expected, path);
}
+
+ [TestMethod]
+ public void IfTest()
+ {
+ var path = GetExecutionPath("IfCirc");
+ var qubits = new QubitDeclaration[] { new QubitDeclaration(0, 2) };
+ var operations = new Operation[]
+ {
+ new Operation()
+ {
+ Gate = "M",
+ IsMeasurement = true,
+ Controls = new List() { new QubitRegister(0) },
+ Targets = new List() { new ClassicalRegister(0, 0) },
+ },
+ new Operation()
+ {
+ Gate = "ApplyIfElseR",
+ DisplayArgs = "(Zero, (X), (Z))",
+ Controls = new List() { new ClassicalRegister(0, 0) },
+ Targets = new List() { new QubitRegister(0) },
+ Children = ImmutableList.Empty.AddRange(
+ new [] {
+ new Operation()
+ {
+ Gate = "X",
+ Targets = new List() { new QubitRegister(0) },
+ ConditionalRender = ConditionalRender.OnZero,
+ },
+ new Operation()
+ {
+ Gate = "Z",
+ Targets = new List() { new QubitRegister(0) },
+ ConditionalRender = ConditionalRender.OnOne,
+ },
+ }
+ )
+ },
+ new Operation()
+ {
+ Gate = "Reset",
+ Targets = new List() { new QubitRegister(0) },
+ },
+ };
+ var expected = new ExecutionPath(qubits, operations);
+ AssertExecutionPathsEqual(expected, path);
+ }
}
[TestClass]
diff --git a/src/Tests/Workspace.ExecutionPathTracer/Circuits.qs b/src/Tests/Workspace.ExecutionPathTracer/Circuits.qs
index fd4b668a60..aaa01b43e0 100644
--- a/src/Tests/Workspace.ExecutionPathTracer/Circuits.qs
+++ b/src/Tests/Workspace.ExecutionPathTracer/Circuits.qs
@@ -4,6 +4,7 @@
namespace Tests.ExecutionPathTracer {
open Microsoft.Quantum.Intrinsic;
+ open Microsoft.Quantum.Simulation.QuantumProcessor.Extensions;
// Custom operation
operation Foo(theta : Double, (qubit : Qubit, bar : String)) : Unit
@@ -85,4 +86,12 @@ namespace Tests.ExecutionPathTracer {
}
}
+ operation IfCirc() : Unit {
+ using (q = Qubit()) {
+ let res = M(q);
+ ApplyIfElseR(res, (X, q), (Z, q));
+ Reset(q);
+ }
+ }
+
}