Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
8 changes: 8 additions & 0 deletions src/Simulation/Core/AbstractCallable.cs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -29,6 +31,12 @@ public AbstractCallable(IOperationFactory m)
/// </summary>
public abstract void Init();

/// <summary>
/// Retrieves the runtime metadata of the Operation. If the Operation has no associated
/// runtime metadata, returns <c>null</c>.
/// </summary>
public virtual RuntimeMetadata? GetRuntimeMetadata(IApplyData args) => null;

object IApplyData.Value => null;

IEnumerable<Qubit> IApplyData.Qubits => null;
Expand Down
4 changes: 4 additions & 0 deletions src/Simulation/Core/Generics/GenericCallable.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -24,6 +26,8 @@ public partial interface ICallable : IApplyData
O Apply<O>(object args);

ICallable Partial(object partialTuple);

RuntimeMetadata? GetRuntimeMetadata(IApplyData args);
}

/// <summary>
Expand Down
11 changes: 11 additions & 0 deletions src/Simulation/Core/Operations/Adjoint.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -72,6 +74,15 @@ public override void Init() { }

public override IApplyData __dataOut(QVoid data) => data;

/// <inheritdoc/>
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() ?? "<null>" })";
public override string __qsharpType() => this.BaseOp?.__qsharpType();

Expand Down
23 changes: 22 additions & 1 deletion src/Simulation/Core/Operations/Controlled.cs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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<Qubit>, I), QVoid> Body => this.BaseOp.ControlledBody;
public override Func<(IQArray<Qubit>, I), QVoid> Body => this.BaseOp.ControlledBody;

public override Func<(IQArray<Qubit>, I), QVoid> AdjointBody => this.BaseOp.ControlledAdjointBody;

Expand Down Expand Up @@ -110,6 +113,24 @@ public override void Init() { }

public override IApplyData __dataOut(QVoid data) => data;

/// <inheritdoc/>
public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args)
{
Debug.Assert(args.Value is ValueTuple<IQArray<Qubit>, I>, $"Failed to retrieve runtime metadata for {this.ToString()}.");

if (args.Value is ValueTuple<IQArray<Qubit>, 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() ?? "<null>" })";
public override string __qsharpType() => GenericControlled.AddControlQubitsToSignature(this.BaseOp?.__qsharpType());
Expand Down
10 changes: 10 additions & 0 deletions src/Simulation/Core/Operations/Operation.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -75,6 +77,14 @@ public Operation(IOperationFactory m) : base(m)
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public ControlledOperation<I, O> Controlled => _controlled.Value;

/// <inheritdoc/>
public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) =>
new RuntimeMetadata()
{
Label = ((ICallable)this).Name,
FormattedNonQubitArgs = args.GetNonQubitArgumentsAsString() ?? "",
Targets = args.GetQubits(),
};

public O Apply(I a)
{
Expand Down
6 changes: 6 additions & 0 deletions src/Simulation/Core/Operations/OperationPartial.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -141,6 +143,10 @@ ICallable<P1, QVoid> ICallable<P, QVoid>.Partial<P1>(Func<P1, P> mapper)

IUnitary<P1> IUnitary<P>.Partial<P1>(Func<P1, P> mapper) => new OperationPartial<P1, P, O>(this, mapper);

/// <inheritdoc/>
public override RuntimeMetadata? GetRuntimeMetadata(IApplyData args) =>
this.BaseOp.GetRuntimeMetadata(args);

public override string ToString() => $"{this.BaseOp}{{_}}";
public override string __qsharpType()
{
Expand Down
137 changes: 137 additions & 0 deletions src/Simulation/Core/RuntimeMetadata.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Contains the metadata associated with an operation's runtime execution path.
/// </summary>
public class RuntimeMetadata
{
/// <summary>
/// Label of gate.
/// </summary>
public string Label { get; set; } = "";

/// <summary>
/// Non-qubit arguments provided to gate, formatted as string.
/// </summary>
public string FormattedNonQubitArgs { get; set; } = "";

/// <summary>
/// True if operation is an adjoint operation.
/// </summary>
public bool IsAdjoint { get; set; }

/// <summary>
/// True if operation is a controlled operation.
/// </summary>
public bool IsControlled { get; set; }

/// <summary>
/// True if operation is a measurement operation.
/// </summary>
public bool IsMeasurement { get; set; }

/// <summary>
/// True if operation is composed of multiple operations.
/// </summary>
/// </summary>
/// <remarks>
/// Currently not used as this is intended for composite operations,
/// such as <c>ApplyToEach</c>.
/// </remarks>
public bool IsComposite { get; set; }

/// <summary>
/// Group of operations for each classical branch (<c>true</c> and <c>false</c>).
/// </summary>
/// <remarks>
/// Currently not used as this is intended for classically-controlled operations.
/// </remarks>
public IEnumerable<IEnumerable<RuntimeMetadata>>? Children { get; set; }

/// <summary>
/// List of control registers.
/// </summary>
public IEnumerable<Qubit> Controls { get; set; } = new List<Qubit>();

/// <summary>
/// List of target registers.
/// </summary>
public IEnumerable<Qubit> Targets { get; set; } = new List<Qubit>();

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<T>(IEnumerable<T> a, IEnumerable<T> 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<Qubit>(this.Controls, other.Controls)) return false;

if (!ListEquals<Qubit>(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<RuntimeMetadata>).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);
}
}
54 changes: 53 additions & 1 deletion src/Simulation/Core/TypeExtensions.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -144,6 +147,55 @@ public static Type[] GetTupleFieldTypes(this Type arg)
}
}

/// <summary>
/// Given an <see cref="object"/>, retrieve its non-qubit fields as a string.
/// Returns null if no non-qubit fields found.
/// </summary>
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<object>()
.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<Type, Type> _normalTypesCache = new ConcurrentDictionary<Type, Type>();

/// <summary>
Expand Down Expand Up @@ -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)
Expand Down
14 changes: 14 additions & 0 deletions src/Simulation/Core/Udts/UDTPartial.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -62,6 +64,18 @@ public ICallable Partial(object partialTuple)
return (ICallable)Activator.CreateInstance(partialType, new object[] { this, partialTuple });
}

/// <inheritdoc/>
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<P, B, U> u;
Expand Down
Loading