From 8b287d45e014686567c667beef15c8d0d18d72d2 Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Thu, 2 Jul 2020 17:07:52 -0400 Subject: [PATCH 01/11] Add ExecutionPathTracer to repo --- src/Core/Core.csproj | 1 + src/Core/ExecutionPathTracer/ExecutionPath.cs | 131 ++++++++ .../ExecutionPathTracer.cs | 307 ++++++++++++++++++ src/Core/ExecutionPathTracer/Register.cs | 77 +++++ 4 files changed, 516 insertions(+) create mode 100644 src/Core/ExecutionPathTracer/ExecutionPath.cs create mode 100644 src/Core/ExecutionPathTracer/ExecutionPathTracer.cs create mode 100644 src/Core/ExecutionPathTracer/Register.cs diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 15ab736d85..4bbfa66a99 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -39,6 +39,7 @@ + diff --git a/src/Core/ExecutionPathTracer/ExecutionPath.cs b/src/Core/ExecutionPathTracer/ExecutionPath.cs new file mode 100644 index 0000000000..fecfde565f --- /dev/null +++ b/src/Core/ExecutionPathTracer/ExecutionPath.cs @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.Text.Json; + +namespace Microsoft.Quantum.IQSharp +{ + /// + /// Represents the qubit resources and operations traced out in an execution path of a Q# operation. + /// + public class ExecutionPath + { + /// + /// Initializes a new instance of the class. + /// + /// + /// An array of QubitDeclarations that represents the declared qubits used in the execution path. + /// + /// + /// An array of Operations that represents the operations used in the execution path. + /// + public ExecutionPath(QubitDeclaration[] qubits, Operation[] operations) + { + this.Qubits = qubits; + this.Operations = operations; + } + + /// + /// An array of QubitDeclarations that represents the declared qubits used in the execution path. + /// + public QubitDeclaration[] Qubits { get; set; } + + /// + /// An array of Operations that represents the operations used in the execution path. + /// + public Operation[] Operations { get; set; } + + /// + /// Serializes ExecutionPath into its JSON representation. + /// + /// + /// Pretty prints the JSON (i.e. with white space and indents) if true (default = false). + /// + public string ToJson(bool prettyPrint = false) + { + var options = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + IgnoreNullValues = true, + WriteIndented = prettyPrint, + }; + return JsonSerializer.Serialize(this, options); + } + } + + /// + /// Represents a qubit resource used in an execution path. + /// + public class QubitDeclaration + { + /// + /// Initializes a new instance of the class. + /// + /// + /// Id of qubit. + /// + /// + /// Number of associated classical registers. + /// + public QubitDeclaration(int id, int? numChildren = null) + { + this.Id = id; + this.NumChildren = numChildren; + } + + /// + /// Id of qubit. + /// + public int Id { get; set; } + + /// + /// Number of associated classical registers. + /// + public int? NumChildren { get; set; } + } + + /// + /// Represents an operation used in an execution path. + /// + public class Operation + { + /// + /// Label of gate. + /// + public string Gate { get; set; } + + /// + /// Non-qubit arguments provided to gate. + /// + public string ArgStr { get; set; } + + /// + /// Group of operations for each classical branch. + /// + /// + /// Currently not used as this is intended for classically-controlled operations. + /// + public List> Children { get; set; } + + /// + /// True if operation is a controlled operations. + /// + public bool Controlled { get; set; } + + /// + /// True if operation is an adjoint operations. + /// + public bool Adjoint { get; set; } + + /// + /// List of control registers. + /// + public List Controls { get; set; } = new List(); + + /// + /// List of target registers. + /// + public List Targets { get; set; } = new List(); + } +} diff --git a/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs new file mode 100644 index 0000000000..5be25521b3 --- /dev/null +++ b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs @@ -0,0 +1,307 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Quantum.Simulation.Core; + +#nullable enable + +namespace Microsoft.Quantum.IQSharp +{ + /// + /// Traces through the operations in a given execution path of a Q# program by hooking on + /// to a simulator via the event listeners OnOperationStartHandler and + /// OnOperationEndHandler, and generates the corresponding ExecutionPath. + /// + public class ExecutionPathTracer + { + private int currDepth = 0; + private int renderDepth; + private IDictionary qubitRegisters = new Dictionary(); + private IDictionary> classicalRegisters = new Dictionary>(); + private List operations = new List(); + private Type[] nestedTypes = new Type[] + { + typeof(Microsoft.Quantum.Canon.ApplyToEach), + typeof(Microsoft.Quantum.Canon.ApplyToEachC), + typeof(Microsoft.Quantum.Canon.ApplyToEachA), + typeof(Microsoft.Quantum.Canon.ApplyToEachCA), + }; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The depth at which to render operations. + /// + public ExecutionPathTracer(int depth = 1) => this.renderDepth = depth + 1; + + /// + /// Returns the generated ExecutionPath. + /// + public ExecutionPath GetExecutionPath() + { + var qubits = this.qubitRegisters.Keys + .OrderBy(k => k) + .Select(k => + { + var qubitDecl = new QubitDeclaration(k); + if (this.classicalRegisters.ContainsKey(k)) + { + qubitDecl.NumChildren = this.classicalRegisters[k].Count; + } + + return qubitDecl; + }) + .ToArray(); + + return new ExecutionPath(qubits, this.operations.ToArray()); + } + + /// + /// Provides the event listener to listen to SimulatorBase's OnOperationStart event. + /// + public void OnOperationStartHandler(ICallable operation, IApplyData arguments) + { + // If the operation type is one of the nestedTypes, go one depth deeper and parse + // those operations instead + if (this.nestedTypes.Contains(operation.GetType())) return; + + this.currDepth++; + + // Parse operations at specified depth + if (this.currDepth == this.renderDepth) + { + var parsedOp = this.ParseOperation(operation, arguments); + if (parsedOp != null) + { + this.operations.Add(parsedOp); + } + } + } + + /// + /// Provides the event listener to listen to SimulatorBase's OnOperationEnd event. + /// + public void OnOperationEndHandler(ICallable operation, IApplyData result) + { + if (this.nestedTypes.Contains(operation.GetType())) return; + this.currDepth--; + } + + private static bool IsPartialApplication(ICallable operation) + { + var t = operation.GetType(); + if (t == null) return false; + + return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(OperationPartial<,,>); + } + + /// + /// Retrieves the QubitRegister associated with the given Qubit or create a new + /// one if it doesn't exist. + /// + private QubitRegister GetQubitRegister(Qubit qubit) + { + if (!this.qubitRegisters.ContainsKey(qubit.Id)) + { + this.qubitRegisters[qubit.Id] = new QubitRegister(qubit.Id); + } + + return this.qubitRegisters[qubit.Id]; + } + + private List GetQubitRegisters(IEnumerable qubits) + { + return qubits.Select(this.GetQubitRegister).ToList(); + } + + /// + /// Creates a new ClassicalRegister and associate it with the given Qubit. + /// + private ClassicalRegister CreateClassicalRegister(Qubit measureQubit) + { + var qId = measureQubit.Id; + + if (!this.classicalRegisters.ContainsKey(qId)) + { + this.classicalRegisters[qId] = new List(); + } + + var cId = this.classicalRegisters[qId].Count; + ClassicalRegister register = new ClassicalRegister(qId, cId); + + // Add classical register under the given qubit id + this.classicalRegisters[qId].Add(register); + + return register; + } + + /// + /// Retrieves the most recent ClassicalRegister associated with the given Qubit. + /// + /// + /// Currently not used as this is intended for classically-controlled operations. + /// + private ClassicalRegister GetClassicalRegister(Qubit controlQubit) + { + var qId = controlQubit.Id; + if (!this.classicalRegisters.ContainsKey(qId) || this.classicalRegisters[qId].Count == 0) + { + throw new Exception("No classical registers found for qubit {qId}."); + } + + // Get most recent measurement on given control qubit + var cId = this.classicalRegisters[qId].Count - 1; + return this.classicalRegisters[qId][cId]; + } + + /// + /// Given a Type and its value, extract its fields and format it as a string. + /// + private string? ExtractArgs(Type t, object value) + { + List fields = new List(); + + foreach (var f in t.GetFields()) + { + // If field is a tuple, recursively extract its inner arguments and format as a tuple string. + if (f.FieldType.IsTuple()) + { + var nestedArgs = f.GetValue(value); + if (nestedArgs != null) + { + var nestedFields = this.ExtractArgs(f.FieldType, nestedArgs); + fields.Add(nestedFields); + } + } + // Add field as an argument if it is not a Qubit type + else if (!f.FieldType.IsQubitsContainer()) + { + fields.Add(f.GetValue(value)?.ToString()); + } + } + + return fields.Any() ? $"({string.Join(",", fields.WhereNotNull())})" : null; + } + + /// + /// Given an operation and its arguments, parse it into an Operation object. + /// + private Operation? ParseOperation(ICallable operation, IApplyData arguments) + { + // If operation is a partial application, perform on baseOp recursively. + if (IsPartialApplication(operation)) + { + dynamic partialOp = operation; + dynamic partialOpArgs = arguments; + + // Recursively get base operation operations + var baseOp = partialOp.BaseOp; + var baseArgs = baseOp.__dataIn(partialOpArgs.Value); + return this.ParseOperation(baseOp, baseArgs); + } + + var controlled = operation.Variant == OperationFunctor.Controlled || + operation.Variant == OperationFunctor.ControlledAdjoint; + var adjoint = operation.Variant == OperationFunctor.Adjoint || + operation.Variant == OperationFunctor.ControlledAdjoint; + + // If operation is controlled, perform on baseOp recursively and mark as controlled. + if (controlled) + { + dynamic ctrlOp = operation; + dynamic ctrlOpArgs = arguments; + + var ctrls = ctrlOpArgs.Value.Item1; + var controlRegs = this.GetQubitRegisters(ctrls); + + // Recursively get base operation operations + var baseOp = ctrlOp.BaseOp; + var baseArgs = baseOp.__dataIn(ctrlOpArgs.Value.Item2); + var parsedBaseOp = this.ParseOperation(baseOp, baseArgs); + + parsedBaseOp.Controlled = true; + parsedBaseOp.Adjoint = adjoint; + parsedBaseOp.Controls.InsertRange(0, controlRegs); + + return parsedBaseOp; + } + + // Handle operation based on type + switch (operation) + { + // Handle CNOT operations as a Controlled X + case Microsoft.Quantum.Intrinsic.CNOT cnot: + case Microsoft.Quantum.Intrinsic.CCNOT ccnot: + var ctrlRegs = new List(); + var targetRegs = new List(); + + switch (arguments.Value) + { + case ValueTuple cnotQs: + var (ctrl, cnotTarget) = cnotQs; + ctrlRegs.Add(this.GetQubitRegister(ctrl)); + targetRegs.Add(this.GetQubitRegister(cnotTarget)); + break; + case ValueTuple ccnotQs: + var (ctrl1, ctrl2, ccnotTarget) = ccnotQs; + ctrlRegs.Add(this.GetQubitRegister(ctrl1)); + ctrlRegs.Add(this.GetQubitRegister(ctrl2)); + targetRegs.Add(this.GetQubitRegister(ccnotTarget)); + break; + } + + return new Operation + { + Gate = "X", + Controlled = true, + Adjoint = adjoint, + Controls = ctrlRegs, + Targets = targetRegs, + }; + + // Measurement operations + case Microsoft.Quantum.Intrinsic.M m: + case Microsoft.Quantum.Measurement.MResetX mx: + case Microsoft.Quantum.Measurement.MResetY my: + case Microsoft.Quantum.Measurement.MResetZ mz: + var measureQubit = arguments.GetQubits().ElementAt(0); + var measureReg = this.GetQubitRegister(measureQubit); + var clsReg = this.CreateClassicalRegister(measureQubit); + + return new Operation + { + Gate = "measure", + Controlled = false, + Adjoint = adjoint, + Controls = new List() { measureReg }, + Targets = new List() { clsReg }, + }; + + // Operations to ignore + case Microsoft.Quantum.Intrinsic.Reset reset: + case Microsoft.Quantum.Intrinsic.ResetAll resetAll: + return null; + + // General operations + default: + Type t = arguments.Value.GetType(); + var argStr = this.ExtractArgs(t, arguments.Value); + var qubitRegs = this.GetQubitRegisters(arguments.GetQubits()); + + return new Operation + { + Gate = operation.Name, + ArgStr = argStr, + Controlled = false, + Adjoint = adjoint, + Controls = new List(), + Targets = qubitRegs.Cast().ToList(), + }; + } + } + } +} \ No newline at end of file diff --git a/src/Core/ExecutionPathTracer/Register.cs b/src/Core/ExecutionPathTracer/Register.cs new file mode 100644 index 0000000000..05642736c8 --- /dev/null +++ b/src/Core/ExecutionPathTracer/Register.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.IQSharp +{ + /// + /// Enum for the 2 types of registers: Qubit and Classical. + /// + public enum RegisterType + { + Qubit, + Classical, + } + + /// + /// Represents a register used by an Operation. + /// + public class Register + { + /// + /// Type of register. + /// + public virtual RegisterType Type { get; set; } + + /// + /// Qubit id of register. + /// + public virtual int QId { get; set; } + + /// + /// Classical bit id of register. + /// + public virtual int? CId { get; set; } + } + + /// + /// Represents a qubit register used by an Operation. + /// + public class QubitRegister : Register + { + /// + /// Creates a new QubitRegister with the given qubit id. + /// + /// + /// Id of qubit register. + /// + public QubitRegister(int qId) + { + this.QId = qId; + } + + public override RegisterType Type => RegisterType.Qubit; + } + + /// + /// Represents a classical register used by an Operation. + /// + public class ClassicalRegister : Register + { + /// + /// Creates a new ClassicalRegister with the given qubit id and classical bit id. + /// + /// + /// Id of qubit register. + /// + /// + /// Id of classical register associated with the given qubit id. + /// + public ClassicalRegister(int qId, int cId) + { + this.QId = qId; + this.CId = cId; + } + + public override RegisterType Type => RegisterType.Classical; + } +} \ No newline at end of file From 90737594aa4e5992d1186948f03011b0c21de3c4 Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Thu, 2 Jul 2020 17:15:04 -0400 Subject: [PATCH 02/11] Add WithExecutionPathTracer extension method to SimulatorBase --- src/Kernel/Extensions.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Kernel/Extensions.cs b/src/Kernel/Extensions.cs index 057ccecde8..f04ac68e28 100644 --- a/src/Kernel/Extensions.cs +++ b/src/Kernel/Extensions.cs @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.Jupyter.Core; using Microsoft.Quantum.IQSharp.Jupyter; +using Microsoft.Quantum.Simulation.Common; namespace Microsoft.Quantum.IQSharp.Kernel { @@ -24,5 +24,16 @@ public static void AddIQSharpKernel(this IServiceCollection services) services.AddSingleton(); services.AddSingleton(); } + + /// + /// Attaches ExecutionPathTracer event listeners to the simulator to generate + /// the ExecutionPath of the operation performed by the simulator. + /// + public static T WithExecutionPathTracer(this T sim, ExecutionPathTracer tracer) where T : SimulatorBase + { + sim.OnOperationStart += tracer.OnOperationStartHandler; + sim.OnOperationEnd += tracer.OnOperationEndHandler; + return sim; + } } } From 359e8d81e44f3cfebd5920c010d266a9f7bf672d Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Fri, 3 Jul 2020 15:53:19 -0400 Subject: [PATCH 03/11] Respond to PR feedback --- src/Core/ExecutionPathTracer/ExecutionPath.cs | 57 +++---- .../ExecutionPathTracer.cs | 143 ++++++------------ src/Core/ExecutionPathTracer/Extensions.cs | 88 +++++++++++ src/Core/ExecutionPathTracer/Register.cs | 22 ++- src/Kernel/Extensions.cs | 12 -- 5 files changed, 180 insertions(+), 142 deletions(-) create mode 100644 src/Core/ExecutionPathTracer/Extensions.cs diff --git a/src/Core/ExecutionPathTracer/ExecutionPath.cs b/src/Core/ExecutionPathTracer/ExecutionPath.cs index fecfde565f..8ea4044edb 100644 --- a/src/Core/ExecutionPathTracer/ExecutionPath.cs +++ b/src/Core/ExecutionPathTracer/ExecutionPath.cs @@ -1,10 +1,12 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#nullable enable + using System.Collections.Generic; using System.Text.Json; -namespace Microsoft.Quantum.IQSharp +namespace Microsoft.Quantum.IQSharp.Core.ExecutionPathTracer { /// /// Represents the qubit resources and operations traced out in an execution path of a Q# operation. @@ -15,43 +17,42 @@ public class ExecutionPath /// Initializes a new instance of the class. /// /// - /// An array of QubitDeclarations that represents the declared qubits used in the execution path. + /// A list of that represents the declared qubits used in the execution path. /// /// - /// An array of Operations that represents the operations used in the execution path. + /// A list of that represents the operations used in the execution path. /// - public ExecutionPath(QubitDeclaration[] qubits, Operation[] operations) + public ExecutionPath(IEnumerable qubits, IEnumerable operations) { this.Qubits = qubits; this.Operations = operations; } /// - /// An array of QubitDeclarations that represents the declared qubits used in the execution path. + /// A list of that represents the declared qubits used in the execution path. /// - public QubitDeclaration[] Qubits { get; set; } + public IEnumerable Qubits { get; private set; } /// - /// An array of Operations that represents the operations used in the execution path. + /// A list of that represents the operations used in the execution path. /// - public Operation[] Operations { get; set; } + public IEnumerable Operations { get; private set; } /// - /// Serializes ExecutionPath into its JSON representation. + /// Serializes into its JSON representation. /// /// - /// Pretty prints the JSON (i.e. with white space and indents) if true (default = false). + /// Pretty prints the JSON (i.e. with white space and indents) if true. /// - public string ToJson(bool prettyPrint = false) - { - var options = new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - IgnoreNullValues = true, - WriteIndented = prettyPrint, - }; - return JsonSerializer.Serialize(this, options); - } + public string ToJson(bool prettyPrint = false) => + JsonSerializer.Serialize(this, + new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + IgnoreNullValues = true, + WriteIndented = prettyPrint, + } + ); } /// @@ -77,12 +78,12 @@ public QubitDeclaration(int id, int? numChildren = null) /// /// Id of qubit. /// - public int Id { get; set; } + public int Id { get; private set; } /// /// Number of associated classical registers. /// - public int? NumChildren { get; set; } + public int? NumChildren { get; private set; } } /// @@ -93,12 +94,12 @@ public class Operation /// /// Label of gate. /// - public string Gate { get; set; } + public string Gate { get; set; } = ""; /// /// Non-qubit arguments provided to gate. /// - public string ArgStr { get; set; } + public string? ArgStr { get; set; } /// /// Group of operations for each classical branch. @@ -106,7 +107,7 @@ public class Operation /// /// Currently not used as this is intended for classically-controlled operations. /// - public List> Children { get; set; } + public IEnumerable> Children { get; set; } = new List>(); /// /// True if operation is a controlled operations. @@ -121,11 +122,11 @@ public class Operation /// /// List of control registers. /// - public List Controls { get; set; } = new List(); + public IEnumerable Controls { get; set; } = new List(); /// /// List of target registers. /// - public List Targets { get; set; } = new List(); + public IEnumerable Targets { get; set; } = new List(); } } diff --git a/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs index 5be25521b3..b763e39bfc 100644 --- a/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs +++ b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs @@ -1,34 +1,35 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Microsoft.Quantum.Simulation.Core; #nullable enable -namespace Microsoft.Quantum.IQSharp +namespace Microsoft.Quantum.IQSharp.Core.ExecutionPathTracer { /// /// Traces through the operations in a given execution path of a Q# program by hooking on - /// to a simulator via the event listeners OnOperationStartHandler and - /// OnOperationEndHandler, and generates the corresponding ExecutionPath. + /// to a simulator via the event listeners and + /// , and generates the corresponding . /// public class ExecutionPathTracer { - private int currDepth = 0; + private int currentDepth = 0; private int renderDepth; private IDictionary qubitRegisters = new Dictionary(); private IDictionary> classicalRegisters = new Dictionary>(); private List operations = new List(); - private Type[] nestedTypes = new Type[] - { - typeof(Microsoft.Quantum.Canon.ApplyToEach), - typeof(Microsoft.Quantum.Canon.ApplyToEachC), - typeof(Microsoft.Quantum.Canon.ApplyToEachA), - typeof(Microsoft.Quantum.Canon.ApplyToEachCA), - }; + private readonly ImmutableList nestedTypes = + ImmutableList.Create( + typeof(Microsoft.Quantum.Canon.ApplyToEach), + typeof(Microsoft.Quantum.Canon.ApplyToEachC), + typeof(Microsoft.Quantum.Canon.ApplyToEachA), + typeof(Microsoft.Quantum.Canon.ApplyToEachCA) + ); /// /// Initializes a new instance of the class. @@ -36,32 +37,29 @@ public class ExecutionPathTracer /// /// The depth at which to render operations. /// - public ExecutionPathTracer(int depth = 1) => this.renderDepth = depth + 1; + public ExecutionPathTracer(int depth = 1) + => this.renderDepth = depth + 1; /// /// Returns the generated ExecutionPath. /// - public ExecutionPath GetExecutionPath() - { - var qubits = this.qubitRegisters.Keys - .OrderBy(k => k) - .Select(k => - { - var qubitDecl = new QubitDeclaration(k); - if (this.classicalRegisters.ContainsKey(k)) - { - qubitDecl.NumChildren = this.classicalRegisters[k].Count; - } - - return qubitDecl; - }) - .ToArray(); - - return new ExecutionPath(qubits, this.operations.ToArray()); - } + public ExecutionPath GetExecutionPath() => + new ExecutionPath( + this.qubitRegisters.Keys + .OrderBy(k => k) + .Select(k => new QubitDeclaration(k, + // Get number of classical registers associated with qubit register (null if none). + (this.classicalRegisters.ContainsKey(k)) + ? this.classicalRegisters[k].Count + : null as int? + )), + this.operations + ); /// - /// Provides the event listener to listen to SimulatorBase's OnOperationStart event. + /// Provides the event listener to listen to + /// 's + /// OnOperationStart event. /// public void OnOperationStartHandler(ICallable operation, IApplyData arguments) { @@ -69,26 +67,25 @@ public void OnOperationStartHandler(ICallable operation, IApplyData arguments) // those operations instead if (this.nestedTypes.Contains(operation.GetType())) return; - this.currDepth++; + this.currentDepth++; // Parse operations at specified depth - if (this.currDepth == this.renderDepth) + if (this.currentDepth == this.renderDepth) { var parsedOp = this.ParseOperation(operation, arguments); - if (parsedOp != null) - { - this.operations.Add(parsedOp); - } + if (parsedOp != null) this.operations.Add(parsedOp); } } /// - /// Provides the event listener to listen to SimulatorBase's OnOperationEnd event. + /// Provides the event listener to listen to + /// 's + /// OnOperationEnd event. /// public void OnOperationEndHandler(ICallable operation, IApplyData result) { if (this.nestedTypes.Contains(operation.GetType())) return; - this.currDepth--; + this.currentDepth--; } private static bool IsPartialApplication(ICallable operation) @@ -100,38 +97,24 @@ private static bool IsPartialApplication(ICallable operation) } /// - /// Retrieves the QubitRegister associated with the given Qubit or create a new + /// Retrieves the associated with the given or create a new /// one if it doesn't exist. /// - private QubitRegister GetQubitRegister(Qubit qubit) - { - if (!this.qubitRegisters.ContainsKey(qubit.Id)) - { - this.qubitRegisters[qubit.Id] = new QubitRegister(qubit.Id); - } + private QubitRegister GetQubitRegister(Qubit qubit) => + this.qubitRegisters.GetOrCreate(qubit.Id, new QubitRegister(qubit.Id)); - return this.qubitRegisters[qubit.Id]; - } - - private List GetQubitRegisters(IEnumerable qubits) - { - return qubits.Select(this.GetQubitRegister).ToList(); - } + private List GetQubitRegisters(IEnumerable qubits) => + qubits.Select(this.GetQubitRegister).ToList(); /// - /// Creates a new ClassicalRegister and associate it with the given Qubit. + /// Creates a new and associate it with the given . /// private ClassicalRegister CreateClassicalRegister(Qubit measureQubit) { var qId = measureQubit.Id; + var cId = this.classicalRegisters.GetOrCreate(qId).Count; - if (!this.classicalRegisters.ContainsKey(qId)) - { - this.classicalRegisters[qId] = new List(); - } - - var cId = this.classicalRegisters[qId].Count; - ClassicalRegister register = new ClassicalRegister(qId, cId); + var register = new ClassicalRegister(qId, cId); // Add classical register under the given qubit id this.classicalRegisters[qId].Add(register); @@ -140,7 +123,7 @@ private ClassicalRegister CreateClassicalRegister(Qubit measureQubit) } /// - /// Retrieves the most recent ClassicalRegister associated with the given Qubit. + /// Retrieves the most recent associated with the given . /// /// /// Currently not used as this is intended for classically-controlled operations. @@ -159,36 +142,7 @@ private ClassicalRegister GetClassicalRegister(Qubit controlQubit) } /// - /// Given a Type and its value, extract its fields and format it as a string. - /// - private string? ExtractArgs(Type t, object value) - { - List fields = new List(); - - foreach (var f in t.GetFields()) - { - // If field is a tuple, recursively extract its inner arguments and format as a tuple string. - if (f.FieldType.IsTuple()) - { - var nestedArgs = f.GetValue(value); - if (nestedArgs != null) - { - var nestedFields = this.ExtractArgs(f.FieldType, nestedArgs); - fields.Add(nestedFields); - } - } - // Add field as an argument if it is not a Qubit type - else if (!f.FieldType.IsQubitsContainer()) - { - fields.Add(f.GetValue(value)?.ToString()); - } - } - - return fields.Any() ? $"({string.Join(",", fields.WhereNotNull())})" : null; - } - - /// - /// Given an operation and its arguments, parse it into an Operation object. + /// Given an operation and its arguments, parse it into an object. /// private Operation? ParseOperation(ICallable operation, IApplyData arguments) { @@ -288,8 +242,7 @@ private ClassicalRegister GetClassicalRegister(Qubit controlQubit) // General operations default: - Type t = arguments.Value.GetType(); - var argStr = this.ExtractArgs(t, arguments.Value); + var argStr = arguments.Value.GetType().ArgsToString(arguments.Value); var qubitRegs = this.GetQubitRegisters(arguments.GetQubits()); return new Operation diff --git a/src/Core/ExecutionPathTracer/Extensions.cs b/src/Core/ExecutionPathTracer/Extensions.cs new file mode 100644 index 0000000000..73fcd7af08 --- /dev/null +++ b/src/Core/ExecutionPathTracer/Extensions.cs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.IQSharp.Core.ExecutionPathTracer +{ + /// + /// Extension methods to be used with and by . + /// + public static class Extensions + { + /// + /// Attaches ExecutionPathTracer event listeners to the simulator to generate + /// the ExecutionPath of the operation performed by the simulator. + /// + public static T WithExecutionPathTracer(this T sim, ExecutionPathTracer tracer) + where T : SimulatorBase + { + sim.OnOperationStart += tracer.OnOperationStartHandler; + sim.OnOperationEnd += tracer.OnOperationEndHandler; + return sim; + } + + /// + /// Gets the value associated with the specified key and creates a new entry with the defaultVal if + /// the key doesn't exist. + /// + public static TValue GetOrCreate(this IDictionary dict, TKey key, TValue defaultVal) + { + TValue val; + if (!dict.TryGetValue(key, out val)) + { + val = defaultVal; + dict.Add(key, val); + } + return val; + } + + /// + /// Gets the value associated with the specified key and creates a new entry of the default type if + /// the key doesn't exist. + /// + public static TValue GetOrCreate(this IDictionary dict, TKey key) + where TValue : new() + { + return dict.GetOrCreate(key, new TValue()); + } + + /// + /// Given a , format its non-qubit arguments into a string. + /// Returns null if no arguments found. + /// + public static string? ArgsToString(this Type t, object args) + { + var argsStrings = t.GetFields() + .Select(f => + { + var argString = null as string; + + // If field is a tuple, recursively extract its inner arguments and format as a tuple string. + if (f.FieldType.IsTuple()) + { + var nestedArgs = f.GetValue(args); + if (nestedArgs != null) argString = f.FieldType.ArgsToString(nestedArgs); + } + // Add field as an argument if it is not a Qubit type + else if (!f.FieldType.IsQubitsContainer()) + { + argString = f.GetValue(args)?.ToString(); + } + + return argString; + }) + .WhereNotNull(); + + return argsStrings.Any() + ? $"({string.Join(",", argsStrings)})" + : null; + } + } +} diff --git a/src/Core/ExecutionPathTracer/Register.cs b/src/Core/ExecutionPathTracer/Register.cs index 05642736c8..6470a0b0ca 100644 --- a/src/Core/ExecutionPathTracer/Register.cs +++ b/src/Core/ExecutionPathTracer/Register.cs @@ -1,19 +1,27 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace Microsoft.Quantum.IQSharp +#nullable enable + +namespace Microsoft.Quantum.IQSharp.Core.ExecutionPathTracer { /// /// Enum for the 2 types of registers: Qubit and Classical. /// public enum RegisterType { + /// + /// Qubit register that holds a qubit. + /// Qubit, + /// + /// Classical register that holds a classical bit. + /// Classical, } /// - /// Represents a register used by an Operation. + /// Represents a register used by an . /// public class Register { @@ -34,12 +42,12 @@ public class Register } /// - /// Represents a qubit register used by an Operation. + /// Represents a qubit register used by an . /// public class QubitRegister : Register { /// - /// Creates a new QubitRegister with the given qubit id. + /// Creates a new with the given qubit id. /// /// /// Id of qubit register. @@ -53,12 +61,12 @@ public QubitRegister(int qId) } /// - /// Represents a classical register used by an Operation. + /// Represents a classical register used by an . /// public class ClassicalRegister : Register { /// - /// Creates a new ClassicalRegister with the given qubit id and classical bit id. + /// Creates a new with the given qubit id and classical bit id. /// /// /// Id of qubit register. diff --git a/src/Kernel/Extensions.cs b/src/Kernel/Extensions.cs index f04ac68e28..1fd81e18e6 100644 --- a/src/Kernel/Extensions.cs +++ b/src/Kernel/Extensions.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Jupyter.Core; using Microsoft.Quantum.IQSharp.Jupyter; -using Microsoft.Quantum.Simulation.Common; namespace Microsoft.Quantum.IQSharp.Kernel { @@ -24,16 +23,5 @@ public static void AddIQSharpKernel(this IServiceCollection services) services.AddSingleton(); services.AddSingleton(); } - - /// - /// Attaches ExecutionPathTracer event listeners to the simulator to generate - /// the ExecutionPath of the operation performed by the simulator. - /// - public static T WithExecutionPathTracer(this T sim, ExecutionPathTracer tracer) where T : SimulatorBase - { - sim.OnOperationStart += tracer.OnOperationStartHandler; - sim.OnOperationEnd += tracer.OnOperationEndHandler; - return sim; - } } } From 4907615664b29e720f9630416d0d0dae80577322 Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Wed, 8 Jul 2020 15:43:04 -0400 Subject: [PATCH 04/11] Use GetRuntimeMetadata --- src/Core/ExecutionPathTracer/ExecutionPath.cs | 2 +- .../ExecutionPathTracer.cs | 139 ++++-------------- 2 files changed, 29 insertions(+), 112 deletions(-) diff --git a/src/Core/ExecutionPathTracer/ExecutionPath.cs b/src/Core/ExecutionPathTracer/ExecutionPath.cs index 8ea4044edb..c1c5227205 100644 --- a/src/Core/ExecutionPathTracer/ExecutionPath.cs +++ b/src/Core/ExecutionPathTracer/ExecutionPath.cs @@ -107,7 +107,7 @@ public class Operation /// /// Currently not used as this is intended for classically-controlled operations. /// - public IEnumerable> Children { get; set; } = new List>(); + public IEnumerable>? Children { get; set; } /// /// True if operation is a controlled operations. diff --git a/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs index b763e39bfc..e758e76a23 100644 --- a/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs +++ b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs @@ -37,8 +37,8 @@ public class ExecutionPathTracer /// /// The depth at which to render operations. /// - public ExecutionPathTracer(int depth = 1) - => this.renderDepth = depth + 1; + public ExecutionPathTracer(int depth = 1) => + this.renderDepth = depth + 1; /// /// Returns the generated ExecutionPath. @@ -72,7 +72,8 @@ public void OnOperationStartHandler(ICallable operation, IApplyData arguments) // Parse operations at specified depth if (this.currentDepth == this.renderDepth) { - var parsedOp = this.ParseOperation(operation, arguments); + var metadata = operation.GetRuntimeMetadata(arguments); + var parsedOp = this.MetadataToOperation(metadata); if (parsedOp != null) this.operations.Add(parsedOp); } } @@ -140,121 +141,37 @@ private ClassicalRegister GetClassicalRegister(Qubit controlQubit) var cId = this.classicalRegisters[qId].Count - 1; return this.classicalRegisters[qId][cId]; } - + /// - /// Given an operation and its arguments, parse it into an object. + /// Parse into its corresponding . /// - private Operation? ParseOperation(ICallable operation, IApplyData arguments) + private Operation? MetadataToOperation(RuntimeMetadata? metadata) { - // If operation is a partial application, perform on baseOp recursively. - if (IsPartialApplication(operation)) - { - dynamic partialOp = operation; - dynamic partialOpArgs = arguments; - - // Recursively get base operation operations - var baseOp = partialOp.BaseOp; - var baseArgs = baseOp.__dataIn(partialOpArgs.Value); - return this.ParseOperation(baseOp, baseArgs); - } - - var controlled = operation.Variant == OperationFunctor.Controlled || - operation.Variant == OperationFunctor.ControlledAdjoint; - var adjoint = operation.Variant == OperationFunctor.Adjoint || - operation.Variant == OperationFunctor.ControlledAdjoint; + if (metadata == null) return null; - // If operation is controlled, perform on baseOp recursively and mark as controlled. - if (controlled) + var op = new Operation() { - dynamic ctrlOp = operation; - dynamic ctrlOpArgs = arguments; - - var ctrls = ctrlOpArgs.Value.Item1; - var controlRegs = this.GetQubitRegisters(ctrls); - - // Recursively get base operation operations - var baseOp = ctrlOp.BaseOp; - var baseArgs = baseOp.__dataIn(ctrlOpArgs.Value.Item2); - var parsedBaseOp = this.ParseOperation(baseOp, baseArgs); - - parsedBaseOp.Controlled = true; - parsedBaseOp.Adjoint = adjoint; - parsedBaseOp.Controls.InsertRange(0, controlRegs); - - return parsedBaseOp; - } - - // Handle operation based on type - switch (operation) + Gate = metadata.Label, + ArgStr = metadata.Args, + Children = metadata.Children?.Select(child => child.Select(this.MetadataToOperation).WhereNotNull()), + Controlled = metadata.IsControlled, + Adjoint = metadata.IsAdjoint, + Controls = this.GetQubitRegisters(metadata.Controls), + Targets = this.GetQubitRegisters(metadata.Targets), + }; + + // Create classical registers for measurement operations + if (metadata.IsMeasurement) { - // Handle CNOT operations as a Controlled X - case Microsoft.Quantum.Intrinsic.CNOT cnot: - case Microsoft.Quantum.Intrinsic.CCNOT ccnot: - var ctrlRegs = new List(); - var targetRegs = new List(); - - switch (arguments.Value) - { - case ValueTuple cnotQs: - var (ctrl, cnotTarget) = cnotQs; - ctrlRegs.Add(this.GetQubitRegister(ctrl)); - targetRegs.Add(this.GetQubitRegister(cnotTarget)); - break; - case ValueTuple ccnotQs: - var (ctrl1, ctrl2, ccnotTarget) = ccnotQs; - ctrlRegs.Add(this.GetQubitRegister(ctrl1)); - ctrlRegs.Add(this.GetQubitRegister(ctrl2)); - targetRegs.Add(this.GetQubitRegister(ccnotTarget)); - break; - } - - return new Operation - { - Gate = "X", - Controlled = true, - Adjoint = adjoint, - Controls = ctrlRegs, - Targets = targetRegs, - }; - - // Measurement operations - case Microsoft.Quantum.Intrinsic.M m: - case Microsoft.Quantum.Measurement.MResetX mx: - case Microsoft.Quantum.Measurement.MResetY my: - case Microsoft.Quantum.Measurement.MResetZ mz: - var measureQubit = arguments.GetQubits().ElementAt(0); - var measureReg = this.GetQubitRegister(measureQubit); - var clsReg = this.CreateClassicalRegister(measureQubit); - - return new Operation - { - Gate = "measure", - Controlled = false, - Adjoint = adjoint, - Controls = new List() { measureReg }, - Targets = new List() { clsReg }, - }; - - // Operations to ignore - case Microsoft.Quantum.Intrinsic.Reset reset: - case Microsoft.Quantum.Intrinsic.ResetAll resetAll: - return null; - - // General operations - default: - var argStr = arguments.Value.GetType().ArgsToString(arguments.Value); - var qubitRegs = this.GetQubitRegisters(arguments.GetQubits()); - - return new Operation - { - Gate = operation.Name, - ArgStr = argStr, - Controlled = false, - Adjoint = adjoint, - Controls = new List(), - Targets = qubitRegs.Cast().ToList(), - }; + var measureQubit = metadata.Targets.ElementAt(0); + var clsReg = this.CreateClassicalRegister(measureQubit); + // TODO: Change this to using IsMeasurement + op.Gate = "measure"; + op.Controls = op.Targets; + op.Targets = new List() { clsReg }; } + + return op; } } } \ No newline at end of file From 43b0b851bba0880dd0b43f41ce7e06bd7846e763 Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Thu, 9 Jul 2020 18:35:17 -0400 Subject: [PATCH 05/11] Remove ApplyToEach --- .../ExecutionPathTracer.cs | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs index e758e76a23..0ab8a96a1c 100644 --- a/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs +++ b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using Microsoft.Quantum.Simulation.Core; @@ -23,13 +22,6 @@ public class ExecutionPathTracer private IDictionary qubitRegisters = new Dictionary(); private IDictionary> classicalRegisters = new Dictionary>(); private List operations = new List(); - private readonly ImmutableList nestedTypes = - ImmutableList.Create( - typeof(Microsoft.Quantum.Canon.ApplyToEach), - typeof(Microsoft.Quantum.Canon.ApplyToEachC), - typeof(Microsoft.Quantum.Canon.ApplyToEachA), - typeof(Microsoft.Quantum.Canon.ApplyToEachCA) - ); /// /// Initializes a new instance of the class. @@ -63,10 +55,6 @@ public ExecutionPath GetExecutionPath() => /// public void OnOperationStartHandler(ICallable operation, IApplyData arguments) { - // If the operation type is one of the nestedTypes, go one depth deeper and parse - // those operations instead - if (this.nestedTypes.Contains(operation.GetType())) return; - this.currentDepth++; // Parse operations at specified depth @@ -85,18 +73,9 @@ public void OnOperationStartHandler(ICallable operation, IApplyData arguments) /// public void OnOperationEndHandler(ICallable operation, IApplyData result) { - if (this.nestedTypes.Contains(operation.GetType())) return; this.currentDepth--; } - private static bool IsPartialApplication(ICallable operation) - { - var t = operation.GetType(); - if (t == null) return false; - - return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(OperationPartial<,,>); - } - /// /// Retrieves the associated with the given or create a new /// one if it doesn't exist. @@ -141,7 +120,7 @@ private ClassicalRegister GetClassicalRegister(Qubit controlQubit) var cId = this.classicalRegisters[qId].Count - 1; return this.classicalRegisters[qId][cId]; } - + /// /// Parse into its corresponding . /// From 10ef3cc14eb5c9066cde8142d3941a2f66cf94fa Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Fri, 10 Jul 2020 17:02:43 -0400 Subject: [PATCH 06/11] Add tests --- src/Tests/ExecutionPathTracerTests.cs | 454 ++++++++++++++++++ .../Intrinsic.qs | 139 ++++++ .../Measurement.qs | 28 ++ 3 files changed, 621 insertions(+) create mode 100644 src/Tests/ExecutionPathTracerTests.cs create mode 100644 src/Tests/Workspace.ExecutionPathTracer/Intrinsic.qs create mode 100644 src/Tests/Workspace.ExecutionPathTracer/Measurement.qs diff --git a/src/Tests/ExecutionPathTracerTests.cs b/src/Tests/ExecutionPathTracerTests.cs new file mode 100644 index 0000000000..8646d32279 --- /dev/null +++ b/src/Tests/ExecutionPathTracerTests.cs @@ -0,0 +1,454 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.Quantum.IQSharp; +using Microsoft.Quantum.Simulation.Simulators; +using Microsoft.Quantum.IQSharp.Core.ExecutionPathTracer; + +namespace Tests.IQSharp +{ + [TestClass] + public class IntrinsicTests + { + public Workspace InitWorkspace() + { + var ws = Startup.Create("Workspace.ExecutionPathTracer"); + ws.Reload(); + Assert.IsFalse(ws.HasErrors); + return ws; + } + + public ExecutionPath GetExecutionPath(string name, int depth = 1) + { + var ws = InitWorkspace(); + var ops = ws.AssemblyInfo.Operations; + var names = ws.AssemblyInfo.Operations.Select(o => o.FullName); + var op = ws.AssemblyInfo.Operations.Single(o => o.FullName == name); + Assert.IsNotNull(op); + + var tracer = new ExecutionPathTracer(depth); + using var qsim = new QuantumSimulator().WithExecutionPathTracer(tracer); + op.RunAsync(qsim, new Dictionary()).Wait(); + + return tracer.GetExecutionPath(); + } + + + [TestMethod] + public void HTest() + { + var path = GetExecutionPath("HCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "H", + Targets = new List() { new QubitRegister(0) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(path.ToJson(), expected.ToJson()); + } + + [TestMethod] + public void MTest() + { + var path = GetExecutionPath("MCirc"); + 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(path.ToJson(), expected.ToJson()); + } + + [TestMethod] + public void CnotTest() + { + var path = GetExecutionPath("CnotCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0), + new QubitDeclaration(1), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "X", + Controlled = true, + Controls = new List() { new QubitRegister(0) }, + Targets = new List() { new QubitRegister(1) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(path.ToJson(), expected.ToJson()); + } + + [TestMethod] + public void CcnotTest() + { + var path = GetExecutionPath("CcnotCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0), + new QubitDeclaration(1), + new QubitDeclaration(2), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "X", + Controlled = true, + Controls = new List() { new QubitRegister(0), new QubitRegister(2) }, + Targets = new List() { new QubitRegister(1) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(path.ToJson(), expected.ToJson()); + } + + [TestMethod] + public void SwapTest() + { + var path = GetExecutionPath("SwapCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0), + new QubitDeclaration(1), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "SWAP", + Targets = new List() { new QubitRegister(0), new QubitRegister(1) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(path.ToJson(), expected.ToJson()); + } + + [TestMethod] + public void RxTest() + { + var path = GetExecutionPath("RxCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "Rx", + ArgStr = "(2)", + Targets = new List() { new QubitRegister(0) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(path.ToJson(), expected.ToJson()); + } + + [TestMethod] + public void AdjointHTest() + { + var path = GetExecutionPath("AdjointHCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "H", + Adjoint = true, + Targets = new List() { new QubitRegister(0) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(path.ToJson(), expected.ToJson()); + } + + [TestMethod] + public void ControlledXTest() + { + var path = GetExecutionPath("ControlledXCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0), + new QubitDeclaration(1), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "X", + Controlled = true, + Controls = new List() { new QubitRegister(0) }, + Targets = new List() { new QubitRegister(1) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(path.ToJson(), expected.ToJson()); + + // JSON should be the same as CNOT's + var path2 = GetExecutionPath("CnotCirc"); + Assert.AreEqual(path.ToJson(), path2.ToJson()); + } + + [TestMethod] + public void ControlledAdjointSTest() + { + var path = GetExecutionPath("ControlledAdjointSCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0), + new QubitDeclaration(1), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "S", + Controlled = true, + Adjoint = true, + Controls = new List() { new QubitRegister(0) }, + Targets = new List() { new QubitRegister(1) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(path.ToJson(), expected.ToJson()); + } + + [TestMethod] + public void FooTest() + { + var path = GetExecutionPath("FooCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "Foo", + ArgStr = "(2.1,(bar))", + Targets = new List() { new QubitRegister(0) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(path.ToJson(), expected.ToJson()); + } + + [TestMethod] + public void ControlledFooTest() + { + var path = GetExecutionPath("ControlledFooCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0), + new QubitDeclaration(1), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "Foo", + ArgStr = "(2.1,(bar))", + Controlled = true, + Controls = new List() { new QubitRegister(0) }, + Targets = new List() { new QubitRegister(1) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(path.ToJson(), expected.ToJson()); + } + + [TestMethod] + public void UnusedQubitTest() + { + var path = GetExecutionPath("UnusedQubitCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0), + new QubitDeclaration(2), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "X", + Controlled = true, + Controls = new List() { new QubitRegister(2) }, + Targets = new List() { new QubitRegister(0) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(path.ToJson(), expected.ToJson()); + } + + [TestMethod] + public void Depth2Test() + { + var path = GetExecutionPath("Depth2Circ", 2); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0, 1), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "H", + Targets = new List() { new QubitRegister(0) }, + }, + new Operation() + { + Gate = "X", + Targets = new List() { new QubitRegister(0) }, + }, + new Operation() + { + Gate = "H", + Targets = new List() { new QubitRegister(0) }, + }, + 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(path.ToJson(), expected.ToJson()); + } + + [TestMethod] + public void PartialOpTest() + { + var path = GetExecutionPath("PartialOpCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0), + new QubitDeclaration(1), + new QubitDeclaration(2), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "H", + Controlled = true, + Controls = new List() { new QubitRegister(0), new QubitRegister(1) }, + Targets = new List() { new QubitRegister(2) }, + }, + new Operation() + { + Gate = "Ry", + ArgStr = "(2.5)", + Targets = new List() { new QubitRegister(0) }, + }, + }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(path.ToJson(), expected.ToJson()); + } + + [TestMethod] + public void EmptyTest() + { + var path = GetExecutionPath("EmptyCirc"); + var qubits = new QubitDeclaration[] { }; + var operations = new Operation[] { }; + var expected = new ExecutionPath(qubits, operations); + Assert.AreEqual(path.ToJson(), expected.ToJson()); + } + + [TestMethod] + public void BigTest() + { + var path = GetExecutionPath("BigCirc"); + var qubits = new QubitDeclaration[] + { + new QubitDeclaration(0, 1), + new QubitDeclaration(1), + new QubitDeclaration(2), + }; + var operations = new Operation[] + { + new Operation() + { + Gate = "H", + Targets = new List() { new QubitRegister(0) }, + }, + new Operation() + { + Gate = "Ry", + ArgStr = "(2.5)", + Targets = new List() { new QubitRegister(1) }, + }, + new Operation() + { + Gate = "Bar", + ArgStr = "((1,2.1),(foo))", + Targets = new List() { new QubitRegister(0) }, + }, + new Operation() + { + Gate = "X", + Targets = new List() { new QubitRegister(0) }, + }, + new Operation() + { + Gate = "X", + Controlled = true, + Controls = new List() { new QubitRegister(0), new QubitRegister(1) }, + Targets = new List() { new QubitRegister(2) }, + }, + new Operation() + { + Gate = "X", + Controlled = true, + Controls = new List() { new QubitRegister(0), new QubitRegister(1) }, + Targets = new List() { new QubitRegister(2) }, + }, + new Operation() + { + Gate = "Bar", + ArgStr = "((1,2.1),(foo))", + Controlled = true, + Adjoint = true, + Controls = new List() { new QubitRegister(2) }, + Targets = new List() { new QubitRegister(0) }, + }, + 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(path.ToJson(), expected.ToJson()); + } + } +} diff --git a/src/Tests/Workspace.ExecutionPathTracer/Intrinsic.qs b/src/Tests/Workspace.ExecutionPathTracer/Intrinsic.qs new file mode 100644 index 0000000000..884688ee05 --- /dev/null +++ b/src/Tests/Workspace.ExecutionPathTracer/Intrinsic.qs @@ -0,0 +1,139 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Tests.ExecutionPathTracer { + + open Microsoft.Quantum.Intrinsic; + + operation HCirc() : Unit { + using (q = Qubit()) { + H(q); + Reset(q); + } + } + + operation MCirc() : Unit { + using (q = Qubit()) { + let res = M(q); + } + } + + operation CnotCirc() : Unit { + using (qs = Qubit[2]) { + CNOT(qs[0], qs[1]); + ResetAll(qs); + } + } + + operation CcnotCirc() : Unit { + using (qs = Qubit[3]) { + CCNOT(qs[0], qs[2], qs[1]); + ResetAll(qs); + } + } + + operation SwapCirc() : Unit { + using (qs = Qubit[2]) { + SWAP(qs[0], qs[1]); + } + } + + operation RxCirc() : Unit { + using (q = Qubit()) { + Rx(2.0, q); + Reset(q); + } + } + + operation AdjointHCirc() : Unit { + using (q = Qubit()) { + Adjoint H(q); + Reset(q); + } + } + + operation ControlledXCirc() : Unit { + using (qs = Qubit[2]) { + Controlled X([qs[0]], qs[1]); + ResetAll(qs); + } + } + + operation ControlledAdjointSCirc() : Unit { + using (qs = Qubit[2]) { + Controlled Adjoint S([qs[0]], qs[1]); + 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]); + ResetAll(qs); + } + } + + operation EmptyCirc() : Unit { + using (qs = Qubit[3]) { + } + } + + operation FooBar(q : Qubit) : Unit { + H(q); + X(q); + H(q); + } + + operation Depth2Circ() : Unit { + using (q = Qubit()) { + FooBar(q); + Reset(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/Measurement.qs b/src/Tests/Workspace.ExecutionPathTracer/Measurement.qs new file mode 100644 index 0000000000..01efa8d5f8 --- /dev/null +++ b/src/Tests/Workspace.ExecutionPathTracer/Measurement.qs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Tests.ExecutionPathTracer { + + open Microsoft.Quantum.Measurement; + + operation MResetXCirc() : Unit { + using (q = Qubit()) { + let res = MResetX(q); + } + } + + operation MResetYCirc() : Unit { + using (q = Qubit()) { + let res = MResetY(q); + } + } + + operation MResetZCirc() : Unit { + using (q = Qubit()) { + let res = MResetZ(q); + } + } + +} + + From bcd1e1710d0286d85ba73ec6d4b4b3564172f85e Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Tue, 14 Jul 2020 21:49:10 -0400 Subject: [PATCH 07/11] Use Newtonsoft instead of System.Text.Json --- src/Core/Core.csproj | 1 - src/Core/ExecutionPathTracer/ExecutionPath.cs | 32 ++++++++++++++----- .../ExecutionPathTracer.cs | 10 +++--- src/Core/ExecutionPathTracer/Register.cs | 14 +++++--- 4 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 4bbfa66a99..15ab736d85 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -39,7 +39,6 @@ - diff --git a/src/Core/ExecutionPathTracer/ExecutionPath.cs b/src/Core/ExecutionPathTracer/ExecutionPath.cs index c1c5227205..2f55689df1 100644 --- a/src/Core/ExecutionPathTracer/ExecutionPath.cs +++ b/src/Core/ExecutionPathTracer/ExecutionPath.cs @@ -4,7 +4,8 @@ #nullable enable using System.Collections.Generic; -using System.Text.Json; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; namespace Microsoft.Quantum.IQSharp.Core.ExecutionPathTracer { @@ -31,11 +32,13 @@ public ExecutionPath(IEnumerable qubits, IEnumerable /// A list of that represents the declared qubits used in the execution path. /// + [JsonProperty("qubits")] public IEnumerable Qubits { get; private set; } /// /// A list of that represents the operations used in the execution path. /// + [JsonProperty("operations")] public IEnumerable Operations { get; private set; } /// @@ -45,12 +48,11 @@ public ExecutionPath(IEnumerable qubits, IEnumerabletrue. /// public string ToJson(bool prettyPrint = false) => - JsonSerializer.Serialize(this, - new JsonSerializerOptions + JsonConvert.SerializeObject(this, + (prettyPrint) ? Formatting.Indented : Formatting.None, + new JsonSerializerSettings { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - IgnoreNullValues = true, - WriteIndented = prettyPrint, + NullValueHandling = NullValueHandling.Ignore, } ); } @@ -69,7 +71,7 @@ public class QubitDeclaration /// /// Number of associated classical registers. /// - public QubitDeclaration(int id, int? numChildren = null) + public QubitDeclaration(int id, int numChildren = 0) { this.Id = id; this.NumChildren = numChildren; @@ -78,12 +80,20 @@ public QubitDeclaration(int id, int? numChildren = null) /// /// Id of qubit. /// + [JsonProperty("id")] public int Id { get; private set; } /// /// Number of associated classical registers. /// - public int? NumChildren { get; private set; } + [JsonProperty("numChildren")] + public int NumChildren { get; private set; } + + /// + /// Used by to determine if + /// should be included in the JSON serialization. + /// + public bool ShouldSerializeNumChildren() => NumChildren > 0; } /// @@ -94,6 +104,7 @@ public class Operation /// /// Label of gate. /// + [JsonProperty("gate")] public string Gate { get; set; } = ""; /// @@ -107,26 +118,31 @@ public class Operation /// /// Currently not used as this is intended for classically-controlled operations. /// + [JsonProperty("children")] public IEnumerable>? Children { get; set; } /// /// True if operation is a controlled operations. /// + [JsonProperty("controlled")] public bool Controlled { get; set; } /// /// True if operation is an adjoint operations. /// + [JsonProperty("adjoint")] public bool Adjoint { get; set; } /// /// List of control registers. /// + [JsonProperty("controls")] public IEnumerable Controls { get; set; } = new List(); /// /// List of target registers. /// + [JsonProperty("targets")] public IEnumerable Targets { get; set; } = new List(); } } diff --git a/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs index 0ab8a96a1c..bbe1529d95 100644 --- a/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs +++ b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs @@ -39,11 +39,9 @@ public ExecutionPath GetExecutionPath() => new ExecutionPath( this.qubitRegisters.Keys .OrderBy(k => k) - .Select(k => new QubitDeclaration(k, - // Get number of classical registers associated with qubit register (null if none). - (this.classicalRegisters.ContainsKey(k)) - ? this.classicalRegisters[k].Count - : null as int? + .Select(k => new QubitDeclaration(k, (this.classicalRegisters.ContainsKey(k)) + ? this.classicalRegisters[k].Count + : 0 )), this.operations ); @@ -153,4 +151,4 @@ private ClassicalRegister GetClassicalRegister(Qubit controlQubit) return op; } } -} \ No newline at end of file +} diff --git a/src/Core/ExecutionPathTracer/Register.cs b/src/Core/ExecutionPathTracer/Register.cs index 6470a0b0ca..b04b98983c 100644 --- a/src/Core/ExecutionPathTracer/Register.cs +++ b/src/Core/ExecutionPathTracer/Register.cs @@ -3,6 +3,9 @@ #nullable enable +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + namespace Microsoft.Quantum.IQSharp.Core.ExecutionPathTracer { /// @@ -28,16 +31,19 @@ public class Register /// /// Type of register. /// + [JsonProperty("type")] public virtual RegisterType Type { get; set; } /// /// Qubit id of register. /// + [JsonProperty("qId")] public virtual int QId { get; set; } /// - /// Classical bit id of register. + /// Classical bit id of register. null if register is a qubit register. /// + [JsonProperty("cId")] public virtual int? CId { get; set; } } @@ -52,10 +58,8 @@ public class QubitRegister : Register /// /// Id of qubit register. /// - public QubitRegister(int qId) - { + public QubitRegister(int qId) => this.QId = qId; - } public override RegisterType Type => RegisterType.Qubit; } @@ -82,4 +86,4 @@ public ClassicalRegister(int qId, int cId) public override RegisterType Type => RegisterType.Classical; } -} \ No newline at end of file +} From ca84324a2a910c8a0f86fa50394431492931faa3 Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Tue, 14 Jul 2020 21:51:22 -0400 Subject: [PATCH 08/11] Rename ArgStr to DisplayArgs --- src/Core/ExecutionPathTracer/ExecutionPath.cs | 6 ++-- .../ExecutionPathTracer.cs | 2 +- .../ExecutionPathVisualizer/executionPath.ts | 4 +-- .../formatters/gateFormatter.ts | 24 ++++++++-------- .../ExecutionPathVisualizer/metadata.ts | 2 +- .../client/ExecutionPathVisualizer/process.ts | 4 +-- .../client/ExecutionPathVisualizer/utils.ts | 4 +-- .../gateFormatter.test.ts | 6 ++-- .../process.test.ts | 28 +++++++++---------- .../utils.test.ts | 2 +- src/Tests/ExecutionPathTracerTests.cs | 14 +++++----- 11 files changed, 49 insertions(+), 47 deletions(-) diff --git a/src/Core/ExecutionPathTracer/ExecutionPath.cs b/src/Core/ExecutionPathTracer/ExecutionPath.cs index 2f55689df1..0bca96de3f 100644 --- a/src/Core/ExecutionPathTracer/ExecutionPath.cs +++ b/src/Core/ExecutionPathTracer/ExecutionPath.cs @@ -108,9 +108,11 @@ public class Operation public string Gate { get; set; } = ""; /// - /// Non-qubit arguments provided to gate. + /// Arguments (except types) provided to gate that + /// will be displayed by the visualizer. /// - public string? ArgStr { get; set; } + [JsonProperty("displayArgs")] + public string? DisplayArgs { get; set; } /// /// Group of operations for each classical branch. diff --git a/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs index bbe1529d95..a2fb4eb74c 100644 --- a/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs +++ b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs @@ -129,7 +129,7 @@ private ClassicalRegister GetClassicalRegister(Qubit controlQubit) var op = new Operation() { Gate = metadata.Label, - ArgStr = metadata.Args, + DisplayArgs = (metadata.FormattedNonQubitArgs.Length > 0) ? metadata.FormattedNonQubitArgs : null, Children = metadata.Children?.Select(child => child.Select(this.MetadataToOperation).WhereNotNull()), Controlled = metadata.IsControlled, Adjoint = metadata.IsAdjoint, diff --git a/src/Kernel/client/ExecutionPathVisualizer/executionPath.ts b/src/Kernel/client/ExecutionPathVisualizer/executionPath.ts index 314230fb84..eb74cb57f6 100644 --- a/src/Kernel/client/ExecutionPathVisualizer/executionPath.ts +++ b/src/Kernel/client/ExecutionPathVisualizer/executionPath.ts @@ -25,8 +25,8 @@ export interface Qubit { export interface Operation { /** Gate label. */ gate: string; - /** Gate arguments as string. */ - argStr?: string, + /** Formatted gate arguments to be displayed. */ + displayArgs?: string, /** Classically-controlled gates. * - children[0]: gates when classical control bit is 0. * - children[1]: gates when classical control bit is 1. diff --git a/src/Kernel/client/ExecutionPathVisualizer/formatters/gateFormatter.ts b/src/Kernel/client/ExecutionPathVisualizer/formatters/gateFormatter.ts index 5132866b57..ca2b22a3d8 100644 --- a/src/Kernel/client/ExecutionPathVisualizer/formatters/gateFormatter.ts +++ b/src/Kernel/client/ExecutionPathVisualizer/formatters/gateFormatter.ts @@ -42,12 +42,12 @@ const formatGates = (opsMetadata: Metadata[]): string => { * @returns SVG representation of gate. */ const _formatGate = (metadata: Metadata): string => { - const { type, x, controlsY, targetsY, label, argStr, width } = metadata; + const { type, x, controlsY, targetsY, label, displayArgs, width } = metadata; switch (type) { case GateType.Measure: return _measure(x, controlsY[0], targetsY[0]); case GateType.Unitary: - return _unitary(label, x, targetsY, width, argStr); + return _unitary(label, x, targetsY, width, displayArgs); case GateType.Swap: if (controlsY.length > 0) return _controlledGate(metadata); else return _swap(x, targetsY); @@ -89,12 +89,12 @@ const _measure = (x: number, qy: number, cy: number): string => { * @param x x coord of gate. * @param y Array of y coords of registers acted upon by gate. * @param width Width of gate. - * @param argStr Arguments passed in to gate. + * @param displayArgs Arguments passed in to gate. * @param renderDashedLine If true, draw dashed lines between non-adjacent unitaries. * * @returns SVG representation of unitary gate. */ -const _unitary = (label: string, x: number, y: number[], width: number, argStr?: string, renderDashedLine: boolean = true): string => { +const _unitary = (label: string, x: number, y: number[], width: number, displayArgs?: string, renderDashedLine: boolean = true): string => { if (y.length === 0) return ""; // Sort y in ascending order @@ -116,7 +116,7 @@ const _unitary = (label: string, x: number, y: number[], width: number, argStr?: const unitaryBoxes: string[] = regGroups.map((group: number[]) => { const maxY: number = group[group.length - 1], minY: number = group[0]; const height: number = maxY - minY + gateHeight; - return _unitaryBox(label, x, minY, width, height, argStr); + return _unitaryBox(label, x, minY, width, height, displayArgs); }); // Draw dashed line between disconnected unitaries @@ -135,19 +135,19 @@ const _unitary = (label: string, x: number, y: number[], width: number, argStr?: * @param y y coord of gate. * @param width Width of gate. * @param height Height of gate. - * @param argStr Arguments passed in to gate. + * @param displayArgs Arguments passed in to gate. * * @returns SVG representation of unitary box. */ -const _unitaryBox = (label: string, x: number, y: number, width: number, height: number = gateHeight, argStr?: string): string => { +const _unitaryBox = (label: string, x: number, y: number, width: number, height: number = gateHeight, displayArgs?: string): string => { y -= gateHeight / 2; const uBox: string = box(x - width / 2, y, width, height); - const labelY = y + height / 2 - ((argStr == null) ? 0 : 7); + const labelY = y + height / 2 - ((displayArgs == null) ? 0 : 7); const labelText: string = text(label, x, labelY); const elems = [uBox, labelText]; - if (argStr != null) { + if (displayArgs != null) { const argStrY = y + height / 2 + 8; - const argText: string = text(argStr, x, argStrY, argsFontSize); + const argText: string = text(displayArgs, x, argStrY, argsFontSize); elems.push(argText); } const svg: string = group(elems); @@ -194,7 +194,7 @@ const _cross = (x: number, y: number): string => { */ const _controlledGate = (metadata: Metadata): string => { const targetGateSvgs: string[] = []; - const { type, x, controlsY, targetsY, label, argStr, width } = metadata; + const { type, x, controlsY, targetsY, label, displayArgs, width } = metadata; // Get SVG for target gates switch (type) { case GateType.Cnot: @@ -204,7 +204,7 @@ const _controlledGate = (metadata: Metadata): string => { targetsY.forEach(y => targetGateSvgs.push(_cross(x, y))); break; case GateType.ControlledUnitary: - targetGateSvgs.push(_unitary(label, x, targetsY, width, argStr, false)); + targetGateSvgs.push(_unitary(label, x, targetsY, width, displayArgs, false)); break; default: throw new Error(`ERROR: Unrecognized gate: ${label} of type ${type}`); diff --git a/src/Kernel/client/ExecutionPathVisualizer/metadata.ts b/src/Kernel/client/ExecutionPathVisualizer/metadata.ts index e436d89722..2fe9bd7619 100644 --- a/src/Kernel/client/ExecutionPathVisualizer/metadata.ts +++ b/src/Kernel/client/ExecutionPathVisualizer/metadata.ts @@ -16,7 +16,7 @@ export interface Metadata { /** Gate label. */ label: string; /** Gate arguments as string. */ - argStr?: string, + displayArgs?: string, /** Gate width. */ width: number; /** Classically-controlled gates. diff --git a/src/Kernel/client/ExecutionPathVisualizer/process.ts b/src/Kernel/client/ExecutionPathVisualizer/process.ts index 0fce86488e..14c94f8104 100644 --- a/src/Kernel/client/ExecutionPathVisualizer/process.ts +++ b/src/Kernel/client/ExecutionPathVisualizer/process.ts @@ -185,7 +185,7 @@ const _opToMetadata = (op: Operation | null, registers: RegisterMap): Metadata = if (op == null) return metadata; - let { gate, argStr, controlled, adjoint, controls, targets, children } = op; + let { gate, displayArgs, controlled, adjoint, controls, targets, children } = op; // Set y coords metadata.controlsY = controls.map(reg => _getRegY(reg, registers)); @@ -233,7 +233,7 @@ const _opToMetadata = (op: Operation | null, registers: RegisterMap): Metadata = if (adjoint && metadata.label.length > 0) metadata.label += "'"; // If gate has extra arguments, display them - if (argStr != null) metadata.argStr = argStr; + if (displayArgs != null) metadata.displayArgs = displayArgs; // Set gate width metadata.width = getGateWidth(metadata); diff --git a/src/Kernel/client/ExecutionPathVisualizer/utils.ts b/src/Kernel/client/ExecutionPathVisualizer/utils.ts index 44d8515b23..2468d514d3 100644 --- a/src/Kernel/client/ExecutionPathVisualizer/utils.ts +++ b/src/Kernel/client/ExecutionPathVisualizer/utils.ts @@ -14,7 +14,7 @@ import { * * @returns Width of given gate (in pixels). */ -const getGateWidth = ({ type, label, argStr, width }: Metadata): number => { +const getGateWidth = ({ type, label, displayArgs, width }: Metadata): number => { switch (type) { case GateType.ClassicalControlled: // Already computed before. @@ -25,7 +25,7 @@ const getGateWidth = ({ type, label, argStr, width }: Metadata): number => { return minGateWidth; default: const labelWidth = _getStringWidth(label); - const argsWidth = (argStr != null) ? _getStringWidth(argStr, argsFontSize) : 0; + const argsWidth = (displayArgs != null) ? _getStringWidth(displayArgs, argsFontSize) : 0; const textWidth = Math.max(labelWidth, argsWidth) + labelPadding * 2; return Math.max(minGateWidth, textWidth); } diff --git a/src/Kernel/client/__tests__/ExecutionPathVisualizerTests/gateFormatter.test.ts b/src/Kernel/client/__tests__/ExecutionPathVisualizerTests/gateFormatter.test.ts index 2a5a761ac5..014346aa4b 100644 --- a/src/Kernel/client/__tests__/ExecutionPathVisualizerTests/gateFormatter.test.ts +++ b/src/Kernel/client/__tests__/ExecutionPathVisualizerTests/gateFormatter.test.ts @@ -449,7 +449,7 @@ describe("Testing _formatGate", () => { controlsY: [], targetsY: [startY], label: 'Ry', - argStr: '(0.25)', + displayArgs: '(0.25)', width: 52, }; expect(_formatGate(metadata)).toMatchSnapshot(); @@ -473,7 +473,7 @@ describe("Testing _formatGate", () => { controlsY: [], targetsY: [startY, startY + registerHeight], label: 'U', - argStr: "('foo', 'bar')", + displayArgs: "('foo', 'bar')", width: 77, }; expect(_formatGate(metadata)).toMatchSnapshot(); @@ -529,7 +529,7 @@ describe("Testing _formatGate", () => { controlsY: [startY], targetsY: [startY + registerHeight], label: 'U', - argStr: "('foo', 'bar')", + displayArgs: "('foo', 'bar')", width: 77, }; expect(_formatGate(metadata)).toMatchSnapshot(); diff --git a/src/Kernel/client/__tests__/ExecutionPathVisualizerTests/process.test.ts b/src/Kernel/client/__tests__/ExecutionPathVisualizerTests/process.test.ts index bd9107c075..b5a6870d2d 100644 --- a/src/Kernel/client/__tests__/ExecutionPathVisualizerTests/process.test.ts +++ b/src/Kernel/client/__tests__/ExecutionPathVisualizerTests/process.test.ts @@ -513,7 +513,7 @@ describe("Testing _opToMetadata", () => { }; let op: Operation = { gate: "RX", - argStr: "(0.25)", + displayArgs: "(0.25)", controlled: false, adjoint: false, controls: [], @@ -525,7 +525,7 @@ describe("Testing _opToMetadata", () => { controlsY: [], targetsY: [startY], label: "RX", - argStr: "(0.25)", + displayArgs: "(0.25)", width: 52 }; expect(_opToMetadata(op, registers)).toEqual(metadata); @@ -533,7 +533,7 @@ describe("Testing _opToMetadata", () => { // Test long argument op = { gate: "RX", - argStr: "(0.25, 1.0, 'foobar', (3.14, 6.67))", + displayArgs: "(0.25, 1.0, 'foobar', (3.14, 6.67))", controlled: false, adjoint: false, controls: [], @@ -545,7 +545,7 @@ describe("Testing _opToMetadata", () => { controlsY: [], targetsY: [startY], label: "RX", - argStr: "(0.25, 1.0, 'foobar', (3.14, 6.67))", + displayArgs: "(0.25, 1.0, 'foobar', (3.14, 6.67))", width: 188 }; expect(_opToMetadata(op, registers)).toEqual(metadata); @@ -553,7 +553,7 @@ describe("Testing _opToMetadata", () => { // Test controlled op = { gate: "RX", - argStr: "(0.25)", + displayArgs: "(0.25)", controlled: true, adjoint: false, controls: [{ type: RegisterType.Qubit, qId: 1 }], @@ -565,7 +565,7 @@ describe("Testing _opToMetadata", () => { controlsY: [startY + registerHeight], targetsY: [startY], label: "RX", - argStr: "(0.25)", + displayArgs: "(0.25)", width: 52 }; expect(_opToMetadata(op, registers)).toEqual(metadata); @@ -578,7 +578,7 @@ describe("Testing _opToMetadata", () => { }; let op: Operation = { gate: "U", - argStr: "('foo', 'bar')", + displayArgs: "('foo', 'bar')", controlled: false, adjoint: false, controls: [], @@ -593,7 +593,7 @@ describe("Testing _opToMetadata", () => { controlsY: [], targetsY: [startY, startY + registerHeight], label: "U", - argStr: "('foo', 'bar')", + displayArgs: "('foo', 'bar')", width: 77 }; expect(_opToMetadata(op, registers)).toEqual(metadata); @@ -601,7 +601,7 @@ describe("Testing _opToMetadata", () => { // Test long argument op = { gate: "U", - argStr: "(0.25, 1.0, 'foobar', (3.14, 6.67))", + displayArgs: "(0.25, 1.0, 'foobar', (3.14, 6.67))", controlled: false, adjoint: false, controls: [], @@ -616,7 +616,7 @@ describe("Testing _opToMetadata", () => { controlsY: [], targetsY: [startY, startY + registerHeight], label: "U", - argStr: "(0.25, 1.0, 'foobar', (3.14, 6.67))", + displayArgs: "(0.25, 1.0, 'foobar', (3.14, 6.67))", width: 188 }; expect(_opToMetadata(op, registers)).toEqual(metadata); @@ -624,7 +624,7 @@ describe("Testing _opToMetadata", () => { // Test controlled op = { gate: "U", - argStr: "('foo', 'bar')", + displayArgs: "('foo', 'bar')", controlled: true, adjoint: false, controls: [{ type: RegisterType.Qubit, qId: 1 }], @@ -639,7 +639,7 @@ describe("Testing _opToMetadata", () => { controlsY: [startY + registerHeight], targetsY: [startY, startY + registerHeight * 2], label: "U", - argStr: "('foo', 'bar')", + displayArgs: "('foo', 'bar')", width: 77 }; expect(_opToMetadata(op, registers)).toEqual(metadata); @@ -1192,7 +1192,7 @@ describe("Testing processOperations", () => { }, { gate: "RX", - argStr: "(0.25)", + displayArgs: "(0.25)", controlled: false, adjoint: false, controls: [], @@ -1234,7 +1234,7 @@ describe("Testing processOperations", () => { controlsY: [], targetsY: [startY + registerHeight], label: "RX", - argStr: "(0.25)", + displayArgs: "(0.25)", width: rxWidth, } ]; diff --git a/src/Kernel/client/__tests__/ExecutionPathVisualizerTests/utils.test.ts b/src/Kernel/client/__tests__/ExecutionPathVisualizerTests/utils.test.ts index 52c21c6f4c..6edea89911 100644 --- a/src/Kernel/client/__tests__/ExecutionPathVisualizerTests/utils.test.ts +++ b/src/Kernel/client/__tests__/ExecutionPathVisualizerTests/utils.test.ts @@ -27,7 +27,7 @@ describe("Testing getGateWidth", () => { expect(getGateWidth(Object.assign({ type: GateType.Unitary, label: 'zz' }))) .toEqual(minGateWidth)); test("unitary gate with arguments", () => - expect(getGateWidth(Object.assign({ type: GateType.Unitary, argStr: '(0.25)', label: 'RX' }))) + expect(getGateWidth(Object.assign({ type: GateType.Unitary, displayArgs: '(0.25)', label: 'RX' }))) .toEqual(52)); test("invalid", () => expect(getGateWidth(Object.assign({ type: GateType.Invalid, label: '' }))) diff --git a/src/Tests/ExecutionPathTracerTests.cs b/src/Tests/ExecutionPathTracerTests.cs index 8646d32279..75026bf93c 100644 --- a/src/Tests/ExecutionPathTracerTests.cs +++ b/src/Tests/ExecutionPathTracerTests.cs @@ -159,7 +159,7 @@ public void RxTest() new Operation() { Gate = "Rx", - ArgStr = "(2)", + DisplayArgs = "(2)", Targets = new List() { new QubitRegister(0) }, }, }; @@ -252,7 +252,7 @@ public void FooTest() new Operation() { Gate = "Foo", - ArgStr = "(2.1,(bar))", + DisplayArgs = "(2.1, (bar))", Targets = new List() { new QubitRegister(0) }, }, }; @@ -274,7 +274,7 @@ public void ControlledFooTest() new Operation() { Gate = "Foo", - ArgStr = "(2.1,(bar))", + DisplayArgs = "(2.1, (bar))", Controlled = true, Controls = new List() { new QubitRegister(0) }, Targets = new List() { new QubitRegister(1) }, @@ -365,7 +365,7 @@ public void PartialOpTest() new Operation() { Gate = "Ry", - ArgStr = "(2.5)", + DisplayArgs = "(2.5)", Targets = new List() { new QubitRegister(0) }, }, }; @@ -403,13 +403,13 @@ public void BigTest() new Operation() { Gate = "Ry", - ArgStr = "(2.5)", + DisplayArgs = "(2.5)", Targets = new List() { new QubitRegister(1) }, }, new Operation() { Gate = "Bar", - ArgStr = "((1,2.1),(foo))", + DisplayArgs = "((1, 2.1), (foo))", Targets = new List() { new QubitRegister(0) }, }, new Operation() @@ -434,7 +434,7 @@ public void BigTest() new Operation() { Gate = "Bar", - ArgStr = "((1,2.1),(foo))", + DisplayArgs = "((1, 2.1), (foo))", Controlled = true, Adjoint = true, Controls = new List() { new QubitRegister(2) }, From ef866f0d7be66350d269ae5e9f79ae3a2feb177a Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Wed, 15 Jul 2020 13:23:49 -0400 Subject: [PATCH 09/11] Clean up code --- src/Core/ExecutionPathTracer/ExecutionPath.cs | 8 ++-- .../ExecutionPathTracer.cs | 12 ++---- src/Core/ExecutionPathTracer/Extensions.cs | 41 ++----------------- src/Core/ExecutionPathTracer/Register.cs | 13 +++--- 4 files changed, 18 insertions(+), 56 deletions(-) diff --git a/src/Core/ExecutionPathTracer/ExecutionPath.cs b/src/Core/ExecutionPathTracer/ExecutionPath.cs index 0bca96de3f..1cc533db6d 100644 --- a/src/Core/ExecutionPathTracer/ExecutionPath.cs +++ b/src/Core/ExecutionPathTracer/ExecutionPath.cs @@ -33,13 +33,13 @@ public ExecutionPath(IEnumerable qubits, IEnumerable that represents the declared qubits used in the execution path. /// [JsonProperty("qubits")] - public IEnumerable Qubits { get; private set; } + public IEnumerable Qubits { get; } /// /// A list of that represents the operations used in the execution path. /// [JsonProperty("operations")] - public IEnumerable Operations { get; private set; } + public IEnumerable Operations { get; } /// /// Serializes into its JSON representation. @@ -81,13 +81,13 @@ public QubitDeclaration(int id, int numChildren = 0) /// Id of qubit. /// [JsonProperty("id")] - public int Id { get; private set; } + public int Id { get; } /// /// Number of associated classical registers. /// [JsonProperty("numChildren")] - public int NumChildren { get; private set; } + public int NumChildren { get; } /// /// Used by to determine if diff --git a/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs index a2fb4eb74c..3944f2c475 100644 --- a/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs +++ b/src/Core/ExecutionPathTracer/ExecutionPathTracer.cs @@ -24,16 +24,15 @@ public class ExecutionPathTracer private List operations = new List(); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class with the depth to render operations at. /// /// /// The depth at which to render operations. /// - public ExecutionPathTracer(int depth = 1) => - this.renderDepth = depth + 1; + public ExecutionPathTracer(int depth = 1) => this.renderDepth = depth + 1; /// - /// Returns the generated ExecutionPath. + /// Returns the generated . /// public ExecutionPath GetExecutionPath() => new ExecutionPath( @@ -69,10 +68,7 @@ public void OnOperationStartHandler(ICallable operation, IApplyData arguments) /// 's /// OnOperationEnd event. /// - public void OnOperationEndHandler(ICallable operation, IApplyData result) - { - this.currentDepth--; - } + public void OnOperationEndHandler(ICallable operation, IApplyData result) => this.currentDepth--; /// /// Retrieves the associated with the given or create a new diff --git a/src/Core/ExecutionPathTracer/Extensions.cs b/src/Core/ExecutionPathTracer/Extensions.cs index 73fcd7af08..f836620843 100644 --- a/src/Core/ExecutionPathTracer/Extensions.cs +++ b/src/Core/ExecutionPathTracer/Extensions.cs @@ -17,8 +17,8 @@ namespace Microsoft.Quantum.IQSharp.Core.ExecutionPathTracer public static class Extensions { /// - /// Attaches ExecutionPathTracer event listeners to the simulator to generate - /// the ExecutionPath of the operation performed by the simulator. + /// Attaches event listeners to the simulator to generate + /// the of the operation performed by the simulator. /// public static T WithExecutionPathTracer(this T sim, ExecutionPathTracer tracer) where T : SimulatorBase @@ -48,41 +48,6 @@ public static TValue GetOrCreate(this IDictionary di /// the key doesn't exist. /// public static TValue GetOrCreate(this IDictionary dict, TKey key) - where TValue : new() - { - return dict.GetOrCreate(key, new TValue()); - } - - /// - /// Given a , format its non-qubit arguments into a string. - /// Returns null if no arguments found. - /// - public static string? ArgsToString(this Type t, object args) - { - var argsStrings = t.GetFields() - .Select(f => - { - var argString = null as string; - - // If field is a tuple, recursively extract its inner arguments and format as a tuple string. - if (f.FieldType.IsTuple()) - { - var nestedArgs = f.GetValue(args); - if (nestedArgs != null) argString = f.FieldType.ArgsToString(nestedArgs); - } - // Add field as an argument if it is not a Qubit type - else if (!f.FieldType.IsQubitsContainer()) - { - argString = f.GetValue(args)?.ToString(); - } - - return argString; - }) - .WhereNotNull(); - - return argsStrings.Any() - ? $"({string.Join(",", argsStrings)})" - : null; - } + where TValue : new() => dict.GetOrCreate(key, new TValue()); } } diff --git a/src/Core/ExecutionPathTracer/Register.cs b/src/Core/ExecutionPathTracer/Register.cs index b04b98983c..a3560b73a3 100644 --- a/src/Core/ExecutionPathTracer/Register.cs +++ b/src/Core/ExecutionPathTracer/Register.cs @@ -9,7 +9,7 @@ namespace Microsoft.Quantum.IQSharp.Core.ExecutionPathTracer { /// - /// Enum for the 2 types of registers: Qubit and Classical. + /// Enum for the 2 types of registers: Qubit and Classical. /// public enum RegisterType { @@ -32,19 +32,19 @@ public class Register /// Type of register. /// [JsonProperty("type")] - public virtual RegisterType Type { get; set; } + public virtual RegisterType Type { get; } /// /// Qubit id of register. /// [JsonProperty("qId")] - public virtual int QId { get; set; } + public virtual int QId { get; protected set; } /// /// Classical bit id of register. null if register is a qubit register. /// [JsonProperty("cId")] - public virtual int? CId { get; set; } + public virtual int? CId { get; protected set; } } /// @@ -58,9 +58,9 @@ public class QubitRegister : Register /// /// Id of qubit register. /// - public QubitRegister(int qId) => - this.QId = qId; + public QubitRegister(int qId) => this.QId = qId; + /// public override RegisterType Type => RegisterType.Qubit; } @@ -84,6 +84,7 @@ public ClassicalRegister(int qId, int cId) this.CId = cId; } + /// public override RegisterType Type => RegisterType.Classical; } } From bc78e63912ece6cc41f5458594088e91c2792052 Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Wed, 15 Jul 2020 14:49:37 -0400 Subject: [PATCH 10/11] Fix typo in tsconfig --- src/Kernel/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kernel/tsconfig.json b/src/Kernel/tsconfig.json index 2d8de5d0dc..1a6932e15a 100644 --- a/src/Kernel/tsconfig.json +++ b/src/Kernel/tsconfig.json @@ -12,6 +12,6 @@ "module": "AMD", "moduleResolution": "node" }, - "exclude": ["node_modules", "wwwroot", "client/__test__"], + "exclude": ["node_modules", "wwwroot", "client/__tests__"], "include": ["client/**/*"] } From 5096ba385ae1434141a576de8a64fa59445d5156 Mon Sep 17 00:00:00 2001 From: Raphael Koh Date: Thu, 16 Jul 2020 13:56:24 -0400 Subject: [PATCH 11/11] Fix tests --- src/Tests/ExecutionPathTracerTests.cs | 113 ++++++++++++++---- src/Tests/Tests.IQsharp.csproj | 6 + .../Intrinsic.qs | 3 +- 3 files changed, 98 insertions(+), 24 deletions(-) diff --git a/src/Tests/ExecutionPathTracerTests.cs b/src/Tests/ExecutionPathTracerTests.cs index 75026bf93c..fea5a8c3c4 100644 --- a/src/Tests/ExecutionPathTracerTests.cs +++ b/src/Tests/ExecutionPathTracerTests.cs @@ -24,9 +24,7 @@ public Workspace InitWorkspace() public ExecutionPath GetExecutionPath(string name, int depth = 1) { var ws = InitWorkspace(); - var ops = ws.AssemblyInfo.Operations; - var names = ws.AssemblyInfo.Operations.Select(o => o.FullName); - var op = ws.AssemblyInfo.Operations.Single(o => o.FullName == name); + var op = ws.AssemblyInfo.Operations.SingleOrDefault(o => o.FullName == $"Tests.ExecutionPathTracer.{name}"); Assert.IsNotNull(op); var tracer = new ExecutionPathTracer(depth); @@ -52,9 +50,15 @@ public void HTest() Gate = "H", Targets = new List() { new QubitRegister(0) }, }, + // TODO: Remove Reset/ResetAll gates once we don't need to zero out qubits + new Operation() + { + Gate = "Reset", + Targets = new List() { new QubitRegister(0) }, + }, }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); } [TestMethod] @@ -75,7 +79,7 @@ public void MTest() }, }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); } [TestMethod] @@ -96,9 +100,14 @@ public void CnotTest() Controls = new List() { new QubitRegister(0) }, Targets = new List() { new QubitRegister(1) }, }, + new Operation() + { + Gate = "ResetAll", + Targets = new List() { new QubitRegister(0), new QubitRegister(1) }, + }, }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); } [TestMethod] @@ -120,9 +129,19 @@ public void CcnotTest() Controls = new List() { new QubitRegister(0), new QubitRegister(2) }, Targets = new List() { new QubitRegister(1) }, }, + new Operation() + { + Gate = "ResetAll", + Targets = new List() + { + new QubitRegister(0), + new QubitRegister(1), + new QubitRegister(2), + }, + }, }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); } [TestMethod] @@ -143,7 +162,7 @@ public void SwapTest() }, }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); } [TestMethod] @@ -162,9 +181,14 @@ public void RxTest() DisplayArgs = "(2)", Targets = new List() { new QubitRegister(0) }, }, + new Operation() + { + Gate = "Reset", + Targets = new List() { new QubitRegister(0) }, + }, }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); } [TestMethod] @@ -183,9 +207,14 @@ public void AdjointHTest() Adjoint = true, Targets = new List() { new QubitRegister(0) }, }, + new Operation() + { + Gate = "Reset", + Targets = new List() { new QubitRegister(0) }, + }, }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); } [TestMethod] @@ -206,9 +235,14 @@ public void ControlledXTest() Controls = new List() { new QubitRegister(0) }, Targets = new List() { new QubitRegister(1) }, }, + new Operation() + { + Gate = "ResetAll", + Targets = new List() { new QubitRegister(0), new QubitRegister(1) }, + }, }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); // JSON should be the same as CNOT's var path2 = GetExecutionPath("CnotCirc"); @@ -234,9 +268,14 @@ public void ControlledAdjointSTest() Controls = new List() { new QubitRegister(0) }, Targets = new List() { new QubitRegister(1) }, }, + new Operation() + { + Gate = "ResetAll", + Targets = new List() { new QubitRegister(0), new QubitRegister(1) }, + }, }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); } [TestMethod] @@ -252,12 +291,12 @@ public void FooTest() new Operation() { Gate = "Foo", - DisplayArgs = "(2.1, (bar))", + DisplayArgs = "(2.1, (\"bar\"))", Targets = new List() { new QubitRegister(0) }, }, }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); } [TestMethod] @@ -274,14 +313,14 @@ public void ControlledFooTest() new Operation() { Gate = "Foo", - DisplayArgs = "(2.1, (bar))", + DisplayArgs = "(2.1, (\"bar\"))", Controlled = true, Controls = new List() { new QubitRegister(0) }, Targets = new List() { new QubitRegister(1) }, }, }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); } [TestMethod] @@ -302,9 +341,19 @@ public void UnusedQubitTest() Controls = new List() { new QubitRegister(2) }, Targets = new List() { new QubitRegister(0) }, }, + new Operation() + { + Gate = "Reset", + Targets = new List() { new QubitRegister(0) }, + }, + new Operation() + { + Gate = "Reset", + Targets = new List() { new QubitRegister(2) }, + }, }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); } [TestMethod] @@ -340,7 +389,7 @@ public void Depth2Test() }, }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); } [TestMethod] @@ -368,9 +417,18 @@ public void PartialOpTest() DisplayArgs = "(2.5)", Targets = new List() { new QubitRegister(0) }, }, + new Operation() + { + Gate = "ResetAll", + Targets = new List() { + new QubitRegister(0), + new QubitRegister(1), + new QubitRegister(2), + }, + }, }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); } [TestMethod] @@ -380,7 +438,7 @@ public void EmptyTest() var qubits = new QubitDeclaration[] { }; var operations = new Operation[] { }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); } [TestMethod] @@ -409,7 +467,7 @@ public void BigTest() new Operation() { Gate = "Bar", - DisplayArgs = "((1, 2.1), (foo))", + DisplayArgs = "((1, 2.1), (\"foo\"))", Targets = new List() { new QubitRegister(0) }, }, new Operation() @@ -434,7 +492,7 @@ public void BigTest() new Operation() { Gate = "Bar", - DisplayArgs = "((1, 2.1), (foo))", + DisplayArgs = "((1, 2.1), (\"foo\"))", Controlled = true, Adjoint = true, Controls = new List() { new QubitRegister(2) }, @@ -446,9 +504,18 @@ public void BigTest() Controls = new List() { new QubitRegister(0) }, Targets = new List() { new ClassicalRegister(0, 0) }, }, + new Operation() + { + Gate = "ResetAll", + Targets = new List() { + new QubitRegister(0), + new QubitRegister(1), + new QubitRegister(2), + }, + }, }; var expected = new ExecutionPath(qubits, operations); - Assert.AreEqual(path.ToJson(), expected.ToJson()); + Assert.AreEqual(expected.ToJson(), path.ToJson()); } } } diff --git a/src/Tests/Tests.IQsharp.csproj b/src/Tests/Tests.IQsharp.csproj index 6b8f79b98e..90536f1c09 100644 --- a/src/Tests/Tests.IQsharp.csproj +++ b/src/Tests/Tests.IQsharp.csproj @@ -48,6 +48,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + diff --git a/src/Tests/Workspace.ExecutionPathTracer/Intrinsic.qs b/src/Tests/Workspace.ExecutionPathTracer/Intrinsic.qs index 884688ee05..3811173584 100644 --- a/src/Tests/Workspace.ExecutionPathTracer/Intrinsic.qs +++ b/src/Tests/Workspace.ExecutionPathTracer/Intrinsic.qs @@ -86,7 +86,8 @@ namespace Tests.ExecutionPathTracer { operation UnusedQubitCirc() : Unit { using (qs = Qubit[3]) { CNOT(qs[2], qs[0]); - ResetAll(qs); + Reset(qs[0]); + Reset(qs[2]); } }