From 9c0ff50f180e4846045ae78fd71608810c11693d Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Wed, 5 Aug 2020 16:16:05 -0400 Subject: [PATCH 1/3] Implement classically controlled ops --- .../ExecutionPathTracer.cs | 20 ++++++++ src/ExecutionPathTracer/Extensions.cs | 43 ++++++++++++++++ src/Tests/ExecutionPathTracerTests.cs | 49 +++++++++++++++++++ .../Workspace.ExecutionPathTracer/Circuits.qs | 9 ++++ 4 files changed, 121 insertions(+) diff --git a/src/ExecutionPathTracer/ExecutionPathTracer.cs b/src/ExecutionPathTracer/ExecutionPathTracer.cs index 9b0bc57190..77e12de32e 100644 --- a/src/ExecutionPathTracer/ExecutionPathTracer.cs +++ b/src/ExecutionPathTracer/ExecutionPathTracer.cs @@ -69,6 +69,9 @@ public void OnOperationStartHandler(ICallable operation, IApplyData arguments) var metadata = operation.GetRuntimeMetadata(arguments); + // TODO: Remove once GetRuntimeMetadata has been implemented + if (operation.Name == "ApplyIfElseR" && metadata != null) metadata.IsComposite = true; + // If metadata is a composite operation (i.e. want to trace its components instead), // we recursively create a tracer that traces its components instead if (metadata != null && metadata.IsComposite) @@ -81,6 +84,12 @@ public void OnOperationStartHandler(ICallable operation, IApplyData arguments) this.currCompositeOp = operation; // Set currentOperation to null so we don't render higher-depth operations unintentionally. this.currentOperation = null; + + // TODO: Change to IsConditional flag once implemented + if (operation.Name == "ApplyIfElseR") + { + this.currentOperation = this.MetadataToOperation(metadata); + } return; } @@ -179,6 +188,17 @@ private void AddCompositeOperations() if (this.compositeTracer == null) throw new NullReferenceException("ERROR: compositeTracer not initialized."); + // TODO: Change to IsConditional flag once implemented + if (this.currCompositeOp != null && this.currCompositeOp.Name == "ApplyIfElseR") + { + if (this.currentOperation == null) throw new NullReferenceException("ERROR: currentOperation is null."); + var numChildren = this.compositeTracer.operations.Count(); + if (numChildren != 2) throw new IndexOutOfRangeException($"ERROR: Found only {numChildren} children for conditional operation."); + this.currentOperation.Children = this.compositeTracer.operations + .Select(op => new List() { op }); + this.operations.Add(this.currentOperation); + return; + } // The composite tracer has done its job and we retrieve the operations it traced this.operations.AddRange(this.compositeTracer.operations); } diff --git a/src/ExecutionPathTracer/Extensions.cs b/src/ExecutionPathTracer/Extensions.cs index 103bbbf7e6..f703d2776b 100644 --- a/src/ExecutionPathTracer/Extensions.cs +++ b/src/ExecutionPathTracer/Extensions.cs @@ -3,11 +3,50 @@ #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; + }; + + /// + public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) + { + var metadata = base.GetRuntimeMetadata(args); + if (metadata == null) throw new NullReferenceException($"Null RuntimeMetadata found for {this.ToString()}."); + metadata.IsComposite = true; + return metadata; + } + } + /// /// Extension methods to be used with and by . /// @@ -22,6 +61,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 e626a557d2..38d77644e0 100644 --- a/src/Tests/ExecutionPathTracerTests.cs +++ b/src/Tests/ExecutionPathTracerTests.cs @@ -614,6 +614,55 @@ public void BigTest() var expected = new ExecutionPath(qubits, operations); Assert.AreEqual(expected.ToJson(), path.ToJson()); } + + [TestMethod] + public void IfTest() + { + var path = GetExecutionPath("IfCirc"); + var qubits = new QubitDeclaration[] { new QubitDeclaration(0, 1) }; + 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))", + Targets = new List() { new QubitRegister(0), new QubitRegister(0) }, + Children = new List>() + { + new List() + { + new Operation() + { + Gate = "X", + Targets = new List() { new QubitRegister(0) }, + }, + }, + new List() + { + new Operation() + { + Gate = "Z", + Targets = new List() { new QubitRegister(0) }, + }, + }, + }, + }, + new Operation() + { + Gate = "Reset", + Targets = new List() { new QubitRegister(0) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(expected.ToJson(), path.ToJson()); + } } [TestClass] diff --git a/src/Tests/Workspace.ExecutionPathTracer/Circuits.qs b/src/Tests/Workspace.ExecutionPathTracer/Circuits.qs index 40c01b8675..6a6777c29f 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 @@ -94,5 +95,13 @@ namespace Tests.ExecutionPathTracer { ResetAll(qs); } } + + operation IfCirc() : Unit { + using (q = Qubit()) { + let res = M(q); + ApplyIfElseR(res, (X, q), (Z, q)); + Reset(q); + } + } } From 1630633588a9baf8ff66213ebe93503267ca53bf Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Thu, 6 Aug 2020 15:59:02 -0400 Subject: [PATCH 2/3] Use IsConditional flag --- src/ExecutionPathTracer/ExecutionPath.cs | 9 ++++++--- src/ExecutionPathTracer/ExecutionPathTracer.cs | 18 +++++------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/ExecutionPathTracer/ExecutionPath.cs b/src/ExecutionPathTracer/ExecutionPath.cs index 38330b5d89..9e598419ec 100644 --- a/src/ExecutionPathTracer/ExecutionPath.cs +++ b/src/ExecutionPathTracer/ExecutionPath.cs @@ -118,9 +118,6 @@ public class Operation /// /// Group of operations for each classical branch. /// - /// - /// Currently not used as this is intended for classically-controlled operations. - /// [JsonProperty("children")] public IEnumerable>? Children { get; set; } @@ -130,6 +127,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 77e12de32e..aa32920ba3 100644 --- a/src/ExecutionPathTracer/ExecutionPathTracer.cs +++ b/src/ExecutionPathTracer/ExecutionPathTracer.cs @@ -69,12 +69,9 @@ public void OnOperationStartHandler(ICallable operation, IApplyData arguments) var metadata = operation.GetRuntimeMetadata(arguments); - // TODO: Remove once GetRuntimeMetadata has been implemented - if (operation.Name == "ApplyIfElseR" && metadata != null) metadata.IsComposite = true; - - // If metadata is a composite operation (i.e. want to trace its components instead), + // If metadata is a composite/conditional operation (i.e. want to trace its components instead), // we recursively create a tracer that traces its components instead - if (metadata != null && metadata.IsComposite) + if (metadata != null && (metadata.IsComposite || metadata.IsConditional)) { var remainingDepth = this.renderDepth - this.currentDepth; this.compositeTracer = new ExecutionPathTracer(remainingDepth); @@ -85,11 +82,7 @@ public void OnOperationStartHandler(ICallable operation, IApplyData arguments) // Set currentOperation to null so we don't render higher-depth operations unintentionally. this.currentOperation = null; - // TODO: Change to IsConditional flag once implemented - if (operation.Name == "ApplyIfElseR") - { - this.currentOperation = this.MetadataToOperation(metadata); - } + if (metadata.IsConditional) this.currentOperation = this.MetadataToOperation(metadata); return; } @@ -188,10 +181,8 @@ private void AddCompositeOperations() if (this.compositeTracer == null) throw new NullReferenceException("ERROR: compositeTracer not initialized."); - // TODO: Change to IsConditional flag once implemented - if (this.currCompositeOp != null && this.currCompositeOp.Name == "ApplyIfElseR") + if (this.currentOperation != null && this.currentOperation.IsConditional) { - if (this.currentOperation == null) throw new NullReferenceException("ERROR: currentOperation is null."); var numChildren = this.compositeTracer.operations.Count(); if (numChildren != 2) throw new IndexOutOfRangeException($"ERROR: Found only {numChildren} children for conditional operation."); this.currentOperation.Children = this.compositeTracer.operations @@ -222,6 +213,7 @@ private void AddCompositeOperations() Gate = metadata.Label, DisplayArgs = displayArgs, Children = metadata.Children?.Select(child => child.Select(this.MetadataToOperation).WhereNotNull()), + IsConditional = metadata.IsConditional, IsControlled = metadata.IsControlled, IsAdjoint = metadata.IsAdjoint, Controls = this.GetQubitRegisters(metadata.Controls), From 6aabc6aecb5470ecc8c6542a515e29af08955cff Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Thu, 6 Aug 2020 16:15:15 -0400 Subject: [PATCH 3/3] Fix test --- src/Tests/ExecutionPathTracerTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Tests/ExecutionPathTracerTests.cs b/src/Tests/ExecutionPathTracerTests.cs index 38d77644e0..a9ef35a929 100644 --- a/src/Tests/ExecutionPathTracerTests.cs +++ b/src/Tests/ExecutionPathTracerTests.cs @@ -633,7 +633,8 @@ public void IfTest() { Gate = "ApplyIfElseR", DisplayArgs = "(Zero, (X), (Z))", - Targets = new List() { new QubitRegister(0), new QubitRegister(0) }, + Controls = new List() { new ClassicalRegister(0, 0) }, + Targets = new List() { new QubitRegister(0) }, Children = new List>() { new List()