diff --git a/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs index 3944f2c475..7a1c3ce6ad 100644 --- a/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs +++ b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs @@ -122,10 +122,17 @@ private ClassicalRegister GetClassicalRegister(Qubit controlQubit) { if (metadata == null) return null; + var displayArgs = (metadata.FormattedNonQubitArgs.Length > 0) + ? metadata.FormattedNonQubitArgs + : null; + + // Add surrounding parentheses around displayArgs if it doesn't already have it (i.e. not a tuple) + if (displayArgs != null && !displayArgs.StartsWith("(")) displayArgs = $"({displayArgs})"; + var op = new Operation() { Gate = metadata.Label, - DisplayArgs = (metadata.FormattedNonQubitArgs.Length > 0) ? metadata.FormattedNonQubitArgs : null, + DisplayArgs = displayArgs, Children = metadata.Children?.Select(child => child.Select(this.MetadataToOperation).WhereNotNull()), Controlled = metadata.IsControlled, Adjoint = metadata.IsAdjoint, diff --git a/src/Kernel/Magic/TraceMagic.cs b/src/Kernel/Magic/TraceMagic.cs index 54cf44477c..db7497f1f6 100644 --- a/src/Kernel/Magic/TraceMagic.cs +++ b/src/Kernel/Magic/TraceMagic.cs @@ -50,7 +50,7 @@ public ExecutionPathVisualizerContent(JToken executionPath, string id) public class TraceMagic : AbstractMagic { private const string ParameterNameOperationName = "__operationName__"; - private const string ParameterNameDepth = "depth"; + private const string ParameterNameDepth = "--depth"; /// /// Constructs a new magic command given a resolver used to find @@ -61,7 +61,7 @@ public TraceMagic(ISymbolResolver resolver, IConfigurationSource configurationSo "trace", new Documentation { - Summary = "Outputs the HTML-based visualization of an execution path of the given operation.", + Summary = "Visualizes the execution path of the given operation.", Description = $@" This magic command renders an HTML-based visualization of a runtime execution path of the given operation using the QuantumSimulator. diff --git a/src/Tests/ExecutionPathTracerTests.cs b/src/Tests/ExecutionPathTracerTests.cs index 17fa0e084f..4f8b3f9e66 100644 --- a/src/Tests/ExecutionPathTracerTests.cs +++ b/src/Tests/ExecutionPathTracerTests.cs @@ -10,8 +10,7 @@ namespace Tests.IQSharp { - [TestClass] - public class IntrinsicTests + public class ExecutionPathTracerTests { public Workspace InitWorkspace() { @@ -33,7 +32,11 @@ public ExecutionPath GetExecutionPath(string name, int depth = 1) return tracer.GetExecutionPath(); } + } + [TestClass] + public class IntrinsicTests : ExecutionPathTracerTests + { [TestMethod] public void HTest() { @@ -275,7 +278,11 @@ public void ControlledAdjointSTest() var expected = new ExecutionPath(qubits, operations); Assert.AreEqual(expected.ToJson(), path.ToJson()); } + } + [TestClass] + public class Circuits : ExecutionPathTracerTests + { [TestMethod] public void FooTest() { @@ -429,7 +436,24 @@ public void EmptyTest() var expected = new ExecutionPath(qubits, operations); Assert.AreEqual(expected.ToJson(), path.ToJson()); } - + + [TestMethod] + public void NoQubitArgsTest() + { + var path = GetExecutionPath("NoQubitArgsCirc"); + var qubits = new QubitDeclaration[] {}; + var operations = new Operation[] + { + new Operation() + { + Gate = "NoQubitCirc", + DisplayArgs = "(2)", + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(expected.ToJson(), path.ToJson()); + } + [TestMethod] public void NestedTest() { @@ -533,4 +557,71 @@ public void BigTest() Assert.AreEqual(expected.ToJson(), path.ToJson()); } } + + [TestClass] + public class MeasurementTests : ExecutionPathTracerTests + { + [TestMethod] + public void MResetXTest() + { + var path = GetExecutionPath("MResetXCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0, 1), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "measure", + Controls = new List() { new QubitRegister(0) }, + Targets = new List() { new ClassicalRegister(0, 0) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(expected.ToJson(), path.ToJson()); + } + + [TestMethod] + public void MResetYTest() + { + var path = GetExecutionPath("MResetYCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0, 1), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "measure", + Controls = new List() { new QubitRegister(0) }, + Targets = new List() { new ClassicalRegister(0, 0) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(expected.ToJson(), path.ToJson()); + } + + [TestMethod] + public void MResetZTest() + { + var path = GetExecutionPath("MResetZCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0, 1), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "measure", + Controls = new List() { new QubitRegister(0) }, + Targets = new List() { new ClassicalRegister(0, 0) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(expected.ToJson(), path.ToJson()); + } + } } diff --git a/src/Tests/IQsharpEngineTests.cs b/src/Tests/IQsharpEngineTests.cs index 54df19f1e1..04c6b00dfc 100644 --- a/src/Tests/IQsharpEngineTests.cs +++ b/src/Tests/IQsharpEngineTests.cs @@ -514,7 +514,7 @@ public async Task TestTraceMagic() )); // Should see depth-2 operations - await AssertTrace("Depth2Circ depth=2", new ExecutionPath( + await AssertTrace("Depth2Circ --depth=2", new ExecutionPath( new QubitDeclaration[] { new QubitDeclaration(0) }, new Operation[] { diff --git a/src/Tests/Tests.IQsharp.csproj b/src/Tests/Tests.IQsharp.csproj index 90536f1c09..c3f45a52b7 100644 --- a/src/Tests/Tests.IQsharp.csproj +++ b/src/Tests/Tests.IQsharp.csproj @@ -54,6 +54,9 @@ PreserveNewest + + PreserveNewest + diff --git a/src/Tests/Workspace.ExecutionPathTracer/Circuits.qs b/src/Tests/Workspace.ExecutionPathTracer/Circuits.qs new file mode 100644 index 0000000000..2090706ffa --- /dev/null +++ b/src/Tests/Workspace.ExecutionPathTracer/Circuits.qs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Tests.ExecutionPathTracer { + + open Microsoft.Quantum.Intrinsic; + + // Custom operation + operation Foo(theta : Double, (qubit : Qubit, bar : String)) : Unit + is Adj + Ctl { + } + + operation FooCirc() : Unit { + using (q = Qubit()) { + Foo(2.1, (q, "bar")); + } + } + + operation ControlledFooCirc() : Unit { + using (qs = Qubit[2]) { + Controlled Foo([qs[0]], (2.1, (qs[1], "bar"))); + } + } + + operation UnusedQubitCirc() : Unit { + using (qs = Qubit[3]) { + CNOT(qs[2], qs[0]); + Reset(qs[0]); + Reset(qs[2]); + } + } + + operation EmptyCirc() : Unit { + using (qs = Qubit[3]) { + } + } + + operation NoQubitCirc(n : Int) : Unit { + } + + operation NoQubitArgsCirc() : Unit { + NoQubitCirc(2); + } + + operation NestedCirc() : Unit { + using (q = Qubit()) { + H(q); + HCirc(); + Reset(q); + } + } + + operation FooBar(q : Qubit) : Unit { + H(q); + X(q); + H(q); + } + + operation Depth2Circ() : Unit { + using (q = Qubit()) { + FooBar(q); + } + } + + operation PartialOpCirc() : Unit { + using (qs = Qubit[3]) { + (Controlled H(qs[0..1], _))(qs[2]); + ((Ry(_, _))(2.5, _))(qs[0]); + ResetAll(qs); + } + } + + operation Bar((alpha : Double, beta : Double), (q : Qubit, name : String)) : Unit + is Adj + Ctl { + } + + operation BigCirc() : Unit { + using (qs = Qubit[3]) { + H(qs[0]); + Ry(2.5, qs[1]); + Bar((1.0, 2.1), (qs[0], "foo")); + X(qs[0]); + CCNOT(qs[0], qs[1], qs[2]); + Controlled CNOT([qs[0]], (qs[1], qs[2])); + Controlled Adjoint Bar([qs[2]], ((1.0, 2.1), (qs[0], "foo"))); + let res = M(qs[0]); + ResetAll(qs); + } + } + +} diff --git a/src/Tests/Workspace.ExecutionPathTracer/Intrinsic.qs b/src/Tests/Workspace.ExecutionPathTracer/Intrinsic.qs index 2796648a12..ffb7b91295 100644 --- a/src/Tests/Workspace.ExecutionPathTracer/Intrinsic.qs +++ b/src/Tests/Workspace.ExecutionPathTracer/Intrinsic.qs @@ -65,83 +65,4 @@ namespace Tests.ExecutionPathTracer { ResetAll(qs); } } - - // Custom operation - operation Foo(theta : Double, (qubit : Qubit, bar : String)) : Unit - is Adj + Ctl { - } - - operation FooCirc() : Unit { - using (q = Qubit()) { - Foo(2.1, (q, "bar")); - } - } - - operation ControlledFooCirc() : Unit { - using (qs = Qubit[2]) { - Controlled Foo([qs[0]], (2.1, (qs[1], "bar"))); - } - } - - operation UnusedQubitCirc() : Unit { - using (qs = Qubit[3]) { - CNOT(qs[2], qs[0]); - Reset(qs[0]); - Reset(qs[2]); - } - } - - operation EmptyCirc() : Unit { - using (qs = Qubit[3]) { - } - } - - operation NestedCirc() : Unit { - using (q = Qubit()) { - H(q); - HCirc(); - Reset(q); - } - } - - operation FooBar(q : Qubit) : Unit { - H(q); - X(q); - H(q); - } - - operation Depth2Circ() : Unit { - using (q = Qubit()) { - FooBar(q); - } - } - - operation PartialOpCirc() : Unit { - using (qs = Qubit[3]) { - (Controlled H(qs[0..1], _))(qs[2]); - ((Ry(_, _))(2.5, _))(qs[0]); - ResetAll(qs); - } - } - - operation Bar((alpha : Double, beta : Double), (q : Qubit, name : String)) : Unit - is Adj + Ctl { - } - - operation BigCirc() : Unit { - using (qs = Qubit[3]) { - H(qs[0]); - Ry(2.5, qs[1]); - Bar((1.0, 2.1), (qs[0], "foo")); - X(qs[0]); - CCNOT(qs[0], qs[1], qs[2]); - Controlled CNOT([qs[0]], (qs[1], qs[2])); - Controlled Adjoint Bar([qs[2]], ((1.0, 2.1), (qs[0], "foo"))); - let res = M(qs[0]); - ResetAll(qs); - } - } - } - -