diff --git a/src/Simulation/Core/AbstractCallable.cs b/src/Simulation/Core/AbstractCallable.cs index dd11d22d7cd..8fc7c712603 100644 --- a/src/Simulation/Core/AbstractCallable.cs +++ b/src/Simulation/Core/AbstractCallable.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#nullable enable + using System.Collections.Generic; namespace Microsoft.Quantum.Simulation.Core @@ -29,6 +31,12 @@ public AbstractCallable(IOperationFactory m) /// public abstract void Init(); + /// + /// Retrieves the runtime metadata of the Operation. If the Operation has no associated + /// runtime metadata, returns null. + /// + public virtual RuntimeMetadata? GetRuntimeMetadata(IApplyData args) => null; + object IApplyData.Value => null; IEnumerable IApplyData.Qubits => null; diff --git a/src/Simulation/Core/Generics/GenericCallable.cs b/src/Simulation/Core/Generics/GenericCallable.cs index e0668b3723f..cdd4ac3fce4 100644 --- a/src/Simulation/Core/Generics/GenericCallable.cs +++ b/src/Simulation/Core/Generics/GenericCallable.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#nullable enable + using System; using System.Collections.Concurrent; using System.Diagnostics; @@ -24,6 +26,8 @@ public partial interface ICallable : IApplyData O Apply(object args); ICallable Partial(object partialTuple); + + RuntimeMetadata? GetRuntimeMetadata(IApplyData args); } /// diff --git a/src/Simulation/Core/Operations/Adjoint.cs b/src/Simulation/Core/Operations/Adjoint.cs index 6db9f4a9f81..8e0efe35ad2 100644 --- a/src/Simulation/Core/Operations/Adjoint.cs +++ b/src/Simulation/Core/Operations/Adjoint.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#nullable enable + using System; using System.Collections.Generic; using System.Diagnostics; @@ -72,6 +74,15 @@ public override void Init() { } public override IApplyData __dataOut(QVoid data) => data; + /// + public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) + { + var baseMetadata = this.BaseOp.GetRuntimeMetadata(args); + if (baseMetadata == null) return null; + baseMetadata.IsAdjoint = !baseMetadata.IsAdjoint; + return baseMetadata; + } + public override string ToString() => $"(Adjoint {BaseOp?.ToString() ?? "" })"; public override string __qsharpType() => this.BaseOp?.__qsharpType(); diff --git a/src/Simulation/Core/Operations/Controlled.cs b/src/Simulation/Core/Operations/Controlled.cs index 8df3a8eb579..770a9081f6f 100644 --- a/src/Simulation/Core/Operations/Controlled.cs +++ b/src/Simulation/Core/Operations/Controlled.cs @@ -1,9 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#nullable enable + using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; namespace Microsoft.Quantum.Simulation.Core @@ -74,7 +77,7 @@ public override void Init() { } string ICallable.FullName => ((ICallable)this.BaseOp).FullName; OperationFunctor ICallable.Variant => ((ICallable)this.BaseOp).ControlledVariant(); - public override Func<(IQArray, I), QVoid> Body => this.BaseOp.ControlledBody; + public override Func<(IQArray, I), QVoid> Body => this.BaseOp.ControlledBody; public override Func<(IQArray, I), QVoid> AdjointBody => this.BaseOp.ControlledAdjointBody; @@ -110,6 +113,24 @@ public override void Init() { } public override IApplyData __dataOut(QVoid data) => data; + /// + public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) + { + Debug.Assert(args.Value is ValueTuple, I>, $"Failed to retrieve runtime metadata for {this.ToString()}."); + + if (args.Value is ValueTuple, I> ctrlArgs) + { + var (controls, baseArgs) = ctrlArgs; + var baseMetadata = this.BaseOp.GetRuntimeMetadata(this.BaseOp.__dataIn(baseArgs)); + if (baseMetadata == null) return null; + baseMetadata.IsControlled = true; + baseMetadata.Controls = controls.Concat(baseMetadata.Controls); + return baseMetadata; + } + + return null; + } + public override string ToString() => $"(Controlled {BaseOp?.ToString() ?? "" })"; public override string __qsharpType() => GenericControlled.AddControlQubitsToSignature(this.BaseOp?.__qsharpType()); diff --git a/src/Simulation/Core/Operations/Operation.cs b/src/Simulation/Core/Operations/Operation.cs index e81248f2528..1dd146c4f22 100644 --- a/src/Simulation/Core/Operations/Operation.cs +++ b/src/Simulation/Core/Operations/Operation.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#nullable enable + using System; using System.Collections.Generic; using System.Diagnostics; @@ -75,6 +77,14 @@ public Operation(IOperationFactory m) : base(m) [DebuggerBrowsable(DebuggerBrowsableState.Never)] public ControlledOperation Controlled => _controlled.Value; + /// + public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) => + new RuntimeMetadata() + { + Label = ((ICallable)this).Name, + FormattedNonQubitArgs = args.GetNonQubitArgumentsAsString() ?? "", + Targets = args.GetQubits(), + }; public O Apply(I a) { diff --git a/src/Simulation/Core/Operations/OperationPartial.cs b/src/Simulation/Core/Operations/OperationPartial.cs index 12e03f01913..5a68394d3eb 100644 --- a/src/Simulation/Core/Operations/OperationPartial.cs +++ b/src/Simulation/Core/Operations/OperationPartial.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#nullable enable + using System; using System.Collections.Generic; using System.Diagnostics; @@ -141,6 +143,10 @@ ICallable ICallable.Partial(Func mapper) IUnitary IUnitary

.Partial(Func mapper) => new OperationPartial(this, mapper); + /// + public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) => + this.BaseOp.GetRuntimeMetadata(args); + public override string ToString() => $"{this.BaseOp}{{_}}"; public override string __qsharpType() { diff --git a/src/Simulation/Core/RuntimeMetadata.cs b/src/Simulation/Core/RuntimeMetadata.cs new file mode 100644 index 00000000000..9054ce12cc2 --- /dev/null +++ b/src/Simulation/Core/RuntimeMetadata.cs @@ -0,0 +1,137 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Quantum.Simulation.Core +{ + ///

+ /// Contains the metadata associated with an operation's runtime execution path. + /// + public class RuntimeMetadata + { + /// + /// Label of gate. + /// + public string Label { get; set; } = ""; + + /// + /// Non-qubit arguments provided to gate, formatted as string. + /// + public string FormattedNonQubitArgs { get; set; } = ""; + + /// + /// True if operation is an adjoint operation. + /// + public bool IsAdjoint { get; set; } + + /// + /// True if operation is a controlled operation. + /// + public bool IsControlled { get; set; } + + /// + /// True if operation is a measurement operation. + /// + public bool IsMeasurement { get; set; } + + /// + /// True if operation is composed of multiple operations. + /// + ///
+ /// + /// Currently not used as this is intended for composite operations, + /// such as ApplyToEach. + /// + public bool IsComposite { get; set; } + + /// + /// Group of operations for each classical branch (true and false). + /// + /// + /// Currently not used as this is intended for classically-controlled operations. + /// + public IEnumerable>? Children { get; set; } + + /// + /// List of control registers. + /// + public IEnumerable Controls { get; set; } = new List(); + + /// + /// List of target registers. + /// + public IEnumerable Targets { get; set; } = new List(); + + private static bool OnlyOneNull(object? a, object? b) => + (a == null && b != null) || (b == null && a != null); + + private static bool IsBothNull(object? a, object? b) => + a == null && b == null; + + private static bool ListEquals(IEnumerable a, IEnumerable b) => + IsBothNull(a, b) || (!OnlyOneNull(a, b) && a.SequenceEqual(b)); + + public override bool Equals(object? obj) + { + var other = obj as RuntimeMetadata; + + if (other is null) return false; + + if (this.Label != other.Label || this.FormattedNonQubitArgs != other.FormattedNonQubitArgs || + this.IsAdjoint != other.IsAdjoint || this.IsControlled != other.IsControlled || + this.IsMeasurement != other.IsMeasurement || this.IsComposite != other.IsComposite) + return false; + + if (!ListEquals(this.Controls, other.Controls)) return false; + + if (!ListEquals(this.Targets, other.Targets)) return false; + + // If only one children is null, return false + if (OnlyOneNull(this.Children, other.Children)) return false; + + // If both children are not null, compare each child element-wise and return + // false if any of them are not equal + if (!IsBothNull(this.Children, other.Children)) + { + if (this.Children.Count() != other.Children.Count() || + this.Children.Zip(other.Children, ListEquals).Contains(false)) + return false; + } + + return true; + } + + public override int GetHashCode() + { + // Stringify qubits, concatenate as string, and get resulting hashcode + var controlsHash = string.Join(",", this.Controls.Select(q => q?.ToString() ?? "")).GetHashCode(); + var targetsHash = string.Join(",", this.Targets.Select(q => q?.ToString() ?? "")).GetHashCode(); + + // Recursively get hashcode of inner `RuntimeMetadata` objects, concatenate into a string, + // and get resulting hashcode + var childrenHash = (this.Children != null) + ? string.Join(", ", this.Children.Select(child => (child != null) + ? string.Join(",", child.Select(m => m?.GetHashCode().ToString() ?? "0")) + : "0" + )).GetHashCode() + : 0; + + // Combine all other properties and get the resulting hashcode + var otherHash = HashCode.Combine(this.Label, this.FormattedNonQubitArgs, this.IsAdjoint, this.IsControlled, + this.IsMeasurement, this.IsComposite); + + // Combine them all together to get the final hashcode + return HashCode.Combine(controlsHash, targetsHash, childrenHash, otherHash); + } + + public static bool operator ==(RuntimeMetadata? x, RuntimeMetadata? y) => + IsBothNull(x, y) || (x?.Equals(y) ?? false); + + public static bool operator !=(RuntimeMetadata? x, RuntimeMetadata? y) => !(x == y); + } +} diff --git a/src/Simulation/Core/TypeExtensions.cs b/src/Simulation/Core/TypeExtensions.cs index 94bc5ebabf6..d710f1c8617 100644 --- a/src/Simulation/Core/TypeExtensions.cs +++ b/src/Simulation/Core/TypeExtensions.cs @@ -1,7 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#nullable enable + using System; +using System.Collections; using System.Collections.Concurrent; using System.Diagnostics; using System.Linq; @@ -144,6 +147,55 @@ public static Type[] GetTupleFieldTypes(this Type arg) } } + /// + /// Given an , retrieve its non-qubit fields as a string. + /// Returns null if no non-qubit fields found. + /// + public static string? GetNonQubitArgumentsAsString(this object o) + { + var t = o.GetType(); + + // If object is a Qubit, ignore it (i.e. return null) + if (o is Qubit) return null; + + // If object is an IApplyData, recursively extract nested fields + // and stringify them + if (o is IApplyData data) + { + var argsString = data.Value.GetNonQubitArgumentsAsString(); + return argsString.Any() ? argsString : null; + } + + // If object is a string, enclose it in quotations + if (o is string s) + { + return (s != null) ? $"\"{s}\"" : null; + } + + // If object is a list, recursively extract its inner arguments and + // concatenate them into a list string + if (typeof(IEnumerable).IsAssignableFrom(t)) + { + var elements = ((IEnumerable)o).Cast() + .Select(x => x.GetNonQubitArgumentsAsString()) + .WhereNotNull(); + return (elements.Any()) ? $"[{string.Join(", ", elements)}]" : null; + } + + // If object is a tuple, recursively extract its inner arguments and + // concatenate them into a tuple string + if (t.IsTuple()) + { + var items = t.GetFields() + .Select(f => f.GetValue(o).GetNonQubitArgumentsAsString()) + .WhereNotNull(); + return (items.Any()) ? $"({string.Join(", ", items)})" : null; + } + + // Otherwise, return argument as a string + return (o != null) ? o.ToString() : null; + } + private static ConcurrentDictionary _normalTypesCache = new ConcurrentDictionary(); /// @@ -259,7 +311,7 @@ public static string OperationVariants(this Type t, object op) return OperationVariants(generic.OperationType, op); } - return OperationVariants(t.BaseType, op); + return OperationVariants(t.BaseType, op); } public static bool TryQSharpOperationType(Type t, out string typeName) diff --git a/src/Simulation/Core/Udts/UDTPartial.cs b/src/Simulation/Core/Udts/UDTPartial.cs index 8d586d4ee80..a97a92f870f 100644 --- a/src/Simulation/Core/Udts/UDTPartial.cs +++ b/src/Simulation/Core/Udts/UDTPartial.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#nullable enable + using System; using System.Collections.Generic; using System.Diagnostics; @@ -62,6 +64,18 @@ public ICallable Partial(object partialTuple) return (ICallable)Activator.CreateInstance(partialType, new object[] { this, partialTuple }); } + /// + public RuntimeMetadata? GetRuntimeMetadata(IApplyData args) + { + Debug.Assert(args.Value is P, $"Failed to retrieve runtime metadata for {typeof(U).Name}."); + var baseArgs = this.Apply((P) args.Value); + return new RuntimeMetadata() + { + Label = typeof(U).Name, + FormattedNonQubitArgs = baseArgs?.GetNonQubitArgumentsAsString() ?? "", + }; + } + internal class DebuggerProxy { private readonly UDTPartial u; diff --git a/src/Simulation/QsharpCore/Intrinsic.cs b/src/Simulation/QsharpCore/Intrinsic.cs new file mode 100644 index 00000000000..76976a0f217 --- /dev/null +++ b/src/Simulation/QsharpCore/Intrinsic.cs @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Intrinsic +{ + public partial class CNOT + { + /// + public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) + { + Debug.Assert(args.Value is ValueTuple, $"Failed to retrieve runtime metadata for {this.ToString()}."); + + if (args.Value is ValueTuple cnotArgs) + { + var (ctrl, target) = cnotArgs; + return new RuntimeMetadata() + { + Label = "X", + IsControlled = true, + Controls = new List() { ctrl }, + Targets = new List() { target }, + }; + } + + return null; + } + } + + public partial class CCNOT + { + /// + public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) + { + Debug.Assert(args.Value is ValueTuple, $"Failed to retrieve runtime metadata for {this.ToString()}."); + + if (args.Value is ValueTuple ccnotArgs) + { + var (ctrl1, ctrl2, target) = ccnotArgs; + return new RuntimeMetadata() + { + Label = "X", + IsControlled = true, + Controls = new List() { ctrl1, ctrl2 }, + Targets = new List() { target }, + }; + } + + return null; + } + } + + public partial class M + { + /// + public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) + { + Debug.Assert(args.Value is Qubit, $"Failed to retrieve runtime metadata for {this.ToString()}."); + + if (args.Value is Qubit target) + { + return new RuntimeMetadata() + { + Label = ((ICallable)this).Name, + IsMeasurement = true, + Targets = new List() { target }, + }; + } + + return null; + } + } +} diff --git a/src/Simulation/QsharpCore/Measurement.cs b/src/Simulation/QsharpCore/Measurement.cs new file mode 100644 index 00000000000..62cf6775fe1 --- /dev/null +++ b/src/Simulation/QsharpCore/Measurement.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable + +using System.Collections.Generic; +using Microsoft.Quantum.Simulation.Core; + +namespace Microsoft.Quantum.Measurement +{ + public partial class MResetX + { + /// + public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) + { + var targets = new List(); + var target = args.Value as Qubit; + if (target != null) targets.Add(target); + + return new RuntimeMetadata() + { + Label = ((ICallable)this).Name, + IsMeasurement = true, + Targets = targets, + }; + } + } + + public partial class MResetY + { + /// + public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) + { + var targets = new List(); + var target = args.Value as Qubit; + if (target != null) targets.Add(target); + + return new RuntimeMetadata() + { + Label = ((ICallable)this).Name, + IsMeasurement = true, + Targets = targets, + }; + } + } + + public partial class MResetZ + { + /// + public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) + { + var targets = new List(); + var target = args.Value as Qubit; + if (target != null) targets.Add(target); + + return new RuntimeMetadata() + { + Label = ((ICallable)this).Name, + IsMeasurement = true, + Targets = targets, + }; + } + } +} diff --git a/src/Simulation/Simulators.Tests/Circuits/RuntimeMetadataTest.qs b/src/Simulation/Simulators.Tests/Circuits/RuntimeMetadataTest.qs new file mode 100644 index 00000000000..dcb7df85c86 --- /dev/null +++ b/src/Simulation/Simulators.Tests/Circuits/RuntimeMetadataTest.qs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits { + + newtype FooUDT = (String, (Qubit, Double)); + + operation FooUDTOp (foo : FooUDT) : Unit is Ctl + Adj { } + +} diff --git a/src/Simulation/Simulators.Tests/RuntimeMetadataTests.cs b/src/Simulation/Simulators.Tests/RuntimeMetadataTests.cs new file mode 100644 index 00000000000..5e551565841 --- /dev/null +++ b/src/Simulation/Simulators.Tests/RuntimeMetadataTests.cs @@ -0,0 +1,641 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Quantum.Simulation.Core; +using Xunit; + +namespace Microsoft.Quantum.Simulation.Simulators.Tests +{ + public class RuntimeMetadataEqualityTests + { + [Fact] + public void WrongType() + { + var a = new RuntimeMetadata { }; + var i = 5; + Assert.False(a.Equals(i)); + } + + [Fact] + public void NullEquality() + { + var a = new RuntimeMetadata { }; + RuntimeMetadata? b = null; + Assert.NotEqual(a, b); + Assert.NotEqual(b, a); + } + + [Fact] + public void CheckEquality() + { + var a = new RuntimeMetadata() + { + Label = "H", + FormattedNonQubitArgs = "", + IsAdjoint = false, + IsControlled = false, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = new List() { }, + Targets = new List() { }, + }; + var b = new RuntimeMetadata() + { + Label = "H", + FormattedNonQubitArgs = "", + IsAdjoint = false, + IsControlled = false, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = new List() { }, + Targets = new List() { }, + }; + Assert.Equal(a, b); + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + + b.Label = "X"; + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + b.Label = "H"; + + b.FormattedNonQubitArgs = "(1)"; + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + b.FormattedNonQubitArgs = ""; + + b.IsAdjoint = true; + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + b.IsAdjoint = false; + + b.IsControlled = true; + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + b.IsControlled = false; + + b.IsMeasurement = true; + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + b.IsMeasurement = false; + + b.IsComposite = true; + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + b.IsComposite = false; + } + + [Fact] + public void ControlsEquality() + { + var a = new RuntimeMetadata() + { + Controls = new List() { }, + }; + var b = new RuntimeMetadata() + { + Controls = new List() { }, + }; + Assert.Equal(a, b); + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + + b.Controls = new List() { new FreeQubit(1) }; + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + + a.Controls = new List() { new FreeQubit(1) }; + Assert.Equal(a, b); + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + } + + [Fact] + public void TargetsEquality() + { + var a = new RuntimeMetadata() + { + Targets = new List() { }, + }; + var b = new RuntimeMetadata() + { + Targets = new List() { }, + }; + Assert.Equal(a, b); + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + + b.Targets = new List() { new FreeQubit(1) }; + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + + a.Targets = new List() { new FreeQubit(1) }; + Assert.Equal(a, b); + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + } + + [Fact] + public void ChildrenEquality() + { + var a = new RuntimeMetadata() + { + Children = new[] + { + new List(), + new List(), + }, + }; + var b = new RuntimeMetadata() + { + Children = new[] + { + new List(), + new List(), + }, + }; + Assert.Equal(a, b); + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + + var aChildren = a.Children.ToList(); + aChildren[0] = new List() { new RuntimeMetadata() { Label = "H" } }; + a.Children = aChildren; + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + + var bChildren = b.Children.ToList(); + bChildren[0] = new List() { new RuntimeMetadata() { Label = "X" } }; + b.Children = bChildren; + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + + bChildren[0] = new List() { new RuntimeMetadata() { Label = "H" } }; + Assert.Equal(a, b); + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + + b.Children = b.Children.SkipLast(1); + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + } + } + + public class IntrinsicTests + { + [Fact] + public void CNOT() + { + var control = new FreeQubit(1); + var target = new FreeQubit(0); + var op = new QuantumSimulator().Get(); + var args = op.__dataIn((control, target)); + var expected = new RuntimeMetadata() + { + Label = "X", + FormattedNonQubitArgs = "", + IsAdjoint = false, + IsControlled = true, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = new List() { control }, + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void CCNOT() + { + var control1 = new FreeQubit(0); + var control2 = new FreeQubit(2); + var target = new FreeQubit(1); + var op = new QuantumSimulator().Get(); + var args = op.__dataIn((control1, control2, target)); + var expected = new RuntimeMetadata() + { + Label = "X", + FormattedNonQubitArgs = "", + IsAdjoint = false, + IsControlled = true, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = new List() { control1, control2 }, + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void Ry() + { + var target = new FreeQubit(0); + var op = new QuantumSimulator().Get(); + var args = op.__dataIn((2.1, target)); + var expected = new RuntimeMetadata() + { + Label = "Ry", + FormattedNonQubitArgs = "(2.1)", + IsAdjoint = false, + IsControlled = false, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = new List() { }, + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void M() + { + var measureQubit = new FreeQubit(0); + var op = new QuantumSimulator().Get(); + var args = op.__dataIn(measureQubit); + var expected = new RuntimeMetadata() + { + Label = "M", + FormattedNonQubitArgs = "", + IsAdjoint = false, + IsControlled = false, + IsMeasurement = true, + IsComposite = false, + Children = null, + Controls = new List() { }, + Targets = new List() { measureQubit }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + } + + public class MeasurementTests + { + [Fact] + public void MResetX() + { + var measureQubit = new FreeQubit(0); + var op = new QuantumSimulator().Get(); + var args = op.__dataIn(measureQubit); + var expected = new RuntimeMetadata() + { + Label = "MResetX", + FormattedNonQubitArgs = "", + IsAdjoint = false, + IsControlled = false, + IsMeasurement = true, + IsComposite = false, + Children = null, + Controls = new List() { }, + Targets = new List() { measureQubit }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void MResetY() + { + var measureQubit = new FreeQubit(0); + var op = new QuantumSimulator().Get(); + var args = op.__dataIn(measureQubit); + var expected = new RuntimeMetadata() + { + Label = "MResetY", + FormattedNonQubitArgs = "", + IsAdjoint = false, + IsControlled = false, + IsMeasurement = true, + IsComposite = false, + Children = null, + Controls = new List() { }, + Targets = new List() { measureQubit }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void MResetZ() + { + var measureQubit = new FreeQubit(0); + var op = new QuantumSimulator().Get(); + var args = op.__dataIn(measureQubit); + var expected = new RuntimeMetadata() + { + Label = "MResetZ", + FormattedNonQubitArgs = "", + IsAdjoint = false, + IsControlled = false, + IsMeasurement = true, + IsComposite = false, + Children = null, + Controls = new List() { }, + Targets = new List() { measureQubit }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + } + + public class UDTTests + { + [Fact] + public void FooUDTOp() + { + Qubit target = new FreeQubit(0); + var op = new QuantumSimulator().Get(); + var args = op.__dataIn(new Circuits.FooUDT(("bar", (target, 2.1)))); + var expected = new RuntimeMetadata() + { + Label = "FooUDTOp", + FormattedNonQubitArgs = "(\"bar\", (2.1))", + IsAdjoint = false, + IsControlled = false, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = new List() { }, + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + } + + public class ControlledOpTests + { + [Fact] + public void ControlledH() + { + IQArray controls = new QArray(new[] { new FreeQubit(0) }); + Qubit target = new FreeQubit(1); + var op = new QuantumSimulator().Get().Controlled; + var args = op.__dataIn((controls, target)); + var expected = new RuntimeMetadata() + { + Label = "H", + FormattedNonQubitArgs = "", + IsAdjoint = false, + IsControlled = true, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = controls, + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void ControlledX() + { + IQArray controls = new QArray(new[] { new FreeQubit(0) }); + Qubit target = new FreeQubit(1); + var op = new QuantumSimulator().Get().Controlled; + var args = op.__dataIn((controls, target)); + var expected = new RuntimeMetadata() + { + Label = "X", + FormattedNonQubitArgs = "", + IsAdjoint = false, + IsControlled = true, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = controls, + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void ControlledCNOT() + { + IQArray controls = new QArray(new[] { new FreeQubit(0) }); + Qubit control = new FreeQubit(1); + Qubit target = new FreeQubit(2); + var op = new QuantumSimulator().Get().Controlled; + var args = op.__dataIn((controls, (control, target))); + var expected = new RuntimeMetadata() + { + Label = "X", + FormattedNonQubitArgs = "", + IsAdjoint = false, + IsControlled = true, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = controls.Append(control), + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void ControlledCCNOT() + { + Qubit control1 = new FreeQubit(0); + Qubit control2 = new FreeQubit(1); + Qubit control3 = new FreeQubit(2); + Qubit target = new FreeQubit(3); + IQArray controls = new QArray(new[] { control1 }); + var op = new QuantumSimulator().Get().Controlled; + var args = op.__dataIn((controls, (control2, control3, target))); + var expected = new RuntimeMetadata() + { + Label = "X", + FormattedNonQubitArgs = "", + IsAdjoint = false, + IsControlled = true, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = new List() { control1, control2, control3 }, + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + } + + public class AdjointTests + { + [Fact] + public void AdjointH() + { + Qubit target = new FreeQubit(0); + var op = new QuantumSimulator().Get().Adjoint; + var args = op.__dataIn(target); + var expected = new RuntimeMetadata() + { + Label = "H", + FormattedNonQubitArgs = "", + IsAdjoint = true, + IsControlled = false, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = new List() { }, + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void AdjointX() + { + Qubit target = new FreeQubit(0); + var op = new QuantumSimulator().Get().Adjoint; + var args = op.__dataIn(target); + var expected = new RuntimeMetadata() + { + Label = "X", + FormattedNonQubitArgs = "", + IsAdjoint = true, + IsControlled = false, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = new List() { }, + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void AdjointAdjointH() + { + Qubit target = new FreeQubit(0); + var op = new QuantumSimulator().Get().Adjoint.Adjoint; + var args = op.__dataIn(target); + var expected = new RuntimeMetadata() + { + Label = "H", + FormattedNonQubitArgs = "", + IsAdjoint = false, + IsControlled = false, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = new List() { }, + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void ControlledAdjointH() + { + IQArray controls = new QArray(new[] { new FreeQubit(0) }); + Qubit target = new FreeQubit(1); + var op1 = new QuantumSimulator().Get().Controlled.Adjoint; + var op2 = new QuantumSimulator().Get().Adjoint.Controlled; + var args = op1.__dataIn((controls, target)); + var expected = new RuntimeMetadata() + { + Label = "H", + FormattedNonQubitArgs = "", + IsAdjoint = true, + IsControlled = true, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = controls, + Targets = new List() { target }, + }; + + Assert.Equal(op1.GetRuntimeMetadata(args), expected); + Assert.Equal(op2.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void ControlledAdjointAdjointH() + { + IQArray controls = new QArray(new[] { new FreeQubit(0) }); + Qubit target = new FreeQubit(1); + var op1 = new QuantumSimulator().Get().Controlled.Adjoint.Adjoint; + var op2 = new QuantumSimulator().Get().Adjoint.Controlled.Adjoint; + var op3 = new QuantumSimulator().Get().Adjoint.Adjoint.Controlled; + var args = op1.__dataIn((controls, target)); + var expected = new RuntimeMetadata() + { + Label = "H", + FormattedNonQubitArgs = "", + IsAdjoint = false, + IsControlled = true, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = controls, + Targets = new List() { target }, + }; + + Assert.Equal(op1.GetRuntimeMetadata(args), expected); + Assert.Equal(op2.GetRuntimeMetadata(args), expected); + Assert.Equal(op3.GetRuntimeMetadata(args), expected); + } + } + + public class PartialOpTests + { + + [Fact] + public void PartialRy() + { + var target = new FreeQubit(0); + var op = new QuantumSimulator().Get().Partial((double d) => + new ValueTuple(d, target)); + var args = op.__dataIn(2.1); + var expected = new RuntimeMetadata() + { + Label = "Ry", + FormattedNonQubitArgs = "(2.1)", + IsAdjoint = false, + IsControlled = false, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = new List() { }, + Targets = new List() { target }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + + [Fact] + public void PartialUDT() + { + var target = new FreeQubit(0); + var op = new QuantumSimulator().Get>(typeof(Circuits.FooUDT)) + .Partial((double d) => (("bar", (target, d)))); + var args = new QTuple(2.1); + var expected = new RuntimeMetadata() + { + Label = "FooUDT", + FormattedNonQubitArgs = "(\"bar\", (2.1))", + IsAdjoint = false, + IsControlled = false, + IsMeasurement = false, + IsComposite = false, + Children = null, + Controls = new List() { }, + Targets = new List() { }, + }; + + Assert.Equal(op.GetRuntimeMetadata(args), expected); + } + } +} diff --git a/src/Simulation/Simulators.Tests/TypeExtensionsTest.cs b/src/Simulation/Simulators.Tests/TypeExtensionsTest.cs new file mode 100644 index 00000000000..8e025482316 --- /dev/null +++ b/src/Simulation/Simulators.Tests/TypeExtensionsTest.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using Microsoft.Quantum.Simulation.Core; +using Xunit; + +namespace Microsoft.Quantum.Simulation.Simulators.Tests +{ + public class ApplyData : IApplyData + { + public T Data; + + public ApplyData(T data) + { + this.Data = data; + } + + object IApplyData.Value => this.Data; + + IEnumerable IApplyData.Qubits => QubitsExtractor.Get(typeof(T))?.Extract(Data); + } + + public class GetNonQubitArgumentsAsStringTests + { + [Fact] + public void BasicTypes() + { + Assert.Equal("3", 3.GetNonQubitArgumentsAsString()); + Assert.Equal("False", false.GetNonQubitArgumentsAsString()); + Assert.Equal("\"Foo\"", "Foo".GetNonQubitArgumentsAsString()); + Assert.Equal("\"\"", "".GetNonQubitArgumentsAsString()); + } + + [Fact] + public void TupleTypes() + { + Assert.Equal("(1, 2)", (1, 2).GetNonQubitArgumentsAsString()); + Assert.Equal("(\"foo\", \"bar\")", ("foo", "bar").GetNonQubitArgumentsAsString()); + Assert.Equal("(\"foo\", \"bar\", \"\")", ("foo", "bar", "").GetNonQubitArgumentsAsString()); + Assert.Equal("(\"foo\", (\"bar\", \"car\"))", ("foo", ("bar", "car")).GetNonQubitArgumentsAsString()); + Assert.Equal("((\"foo\"), (\"bar\", \"car\"))", (("foo", new FreeQubit(0)), ("bar", "car")).GetNonQubitArgumentsAsString()); + } + + [Fact] + public void ArrayTypes() + { + Assert.Equal("[1, 2, 3]", new[] { 1, 2, 3 }.GetNonQubitArgumentsAsString()); + Assert.Equal("[\"foo\", \"bar\"]", new[] { "foo", "bar" }.GetNonQubitArgumentsAsString()); + + var arr = new[] { + (new FreeQubit(0), "foo"), + (new FreeQubit(1), "bar"), + }; + Assert.Equal("[(\"foo\"), (\"bar\")]", arr.GetNonQubitArgumentsAsString()); + } + + [Fact] + public void IApplyDataTypes() + { + IApplyData data; + data = new ApplyData(3); + Assert.Equal("3", data.GetNonQubitArgumentsAsString()); + + data = new ApplyData(false); + Assert.Equal("False", data.GetNonQubitArgumentsAsString()); + + data = new ApplyData("Foo"); + Assert.Equal("\"Foo\"", data.GetNonQubitArgumentsAsString()); + + data = new ApplyData>((1, "foo")); + Assert.Equal("(1, \"foo\")", data.GetNonQubitArgumentsAsString()); + + data = new ApplyData, ValueTuple>>((("foo", new FreeQubit(0)), ("bar", "car"))); + Assert.Equal("((\"foo\"), (\"bar\", \"car\"))", data.GetNonQubitArgumentsAsString()); + + data = new ApplyData(new[] { 1, 2, 3 }); + Assert.Equal("[1, 2, 3]", data.GetNonQubitArgumentsAsString()); + + var arr = new[] { + (new FreeQubit(0), "foo"), + (new FreeQubit(1), "bar"), + }; + data = new ApplyData<(FreeQubit, string)[]>(arr); + Assert.Equal("[(\"foo\"), (\"bar\")]", data.GetNonQubitArgumentsAsString()); + } + } +}