From 425a1c57d076eacaeb4860f14b57c9432f4ff43f Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Thu, 20 Aug 2020 14:19:42 -0400 Subject: [PATCH 1/2] First step to implementing classically-controlled gates --- src/ExecutionPathTracer/ExecutionPath.cs | 19 ++++++++ .../ExecutionPathTracer.cs | 11 +++++ src/ExecutionPathTracer/Extensions.cs | 34 ++++++++++++++ src/Tests/ExecutionPathTracerTests.cs | 47 +++++++++++++++++++ .../Workspace.ExecutionPathTracer/Circuits.qs | 9 ++++ 5 files changed, 120 insertions(+) diff --git a/src/ExecutionPathTracer/ExecutionPath.cs b/src/ExecutionPathTracer/ExecutionPath.cs index cdba2f657e..5638d9546d 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. /// @@ -152,6 +165,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..58225f8486 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.Gate.StartsWith("ApplyIfElse")) + { + 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)) 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 dda0ddee30..116815c49b 100644 --- a/src/Tests/ExecutionPathTracerTests.cs +++ b/src/Tests/ExecutionPathTracerTests.cs @@ -576,6 +576,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); + } + } + } From 398059097c5be32249c365a11c5e52070e0fd687 Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Thu, 20 Aug 2020 14:25:32 -0400 Subject: [PATCH 2/2] Use IsConditional --- src/ExecutionPathTracer/ExecutionPath.cs | 6 ++++++ src/ExecutionPathTracer/ExecutionPathTracer.cs | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ExecutionPathTracer/ExecutionPath.cs b/src/ExecutionPathTracer/ExecutionPath.cs index 5638d9546d..9e5b25d7dd 100644 --- a/src/ExecutionPathTracer/ExecutionPath.cs +++ b/src/ExecutionPathTracer/ExecutionPath.cs @@ -141,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. /// diff --git a/src/ExecutionPathTracer/ExecutionPathTracer.cs b/src/ExecutionPathTracer/ExecutionPathTracer.cs index 58225f8486..92ef6c6622 100644 --- a/src/ExecutionPathTracer/ExecutionPathTracer.cs +++ b/src/ExecutionPathTracer/ExecutionPathTracer.cs @@ -76,7 +76,7 @@ public void OnOperationEndHandler(ICallable operation, IApplyData result) 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.Gate.StartsWith("ApplyIfElse")) + if (parentOp.IsConditional) { if (parentOp.Children.Count() == 1) currentOperation.ConditionalRender = ConditionalRender.OnZero; else currentOperation.ConditionalRender = ConditionalRender.OnOne; @@ -155,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),