This repository has been archived by the owner on Jan 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 926
/
Copy pathQuantumVizCounter.cs
202 lines (173 loc) · 7.14 KB
/
QuantumVizCounter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#nullable enable
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Quantum.Simulation.Core;
using Microsoft.Quantum.Simulation.Simulators.QCTraceSimulators;
using Newtonsoft.Json;
namespace Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime
{
/// <summary>
/// Special listener to generate quantum-viz.js circuit with resources information.
/// </summary>
public class QuantumVizCounter : IQCTraceSimulatorListener
{
// This stack tracks all operations currently on the call stack, the
// most recent one on the top.
private readonly Stack<Operation> callStack = new Stack<Operation>();
/// <summary>
/// Returns the quantum-viz.js circuit, that can be serialized to valid
/// JSON output.
/// </summary>
public Circuit Circuit { get; } = new Circuit();
public QuantumVizCounter(PrimitiveOperationsCounterConfiguration config)
{
// Place the root call onto the stack. This operation will not end
// and still be on the stack once the simulator has run the Q# entry
// point operation.
var operation = new Operation { Gate = CallGraphEdge.CallGraphRootHashed };
Circuit.Operations.Add(operation);
callStack.Push(operation);
}
#region ITracingSimulatorListener implementation
/// <summary>
/// A primitive operation call causes an update of the corresponding
/// counter of the operation call on the top of the call stack.
/// </summary>
public void OnPrimitiveOperation(int id, object[] qubitsTraceData, double duration)
{
switch (id)
{
case (int)PrimitiveOperationsGroups.CNOT:
callStack.Peek().CNOTCount += 1.0;
break;
case (int)PrimitiveOperationsGroups.QubitClifford:
callStack.Peek().CliffordCount += 1.0;
break;
case (int)PrimitiveOperationsGroups.R:
callStack.Peek().RCount += 1.0;
break;
case (int)PrimitiveOperationsGroups.T:
callStack.Peek().TCount += 1.0;
break;
}
}
/// <summary>
/// With the start of a new operation call, a new entry is added to the
/// operation call stack. Also we read the affected qubits from the
/// <c>qubitsTraceData</c> argument.
/// </summary>
public void OnOperationStart(HashedString name, OperationFunctor variant, object[] qubitsTraceData)
{
var operation = new Operation { Gate = name };
foreach (var qubit in qubitsTraceData)
{
operation.Targets.Add((QubitReference)qubit);
}
operation.IsAdjoint = variant == OperationFunctor.Adjoint || variant == OperationFunctor.ControlledAdjoint;
callStack.Push(operation);
}
/// <summary>
/// When the operation ends, it's popped from the stack and added as
/// child to the next top-most operation on the stack.
/// </summary>
public void OnOperationEnd(object[] returnedQubitsTraceData)
{
var lastOperation = callStack.Pop();
Debug.Assert(callStack.Count != 0, "Operation call stack must never get empty");
// Add operation to parent and also update qubits
callStack.Peek().AddChild(lastOperation);
}
/// <summary>
/// Adds an outer interface qubit to the circuit data structure, whenever
/// a new qubit is allocated.
/// </summary>
public void OnAllocate(object[] qubitsTraceData)
{
foreach (var qubit in qubitsTraceData)
{
Circuit.Qubits.Add(new Qubit { Id = ((QubitReference)qubit).Id });
}
}
public void OnRelease(object[] qubitsTraceData) {}
public void OnBorrow(object[] qubitsTraceData, long newQubitsAllocated) {}
public void OnReturn(object[] qubitsTraceData, long newQubitsAllocated) {}
/// <summary>
/// Tracing data is captured in the custom qubit type using the qubit's id.
/// </summary>
public object? NewTracingData(long qubitId) => new QubitReference { Id = qubitId };
/// <summary>
/// This ensures that the listener traces qubit data.
/// </summary>
public bool NeedsTracingDataInQubits => true;
#endregion
}
public class Circuit {
[JsonProperty(PropertyName = "qubits")]
public HashSet<Qubit> Qubits { get; set; } = new HashSet<Qubit>();
[JsonProperty(PropertyName = "operations")]
public List<Operation> Operations { get; set; } = new List<Operation>();
}
public class Operation {
[JsonProperty(PropertyName = "gate")]
public string Gate { get; set; } = string.Empty;
[JsonProperty(PropertyName = "displayArgs")]
public string DisplayArgs { get => $"CNOT: {CNOTCount} Clifford: {CliffordCount} R: {RCount} T: {TCount}"; }
[JsonProperty(PropertyName = "children")]
public List<Operation> Children { get; set; } = new List<Operation>();
[JsonProperty(PropertyName = "targets")]
public HashSet<QubitReference> Targets { get; set; } = new HashSet<QubitReference>();
[JsonProperty(PropertyName = "isAdjoint")]
public bool IsAdjoint { get; set; } = false;
// We're not outputting the resource count fields in the JSON, since we
// include them in DisplayArgs field
[JsonIgnore]
public double CNOTCount { get; set; } = 0.0;
[JsonIgnore]
public double CliffordCount { get; set; } = 0.0;
[JsonIgnore]
public double RCount { get; set; } = 0.0;
[JsonIgnore]
public double TCount { get; set; } = 0.0;
/// <summary>
/// Adds operation and updates counters and targets
/// </summary>
public void AddChild(Operation child)
{
Children.Add(child);
CNOTCount += child.CNOTCount;
CliffordCount += child.CliffordCount;
RCount += child.RCount;
TCount += child.TCount;
foreach (var target in child.Targets)
{
Targets.Add(target);
}
}
}
public class Qubit {
[JsonProperty(PropertyName = "id")]
public long Id { get; set; } = 0;
public override bool Equals(object? obj)
{
return (obj != null) && ((Qubit)obj).Id == Id;
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
public class QubitReference {
[JsonProperty(PropertyName = "qId")]
public long Id { get; set; } = 0;
public override bool Equals(object? obj)
{
return (obj != null) && ((QubitReference)obj).Id == Id;
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
}