Repository: microsoft/qsharp
Pull request #1080 (2024-01-28 21:43:34)

RE and widget updates
In trying to add some desired features and improvements, I hit a few existing bugs and glitches. In the process of trying to address those I ended up doing quite a bit of refactoring. (This PR actually removes more code than it adds, despite adding several new features).

- Fixed issue where result name was blank if only one row was present (i.e. the run selected just one qubit type)
    - Note: There is still a bug with sometimes losing the current selection when new results/rows are added, which can cause the row 'name' to change due to the recent change to dynamically calculate the series/row name. This is because the name is used as the unique key for some saved state. I look more into this later.
- Fixed issue where multiple selections could occur on the scatter chart.
- Fixed issue where an error was reporting in the editor for UX components importing from the ticks module.
- Fixed some color rendering issues with JupyterLab in the browser in dark theme.
- Made some of the DOM manipulation code more declarative and canonical, reducing code size and complexity.
- Updated hover, selection, and tooltip display behavior to be more consistent.
- Added keyboard up/down handler to change the selected row when the results table is focused.
- The column selection menu now closes when you click outside of it.
- Minor adjustments to chart layout (e.g. reducing the left padding)

I think we want much of this for 1.1, so hopefully can get it in tomorrow (Monday). I've tested in VS Code and Jupyter Notebooks. CC @ivanbasov 

github-actions[bot]: ## Benchmark for 00d5033
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| Array append evaluation | 864.7±7.30µs | 860.7±7.34µs | -0.46% |
| Array update evaluation | **931.5±6.97µs** | 942.9±52.60µs | **+1.22%** |
| Deutsch-Jozsa evaluation | 31.3±0.19ms | 31.3±0.33ms | 0.00% |
| Large file parity evaluation | **35.9±0.06ms** | 36.0±0.35ms | **+0.28%** |
| Large input file | 39.0±0.97ms | 39.4±1.34ms | +1.03% |
| Standard library | 21.1±0.33ms | **20.8±0.21ms** | **-1.42%** |
| Teleport evaluation | 104.0±2.68µs | 103.9±2.16µs | -0.10% |

  </details>
  
github-actions[bot]: ## Benchmark for 4ae5e9f
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| Array append evaluation | 876.2±25.39µs | 874.9±8.41µs | -0.15% |
| Array update evaluation | **938.0±21.32µs** | 953.8±15.16µs | **+1.68%** |
| Deutsch-Jozsa evaluation | 32.3±0.24ms | **31.6±0.39ms** | **-2.17%** |
| Large file parity evaluation | 36.5±0.13ms | **36.2±0.15ms** | **-0.82%** |
| Large input file | 40.1±1.82ms | 40.1±1.51ms | 0.00% |
| Standard library | 21.7±0.55ms | 22.2±1.14ms | +2.30% |
| Teleport evaluation | 110.2±1.52µs | **106.8±1.93µs** | **-3.09%** |

  </details>
  
github-actions[bot]: ## Benchmark for 9116725
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| Array append evaluation | 877.6±8.11µs | **863.5±11.90µs** | **-1.61%** |
| Array update evaluation | 954.8±10.32µs | **940.0±7.67µs** | **-1.55%** |
| Deutsch-Jozsa evaluation | 31.4±0.22ms | 31.4±0.67ms | 0.00% |
| Large file parity evaluation | 36.1±1.00ms | **35.9±0.05ms** | **-0.55%** |
| Large input file | 38.3±0.79ms | 38.0±0.71ms | -0.78% |
| Standard library | 21.0±0.25ms | 20.9±0.23ms | -0.48% |
| Teleport evaluation | 105.1±1.84µs | 104.3±2.39µs | -0.76% |

  </details>
  
github-actions[bot]: ## Benchmark for b53e13e
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| Array append evaluation | **873.5±4.74µs** | 893.1±7.31µs | **+2.24%** |
| Array update evaluation | **949.5±11.97µs** | 966.6±4.30µs | **+1.80%** |
| Deutsch-Jozsa evaluation | 31.4±0.20ms | 31.5±0.96ms | +0.32% |
| Large file parity evaluation | 36.0±0.16ms | 36.1±0.17ms | +0.28% |
| Large input file | 38.1±2.90ms | **37.5±0.53ms** | **-1.57%** |
| Standard library | 20.9±0.23ms | 21.0±0.20ms | +0.48% |
| Teleport evaluation | 104.3±1.65µs | 105.2±2.15µs | +0.86% |

  </details>
  

Patch:
diff --git a/compiler/qsc_eval/src/backend.rs b/compiler/qsc_eval/src/backend.rs
index 5deb5507..950021ec 100644
--- a/compiler/qsc_eval/src/backend.rs
+++ b/compiler/qsc_eval/src/backend.rs
@@ -189,7 +189,23 @@ impl Backend for SparseSim {
     }
 
     fn capture_quantum_state(&mut self) -> (Vec<(BigUint, Complex<f64>)>, usize) {
-        self.sim.get_state()
+        let (state, count) = self.sim.get_state();
+        // Because the simulator returns the state indices with opposite endianness from the
+        // expected one, we need to reverse the bit order of the indices.
+        let mut new_state = state
+            .into_iter()
+            .map(|(idx, val)| {
+                let mut new_idx = BigUint::default();
+                for i in 0..(count as u64) {
+                    if idx.bit((count as u64) - 1 - i) {
+                        new_idx.set_bit(i, true);
+                    }
+                }
+                (new_idx, val)
+            })
+            .collect::<Vec<_>>();
+        new_state.sort_unstable_by(|a, b| a.0.cmp(&b.0));
+        (new_state, count)
     }
 
     fn qubit_is_zero(&mut self, q: usize) -> bool {
diff --git a/katas/content/multi_qubit_systems/prepare_superposition/index.md b/katas/content/multi_qubit_systems/prepare_superposition/index.md
index a5d20b42..ac3f71b5 100644
--- a/katas/content/multi_qubit_systems/prepare_superposition/index.md
+++ b/katas/content/multi_qubit_systems/prepare_superposition/index.md
@@ -1,10 +1,8 @@
 **Input:** A two-qubit system in the basis state $|00\rangle = \begin{bmatrix} 1 \\ 0 \\ 0 \\ 0 \end{bmatrix}$.
 
-**Goal:** Transform the system into the state $\frac{1}{\sqrt2}\big(|00\rangle - |10\rangle\big) = \frac{1}{\sqrt2}\begin{bmatrix} 1 \\\ 0 \\\ -1 \\\ 0 \end{bmatrix}$.
-
-> Note that the quantum states are represented in little-endian format For a state $|qs_1 qs_0\rangle$, the "first" qubit (`qs[0]` in a Q# qubit register) refers to the least-significant qubit $qs_0$, while the "second" qubit (`qs[1]` in a Q# register) refers to the most-significant qubit $qs_1$.
+**Goal:** Transform the system into the state $\frac{1}{\sqrt2}\big(|00\rangle - |01\rangle\big) = \frac{1}{\sqrt2}\begin{bmatrix} 1 \\\ -1 \\\ 0 \\\ 0 \end{bmatrix}$.
 
 <details>
     <summary><b>Need a hint?</b></summary>
-    Represent the target state as a tensor product $\frac{1}{\sqrt2}\big(|0\rangle - |1\rangle\big) \otimes |0\rangle = \frac{1}{\sqrt2}\begin{bmatrix} 1 \\\ -1 \end{bmatrix} \otimes \begin{bmatrix} 1 \\\ 0 \end{bmatrix}$.
+    Represent the target state as a tensor product $|0\rangle \otimes \frac{1}{\sqrt2}\big(|0\rangle - |1\rangle\big) = \begin{bmatrix} 1 \\\ 0 \end{bmatrix} \otimes \frac{1}{\sqrt2}\begin{bmatrix} 1 \\\ -1 \end{bmatrix}$.
 </details>
diff --git a/katas/content/multi_qubit_systems/prepare_superposition/solution.md b/katas/content/multi_qubit_systems/prepare_superposition/solution.md
index 044ccfda..5179aaad 100644
--- a/katas/content/multi_qubit_systems/prepare_superposition/solution.md
+++ b/katas/content/multi_qubit_systems/prepare_superposition/solution.md
@@ -2,7 +2,7 @@
 $$ \begin{bmatrix} 1 \\\ 0 \\\ 0 \\\ 0 \end{bmatrix} = \begin{bmatrix} 1 \\\ 0 \end{bmatrix} \otimes \begin{bmatrix} 1 \\\ 0 \end{bmatrix} = |0\rangle \otimes |0\rangle$$
 
 The goal state can be separated as follows:
-$$ \frac{1}{\sqrt2} \begin{bmatrix} 1 \\\ 0 \\\ -1 \\\ 0 \end{bmatrix} = \frac{1}{\sqrt2}\begin{bmatrix} 1 \\\ -1 \end{bmatrix} \otimes \begin{bmatrix} 1 \\\ 0 \end{bmatrix} = \frac{1}{\sqrt2}\big(|0\rangle - |1\rangle\big) \otimes |0\rangle$$
+$$ \frac{1}{\sqrt2} \begin{bmatrix} 1 \\\ -1 \\\ 0 \\\ 0 \end{bmatrix} = \begin{bmatrix} 1 \\\ 0 \end{bmatrix} \otimes \frac{1}{\sqrt2}\begin{bmatrix} 1 \\\ -1 \end{bmatrix} = |0\rangle \otimes \frac{1}{\sqrt2}\big(|0\rangle - |1\rangle\big)$$
 
 This means that the first qubit is already in the state we want it to be, but the second qubit needs to be transformed from the $ \begin{bmatrix} 1 \\\ 0 \end{bmatrix} $ into $ \frac{1}{\sqrt{2}}\begin{bmatrix} 1 \\\ -1\end{bmatrix}$ state.
 
diff --git a/katas/content/multi_qubit_systems/prepare_with_complex/index.md b/katas/content/multi_qubit_systems/prepare_with_complex/index.md
index 25d268d7..ff5a0641 100644
--- a/katas/content/multi_qubit_systems/prepare_with_complex/index.md
+++ b/katas/content/multi_qubit_systems/prepare_with_complex/index.md
@@ -1,11 +1,9 @@
 
 **Input:** A two-qubit system in the basis state $|00\rangle = \begin{bmatrix} 1 \\ 0 \\ 0 \\ 0 \end{bmatrix}$.
 
-**Goal:** Transform the system into the state $\frac{1}{2}\big(|00\rangle + e^{i\pi/2}|01\rangle + e^{i\pi/4}|10\rangle + e^{3i\pi/4}|11\rangle\big) = \frac{1}{2}\begin{bmatrix} 1 \\ e^{i\pi/2} \\ e^{i\pi/4} \\ e^{3i\pi/4} \end{bmatrix}$.
-
-> Note that the quantum states are represented in little-endian format For a state $|qs_1 qs_0\rangle$, the "first" qubit (`qs[0]` in a Q# qubit register) refers to the least-significant qubit $qs_0$, while the "second" qubit (`qs[1]` in a Q# register) refers to the most-significant qubit $qs_1$.
+**Goal:** Transform the system into the state $\frac{1}{2}\big(|00\rangle + e^{i\pi/4}|01\rangle + e^{i\pi/2}|10\rangle + e^{3i\pi/4}|11\rangle\big) = \frac{1}{2}\begin{bmatrix} 1 \\ e^{i\pi/4} \\ e^{i\pi/2} \\ e^{3i\pi/4} \end{bmatrix}$.
 
 <details>
     <summary><b>Need a hint?</b></summary>
-    Represent the target state as a tensor product $\frac{1}{\sqrt2}\big(|0\rangle + e^{i\pi/4}|1\rangle\big) \otimes \frac{1}{\sqrt2}\big(|0\rangle + e^{i\pi/2}|1\rangle\big) = \frac{1}{\sqrt2}\begin{bmatrix} 1 \\\ e^{i\pi/4} \end{bmatrix} \otimes \frac{1}{\sqrt2} \begin{bmatrix} 1 \\\ e^{i\pi/2} \end{bmatrix}$.
+    Represent the target state as a tensor product $\frac{1}{\sqrt2}\big(|0\rangle + e^{i\pi/2}|1\rangle\big) \otimes \frac{1}{\sqrt2}\big(|0\rangle + e^{i\pi/4}|1\rangle\big) = \frac{1}{\sqrt2} \begin{bmatrix} 1 \\\ e^{i\pi/2} \end{bmatrix} \otimes \frac{1}{\sqrt2}\begin{bmatrix} 1 \\\ e^{i\pi/4} \end{bmatrix}$.
 </details>
diff --git a/katas/content/multi_qubit_systems/prepare_with_complex/solution.md b/katas/content/multi_qubit_systems/prepare_with_complex/solution.md
index 61383822..df9e0a33 100644
--- a/katas/content/multi_qubit_systems/prepare_with_complex/solution.md
+++ b/katas/content/multi_qubit_systems/prepare_with_complex/solution.md
@@ -4,9 +4,9 @@ $$ \begin{bmatrix} 1 \\\ 0 \\\ 0 \\\ 0 \end{bmatrix} = \begin{bmatrix} 1 \\\ 0 \
 The goal state, factored as a tensor product, looks like this (remember that $e^{3i\pi/4} = e^{i\pi/4} e^{i\pi/2}$):
 
 $$
-\frac{1}{2}\begin{bmatrix} 1 \\\ e^{i\pi/2} \\\ e^{i\pi/4} \\\ e^{3i\pi/4} \end{bmatrix} =
-\frac{1}{\sqrt2} \begin{bmatrix} 1 \\\ e^{i\pi/4} \end{bmatrix} \otimes \frac{1}{\sqrt2}\begin{bmatrix} 1 \\\ e^{i\pi/2} \end{bmatrix} =
-\frac{1}{\sqrt2}\big(|0\rangle + e^{i\pi/4}|1\rangle\big) \otimes \frac{1}{\sqrt2}\big(|0\rangle + e^{i\pi/2}|1\rangle\big) $$
+\frac{1}{2}\begin{bmatrix} 1 \\\ e^{i\pi/4} \\\ e^{i\pi/2} \\\ e^{3i\pi/4} \end{bmatrix} =
+\frac{1}{\sqrt2} \begin{bmatrix} 1 \\\ e^{i\pi/2} \end{bmatrix} \otimes \frac{1}{\sqrt2}\begin{bmatrix} 1 \\\ e^{i\pi/4} \end{bmatrix} =
+\frac{1}{\sqrt2}\big(|0\rangle + e^{i\pi/2}|1\rangle\big) \otimes \frac{1}{\sqrt2}\big(|0\rangle + e^{i\pi/4}|1\rangle\big) $$
 
 We will again need to adjust the states of both qubits independently.
 
diff --git a/katas/content/multi_qubit_systems/prepare_with_real/index.md b/katas/content/multi_qubit_systems/prepare_with_real/index.md
index d14bee4f..46f834f5 100644
--- a/katas/content/multi_qubit_systems/prepare_with_real/index.md
+++ b/katas/content/multi_qubit_systems/prepare_with_real/index.md
@@ -1,11 +1,9 @@
 
 **Input:** A two-qubit system in the basis state $|00\rangle = \begin{bmatrix} 1 \\\ 0 \\\ 0 \\\ 0 \end{bmatrix}$.
 
-**Goal:** Transform the system into the state $\frac{1}{2}\big(|00\rangle + |01\rangle - |10\rangle - |11\rangle\big) = \frac{1}{2}\begin{bmatrix} 1 \\\ 1 \\\ -1 \\\ -1 \end{bmatrix}$.
-
-> Note that the quantum states are represented in little-endian format For a state $|qs_1 qs_0\rangle$, the "first" qubit (`qs[0]` in a Q# qubit register) refers to the least-significant qubit $qs_0$, while the "second" qubit (`qs[1]` in a Q# register) refers to the most-significant qubit $qs_1$.
+**Goal:** Transform the system into the state $\frac{1}{2}\big(|00\rangle - |01\rangle + |10\rangle - |11\rangle\big) = \frac{1}{2}\begin{bmatrix} 1 \\\ -1 \\\ 1 \\\ -1 \end{bmatrix}$.
 
 <details>
     <summary><b>Need a hint?</b></summary>
-    Represent the target state as a tensor product $\frac{1}{\sqrt2}\big(|0\rangle - |1\rangle\big) \otimes \frac{1}{\sqrt2}\big(|0\rangle + |1\rangle\big) = \frac{1}{\sqrt2} \begin{bmatrix} 1 \\\ -1 \end{bmatrix} \otimes \frac{1}{\sqrt2}\begin{bmatrix} 1 \\\ 1 \end{bmatrix}$.
+    Represent the target state as a tensor product $\frac{1}{\sqrt2}\big(|0\rangle + |1\rangle\big) \otimes \frac{1}{\sqrt2}\big(|0\rangle - |1\rangle\big) = \frac{1}{\sqrt2} \begin{bmatrix} 1 \\\ 1 \end{bmatrix} \otimes \frac{1}{\sqrt2}\begin{bmatrix} 1 \\\ -1 \end{bmatrix}$.
 </details>
diff --git a/katas/content/multi_qubit_systems/prepare_with_real/solution.md b/katas/content/multi_qubit_systems/prepare_with_real/solution.md
index 9bf5b750..195c251c 100644
--- a/katas/content/multi_qubit_systems/prepare_with_real/solution.md
+++ b/katas/content/multi_qubit_systems/prepare_with_real/solution.md
@@ -1,6 +1,6 @@
 ﻿Again, to start we will represent the goal state as a tensor product of single-qubit states; this gives us the following representation:
 
-$$ \frac{1}{2}\big(|00\rangle + |01\rangle - |10\rangle - |11\rangle\big) = \frac{1}{2}\begin{bmatrix} 1 \\\ 1 \\\ -1 \\\ -1 \end{bmatrix} = \frac{1}{\sqrt2} \begin{bmatrix} 1 \\\ -1 \end{bmatrix} \otimes \frac{1}{\sqrt2}\begin{bmatrix} 1 \\\ 1 \end{bmatrix} = \frac{1}{\sqrt2}\big(|0\rangle - |1\rangle\big) \otimes \frac{1}{\sqrt2}\big(|0\rangle + |1\rangle\big)$$
+$$ \frac{1}{2}\big(|00\rangle - |01\rangle + |10\rangle - |11\rangle\big) = \frac{1}{2}\begin{bmatrix} 1 \\\ -1 \\\ 1 \\\ -1 \end{bmatrix} = \frac{1}{\sqrt2} \begin{bmatrix} 1 \\\ 1 \end{bmatrix} \otimes \frac{1}{\sqrt2}\begin{bmatrix} 1 \\\ -1 \end{bmatrix} = \frac{1}{\sqrt2}\big(|0\rangle + |1\rangle\big) \otimes \frac{1}{\sqrt2}\big(|0\rangle - |1\rangle\big)  $$
 
 This time we need to transform both the first and the second qubits. Let's start with the first qubit. Applying the **H** gate transforms its state as follows:
 
diff --git a/katas/content/qubit/index.md b/katas/content/qubit/index.md
index dc3ed917..a3d85e39 100644
--- a/katas/content/qubit/index.md
+++ b/katas/content/qubit/index.md
@@ -38,8 +38,6 @@ The state of a qubit is represented by a complex vector of size 2:
 $$\begin{bmatrix} \alpha \\\ \beta \end{bmatrix}$$
 
 Here $\alpha$ and $\beta$ are complex numbers. $\alpha$ represents how "close" the qubit is to state $0$, and $\beta$ represents how "close" the qubit is to state $1$. This vector is normalized: $|\alpha|^2 + |\beta|^2 = 1$.
-α and β are known as the probability amplitudes of states 0 and 1, respectively.
-
 $\alpha$ and $\beta$ are known as the probability amplitudes of states $0$ and $1$, respectively.
 
 ## Basis States
@@ -195,7 +193,7 @@ Note that each row has the following format:
 <table>
     <thead>
         <tr>
-            <th>Basis State<br>(|𝜓ₙ…𝜓₁⟩)</th>
+            <th>Basis State<br>(|𝜓₁…𝜓ₙ⟩)</th>
             <th>Amplitude</th>
             <th>Measurement Probability</th>
             <th>Phase</th>
diff --git a/npm/src/browser.ts b/npm/src/browser.ts
index 57c5bb82..2853f4ec 100644
--- a/npm/src/browser.ts
+++ b/npm/src/browser.ts
@@ -249,3 +249,5 @@ export type {
 } from "../lib/web/qsc_wasm.js";
 export { type IStructStepResult, StepResultId } from "../lib/web/qsc_wasm.js";
 export { type LanguageServiceEvent } from "./language-service/language-service.js";
+
+export * as utils from "./utils.js";
diff --git a/npm/src/main.ts b/npm/src/main.ts
index 38736e48..c134f5be 100644
--- a/npm/src/main.ts
+++ b/npm/src/main.ts
@@ -145,3 +145,5 @@ export function getLanguageServiceWorker(): ILanguageServiceWorker {
 
   return proxy;
 }
+
+export * as utils from "./utils.js";
diff --git a/npm/src/ux/ticks.ts b/npm/src/utils.ts
similarity index 78%
rename from npm/src/ux/ticks.ts
rename to npm/src/utils.ts
index 495b832a..05b230fb 100644
--- a/npm/src/ux/ticks.ts
+++ b/npm/src/utils.ts
@@ -162,3 +162,40 @@ export function CreateTimeTicks(min: number, max: number): Tick[] {
 
   return result;
 }
+
+type SeriesOfPoints = Array<{ items: Array<{ x: number; y: number }> }>;
+
+// Given an array of object, and each of those objects has an 'items' property
+// that is another array of objects with number properties x & y, return a
+// tuple of [minX, maxX, minY, maxY] covering all items given
+export function getMinMaxXYForItems(
+  data: SeriesOfPoints,
+): [number, number, number, number] {
+  return data.reduce(
+    (priorSeriesResult, curr) =>
+      curr.items.reduce(
+        (priorItemResult, curr) => [
+          Math.min(priorItemResult[0], curr.x),
+          Math.max(priorItemResult[1], curr.x),
+          Math.min(priorItemResult[2], curr.y),
+          Math.max(priorItemResult[3], curr.y),
+        ],
+        priorSeriesResult,
+      ),
+    [Number.MAX_VALUE, Number.MIN_VALUE, Number.MAX_VALUE, Number.MIN_VALUE],
+  );
+}
+
+export function getRanges(data: SeriesOfPoints, rangeCoefficient: number) {
+  const [minX, maxX, minY, maxY] = getMinMaxXYForItems(data);
+
+  const rangeX = {
+    min: minX / rangeCoefficient,
+    max: maxX * rangeCoefficient,
+  };
+  const rangeY = {
+    min: minY / rangeCoefficient,
+    max: maxY * rangeCoefficient,
+  };
+  return { rangeX, rangeY };
+}
diff --git a/npm/ux/data.ts b/npm/ux/data.ts
index c6b9c9f6..70131b14 100644
--- a/npm/ux/data.ts
+++ b/npm/ux/data.ts
@@ -10,7 +10,6 @@ export type ReData = {
   tfactory: any;
   errorBudget: any;
   logicalCounts: any;
-  new: boolean;
   frontierEntries: FrontierEntry[];
 };
 
@@ -23,7 +22,6 @@ export type SingleEstimateResult = {
   tfactory: any;
   errorBudget: any;
   logicalCounts: any;
-  new: boolean;
 };
 
 export type FrontierEntry = {
@@ -62,7 +60,6 @@ export function CreateSingleEstimateResult(
       tfactory: entry.tfactory,
       errorBudget: entry.errorBudget,
       logicalCounts: input.logicalCounts,
-      new: input.new,
     };
   }
 }
diff --git a/npm/ux/estimatesOverview.tsx b/npm/ux/estimatesOverview.tsx
index 5ef066bd..dcf4820b 100644
--- a/npm/ux/estimatesOverview.tsx
+++ b/npm/ux/estimatesOverview.tsx
@@ -13,14 +13,7 @@ import {
   SingleEstimateResult,
 } from "./data.js";
 import { ResultsTable, Row } from "./resultsTable.js";
-import {
-  Axis,
-  HideTooltip,
-  PlotItem,
-  ScatterChart,
-  ScatterSeries,
-  SelectPoint,
-} from "./scatterChart.js";
+import { Axis, PlotItem, ScatterChart, ScatterSeries } from "./scatterChart.js";
 
 const columnNames = [
   "Run name",
@@ -77,7 +70,6 @@ function reDataToRow(input: ReData, color: string): Row {
       },
       data.physicalCounts.rqops,
       data.physicalCounts.physicalQubits,
-      data.new ? "New" : "Cached",
     ],
     color: color,
   };
@@ -118,6 +110,11 @@ function reDataToRowScatter(data: ReData, color: string): ScatterSeries {
 }
 
 function createRunNames(estimatesData: ReData[]): string[] {
+  // If there's only 1 entry, use the shared run name
+  if (estimatesData.length === 1) {
+    return [estimatesData[0].jobParams.sharedRunName];
+  }
+
   const fields: string[][] = [];
 
   estimatesData.forEach(() => {
@@ -178,6 +175,7 @@ export function EstimatesOverview(props: {
   setEstimate: (estimate: SingleEstimateResult | null) => void;
 }) {
   const [selectedRow, setSelectedRow] = useState<string | null>(null);
+  const [selectedPoint, setSelectedPoint] = useState<[number, number]>();
 
   const runNameRenderingError =
     props.runNames != null &&
@@ -198,24 +196,24 @@ export function EstimatesOverview(props: {
   });
 
   function onPointSelected(seriesIndex: number, pointIndex: number): void {
+    if (seriesIndex < 0) {
+      // Point was deselected
+      onRowSelected("");
+      return;
+    }
+
     const data = props.estimatesData[seriesIndex];
     props.setEstimate(CreateSingleEstimateResult(data, pointIndex));
     const rowId = props.estimatesData[seriesIndex].jobParams.runName;
     setSelectedRow(rowId);
+    setSelectedPoint([seriesIndex, pointIndex]);
   }
 
-  function onRowSelected(rowId: string, ev?: Event) {
+  function onRowSelected(rowId: string) {
     setSelectedRow(rowId);
-    // On any selection, clear the "new" flag on all rows. This ensures that
-    // new rows do not steal focus from the user selected row.
-    props.estimatesData.forEach((data) => (data.new = false));
-
-    const root = findRoot(ev);
-    if (root) {
-      HideTooltip(root);
-    }
     if (!rowId) {
       props.setEstimate(null);
+      setSelectedPoint(undefined);
     } else {
       const index = props.estimatesData.findIndex(
         (data) => data.jobParams.runName === rowId,
@@ -223,24 +221,15 @@ export function EstimatesOverview(props: {
 
       if (index == -1) {
         props.setEstimate(null);
+        setSelectedPoint(undefined);
       } else {
         const estimateFound = props.estimatesData[index];
+        setSelectedPoint([index, 0]);
         props.setEstimate(CreateSingleEstimateResult(estimateFound, 0));
-        if (root) {
-          SelectPoint(index, 0, root);
-        }
       }
     }
   }
 
-  function findRoot(ev?: Event): Element | undefined {
-    return (
-      (ev?.currentTarget as Element | undefined)?.closest(
-        ".qs-estimatesOverview",
-      ) ?? undefined
-    );
-  }
-
   const colorRenderingError =
     props.colors != null &&
     props.colors.length > 0 &&
@@ -253,36 +242,32 @@ export function EstimatesOverview(props: {
       ? props.colors
       : ColorMap(props.estimatesData.length);
 
-  if (props.isSimplifiedView) {
+  function getResultTable() {
     return (
-      <div className="qs-estimatesOverview">
-        {runNameRenderingError != "" && (
-          <div class="qs-estimatesOverview-error">{runNameRenderingError}</div>
+      <ResultsTable
+        columnNames={columnNames}
+        rows={props.estimatesData.map((dataItem, index) =>
+          reDataToRow(dataItem, colormap[index]),
         )}
-        {colorRenderingError != "" && (
-          <div class="qs-estimatesOverview-error">{colorRenderingError}</div>
+        initialColumns={initialColumns}
+        selectedRow={selectedRow}
+        onRowSelected={onRowSelected}
+        onRowDeleted={props.onRowDeleted}
+      />
+    );
+  }
+
+  function getScatterChart() {
+    return (
+      <ScatterChart
+        xAxis={xAxis}
+        yAxis={yAxis}
+        data={props.estimatesData.map((dataItem, index) =>
+          reDataToRowScatter(dataItem, colormap[index]),
         )}
-        <ResultsTable
-          columnNames={columnNames}
-          rows={props.estimatesData.map((dataItem, index) =>
-            reDataToRow(dataItem, colormap[index]),
-          )}
-          initialColumns={initialColumns}
-          // should be able to deselect rows for making screenshots
-          ensureSelected={false}
-          onRowDeleted={props.onRowDeleted}
-          selectedRow={selectedRow}
-          onRowSelected={onRowSelected}
-        />
-        <ScatterChart
-          xAxis={xAxis}
-          yAxis={yAxis}
-          data={props.estimatesData.map((dataItem, index) =>
-            reDataToRowScatter(dataItem, colormap[index]),
-          )}
-          onPointSelected={onPointSelected}
-        />
-      </div>
+        onPointSelected={onPointSelected}
+        selectedPoint={selectedPoint}
+      />
     );
   }
 
@@ -294,36 +279,27 @@ export function EstimatesOverview(props: {
       {colorRenderingError != "" && (
         <div class="qs-estimatesOverview-error">{colorRenderingError}</div>
       )}
-      <details open>
-        <summary style="font-size: 1.5em; font-weight: bold; margin: 24px 8px;">
-          Results
-        </summary>
-        <ResultsTable
-          columnNames={columnNames}
-          rows={props.estimatesData.map((dataItem, index) =>
-            reDataToRow(dataItem, colormap[index]),
-          )}
-          initialColumns={initialColumns}
-          selectedRow={selectedRow}
-          onRowSelected={onRowSelected}
-          // should be able to deselect rows for making screenshots
-          ensureSelected={false}
-          onRowDeleted={props.onRowDeleted}
-        />
-      </details>
-      <details open>
-        <summary style="font-size: 1.5em; font-weight: bold; margin: 24px 8px;">
-          Space-time diagram
-        </summary>
-        <ScatterChart
-          xAxis={xAxis}
-          yAxis={yAxis}
-          data={props.estimatesData.map((dataItem, index) =>
-            reDataToRowScatter(dataItem, colormap[index]),
-          )}
-          onPointSelected={onPointSelected}
-        />
-      </details>
+      {!props.isSimplifiedView ? (
+        <>
+          <details open>
+            <summary style="font-size: 1.5em; font-weight: bold; margin: 24px 8px;">
+              Results
+            </summary>
+            {getResultTable()}
+          </details>
+          <details open>
+            <summary style="font-size: 1.5em; font-weight: bold; margin: 24px 8px;">
+              Space-time diagram
+            </summary>
+            {getScatterChart()}
+          </details>
+        </>
+      ) : (
+        <>
+          {getResultTable()}
+          {getScatterChart()}
+        </>
+      )}
     </div>
   );
 }
diff --git a/npm/ux/histogram.tsx b/npm/ux/histogram.tsx
index 29e0d201..f132d306 100644
--- a/npm/ux/histogram.tsx
+++ b/npm/ux/histogram.tsx
@@ -35,7 +35,6 @@ function resultToKet(result: string): string {
     // The result is a simple array of Zero and One
     // The below will return an array of "Zero" or "One" in the order found
     const matches = result.match(/(One|Zero)/g);
-    matches?.reverse();
     let ket = "|";
     matches?.forEach((digit) => (ket += digit == "One" ? "1" : "0"));
     ket += "⟩";
diff --git a/npm/ux/qsharp-ux.css b/npm/ux/qsharp-ux.css
index c54a144b..b04acbf1 100644
--- a/npm/ux/qsharp-ux.css
+++ b/npm/ux/qsharp-ux.css
@@ -28,7 +28,10 @@ modern-normalize (see https://mattbrictson.com/blog/css-normalize-and-reset for
 /* TODO: Use the qs- prefixes and apply consistently */
 :root {
   --heading-background: #262679;
-  --main-background: var(--vscode-editor-background, #ececf0);
+  --main-background: var(
+    --vscode-editor-background,
+    var(--jp-layout-color0, #ececf0)
+  );
   --main-color: var(
     --vscode-editor-foreground,
     var(--jp-widgets-color, #202020)
@@ -529,6 +532,7 @@ html {
 .qs-resultsTable-sortedTable {
   border-collapse: collapse;
   margin: 12px 0px;
+  outline: none;
 }
 
 .qs-resultsTable-sortedTable th,
@@ -548,11 +552,11 @@ html {
 }
 
 .qs-resultsTable-sortedTable tbody tr:hover {
-  background: var(--vscode-list-hoverBackground);
+  background: var(--vscode-list-hoverBackground, var(--js-layout-color2));
 }
 
 .qs-resultsTable-sortedTableSelectedRow td {
-  background: var(--vscode-button-background);
+  background: var(--vscode-button-background, var(--jp-layout-color3));
   color: var(--vscode-button-foreground);
   font-weight: 600;
 }
@@ -565,7 +569,7 @@ html {
 .qs-resultsTable-showColumnMenu {
   display: block;
   position: absolute;
-  background-color: var(--vscode-menu-background);
+  background-color: var(--vscode-menu-background, var(--main-background));
   border: 1px solid #8888;
   padding: 4px;
   z-index: 100;
@@ -574,7 +578,7 @@ html {
 
 .qs-resultsTable-menuItem {
   cursor: pointer;
-  background-color: var(--vscode-list-hoverBackground);
+  background-color: var(--vscode-list-hoverBackground, var(--jp-layout-color1));
   padding: 4px;
   margin: 2px;
   font-size: 14px;
@@ -582,7 +586,7 @@ html {
 }
 
 .qs-resultsTable-menuItem:hover {
-  background-color: var(--vscode-menu-border);
+  background-color: var(--vscode-menu-border, var(--jp-layout-color2));
 }
 
 .qs-resultsTable-columnSelected {
@@ -590,8 +594,8 @@ html {
   padding: 4px;
   font-weight: 400;
   font-size: 14px;
-  background-color: var(--vscode-button-background);
-  color: var(--vscode-button-foreground);
+  background-color: var(--vscode-button-background, var(--jp-brand-color1));
+  color: var(--vscode-button-foreground, var(--jp-ui-inverse-font-color0));
   margin: 2px;
   border-radius: 3px;
 }
@@ -616,10 +620,6 @@ html {
   animation: codicon-spin 1.5s steps(45) infinite;
 }
 
-.qs-scatterChart {
-  font-size: 14px;
-}
-
 .qs-scatterChart-x-axisTitle,
 .qs-scatterChart-y-axisTitle {
   text-anchor: middle;
@@ -637,14 +637,14 @@ html {
   stroke-width: 4px;
 }
 
-.qs-scatterChart-point-selected {
-  r: 6px;
-  stroke-width: 3px;
+.qs-scatterChart-hover:hover {
+  r: 4px;
+  stroke-width: 4px;
   fill: white;
 }
 
-.qs-scatterChart-point:hover {
-  r: 4px;
+.qs-scatterChart-point-selected {
+  r: 8px;
   stroke-width: 4px;
   fill: white;
 }
@@ -654,19 +654,29 @@ html {
   fill: var(--main-color);
 }
 
-.qs-scatterChart-tooltip-text {
-  text-anchor: left;
-  fill: black;
+.qs-scatterChart-tooltip,
+.qs-scatterChart-selectedInfo {
+  position: absolute;
+  visibility: hidden;
+  background: var(--main-background);
+  color: var(--main-color);
+  border: var(--border-color) 1px solid;
+  padding: 4px;
 }
 
-.qs-scatterChart-tooltip-rect {
-  fill: white;
-  stroke: black;
-  stroke-width: 1px;
-  height: 22px;
+/* This rule adds the little triange pointer at the top */
+.qs-scatterChart-tooltip::after,
+.qs-scatterChart-selectedInfo::after {
+  content: "";
+  position: absolute;
+  bottom: 100%;
+  left: 50%;
+  margin-left: -5px;
+  border-width: 5px;
+  border-style: solid;
+  border-color: transparent transparent var(--border-color) transparent;
 }
 
-.qs-scatterChart-line,
 .qs-scatterChart-axis,
 .qs-scatterChart-tick-line {
   stroke: var(--border-color);
diff --git a/npm/ux/resultsTable.tsx b/npm/ux/resultsTable.tsx
index e42aab6f..db8051fe 100644
--- a/npm/ux/resultsTable.tsx
+++ b/npm/ux/resultsTable.tsx
@@ -1,7 +1,7 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
 
-import { useRef, useState } from "preact/hooks";
+import { useRef, useState, useEffect } from "preact/hooks";
 
 export type CellValue = string | number | { value: string; sortBy: number };
 export type Row = {
@@ -14,10 +14,9 @@ export function ResultsTable(props: {
   columnNames: string[];
   rows: Row[];
   initialColumns: number[];
-  ensureSelected: boolean;
   onRowDeleted(rowId: string): void;
-  selectedRow: string | null; // type selected to confirm with the useState pattern on the parent component
-  onRowSelected(rowId: string, ev?: Event): void;
+  selectedRow: string | null;
+  onRowSelected(rowId: string): void;
 }) {
   const [showColumns, setShowColumns] = useState(props.initialColumns);
   const [sortColumn, setSortColumn] = useState<{
@@ -28,26 +27,9 @@ export function ResultsTable(props: {
   const [showColumnMenu, setShowColumnMenu] = useState(false);
   const [showRowMenu, setShowRowMenu] = useState("");
 
-  // Find the first row that is new in the current sort order
-  const newest = getSortedRows(props.rows).find(
-    (row) => (row.cells[row.cells.length - 1] as string) === "New",
-  );
-
-  // Select the first of the newest rows, otherwise preserve the existing selection
-  if (newest && props.ensureSelected) {
-    const rowId = newest.cells[0].toString();
-    onRowSelected(rowId, undefined);
-  } else if (
-    !props.selectedRow &&
-    props.ensureSelected &&
-    props.rows.length > 0
-  ) {
-    const rowId = props.rows[0].cells[0].toString();
-    onRowSelected(rowId, undefined);
-  }
-
   // Use to track the column being dragged
   const draggingCol = useRef("");
+  const columnMenu = useRef<HTMLDivElement>(null);
 
   /*
   Note: Drag and drop events can occur faster than preact reconciles state.
@@ -83,8 +65,8 @@ export function ResultsTable(props: {
     }
   }
 
-  function onRowSelected(rowId: string, ev?: Event) {
-    props.onRowSelected(rowId, ev);
+  function onRowSelected(rowId: string) {
+    props.onRowSelected(rowId);
   }
 
   function onDragOver(ev: DragEvent) {
@@ -200,11 +182,9 @@ export function ResultsTable(props: {
     }
   }
 
-  function onRowClicked(rowId: string, ev: Event) {
-    if (props.selectedRow === rowId && props.ensureSelected) return;
-
+  function onRowClicked(rowId: string) {
     const newSelectedRow = props.selectedRow === rowId ? "" : rowId;
-    onRowSelected(newSelectedRow, ev);
+    onRowSelected(newSelectedRow);
   }
 
   function onClickRowMenu(ev: MouseEvent, rowid: string) {
@@ -254,17 +234,59 @@ export function ResultsTable(props: {
     // Clear out any menus or selections for the row if needed
     setShowRowMenu("");
     if (props.selectedRow === rowId) {
-      onRowSelected("", e);
+      onRowSelected("");
     }
     props.onRowDeleted(rowId);
   }
 
+  function onKeyDown(ev: KeyboardEvent) {
+    if (!props.selectedRow) return;
+    const sortedRowNames = getSortedRows(props.rows).map((row) =>
+      row.cells[0].toString(),
+    );
+    const currIndex = sortedRowNames.indexOf(props.selectedRow);
+
+    switch (ev.code) {
+      case "ArrowDown":
+        if (currIndex < sortedRowNames.length - 1) {
+          ev.preventDefault();
+          props.onRowSelected(sortedRowNames[currIndex + 1]);
+        }
+        break;
+      case "ArrowUp":
+        if (currIndex > 0) {
+          ev.preventDefault();
+          props.onRowSelected(sortedRowNames[currIndex - 1]);
+        }
+        break;
+      default:
+      // Not of interest
+    }
+  }
+
+  useEffect(() => {
+    // Post rendering, if the column menu is displayed, then ensure it
+    // has focus so that clicking anywhere outside of it caused the blur
+    // event that closes it.
+    if (showColumnMenu && columnMenu.current) {
+      columnMenu.current.focus();
+    }
+  });
+
   return (
-    <table class="qs-resultsTable-sortedTable">
+    <table
+      class="qs-resultsTable-sortedTable"
+      tabIndex={0}
+      onKeyDown={onKeyDown}
+    >
       <thead>
         <tr>
           <th>
-            <div style="position: relative">
+            <div
+              style="position: relative"
+              tabIndex={0}
+              onBlur={() => setShowColumnMenu(false)}
+            >
               <svg
                 width="16"
                 height="16"
@@ -289,6 +311,7 @@ export function ResultsTable(props: {
                 />
               </svg>
               <div
+                ref={columnMenu}
                 class={
                   showColumnMenu
                     ? "qs-resultsTable-columnMenu qs-resultsTable-showColumnMenu"
@@ -354,7 +377,7 @@ export function ResultsTable(props: {
           const rowId = row.cells[0].toString();
           return (
             <tr
-              onClick={(e) => onRowClicked(rowId, e)}
+              onClick={() => onRowClicked(rowId)}
               data-rowid={rowId}
               class={
                 rowId === props.selectedRow
diff --git a/npm/ux/scatterChart.tsx b/npm/ux/scatterChart.tsx
index c576a4a3..0ca5672d 100644
--- a/npm/ux/scatterChart.tsx
+++ b/npm/ux/scatterChart.tsx
@@ -1,8 +1,8 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
 
-import { CreateIntegerTicks, CreateTimeTicks, Tick } from "../src/ux/ticks.js";
-import { useEffect, useRef } from "preact/hooks";
+import { useRef, useEffect } from "preact/hooks";
+import * as utils from "../src/utils.js";
 
 export type ScatterSeries = {
   color: string;
@@ -25,157 +25,41 @@ type Range = {
   max: number;
 };
 
-export function HideTooltip(root: Element) {
-  // could be called extenally with a root out of the chart.
-  const chart =
-    root.id == "scatterChart" ? root : root.querySelector("#scatterChart");
-
-  chart
-    ?.querySelector("#tooltip-selected")
-    ?.setAttribute("visibility", "hidden");
-}
-
-function drawTooltip(
-  target: SVGCircleElement,
-  root: Element,
-  clicked: boolean = false,
-) {
-  const xAttr = target.getAttribute("cx");
-  const x = xAttr ? parseInt(xAttr) : 0;
-  const yAttr = target.getAttribute("cy");
-  const y = yAttr ? parseInt(yAttr) : -0;
-  const text = target.getAttribute("data-label");
-  const tooltipTextLeftPadding = 5;
-  const tooltipRectanglePaddingHeight = 10;
-  const tooltipTextPaddingHeight = 25;
-  const tooltipId = clicked ? "#tooltip-selected" : "#tooltip-hover";
-  const tooltip = root.querySelector(tooltipId);
-  const tooltipRect = tooltip?.querySelector("#tooltipRect");
-  const tooltipText = tooltip?.querySelector(
-    "#tooltipText",
-  ) as unknown as SVGTextElement;
-
-  if (tooltipText) {
-    tooltipText.setAttribute("x", (x + tooltipTextLeftPadding).toString());
-    tooltipText.setAttribute("y", (y + tooltipTextPaddingHeight).toString());
-    tooltipText.textContent = text;
-  }
-  if (tooltipRect && tooltipText) {
-    const box = tooltipText.getBBox();
-    const textWidth = box.width;
-    tooltipRect.setAttribute(
-      "width",
-      (textWidth + 2 * tooltipTextLeftPadding).toString(),
-    );
-    tooltipRect.setAttribute("x", x.toString());
-    tooltipRect.setAttribute(
-      "y",
-      (y + tooltipRectanglePaddingHeight).toString(),
-    );
-  }
-  if (tooltip) {
-    tooltip.setAttribute("visibility", "visible");
-    tooltip.setAttribute("clicked", clicked.toString());
-  }
-}
-
-function deselectPoint(root: Element) {
-  if (root.getAttribute("selectedPoint")) {
-    const point = root.querySelector(
-      ("#" + root.getAttribute("selectedPoint")) as string,
-    );
-    if (point) {
-      point.classList.remove("qs-scatterChart-point-selected");
-    }
-  }
-}
-
-export function SelectPoint(
-  seriesIndex: number,
-  pointIndex: number,
-  root: Element,
-) {
-  // could be called extenally with a root out of the chart.
-  const chart =
-    root.id == "scatterChart" ? root : root.querySelector("#scatterChart");
-  if (chart == null) {
-    return;
-  }
-  deselectPoint(chart);
-  const point = chart.querySelector(`#point-${seriesIndex}-${pointIndex}`);
-  if (point) {
-    point.classList.add("qs-scatterChart-point-selected");
-    chart.setAttribute("selectedPoint", point.id);
-    drawTooltip(point as unknown as SVGCircleElement, root, true);
-  }
-}
-
 export function ScatterChart(props: {
   data: ScatterSeries[];
   xAxis: Axis;
   yAxis: Axis;
   onPointSelected(seriesIndex: number, pointIndex: number): void;
+  selectedPoint?: [number, number];
 }) {
-  const data = props.data;
-
-  function findMinMaxSingle(
-    series: ScatterSeries,
-  ): [number, number, number, number] {
-    const xs = series.items.map((item) => item.x);
-    const ys = series.items.map((item) => item.y);
-    const minX = Math.min(...xs);
-    const maxX = Math.max(...xs);
-    const minY = Math.min(...ys);
-    const maxY = Math.max(...ys);
-    return [minX, maxX, minY, maxY];
-  }
-
-  function findMinMaxAll(
-    series: ScatterSeries[],
-  ): [number, number, number, number] {
-    const minMax = series.map(findMinMaxSingle);
-    const minX = Math.min(...minMax.map((x) => x[0]));
-    const maxX = Math.max(...minMax.map((x) => x[1]));
-    const minY = Math.min(...minMax.map((x) => x[2]));
-    const maxY = Math.max(...minMax.map((x) => x[3]));
-    return [minX, maxX, minY, maxY];
-  }
+  const selectedTooltipDiv = useRef<HTMLDivElement>(null);
 
-  const [minX, maxX, minY, maxY] = findMinMaxAll(data);
+  const { rangeX, rangeY } = utils.getRanges(props.data, 2 /* coefficient */);
 
-  const rangeCoefficient = 2;
-  const rangeX: Range = {
-    min: minX / rangeCoefficient,
-    max: maxX * rangeCoefficient,
-  };
-  const rangeY: Range = {
-    min: minY / rangeCoefficient,
-    max: maxY * rangeCoefficient,
-  };
-
-  function createAxisTicks(range: Range, isTime: boolean): Tick[] {
-    if (isTime) {
-      return CreateTimeTicks(range.min, range.max);
-    } else {
-      return CreateIntegerTicks(range.min, range.max);
-    }
+  function createAxisTicks(range: Range, isTime: boolean): utils.Tick[] {
+    return isTime
+      ? utils.CreateTimeTicks(range.min, range.max)
+      : utils.CreateIntegerTicks(range.min, range.max);
   }
 
   const xTicks = createAxisTicks(rangeX, props.xAxis.isTime);
   const yTicks = createAxisTicks(rangeY, props.yAxis.isTime);
 
-  function coordinateToSvgLogarithmic(
-    value: number,
-    range: Range,
-    size: number,
-  ): number {
+  function coordinateToLogarithmic(value: number, range: Range): number {
     return (
-      ((Math.log(value) - Math.log(range.min)) /
-        (Math.log(range.max) - Math.log(range.min))) *
-      size
+      (Math.log(value) - Math.log(range.min)) /
+      (Math.log(range.max) - Math.log(range.min))
     );
   }
 
+  function toLogX(val: number): number {
+    return coordinateToLogarithmic(val, rangeX) * plotAreaWidth;
+  }
+
+  function toLogY(val: number): number {
+    return -coordinateToLogarithmic(val, rangeY) * plotAreaHeight;
+  }
+
   const yAxisTitleWidth = 20;
   const yAxisTickCaptionMaxWidth = 100;
   const axisTickLength = 5;
@@ -201,199 +85,216 @@ export function ScatterChart(props: {
   const plotAreaWidth = svgWidth - xLeftMargin - xRightMargin;
   const plotAreaHeight = svgHeight - yMargin;
 
-  const viewBox = `${svgXMin - svgViewBoxWidthPadding} ${
-    -plotAreaHeight - svgViewBoxHeightPadding
-  } ${svgWidth + svgViewBoxWidthPadding} ${
-    svgHeight + svgViewBoxHeightPadding
-  }`;
+  const viewBox = `${svgXMin} ${-plotAreaHeight - svgViewBoxHeightPadding} ${
+    svgWidth + svgViewBoxWidthPadding
+  } ${svgHeight + svgViewBoxHeightPadding}`;
 
   const yAxisTextPaddingFromTicks = 5;
   const yAxisTextYPadding = 4;
 
-  function trySetAttribute(element: Element, attribute: string, value: string) {
-    if (element.getAttribute(attribute) !== value) {
-      element.setAttribute(attribute, value);
-    }
+  function renderTooltip(
+    topDiv: HTMLDivElement,
+    point: SVGCircleElement,
+    tooltip: HTMLDivElement,
+  ) {
+    const label = point.getAttribute("data-label");
+    tooltip.textContent = label;
+    const halfWidth = tooltip.offsetWidth / 2;
+    const pointRect = point.getBoundingClientRect();
+    const centerY = (pointRect.top + pointRect.bottom) / 2;
+    const centerX = (pointRect.left + pointRect.right) / 2;
+    const divRect = topDiv.getBoundingClientRect();
+    tooltip.style.left = `${centerX - divRect.left - halfWidth}px`;
+    tooltip.style.top = `${centerY - divRect.top + 12}px`;
+    tooltip.style.visibility = "visible";
   }
 
-  function hideHoverTooltip() {
-    const tooltip = chart?.querySelector("#tooltip-hover");
-
-    if (tooltip) {
-      if (tooltip.getAttribute("clicked") === "false") {
-        tooltip.setAttribute("visibility", "hidden");
-      }
+  function onPointMouseEvent(ev: MouseEvent, eventType: string) {
+    // Ensure we have a point as the target
+    if (!(ev.target instanceof SVGCircleElement)) return;
+    const target = ev.target as SVGCircleElement;
+    if (!target.classList.contains("qs-scatterChart-point")) return;
+
+    // Get the div enclosing the chart, and the popup child of it.
+    const topDiv = target.closest("div") as HTMLDivElement;
+    const popup = topDiv.querySelector(
+      ".qs-scatterChart-tooltip",
+    ) as HTMLDivElement;
+
+    switch (eventType) {
+      case "over":
+        {
+          renderTooltip(topDiv, target, popup);
+        }
+        break;
+      case "out":
+        popup.style.visibility = "hidden";
+        break;
+      case "click":
+        {
+          if (target.classList.contains("qs-scatterChart-point-selected")) {
+            // Clicked on the already selected point, so delete the point/row
+            props.onPointSelected(-1, 0);
+          } else {
+            const index = JSON.parse(target.getAttribute("data-index")!);
+            props.onPointSelected(index[0], index[1]);
+          }
+        }
+        break;
+      default:
+        console.error("Unknown event type: ", eventType);
     }
   }
 
-  const scatterChartContainerRef = useRef<SVGSVGElement | null>(null);
-  let chart: Element | null = null;
-
-  function updateCoordinates() {
-    if (!chart) {
-      return;
-    }
-
-    chart.querySelectorAll("[x-data-value]").forEach((element) => {
-      const value = Number(element.getAttribute("x-data-value"));
-      const x = coordinateToSvgLogarithmic(value, rangeX, plotAreaWidth);
-      const padding = element.getAttribute("x-data-padding");
-      const value_with_padding = (x + Number(padding)).toString();
-      trySetAttribute(element, "x", value_with_padding);
-      trySetAttribute(element, "x1", value_with_padding);
-      trySetAttribute(element, "x2", value_with_padding);
-      trySetAttribute(element, "cx", value_with_padding);
-    });
-
-    chart.querySelectorAll("[y-data-value]").forEach((element) => {
-      const value = Number(element.getAttribute("y-data-value"));
-      const y = -coordinateToSvgLogarithmic(value, rangeY, plotAreaHeight);
-      const padding = element.getAttribute("y-data-padding");
-      const value_with_padding = (y + Number(padding)).toString();
-      trySetAttribute(element, "y", value_with_padding);
-      trySetAttribute(element, "y1", value_with_padding);
-      trySetAttribute(element, "y2", value_with_padding);
-      trySetAttribute(element, "cy", value_with_padding);
-    });
+  function getSelectedPointData() {
+    if (!props.selectedPoint) return null;
+    const series = props.data[props.selectedPoint[0]];
+    const item = series.items[props.selectedPoint[1]];
+    return { ...item, color: series.color };
   }
+  const selectedPoint = getSelectedPointData();
 
+  // Need to render first to get the element layout to position the tooltip
   useEffect(() => {
-    chart = scatterChartContainerRef.current as Element | null;
-    updateCoordinates();
+    if (!selectedTooltipDiv.current) return;
+    if (!props.selectedPoint) {
+      selectedTooltipDiv.current.style.visibility = "hidden";
+    } else {
+      // Locate the selected point and put the tooltip under it
+      const topDiv = selectedTooltipDiv.current.parentElement as HTMLDivElement;
+      const selectedPoint = topDiv?.querySelector(
+        ".qs-scatterChart-point-selected",
+      ) as SVGCircleElement;
+      if (!selectedPoint) return;
+      renderTooltip(topDiv, selectedPoint, selectedTooltipDiv.current);
+    }
   });
 
+  // The mouse events (over, out, and click) bubble, so put the hanlders on the
+  // SVG element and check the target element in the handler.
   return (
-    <div style="display: flex; flex-wrap: wrap; margin-top: 8px;">
-      <div class="chart-container">
-        <svg
-          id="scatterChart"
-          ref={scatterChartContainerRef}
-          viewBox={viewBox}
-          width={svgWidth}
-          height={svgHeight}
+    <div style="position: relative">
+      <svg
+        style="margin-top: 12px"
+        viewBox={viewBox}
+        width={svgWidth}
+        height={svgHeight}
+        onMouseOver={(ev) => onPointMouseEvent(ev, "over")}
+        onMouseOut={(ev) => onPointMouseEvent(ev, "out")}
+        onClick={(ev) => onPointMouseEvent(ev, "click")}
+      >
+        <line
+          class="qs-scatterChart-axis"
+          x1="0"
+          y1="0"
+          x2={plotAreaWidth}
+          y2="0"
+        />
+
+        {xTicks.map((tick) => {
+          return (
+            <>
+              <line
+                y1="1"
+                y2={axisTickLength}
+                x1={toLogX(tick.value)}
+                x2={toLogX(tick.value)}
+                class="qs-scatterChart-tick-line"
+              />
+              <text
+                y={axisTickLength + xAxisTickCaptionMaxHeight}
+                x={toLogX(tick.value)}
+                class="qs-scatterChart-x-tick-text"
+              >
+                {tick.label}
+              </text>
+            </>
+          );
+        })}
+
+        <line
+          class="qs-scatterChart-axis"
+          x1="0"
+          y1="0"
+          x2="0"
+          y2={-svgHeight}
+        />
+
+        {yTicks.map((tick) => {
+          return (
+            <>
+              <line
+                x1="0"
+                x2={-axisTickLength}
+                y1={toLogY(tick.value)}
+                y2={toLogY(tick.value)}
+                class="qs-scatterChart-tick-line"
+              />
+              <text
+                x={-axisTickLength - yAxisTextPaddingFromTicks}
+                y={toLogY(tick.value) + yAxisTextYPadding}
+                class="qs-scatterChart-y-tick-text"
+              >
+                {tick.label}
+              </text>
+            </>
+          );
+        })}
+
+        <text
+          x={plotAreaWidth / 2}
+          y={yMargin}
+          class="qs-scatterChart-x-axisTitle"
         >
-          <line
-            class="qs-scatterChart-axis"
-            x1="0"
-            y1="0"
-            x2={plotAreaWidth}
-            y2="0"
-          />
-
-          {xTicks.map((tick) => {
-            return (
-              <g>
-                <line
-                  y1="1"
-                  y2={axisTickLength}
-                  x-data-value={tick.value}
-                  class="qs-scatterChart-tick-line"
-                />
-                <text
-                  y={axisTickLength + xAxisTickCaptionMaxHeight}
-                  x-data-value={tick.value}
-                  class="qs-scatterChart-x-tick-text"
-                >
-                  {tick.label}
-                </text>
-              </g>
-            );
-          })}
+          {props.xAxis.label} (logarithmic)
+        </text>
 
-          <line
-            class="qs-scatterChart-axis"
-            x1="0"
-            y1="0"
-            x2="0"
-            y2={-svgHeight}
-            stroke="var(--border-color)"
-          />
+        <text
+          x={xLeftMargin - axisTitleHeight}
+          y={plotAreaHeight / 2}
+          class="qs-scatterChart-y-axisTitle"
+        >
+          {props.yAxis.label} (logarithmic)
+        </text>
 
-          {yTicks.map((tick) => {
-            return (
-              <g>
-                <line
-                  x1="0"
-                  x2={-axisTickLength}
-                  y-data-value={tick.value}
-                  class="qs-scatterChart-tick-line"
+        <text
+          class="qs-scatterChart-watermark"
+          x={xLeftMargin - axisTitleHeight}
+          y={-svgHeight + yMargin}
+        >
+          Created with Azure Quantum Resource Estimator
+        </text>
+        <g>
+          {props.data.map((series, seriesIdx) => {
+            return series.items.map((plot, plotIdx) => {
+              return (
+                <circle
+                  data-index={JSON.stringify([seriesIdx, plotIdx])}
+                  data-label={plot.label}
+                  class="qs-scatterChart-point qs-scatterChart-hover"
+                  cx={toLogX(plot.x)}
+                  cy={toLogY(plot.y)}
+                  stroke={series.color}
                 />
-                <text
-                  x={-axisTickLength - yAxisTextPaddingFromTicks}
-                  y-data-value={tick.value}
-                  class="qs-scatterChart-y-tick-text"
-                  y-data-padding={yAxisTextYPadding}
-                >
-                  {tick.label}
-                </text>
-              </g>
-            );
+              );
+            });
           })}
-
-          <text
-            x={plotAreaWidth / 2}
-            y={yMargin}
-            class="qs-scatterChart-x-axisTitle"
-          >
-            {props.xAxis.label} (logarithmic)
-          </text>
-
-          <text
-            x={xLeftMargin - axisTitleHeight}
-            y={plotAreaHeight / 2}
-            class="qs-scatterChart-y-axisTitle"
-          >
-            {props.yAxis.label} (logarithmic)
-          </text>
-
-          <text
-            class="qs-scatterChart-watermark"
-            x={xLeftMargin - axisTitleHeight}
-            y={-svgHeight + yMargin}
-          >
-            Created with Azure Quantum Resource Estimator
-          </text>
-          <g>
-            {data.map((data, seriesIndex) => {
-              return data.items.map((item, pointIndex) => {
-                return (
-                  <circle
-                    id={`point-${seriesIndex}-${pointIndex}`}
-                    data-label={item.label}
-                    class="qs-scatterChart-point"
-                    x-data-value={item.x}
-                    y-data-value={item.y}
-                    stroke={data.color}
-                    onMouseOver={(e) => {
-                      const circle = e.currentTarget;
-
-                      if (chart) {
-                        drawTooltip(circle, chart, false);
-                      }
-                      circle?.parentNode?.appendChild(circle); // move the hovered cicrle up on the rendering stack
-                    }}
-                    onClick={() => {
-                      if (chart) {
-                        SelectPoint(seriesIndex, pointIndex, chart);
-                      }
-                      props.onPointSelected(seriesIndex, pointIndex);
-                    }}
-                    onMouseOut={() => hideHoverTooltip()}
-                  />
-                );
-              });
-            })}
-          </g>
-          <g id="tooltip-selected" visibility="hidden">
-            <rect id="tooltipRect" class="qs-scatterChart-tooltip-rect" />
-            <text id="tooltipText" class="qs-scatterChart-tooltip-text" />
-          </g>
-          <g id="tooltip-hover" visibility="hidden">
-            <rect id="tooltipRect" class="qs-scatterChart-tooltip-rect" />
-            <text id="tooltipText" class="qs-scatterChart-tooltip-text" />
-          </g>
-        </svg>
-      </div>
+        </g>
+        {
+          // Render the selected point last, so it's always on top of the others
+          selectedPoint ? (
+            <circle
+              class="qs-scatterChart-point qs-scatterChart-point-selected"
+              data-label={selectedPoint.label}
+              cx={toLogX(selectedPoint.x)}
+              cy={toLogY(selectedPoint.y)}
+              stroke={selectedPoint.color}
+            />
+          ) : null
+        }
+      </svg>
+      <div class="qs-scatterChart-selectedInfo" ref={selectedTooltipDiv}></div>
+      <div class="qs-scatterChart-tooltip"></div>
     </div>
   );
 }
diff --git a/npm/ux/tsconfig.json b/npm/ux/tsconfig.json
index 0fe35e1e..d03570df 100644
--- a/npm/ux/tsconfig.json
+++ b/npm/ux/tsconfig.json
@@ -4,7 +4,6 @@
     "target": "ES2020",
     "noEmit": true,
     "lib": ["DOM", "ES2020"],
-    "rootDir": ".",
     "strict": true /* enable all strict type-checking options */,
     "jsx": "react-jsx",
     "jsxImportSource": "preact",
diff --git a/pip/samples/project.ipynb b/pip/samples/project.ipynb
index aeca3e96..3890968c 100644
--- a/pip/samples/project.ipynb
+++ b/pip/samples/project.ipynb
@@ -15,7 +15,7 @@
       "outputs": [
         {
           "data": {
-            "application/javascript": "// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\n// This file provides CodeMirror syntax highlighting for Q# magic cells\n// in classic Jupyter Notebooks. It does nothing in other (Jupyter Notebook 7,\n// VS Code, Azure Notebooks, etc.) environments.\n\n// Detect the prerequisites and do nothing if they don't exist.\nif (window.require && window.CodeMirror && window.Jupyter) {\n  // The simple mode plugin for CodeMirror is not loaded by default, so require it.\n  window.require([\"codemirror/addon/mode/simple\"], function defineMode() {\n    let rules = [\n      {\n        token: \"comment\",\n        regex: /(\\/\\/).*/,\n        beginWord: false,\n      },\n      {\n        token: \"string\",\n        regex: String.raw`^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)`,\n        beginWord: false,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(namespace|open|as|operation|function|body|adjoint|newtype|controlled|internal)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(if|elif|else|repeat|until|fixup|for|in|return|fail|within|apply)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(Adjoint|Controlled|Adj|Ctl|is|self|auto|distribute|invert|intrinsic)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(let|set|use|borrow|mutable)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"operatorKeyword\",\n        regex: String.raw`(not|and|or)\\b|(w/)`,\n        beginWord: true,\n      },\n      {\n        token: \"operatorKeyword\",\n        regex: String.raw`(=)|(!)|(<)|(>)|(\\\\+)|(-)|(\\\\*)|(\\\\/)|(\\\\^)|(%)|(\\\\|)|(\\\\&\\\\&\\\\&)|(\\\\~\\\\~\\\\~)|(\\\\.\\\\.\\\\.)|(\\\\.\\\\.)|(\\\\?)`,\n        beginWord: false,\n      },\n      {\n        token: \"meta\",\n        regex: String.raw`(Int|BigInt|Double|Bool|Qubit|Pauli|Result|Range|String|Unit)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"atom\",\n        regex: String.raw`(true|false|Pauli(I|X|Y|Z)|One|Zero)\\b`,\n        beginWord: true,\n      },\n    ];\n    let simpleRules = [];\n    for (let rule of rules) {\n      simpleRules.push({\n        token: rule.token,\n        regex: new RegExp(rule.regex, \"g\"),\n        sol: rule.beginWord,\n      });\n      if (rule.beginWord) {\n        // Need an additional rule due to the fact that CodeMirror simple mode doesn't work with ^ token\n        simpleRules.push({\n          token: rule.token,\n          regex: new RegExp(String.raw`\\W` + rule.regex, \"g\"),\n          sol: false,\n        });\n      }\n    }\n\n    // Register the mode defined above with CodeMirror\n    window.CodeMirror.defineSimpleMode(\"qsharp\", { start: simpleRules });\n    window.CodeMirror.defineMIME(\"text/x-qsharp\", \"qsharp\");\n\n    // Tell Jupyter to associate %%qsharp magic cells with the qsharp mode\n    window.Jupyter.CodeCell.options_default.highlight_modes[\"qsharp\"] = {\n      reg: [/^%%qsharp/],\n    };\n\n    // Force re-highlighting of all cells the first time this code runs\n    for (const cell of window.Jupyter.notebook.get_cells()) {\n      cell.auto_highlight();\n    }\n  });\n}\n",
+            "application/javascript": "// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\n// This file provides CodeMirror syntax highlighting for Q# magic cells\n// in classic Jupyter Notebooks. It does nothing in other (Jupyter Notebook 7,\n// VS Code, Azure Notebooks, etc.) environments.\n\n// Detect the prerequisites and do nothing if they don't exist.\nif (window.require && window.CodeMirror && window.Jupyter) {\n  // The simple mode plugin for CodeMirror is not loaded by default, so require it.\n  window.require([\"codemirror/addon/mode/simple\"], function defineMode() {\n    let rules = [\n      {\n        token: \"comment\",\n        regex: /(\\/\\/).*/,\n        beginWord: false,\n      },\n      {\n        token: \"string\",\n        regex: String.raw`^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)`,\n        beginWord: false,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(namespace|open|as|operation|function|body|adjoint|newtype|controlled|internal)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(if|elif|else|repeat|until|fixup|for|in|return|fail|within|apply)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(Adjoint|Controlled|Adj|Ctl|is|self|auto|distribute|invert|intrinsic)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(let|set|use|borrow|mutable)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"operatorKeyword\",\n        regex: String.raw`(not|and|or)\\b|(w/)`,\n        beginWord: true,\n      },\n      {\n        token: \"operatorKeyword\",\n        regex: String.raw`(=)|(!)|(<)|(>)|(\\+)|(-)|(\\*)|(/)|(\\^)|(%)|(\\|)|(&&&)|(~~~)|(\\.\\.\\.)|(\\.\\.)|(\\?)`,\n        beginWord: false,\n      },\n      {\n        token: \"meta\",\n        regex: String.raw`(Int|BigInt|Double|Bool|Qubit|Pauli|Result|Range|String|Unit)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"atom\",\n        regex: String.raw`(true|false|Pauli(I|X|Y|Z)|One|Zero)\\b`,\n        beginWord: true,\n      },\n    ];\n    let simpleRules = [];\n    for (let rule of rules) {\n      simpleRules.push({\n        token: rule.token,\n        regex: new RegExp(rule.regex, \"g\"),\n        sol: rule.beginWord,\n      });\n      if (rule.beginWord) {\n        // Need an additional rule due to the fact that CodeMirror simple mode doesn't work with ^ token\n        simpleRules.push({\n          token: rule.token,\n          regex: new RegExp(String.raw`\\W` + rule.regex, \"g\"),\n          sol: false,\n        });\n      }\n    }\n\n    // Register the mode defined above with CodeMirror\n    window.CodeMirror.defineSimpleMode(\"qsharp\", { start: simpleRules });\n    window.CodeMirror.defineMIME(\"text/x-qsharp\", \"qsharp\");\n\n    // Tell Jupyter to associate %%qsharp magic cells with the qsharp mode\n    window.Jupyter.CodeCell.options_default.highlight_modes[\"qsharp\"] = {\n      reg: [/^%%qsharp/],\n    };\n\n    // Force re-highlighting of all cells the first time this code runs\n    for (const cell of window.Jupyter.notebook.get_cells()) {\n      cell.auto_highlight();\n    }\n  });\n}\n",
             "text/plain": []
           },
           "metadata": {},
@@ -62,7 +62,7 @@
               "<table>\n",
               "  <thead>\n",
               "    <tr>\n",
-              "      <th style=\"text-align: left\">Basis State<br />(|𝜓ₙ…𝜓₁⟩)</th>\n",
+              "      <th style=\"text-align: left\">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
               "      <th style=\"text-align: left\">Amplitude</th>\n",
               "      <th style=\"text-align: left\">Measurement Probability</th>\n",
               "      <th style=\"text-align: left\" colspan=\"2\">Phase</th>\n",
@@ -143,7 +143,7 @@
       "name": "python",
       "nbconvert_exporter": "python",
       "pygments_lexer": "ipython3",
-      "version": "3.11.7"
+      "version": "3.11.6"
     }
   },
   "nbformat": 4,
diff --git a/pip/samples/sample.ipynb b/pip/samples/sample.ipynb
index b50152ac..afe2336e 100644
--- a/pip/samples/sample.ipynb
+++ b/pip/samples/sample.ipynb
@@ -10,6 +10,12 @@
         "This enables the `%%qsharp` magic and initializes a Q# interpreter singleton."
       ]
     },
+    {
+      "cell_type": "markdown",
+      "id": "ed1b75bf",
+      "metadata": {},
+      "source": []
+    },
     {
       "cell_type": "code",
       "execution_count": 1,
@@ -55,7 +61,7 @@
               "<table>\n",
               "  <thead>\n",
               "    <tr>\n",
-              "      <th style=\"text-align: left\">Basis State<br />(|𝜓ₙ…𝜓₁⟩)</th>\n",
+              "      <th style=\"text-align: left\">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
               "      <th style=\"text-align: left\">Amplitude</th>\n",
               "      <th style=\"text-align: left\">Measurement Probability</th>\n",
               "      <th style=\"text-align: left\" colspan=\"2\">Phase</th>\n",
@@ -347,7 +353,7 @@
             "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
             "\u001b[0;31mQSharpError\u001b[0m                               Traceback (most recent call last)",
             "Cell \u001b[0;32mIn[7], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mqsharp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43meval\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43moperation Foo() : Unit \u001b[39;49m\u001b[38;5;124;43m{\u001b[39;49m\u001b[38;5;124;43m use q = Qubit(); X(q) } Foo()\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n",
-            "File \u001b[0;32m/workspaces/qsharp/pip/qsharp/_qsharp.py:120\u001b[0m, in \u001b[0;36meval\u001b[0;34m(source)\u001b[0m\n\u001b[1;32m    117\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcallback\u001b[39m(output: Output) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m    118\u001b[0m     \u001b[38;5;28mprint\u001b[39m(output)\n\u001b[0;32m--> 120\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mget_interpreter\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minterpret\u001b[49m\u001b[43m(\u001b[49m\u001b[43msource\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcallback\u001b[49m\u001b[43m)\u001b[49m\n",
+            "File \u001b[0;32m~/Programming/qsharp/pip/qsharp/_qsharp.py:120\u001b[0m, in \u001b[0;36meval\u001b[0;34m(source)\u001b[0m\n\u001b[1;32m    117\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcallback\u001b[39m(output: Output) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m    118\u001b[0m     \u001b[38;5;28mprint\u001b[39m(output)\n\u001b[0;32m--> 120\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mget_interpreter\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minterpret\u001b[49m\u001b[43m(\u001b[49m\u001b[43msource\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcallback\u001b[49m\u001b[43m)\u001b[49m\n",
             "\u001b[0;31mQSharpError\u001b[0m: Error: Qubit1 released while not in |0⟩ state\nCall stack:\n    at QIR.Runtime.__quantum__rt__qubit_release in core/qir.qs\n    at Foo in line_5\n\u001b[31mQsc.Eval.ReleasedQubitNotZero\u001b[0m\n\n  \u001b[31m×\u001b[0m runtime error\n\u001b[31m  ╰─▶ \u001b[0mQubit1 released while not in |0⟩ state\n   ╭─[\u001b[36;1;4mline_5\u001b[0m:1:1]\n \u001b[2m1\u001b[0m │ operation Foo() : Unit { use q = Qubit(); X(q) } Foo()\n   · \u001b[35;1m                         ────────┬───────\u001b[0m\n   ·                                  \u001b[35;1m╰── \u001b[35;1mQubit1\u001b[0m\u001b[0m\n   ╰────\n\u001b[36m  help: \u001b[0mqubits should be returned to the |0⟩ state before being released to\n        satisfy the assumption that allocated qubits start in the |0⟩ state\n"
           ]
         }
@@ -381,7 +387,7 @@
               "<table>\n",
               "  <thead>\n",
               "    <tr>\n",
-              "      <th style=\"text-align: left\">Basis State<br />(|𝜓ₙ…𝜓₁⟩)</th>\n",
+              "      <th style=\"text-align: left\">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
               "      <th style=\"text-align: left\">Amplitude</th>\n",
               "      <th style=\"text-align: left\">Measurement Probability</th>\n",
               "      <th style=\"text-align: left\" colspan=\"2\">Phase</th>\n",
@@ -390,7 +396,7 @@
               "  <tbody>\n",
               "    <tr>\n",
               "  <td style=\"text-align: left\">\n",
-              "    <span style=\"display: inline-block\">|011⟩</span>\n",
+              "    <span style=\"display: inline-block\">|110⟩</span>\n",
               "  </td>\n",
               "  <td style=\"text-align: left\">\n",
               "    <span style=\"display: inline-block\">1.0000+0.0000𝑖</span>\n",
@@ -410,7 +416,7 @@
             ],
             "text/plain": [
               "STATE:\n",
-              "|011⟩: 1.0000+0.0000𝑖"
+              "|110⟩: 1.0000+0.0000𝑖"
             ]
           },
           "metadata": {},
@@ -489,7 +495,7 @@
               "<table>\n",
               "  <thead>\n",
               "    <tr>\n",
-              "      <th style=\"text-align: left\">Basis State<br />(|𝜓ₙ…𝜓₁⟩)</th>\n",
+              "      <th style=\"text-align: left\">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
               "      <th style=\"text-align: left\">Amplitude</th>\n",
               "      <th style=\"text-align: left\">Measurement Probability</th>\n",
               "      <th style=\"text-align: left\" colspan=\"2\">Phase</th>\n",
@@ -542,7 +548,7 @@
               "<table>\n",
               "  <thead>\n",
               "    <tr>\n",
-              "      <th style=\"text-align: left\">Basis State<br />(|𝜓ₙ…𝜓₁⟩)</th>\n",
+              "      <th style=\"text-align: left\">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
               "      <th style=\"text-align: left\">Amplitude</th>\n",
               "      <th style=\"text-align: left\">Measurement Probability</th>\n",
               "      <th style=\"text-align: left\" colspan=\"2\">Phase</th>\n",
@@ -595,7 +601,7 @@
               "<table>\n",
               "  <thead>\n",
               "    <tr>\n",
-              "      <th style=\"text-align: left\">Basis State<br />(|𝜓ₙ…𝜓₁⟩)</th>\n",
+              "      <th style=\"text-align: left\">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
               "      <th style=\"text-align: left\">Amplitude</th>\n",
               "      <th style=\"text-align: left\">Measurement Probability</th>\n",
               "      <th style=\"text-align: left\" colspan=\"2\">Phase</th>\n",
@@ -648,7 +654,7 @@
               "<table>\n",
               "  <thead>\n",
               "    <tr>\n",
-              "      <th style=\"text-align: left\">Basis State<br />(|𝜓ₙ…𝜓₁⟩)</th>\n",
+              "      <th style=\"text-align: left\">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
               "      <th style=\"text-align: left\">Amplitude</th>\n",
               "      <th style=\"text-align: left\">Measurement Probability</th>\n",
               "      <th style=\"text-align: left\" colspan=\"2\">Phase</th>\n",
@@ -701,7 +707,7 @@
               "<table>\n",
               "  <thead>\n",
               "    <tr>\n",
-              "      <th style=\"text-align: left\">Basis State<br />(|𝜓ₙ…𝜓₁⟩)</th>\n",
+              "      <th style=\"text-align: left\">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>\n",
               "      <th style=\"text-align: left\">Amplitude</th>\n",
               "      <th style=\"text-align: left\">Measurement Probability</th>\n",
               "      <th style=\"text-align: left\" colspan=\"2\">Phase</th>\n",
@@ -810,7 +816,7 @@
         {
           "data": {
             "text/plain": [
-              "[One, One, Zero, One, Zero, One, One, Zero, One, Zero]"
+              "[One, Zero, Zero, Zero, Zero, Zero, Zero, One, Zero, Zero]"
             ]
           },
           "execution_count": 11,
@@ -837,7 +843,26 @@
       "execution_count": null,
       "id": "7bd77379",
       "metadata": {},
-      "outputs": [],
+      "outputs": [
+        {
+          "name": "stdout",
+          "output_type": "stream",
+          "text": [
+            "Requirement already satisfied: matplotlib in /opt/homebrew/lib/python3.11/site-packages (3.8.2)\n",
+            "Requirement already satisfied: contourpy>=1.0.1 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (1.2.0)\n",
+            "Requirement already satisfied: cycler>=0.10 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (0.12.1)\n",
+            "Requirement already satisfied: fonttools>=4.22.0 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (4.46.0)\n",
+            "Requirement already satisfied: kiwisolver>=1.3.1 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (1.4.5)\n",
+            "Requirement already satisfied: numpy<2,>=1.21 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (1.23.5)\n",
+            "Requirement already satisfied: packaging>=20.0 in /Users/swernli/Library/Python/3.11/lib/python/site-packages (from matplotlib) (23.2)\n",
+            "Requirement already satisfied: pillow>=8 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (10.1.0)\n",
+            "Requirement already satisfied: pyparsing>=2.3.1 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (3.1.1)\n",
+            "Requirement already satisfied: python-dateutil>=2.7 in /Users/swernli/Library/Python/3.11/lib/python/site-packages (from matplotlib) (2.8.2)\n",
+            "Requirement already satisfied: six>=1.5 in /opt/homebrew/lib/python3.11/site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)\n",
+            "Note: you may need to restart the kernel to use updated packages.\n"
+          ]
+        }
+      ],
       "source": [
         "%pip install matplotlib\n"
       ]
@@ -850,7 +875,7 @@
       "outputs": [
         {
           "data": {
-            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhYAAAGdCAYAAABO2DpVAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAWKElEQVR4nO3de4xUhdn48Wd1uyPK7iIIArKA1htesApoKG0Fq1KqBm2ijcFKkLRR1gsSkwptao3VxaSvsU3teomtTaNCGkNtqkLVFDAIFbAabZUKal0vgIruAm8cLTu/P5ru790iyMAzwODnk5yEOXPOnGcSZvebM2dnakqlUikAABLst6cHAAD2HcICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEhTu7sP2NnZGW+//XbU19dHTU3N7j48ALATSqVSbNy4MQYOHBj77bft8xK7PSzefvvtaGpq2t2HBQAStLW1xaBBg7Z5/24Pi/r6+oj492ANDQ27+/AAwE7o6OiIpqamrt/j27Lbw+I/b380NDQICwCoMp91GYOLNwGANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEhTdli89dZbcckll0SfPn2iR48eceKJJ8aKFSsqMRsAUGXK+q6QDz74IMaMGRPjxo2Lxx57LPr27RuvvPJKHHzwwZWaDwCoImWFxa233hpNTU3x61//umvd4Ycfnj4UAFCdynor5A9/+EOMHDkyLrzwwujXr1+cfPLJcc8992x3n2KxGB0dHd0WAGDfVNYZi1dffTVaW1tjxowZMWvWrFi+fHlcffXVUVdXF5MnT/7UfVpaWuLGG29MGRYgImLo9Y/s6RFgr/X67HP26PFrSqVSaUc3rquri5EjR8bTTz/dte7qq6+O5cuXx9KlSz91n2KxGMViset2R0dHNDU1RXt7ezQ0NOzC6MDnlbCAbatUWHR0dERjY+Nn/v4u662QAQMGxHHHHddt3bBhw+KNN97Y5j6FQiEaGhq6LQDAvqmssBgzZkysWrWq27p//OMfMWTIkNShAIDqVFZYXHvttbFs2bK45ZZbYvXq1fHAAw/E3XffHc3NzZWaDwCoImWFxahRo2LevHnx4IMPxgknnBA33XRT3H777TFp0qRKzQcAVJGy/iokIuLcc8+Nc889txKzAABVzneFAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkEZYAABphAUAkKassPjxj38cNTU13ZZjjz22UrMBAFWmttwdjj/++HjiiSf+/wPUlv0QAMA+quwqqK2tjf79+1diFgCgypV9jcUrr7wSAwcOjCOOOCImTZoUb7zxxna3LxaL0dHR0W0BAPZNZYXFaaedFvfdd1/Mnz8/Wltb47XXXouvfvWrsXHjxm3u09LSEo2NjV1LU1PTLg8NAOydakqlUmlnd/7www9jyJAhcdttt8XUqVM/dZtisRjFYrHrdkdHRzQ1NUV7e3s0NDTs7KGBz7Gh1z+yp0eAvdbrs8+pyON2dHREY2PjZ/7+3qUrL3v16hVHH310rF69epvbFAqFKBQKu3IYAKBK7NLnWGzatCnWrFkTAwYMyJoHAKhiZYXFddddF4sWLYrXX389nn766bjgggti//33j4svvrhS8wEAVaSst0LefPPNuPjii+P999+Pvn37xle+8pVYtmxZ9O3bt1LzAQBVpKywmDNnTqXmAAD2Ab4rBABIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIs0thMXv27KipqYnp06cnjQMAVLOdDovly5fHXXfdFcOHD8+cBwCoYjsVFps2bYpJkybFPffcEwcffHD2TABAldqpsGhubo5zzjknzjzzzOx5AIAqVlvuDnPmzIlnn302li9fvkPbF4vFKBaLXbc7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yvkcCwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANIICwAgjbAAANLsVFjccccdMXTo0DjggAPitNNOi2eeeSZ7LgCgCpUdFnPnzo0ZM2bEDTfcEM8++2ycdNJJMX78+Fi/fn0l5gMAqkjZYXHbbbfFd7/73ZgyZUocd9xxceedd8aBBx4Yv/rVryoxHwBQRWrL2fjjjz+OlStXxsyZM7vW7bfffnHmmWfG0qVLP3WfYrEYxWKx63Z7e3tERHR0dOzMvNvVWfzf9MeEfUklXnd7gtc6bFulXuf/edxSqbTd7coKi/feey+2bNkShx56aLf1hx56aLz88sufuk9LS0vceOONW61vamoq59BAgsbb9/QEQKVV+nW+cePGaGxs3Ob9ZYXFzpg5c2bMmDGj63ZnZ2ds2LAh+vTpEzU1NZU+PHtIR0dHNDU1RVtbWzQ0NOzpcYAK8Vr//CiVSrFx48YYOHDgdrcrKywOOeSQ2H///WPdunXd1q9bty769+//qfsUCoUoFArd1vXq1aucw1LFGhoa/LCBzwGv9c+H7Z2p+I+yLt6sq6uLESNGxJNPPtm1rrOzM5588skYPXp0+RMCAPuUst8KmTFjRkyePDlGjhwZp556atx+++2xefPmmDJlSiXmAwCqSNlh8e1vfzvefffd+NGPfhRr166NL33pSzF//vytLujk861QKMQNN9yw1dtgwL7Fa53/VlP6rL8bAQDYQb4rBABIIywAgDTCAgBIIywAgDTCgu1auHBh1NTUbHMZN27cnh4RqIC2tra47LLLYuDAgVFXVxdDhgyJa665Jt5///09PRp7OWHBdn35y1+Od955Z6vlrrvuipqampg2bdpOPe7HH3+cPCmQ5dVXX42RI0fGK6+8Eg8++GCsXr067rzzzq4PQ9ywYcOeHpG9mLBgu+rq6qJ///7dlg8++CCuu+66mDVrVlx44YUREfHiiy/GhAkTomfPnnHooYfGd77znXjvvfe6Hmfs2LFx5ZVXxvTp0+OQQw6J8ePHR0TEokWL4tRTT41CoRADBgyI66+/Pv71r3/tkecK/Ftzc3PU1dXFn/70pzj99NNj8ODBMWHChHjiiSfirbfeih/84AcRETF06NC45ZZb4rLLLov6+voYPHhw3H333d0eq62tLS666KLo1atX9O7dOyZOnBivv/76HnhW7C7CgrJ8+OGHMXHixBg7dmzcdNNNXevOOOOMOPnkk2PFihUxf/78WLduXVx00UXd9v3Nb34TdXV1sWTJkrjzzjvjrbfeim9+85sxatSoeP7556O1tTXuvffe+MlPfrInnhoQERs2bIgFCxbEtGnTokePHt3u69+/f0yaNCnmzp3b9dXZ//M//xMjR46Mv/71rzFt2rS44oorYtWqVRER8cknn8T48eOjvr4+nnrqqViyZEn07NkzvvGNbzhruS8rwQ7asmVLacKECaVhw4aVOjo6utbfdNNNpbPPPrvbtm1tbaWIKK1atapUKpVKp59+eunkk0/uts2sWbNKxxxzTKmzs7Nr3R133FHq2bNnacuWLRV8JsC2LFu2rBQRpXnz5n3q/bfddlspIkrr1q0rDRkypHTJJZd03dfZ2Vnq169fqbW1tVQqlUq//e1vt3qNF4vFUo8ePUoLFiyo6PNgz6n416az75g1a1YsXbo0nnnmmaivr+9a//zzz8ef//zn6Nmz51b7rFmzJo4++uiIiBgxYkS3+1566aUYPXp01NTUdK0bM2ZMbNq0Kd58880YPHhwhZ4J8FlKO/ihzMOHD+/6d01NTfTv3z/Wr18fEf/+2bB69epuPy8iIj766KNYs2ZN3rDsVYQFO2TOnDnx05/+NB555JE46qijut23adOmOO+88+LWW2/dar8BAwZ0/fuggw6q+JzArjnyyCOjpqYmXnrppbjgggu2uv+ll16Kgw8+OPr27RsREV/4whe63V9TUxOdnZ0R8e+fDSNGjIj7779/q8f5z/7se4QFn+m5556LqVOnxuzZs7suuvy/TjnllHjooYdi6NChUVu74/+lhg0bFg899FCUSqWusxZLliyJ+vr6GDRoUNr8wI7r06dPnHXWWfHLX/4yrr322m7XWaxduzbuv//+uPTSS7udadyWU045JebOnRv9+vWLhoaGSo7NXsTFm2zXe++9F+eff36MHTs2Lrnkkli7dm235d13343m5ubYsGFDXHzxxbF8+fJYs2ZNLFiwIKZMmRJbtmzZ5mNPmzYt2tra4qqrroqXX345Hn744bjhhhtixowZsd9+/mvCnvKLX/wiisVijB8/PhYvXhxtbW0xf/78OOuss+Kwww6Lm2++eYceZ9KkSXHIIYfExIkT46mnnorXXnstFi5cGFdffXW8+eabFX4W7Cl+erNdjzzySPzzn/+MRx99NAYMGLDVMmrUqBg4cGAsWbIktmzZEmeffXaceOKJMX369OjVq9d2A+Gwww6LRx99NJ555pk46aST4vLLL4+pU6fGD3/4w934DIH/dtRRR8WKFSviiCOOiIsuuii++MUvxve+970YN25cLF26NHr37r1Dj3PggQfG4sWLY/DgwfGtb30rhg0bFlOnTo2PPvrIGYx9mK9NBwDSOGMBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAmv8HvX2m8Y1VbDQAAAAASUVORK5CYII=",
+            "image/png": "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8skn4/TTT48RI0bE1q1bizEfAFBCCg6L2bNnx3e+850YN25cnHLKKXH77bfHYYcdFr/61a+KMR8AUELaFrLzRx99FOvXr4/a2trmbYccckicf/75sWrVqk88JpfLRS6Xa15vaGiIiIhsNtuaefeoKffP5M8JB5NivO/2B+912L1ivc//87z5fH6P+xUUFu+++240NjbGUUcd1WL7UUcdFS+88MInHlNXVxfTp0/fZXt1dXUhpwYSqJyzvycAiq3Y7/Pt27dHZWXlbh8vKCxao7a2NqZOndq83tTUFNu2bYvOnTtHWVlZsU/PfpLNZqO6ujrq6+ujoqJif48DFIn3+udHPp+P7du3R1VV1R73KygsjjzyyGjTpk1s2bKlxfYtW7ZEt27dPvGYTCYTmUymxbaOHTsWclpKWEVFhR828Dngvf75sKcrFf9R0M2b7dq1i/79+8ejjz7avK2pqSkeffTRGDRoUOETAgAHlYI/Cpk6dWqMHTs2BgwYEGeddVbMmTMndu7cGePGjSvGfABACSk4LL7xjW/EO++8EzfeeGNs3rw5vvjFL8aSJUt2uaGTz7dMJhM33XTTLh+DAQcX73X+V1n+0/7fCADAXvJdIQBAMsICAEhGWAAAyQgLACAZYcEeLV++PMrKyna7DB06dH+PCBRBfX19jB8/PqqqqqJdu3bRq1evuOaaa+K9997b36NxgBMW7NE555wTb7/99i7LHXfcEWVlZTFx4sRWPe9HH32UeFIglZdffjkGDBgQL730Utx3332xcePGuP3225t/GeK2bdv294gcwIQFe9SuXbvo1q1bi+X999+P6667Lq6//vq4+OKLIyLiueeei5qamujQoUMcddRR8a1vfSvefffd5ucZMmRIXHXVVTFlypQ48sgjY8SIERER8dhjj8VZZ50VmUwmunfvHtOmTYt//etf++W1Av82adKkaNeuXfzpT3+Kc889N3r27Bk1NTWxbNmyePPNN+OHP/xhRET07t07fvrTn8b48eOjvLw8evbsGXfeeWeL56qvr49LLrkkOnbsGJ06dYrRo0fHq6++uh9eFZ8VYUFBPvjggxg9enQMGTIkZsyY0bztvPPOizPOOCPWrVsXS5YsiS1btsQll1zS4th58+ZFu3btYuXKlXH77bfHm2++GV/72tdi4MCB8cwzz8TcuXPj7rvvjp/85Cf746UBEbFt27ZYunRpTJw4Mdq3b9/isW7dusWYMWNi0aJFzV+d/bOf/SwGDBgQTz31VEycODGuvPLK2LBhQ0REfPzxxzFixIgoLy+Pxx9/PFauXBkdOnSIr371q65aHszysJcaGxvzNTU1+T59+uSz2Wzz9hkzZuSHDx/eYt/6+vp8ROQ3bNiQz+fz+XPPPTd/xhlntNjn+uuvz5900kn5pqam5m233XZbvkOHDvnGxsYivhJgd1avXp2PiPzixYs/8fHZs2fnIyK/ZcuWfK9evfKXXXZZ82NNTU35rl275ufOnZvP5/P5X//617u8x3O5XL59+/b5pUuXFvV1sP8U/WvTOXhcf/31sWrVqlizZk2Ul5c3b3/mmWfiL3/5S3To0GGXYzZt2hQnnnhiRET079+/xWPPP/98DBo0KMrKypq3DR48OHbs2BFvvPFG9OzZs0ivBPg0+b38pcz9+vVr/nNZWVl069Yttm7dGhH//tmwcePGFj8vIiI+/PDD2LRpU7phOaAIC/bKwoUL45ZbbomHHnooTjjhhBaP7dixI0aNGhU333zzLsd17969+c+HH3540ecE9s3xxx8fZWVl8fzzz8dFF120y+PPP/98HHHEEdGlS5eIiPjCF77Q4vGysrJoamqKiH//bOjfv3/Mnz9/l+f5z/EcfIQFn+rpp5+OCRMmxKxZs5pvuvxvZ555Ztx///3Ru3fvaNt27/9K9enTJ+6///7I5/PNVy1WrlwZ5eXl0aNHj2TzA3uvc+fOMWzYsPjlL38Z1157bYv7LDZv3hzz58+Pyy+/vMWVxt0588wzY9GiRdG1a9eoqKgo5tgcQNy8yR69++67ceGFF8aQIUPisssui82bN7dY3nnnnZg0aVJs27YtLr300li7dm1s2rQpli5dGuPGjYvGxsbdPvfEiROjvr4+rr766njhhRfiwQcfjJtuuimmTp0ahxzirybsL7feemvkcrkYMWJErFixIurr62PJkiUxbNiwOProo2PmzJl79TxjxoyJI488MkaPHh2PP/54vPLKK7F8+fKYPHlyvPHGG0V+FewvfnqzRw899FC89tpr8fDDD0f37t13WQYOHBhVVVWxcuXKaGxsjOHDh8dpp50WU6ZMiY4dO+4xEI4++uh4+OGHY82aNXH66afHFVdcERMmTIgf/ehHn+ErBP7XCSecEOvWrYtjjz02LrnkkjjuuOPiu9/9bgwdOjRWrVoVnTp12qvnOeyww2LFihXRs2fP+PrXvx59+vSJCRMmxIcffugKxkHM16YDAMm4YgEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkvl/ckGCHgMKFIgAAAAASUVORK5CYII=",
             "text/plain": [
               "<Figure size 640x480 with 1 Axes>"
             ]
@@ -881,7 +906,21 @@
       "execution_count": null,
       "id": "eaf766f4",
       "metadata": {},
-      "outputs": [],
+      "outputs": [
+        {
+          "name": "stdout",
+          "output_type": "stream",
+          "text": [
+            "Requirement already satisfied: pandas in /opt/homebrew/lib/python3.11/site-packages (2.1.3)\n",
+            "Requirement already satisfied: numpy<2,>=1.23.2 in /opt/homebrew/lib/python3.11/site-packages (from pandas) (1.23.5)\n",
+            "Requirement already satisfied: python-dateutil>=2.8.2 in /Users/swernli/Library/Python/3.11/lib/python/site-packages (from pandas) (2.8.2)\n",
+            "Requirement already satisfied: pytz>=2020.1 in /opt/homebrew/lib/python3.11/site-packages (from pandas) (2023.3.post1)\n",
+            "Requirement already satisfied: tzdata>=2022.1 in /opt/homebrew/lib/python3.11/site-packages (from pandas) (2023.3)\n",
+            "Requirement already satisfied: six>=1.5 in /opt/homebrew/lib/python3.11/site-packages (from python-dateutil>=2.8.2->pandas) (1.16.0)\n",
+            "Note: you may need to restart the kernel to use updated packages.\n"
+          ]
+        }
+      ],
       "source": [
         "%pip install pandas\n"
       ]
@@ -892,20 +931,6 @@
       "id": "8095640a",
       "metadata": {},
       "outputs": [
-        {
-          "name": "stderr",
-          "output_type": "stream",
-          "text": [
-            "/tmp/ipykernel_3468/2211578213.py:1: DeprecationWarning: \n",
-            "Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),\n",
-            "(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)\n",
-            "but was not found to be installed on your system.\n",
-            "If this would cause problems for you,\n",
-            "please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466\n",
-            "        \n",
-            "  import pandas\n"
-          ]
-        },
         {
           "data": {
             "text/plain": [
@@ -918,7 +943,7 @@
         },
         {
           "data": {
-            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhYAAAGvCAYAAAAHapzzAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAXaklEQVR4nO3da4wVhfn48ecgckS7uyIIQlhRihVRwQpqCNWoqHi3NqmX0JRYa6KsV2JiqVG0xi6+qKUNFqmxJU1jMW1ibWrFCyLGAhYw3qsFBV2kiIruAo1HZff3onH//y1ge5Zn9+yBzyeZFzNnxnlMXPebmTk7hba2trYAAEjQq9IDAAC7D2EBAKQRFgBAGmEBAKQRFgBAGmEBAKQRFgBAGmEBAKTp3d0nbG1tjfXr10dNTU0UCoXuPj0A0AltbW2xefPmGDJkSPTqtfPrEt0eFuvXr4/6+vruPi0AkKCpqSmGDh2608+7PSxqamoi4t+D1dbWdvfpAYBOaGlpifr6+vbf4zvT7WHxxe2P2tpaYQEAVea/Pcbg4U0AII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSlB0W7777bnznO9+J/v37R9++fePoo4+OFStWdMVsAECVKetdIR999FFMmDAhTjnllHj00UfjwAMPjFWrVkW/fv26aj4AoIqUFRZ33XVX1NfXx69//ev2bYceemj6UABAdSrrVsif/vSnGDduXHz729+OgQMHxte//vW47777vvSYUqkULS0tHRYAYPdU1hWLt956K+bMmRPTpk2LH/7wh7F8+fK49tpro0+fPjFlypQdHtPY2Bi33357yrAAPdUhP3ik0iPQjdbOPKfSI/RYhba2trb/dec+ffrEuHHjYsmSJe3brr322li+fHksXbp0h8eUSqUolUrt6y0tLVFfXx/Nzc1RW1u7C6MD9BzCYs+yJ4ZFS0tL1NXV/dff32XdChk8eHCMGjWqw7Yjjjgi3nnnnZ0eUywWo7a2tsMCAOyeygqLCRMmxBtvvNFh2z/+8Y8YNmxY6lAAQHUqKyxuuOGGWLZsWfz4xz+O1atXxwMPPBC//OUvo6GhoavmAwCqSFlhcdxxx8VDDz0Uv/vd7+Koo46KO+64I2bNmhWTJ0/uqvkAgCpS1rdCIiLOPffcOPfcc7tiFgCgynlXCACQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGmEBQCQRlgAAGnKCovbbrstCoVCh2XkyJFdNRsAUGV6l3vAkUceGU8++eT/+wf0LvsfAQDspsqugt69e8dBBx3UFbMAAFWu7GcsVq1aFUOGDInhw4fH5MmT45133vnS/UulUrS0tHRYAIDdU1lhccIJJ8S8efNiwYIFMWfOnFizZk2ceOKJsXnz5p0e09jYGHV1de1LfX39Lg8NAPRMhba2trbOHvzxxx/HsGHD4u67747LL798h/uUSqUolUrt6y0tLVFfXx/Nzc1RW1vb2VMD9CiH/OCRSo9AN1o785xKj9DtWlpaoq6u7r/+/t6lJy/333//+NrXvharV6/e6T7FYjGKxeKunAYAqBK79HcstmzZEm+++WYMHjw4ax4AoIqVFRY33nhjLF68ONauXRtLliyJCy+8MPbaa6+49NJLu2o+AKCKlHUrZN26dXHppZfGhx9+GAceeGB84xvfiGXLlsWBBx7YVfMBAFWkrLCYP39+V80BAOwGvCsEAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEgjLACANMICAEizS2Exc+bMKBQKcf311yeNAwBUs06HxfLly2Pu3LkxevTozHkAgCrWqbDYsmVLTJ48Oe67777o169f9kwAQJXqVFg0NDTEOeecE6eddlr2PABAFetd7gHz58+P559/PpYvX/4/7V8qlaJUKrWv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0dbW1hWzAABVzt+xAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAIE2nwuKee+6JQw45JPbZZ5844YQT4m9/+1v2XABAFSo7LB588MGYNm1azJgxI55//vkYM2ZMTJo0KTZu3NgV8wEAVaTssLj77rvjiiuuiMsuuyxGjRoV9957b+y7777xq1/9qivmAwCqSO9ydv70009j5cqVMX369PZtvXr1itNOOy2WLl26w2NKpVKUSqX29ebm5oiIaGlp6cy8Va219K9Kj0A32hP/G9+T+fnes+yJP99f/Du3tbV96X5lhcUHH3wQ27Zti0GDBnXYPmjQoHj99dd3eExjY2Pcfvvt222vr68v59RQdepmVXoCoKvsyT/fmzdvjrq6up1+XlZYdMb06dNj2rRp7eutra2xadOm6N+/fxQKha4+PRXW0tIS9fX10dTUFLW1tZUeB0jk53vP0tbWFps3b44hQ4Z86X5lhcWAAQNir732ivfee6/D9vfeey8OOuigHR5TLBajWCx22Lb//vuXc1p2A7W1tf7HA7spP997ji+7UvGFsh7e7NOnT4wdOzYWLlzYvq21tTUWLlwY48ePL39CAGC3UvatkGnTpsWUKVNi3Lhxcfzxx8esWbNi69atcdlll3XFfABAFSk7LC6++OJ4//3349Zbb40NGzbEMcccEwsWLNjugU6I+PetsBkzZmx3Owyofn6+2ZFC23/73ggAwP/Iu0IAgDTCAgBIIywAgDTCAgBIIywAgDTCgi6zbt26WLduXaXHABJ9/vnn8eSTT8bcuXNj8+bNERGxfv362LJlS4Uno6cQFqRqbW2NH/3oR1FXVxfDhg2LYcOGxf777x933HFHtLa2Vno8YBe8/fbbcfTRR8cFF1wQDQ0N8f7770dExF133RU33nhjhaejp+jyl5CxZ7n55pvj/vvvj5kzZ8aECRMiIuLZZ5+N2267LT755JO48847Kzwh0FnXXXddjBs3Ll588cXo379/+/YLL7wwrrjiigpORk/iD2SRasiQIXHvvffG+eef32H7ww8/HFOnTo133323QpMBu6p///6xZMmSOPzww6OmpiZefPHFGD58eKxduzZGjRoV//rXvyo9Ij2AWyGk2rRpU4wcOXK77SNHjoxNmzZVYCIgS2tra2zbtm277evWrYuampoKTERPJCxINWbMmJg9e/Z222fPnh1jxoypwERAljPOOCNmzZrVvl4oFGLLli0xY8aMOPvssys3GD2KWyGkWrx4cZxzzjlx8MEHx/jx4yMiYunSpdHU1BR/+ctf4sQTT6zwhEBnrVu3LiZNmhRtbW2xatWqGDduXKxatSoGDBgQzzzzTAwcOLDSI9IDCAvSrV+/Pu655554/fXXIyLiiCOOiKlTp8aQIUMqPBmwqz7//POYP39+vPTSS7Fly5Y49thjY/LkydG3b99Kj0YPISxI89lnn8WZZ54Z9957bxx22GGVHgeACvB1U9Lsvffe8dJLL1V6DKALrVq1KhYtWhQbN27c7m/T3HrrrRWaip7EFQtS3XDDDVEsFmPmzJmVHgVIdt9998VVV10VAwYMiIMOOigKhUL7Z4VCIZ5//vkKTkdPISxIdc0118RvfvObOOyww2Ls2LGx3377dfj87rvvrtBkwK4aNmxYTJ06NW666aZKj0IPJixIdcopp+z0s0KhEE899VQ3TgNkqq2tjRdeeCGGDx9e6VHowYQFAP+Tyy+/PI477ri48sorKz0KPZiHN+kSq1evjjfffDNOOumk6Nu3b7S1tXW4HwtUnxEjRsQtt9wSS5cujdGjR8fee+/d4fNrr722QpPRk7hiQaoPP/wwLrrooli0aFEUCoVYtWpVDB8+PL73ve9Fv3794ic/+UmlRwQ66dBDD93pZ4VCId56661unIaeyhULUt1www2x9957xzvvvBNHHHFE+/aLL744pk2bJiygiq1ZsyYiIj744IOIiBgwYEAlx6GH8q4QUj3++ONx1113xdChQztsP+yww+Ltt9+u0FTArvr444+joaEhBgwYEIMGDYpBgwbFgAED4uqrr47m5uZKj0cP4ooFqbZu3Rr77rvvdts3bdoUxWKxAhMBu2rTpk0xfvz4ePfdd2Py5MntVyNfe+21mDdvXixcuDCWLFkS/fr1q/Ck9ASesSDV2WefHWPHjo077rgjampq4qWXXophw4bFJZdcEq2trfGHP/yh0iMCZbr++utj4cKF8eSTT8agQYM6fLZhw4Y444wzYuLEifHTn/60QhPSkwgLUr3yyisxceLEOPbYY+Opp56K888/P1599dXYtGlT/PWvf42vfvWrlR4RKNMhhxwSc+fOjUmTJu3w8wULFsSVV14Za9eu7d7B6JGEBemam5tj9uzZ8eKLL7a//bChoSEGDx5c6dGATigWi/Hmm29u9+zUF9atWxcjRoyITz75pJsnoyfyjAUpJk6cGA0NDfGtb30r6urq4uabb+7w+QcffBDDhw/3dTSoQgMGDIi1a9fuNCzWrFkTBxxwQDdPRU/lWyGkWLRoUVx00UUxY8aMHX6+bds23wqBKjVp0qS4+eab49NPP93us1KpFLfcckuceeaZFZiMnsitEFL06tUr5s6dGzfeeGOceuqp8dvf/rbDC8jee++9GDJkSGzbtq2CUwKdsW7duhg3blwUi8VoaGiIkSNHRltbW/z973+PX/ziF1EqlWLFihVRX19f6VHpAYQFKXr16hUbNmyIDz/8MC644IIoFovx8MMPt7+sSFhAdVuzZk1MnTo1Hn/88fji10ahUIjTTz89Zs+eHSNGjKjwhPQUwoIUX4TFwIEDo7m5OS699NJ47rnn4sEHH4zTTjtNWMBu4qOPPopVq1ZFxL/fHeLZCv6TZyxIV1dXF4888khcccUVcfbZZ/tuO+xG+vXrF8cff3wcf/zxooId8q0QUvznm0sLhULMnDkzjjnmmPj+978fTz31VIUmA6A7uWJBip3dUbvkkkvi2WefjZdffrmbJwKgElyxIMWiRYt2eln0mGOOiZUrV8YjjzzSzVMB0N08vAkApHErBABIIywAgDTCAgBIIywAgDTCAgBIIywAgDTCAgBIIywAgDT/BzRqNf3Uih03AAAAAElFTkSuQmCC",
+            "image/png": "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CjUbAFBi8gqLJ554Iqqrq2PFihXx97//PT755JMYM2ZM7Nixo1DzAQAlpFM+Oz/00EOt1ufPnx89evSI1atXxwknnJB0MACg9OQVFv+tvr4+IiIOOuigXe6Ty+Uil8u1rDc0NOzOKQGAdqzNYdHU1BRXXXVVjBo1Ko444ohd7ldbWxvTp09v62kASkK/6x4o9gjsQetnji/2CO1Wm78VUl1dHS+88EIsWrToC/erqamJ+vr6lqWurq6tpwQA2rk2XbG4/PLL469//WssXbo0evfu/YX7ZrPZyGazbRoOACgteYVFc3NzXHHFFXHvvffG448/Hv379y/UXABACcorLKqrq2PhwoVx//33R1lZWWzevDkiIioqKqJz584FGRAAKB15PWMxd+7cqK+vj+985zvRq1evluWee+4p1HwAQAnJ+1YIAMCueFcIAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQTN5hsXTp0jjjjDOisrIyMplM3HfffQUYCwAoRXmHxY4dO2LYsGFxyy23FGIeAKCEdcr3gHHjxsW4ceMKMQsAUOLyDot85XK5yOVyLesNDQ2FPiUAUCQFf3iztrY2KioqWpaqqqpCnxIAKJKCh0VNTU3U19e3LHV1dYU+JQBQJAW/FZLNZiObzRb6NABAO+DvWAAAyeR9xWL79u3x2muvtayvW7cunn322TjooIOiT58+SYcDAEpL3mGxatWqOPHEE1vWp0yZEhEREydOjPnz5ycbDAAoPXmHxXe+851obm4uxCwAQInzjAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJBMm8LilltuiX79+sV+++0Xxx13XDz99NOp5wIASlDeYXHPPffE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+vXrdxkW69ati4MOOmgPT0V75VshJPHYY4/FueeeG9OmTfvczxsbG30rBErU2LFj4/rrr4+PP/54p89yuVxMnTo1Tj311CJMRnvkVghJdOjQIebNmxfXXHNNnHTSSXHnnXe2egHZ22+/HZWVldHY2FjEKYG22LhxY4wYMSKy2WxUV1fH4MGDo7m5Of71r3/Fb37zm8jlcrFq1aqoqqoq9qi0A8KCJDp06BCbN2+O9957L84666zIZrNx//33t7ysSFhAaVu3bl1MmjQpHnnkkfjs10Ymk4lTTjkl5syZEwMHDizyhLQXwoIkPguLHj16RH19fVxwwQXx1FNPxT333BMnn3yysIC9xPvvvx9r166NiP+8O8SzFfw3z1iQXEVFRTzwwANxySWXxGmnnea77bAX6dKlSxx77LFx7LHHigo+l2+FkMR/v7k0k8nEzJkz46ijjoof/ehH8eijjxZpMgD2JFcsSGJXd9TOP//8ePLJJ+P555/fwxMBUAyuWJDEY489tsvLokcddVSsXr06HnjggT08FQB7moc3AYBk3AoBAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkMz/A0oPESovBdCBAAAAAElFTkSuQmCC",
             "text/plain": [
               "<Figure size 640x480 with 1 Axes>"
             ]
@@ -958,7 +983,7 @@
             "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
             "\u001b[0;31mQSharpError\u001b[0m                               Traceback (most recent call last)",
             "Cell \u001b[0;32mIn[14], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mqsharp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\"\"\u001b[39;49m\u001b[38;5;124;43mRandomBit(\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43ma\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m)\u001b[39;49m\u001b[38;5;124;43m\"\"\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m10\u001b[39;49m\u001b[43m)\u001b[49m\n",
-            "File \u001b[0;32m/workspaces/qsharp/pip/qsharp/_qsharp.py:165\u001b[0m, in \u001b[0;36mrun\u001b[0;34m(entry_expr, shots, on_result, save_events)\u001b[0m\n\u001b[1;32m    163\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m _ \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(shots):\n\u001b[1;32m    164\u001b[0m     results\u001b[38;5;241m.\u001b[39mappend({\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mevents\u001b[39m\u001b[38;5;124m\"\u001b[39m: []})\n\u001b[0;32m--> 165\u001b[0m     run_results \u001b[38;5;241m=\u001b[39m \u001b[43mget_interpreter\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    166\u001b[0m \u001b[43m        \u001b[49m\u001b[43mentry_expr\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mon_save_events\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43msave_events\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mprint_output\u001b[49m\n\u001b[1;32m    167\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    168\u001b[0m     results[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m][\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_results\n\u001b[1;32m    169\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m on_result:\n",
+            "File \u001b[0;32m~/Programming/qsharp/pip/qsharp/_qsharp.py:165\u001b[0m, in \u001b[0;36mrun\u001b[0;34m(entry_expr, shots, on_result, save_events)\u001b[0m\n\u001b[1;32m    163\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m _ \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(shots):\n\u001b[1;32m    164\u001b[0m     results\u001b[38;5;241m.\u001b[39mappend({\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mevents\u001b[39m\u001b[38;5;124m\"\u001b[39m: []})\n\u001b[0;32m--> 165\u001b[0m     run_results \u001b[38;5;241m=\u001b[39m \u001b[43mget_interpreter\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    166\u001b[0m \u001b[43m        \u001b[49m\u001b[43mentry_expr\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mon_save_events\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43msave_events\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mprint_output\u001b[49m\n\u001b[1;32m    167\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    168\u001b[0m     results[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m][\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_results\n\u001b[1;32m    169\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m on_result:\n",
             "\u001b[0;31mQSharpError\u001b[0m: \u001b[31mQsc.TypeCk.TyMismatch\u001b[0m\n\n  \u001b[31m×\u001b[0m type error\n\u001b[31m  ╰─▶ \u001b[0mexpected Unit, found String\n   ╭─[\u001b[36;1;4m<entry>\u001b[0m:1:1]\n \u001b[2m1\u001b[0m │ RandomBit(\"a\")\n   · \u001b[35;1m──────────────\u001b[0m\n   ╰────\n"
           ]
         }
@@ -1014,7 +1039,7 @@
             "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
             "\u001b[0;31mQSharpError\u001b[0m                               Traceback (most recent call last)",
             "Cell \u001b[0;32mIn[16], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mqsharp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mBad()\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m10\u001b[39;49m\u001b[43m)\u001b[49m\n",
-            "File \u001b[0;32m/workspaces/qsharp/pip/qsharp/_qsharp.py:165\u001b[0m, in \u001b[0;36mrun\u001b[0;34m(entry_expr, shots, on_result, save_events)\u001b[0m\n\u001b[1;32m    163\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m _ \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(shots):\n\u001b[1;32m    164\u001b[0m     results\u001b[38;5;241m.\u001b[39mappend({\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mevents\u001b[39m\u001b[38;5;124m\"\u001b[39m: []})\n\u001b[0;32m--> 165\u001b[0m     run_results \u001b[38;5;241m=\u001b[39m \u001b[43mget_interpreter\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    166\u001b[0m \u001b[43m        \u001b[49m\u001b[43mentry_expr\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mon_save_events\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43msave_events\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mprint_output\u001b[49m\n\u001b[1;32m    167\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    168\u001b[0m     results[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m][\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_results\n\u001b[1;32m    169\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m on_result:\n",
+            "File \u001b[0;32m~/Programming/qsharp/pip/qsharp/_qsharp.py:165\u001b[0m, in \u001b[0;36mrun\u001b[0;34m(entry_expr, shots, on_result, save_events)\u001b[0m\n\u001b[1;32m    163\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m _ \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(shots):\n\u001b[1;32m    164\u001b[0m     results\u001b[38;5;241m.\u001b[39mappend({\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mevents\u001b[39m\u001b[38;5;124m\"\u001b[39m: []})\n\u001b[0;32m--> 165\u001b[0m     run_results \u001b[38;5;241m=\u001b[39m \u001b[43mget_interpreter\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    166\u001b[0m \u001b[43m        \u001b[49m\u001b[43mentry_expr\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mon_save_events\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43msave_events\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mprint_output\u001b[49m\n\u001b[1;32m    167\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    168\u001b[0m     results[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m][\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mresult\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_results\n\u001b[1;32m    169\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m on_result:\n",
             "\u001b[0;31mQSharpError\u001b[0m: Error: Qubit1 released while not in |0⟩ state\nCall stack:\n    at QIR.Runtime.__quantum__rt__qubit_release in core/qir.qs\n    at Bad in line_9\n\u001b[31mQsc.Eval.ReleasedQubitNotZero\u001b[0m\n\n  \u001b[31m×\u001b[0m runtime error\n\u001b[31m  ╰─▶ \u001b[0mQubit1 released while not in |0⟩ state\n   ╭─[\u001b[36;1;4mline_9\u001b[0m:7:1]\n \u001b[2m7\u001b[0m │         // Do something bad, sometimes\n \u001b[2m8\u001b[0m │         use q2 = Qubit();\n   · \u001b[35;1m        ────────┬────────\u001b[0m\n   ·                 \u001b[35;1m╰── \u001b[35;1mQubit1\u001b[0m\u001b[0m\n \u001b[2m9\u001b[0m │         X(q2);\n   ╰────\n\u001b[36m  help: \u001b[0mqubits should be returned to the |0⟩ state before being released to\n        satisfy the assumption that allocated qubits start in the |0⟩ state\n"
           ]
         }
@@ -1040,7 +1065,7 @@
       "name": "python",
       "nbconvert_exporter": "python",
       "pygments_lexer": "ipython3",
-      "version": "3.12.1"
+      "version": "3.11.6"
     }
   },
   "nbformat": 4,
diff --git a/pip/src/state_header_template.html b/pip/src/state_header_template.html
index 44d301a9..2093cde6 100644
--- a/pip/src/state_header_template.html
+++ b/pip/src/state_header_template.html
@@ -1,7 +1,7 @@
 <table>
   <thead>
     <tr>
-      <th style="text-align: left">Basis State<br />(|𝜓ₙ…𝜓₁⟩)</th>
+      <th style="text-align: left">Basis State<br />(|𝜓₁…𝜓ₙ⟩)</th>
       <th style="text-align: left">Amplitude</th>
       <th style="text-align: left">Measurement Probability</th>
       <th style="text-align: left" colspan="2">Phase</th>
diff --git a/playground/src/state.tsx b/playground/src/state.tsx
index 5d613944..99830021 100644
--- a/playground/src/state.tsx
+++ b/playground/src/state.tsx
@@ -24,7 +24,7 @@ export function StateTable(props: { dump: Dump }) {
           <th>
             Basis State
             <br />
-            (|𝜓ₙ…𝜓₁⟩)
+            (|𝜓₁…𝜓ₙ⟩)
           </th>
           <th>Amplitude</th>
           <th>Measurement Probability</th>
diff --git a/vscode/src/webview/webview.tsx b/vscode/src/webview/webview.tsx
index 394af61b..4830dcdd 100644
--- a/vscode/src/webview/webview.tsx
+++ b/vscode/src/webview/webview.tsx
@@ -83,10 +83,6 @@ function onMessage(event: any) {
         };
         // Copy over any existing estimates
         if ((state as EstimatesState).estimatesData?.estimates) {
-          // Clear the new flag on any existing estimates
-          (state as EstimatesState).estimatesData.estimates.map(
-            (estimate) => (estimate.new = false),
-          );
           newState.estimatesData.estimates.push(
             ...(state as EstimatesState).estimatesData.estimates,
           );
@@ -94,11 +90,8 @@ function onMessage(event: any) {
         // Append any new estimates
         if (message.estimates) {
           if (Array.isArray(message.estimates)) {
-            // Mark all the new estimates
-            message.estimates.map((estimate: ReData) => (estimate.new = true));
             newState.estimatesData.estimates.push(...message.estimates);
           } else {
-            message.estimates.new = true;
             newState.estimatesData.estimates.push(message.estimates);
           }
         }


Test patch:
diff --git a/compiler/qsc/src/interpret/tests.rs b/compiler/qsc/src/interpret/tests.rs
index 1d9121df..693f1247 100644
--- a/compiler/qsc/src/interpret/tests.rs
+++ b/compiler/qsc/src/interpret/tests.rs
@@ -330,7 +330,7 @@ mod given_interpreter {
             let (result, output) = line(&mut interpreter, "X(q0); X(qs[1]);");
             is_only_value(&result, &output, &Value::unit());
             let (result, output) = line(&mut interpreter, "DumpMachine()");
-            is_unit_with_output(&result, &output, "STATE:\n|0101⟩: 1+0i");
+            is_unit_with_output(&result, &output, "STATE:\n|1010⟩: 1+0i");
         }
 
         #[test]
diff --git a/compiler/qsc_eval/src/intrinsic/tests.rs b/compiler/qsc_eval/src/intrinsic/tests.rs
index 4c05191d..502debc4 100644
--- a/compiler/qsc_eval/src/intrinsic/tests.rs
+++ b/compiler/qsc_eval/src/intrinsic/tests.rs
@@ -281,7 +281,7 @@ fn dump_machine_endianness() {
         }"},
         &expect![[r#"
             STATE:
-            |0010⟩: 1+0i
+            |0100⟩: 1+0i
         "#]],
     );
 }
@@ -1045,7 +1045,7 @@ fn qubit_nested_bind_not_released() {
         }"},
         &expect![[r#"
             STATE:
-            |10⟩: 1+0i
+            |01⟩: 1+0i
         "#]],
     );
 }
diff --git a/npm/test/basics.js b/npm/test/basics.js
index 049b9af5..7fcc3ef5 100644
--- a/npm/test/basics.js
+++ b/npm/test/basics.js
@@ -12,11 +12,11 @@ import {
   getLanguageService,
   getLanguageServiceWorker,
   getDebugServiceWorker,
+  utils,
 } from "../dist/main.js";
 import { QscEventTarget } from "../dist/compiler/events.js";
 import { getAllKatas, getExerciseSources, getKata } from "../dist/katas.js";
 import samples from "../dist/samples.generated.js";
-import { CreateIntegerTicks, CreateTimeTicks } from "../dist/ux/ticks.js";
 
 /** @type {import("../dist/log.js").TelemetryEvent[]} */
 const telemetryEvents = [];
@@ -1146,7 +1146,7 @@ function getLabels(ticks) {
 function runAndAssertIntegerTicks(min, max, expected) {
   const message = `min: ${min}, max: ${max}`;
   assert.deepStrictEqual(
-    getValues(CreateIntegerTicks(min, max)),
+    getValues(utils.CreateIntegerTicks(min, max)),
     expected,
     message,
   );
@@ -1155,7 +1155,7 @@ function runAndAssertIntegerTicks(min, max, expected) {
 function runAndAssertTimeTicks(min, max, expected) {
   const message = `min: ${min}, max: ${max}`;
   assert.deepStrictEqual(
-    getLabels(CreateTimeTicks(min, max)),
+    getLabels(utils.CreateTimeTicks(min, max)),
     expected,
     message,
   );
diff --git a/pip/tests/test_interpreter.py b/pip/tests/test_interpreter.py
index 9adbad8c..43c91276 100644
--- a/pip/tests/test_interpreter.py
+++ b/pip/tests/test_interpreter.py
@@ -34,7 +34,7 @@ def test_dump_output() -> None:
     def callback(output):
         nonlocal called
         called = True
-        assert output.__repr__() == "STATE:\n|01⟩: 1.0000+0.0000𝑖"
+        assert output.__repr__() == "STATE:\n|10⟩: 1.0000+0.0000𝑖"
 
     called = False
     value = e.interpret(
@@ -72,7 +72,7 @@ def test_dump_machine() -> None:
     e = Interpreter(TargetProfile.Unrestricted)
 
     def callback(output):
-        assert output.__repr__() == "STATE:\n|01⟩: 1.0000+0.0000𝑖"
+        assert output.__repr__() == "STATE:\n|10⟩: 1.0000+0.0000𝑖"
 
     value = e.interpret(
         """
@@ -86,11 +86,11 @@ def test_dump_machine() -> None:
     state_dump = e.dump_machine()
     assert state_dump.qubit_count == 2
     assert len(state_dump) == 1
-    assert state_dump[1][0] == 1.0
-    assert state_dump[1][1] == 0.0
+    assert state_dump[2][0] == 1.0
+    assert state_dump[2][1] == 0.0
     state_dict = state_dump.get_dict()
-    assert state_dict[1][0] == 1.0
-    assert state_dict[1][1] == 0.0
+    assert state_dict[2][0] == 1.0
+    assert state_dict[2][1] == 0.0
 
 
 def test_error() -> None:
diff --git a/pip/tests/test_qsharp.py b/pip/tests/test_qsharp.py
index e20848bc..574603c4 100644
--- a/pip/tests/test_qsharp.py
+++ b/pip/tests/test_qsharp.py
@@ -72,7 +72,7 @@ def test_dump_machine() -> None:
     state_dump = qsharp.dump_machine()
     assert state_dump.qubit_count == 2
     assert len(state_dump) == 1
-    assert state_dump[1] == (1.0, 0.0)
+    assert state_dump[2] == (1.0, 0.0)
     qsharp.eval("X(q2);")
     state_dump = qsharp.dump_machine()
     assert state_dump.qubit_count == 2
@@ -87,12 +87,33 @@ def test_dump_operation() -> None:
     res = qsharp.utils.dump_operation("qs => H(qs[0])", 1)
     assert res == [[complex(0.707107, 0.0), complex(0.707107, 0.0)],
                    [complex(0.707107, 0.0), complex(-0.707107, 0.0)]]
+    res = qsharp.utils.dump_operation("qs => CNOT(qs[0], qs[1])", 2)
+    assert res == [[complex(1.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0)],
+                   [complex(0.0, 0.0), complex(1.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0)],
+                   [complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(1.0, 0.0)],
+                   [complex(0.0, 0.0), complex(0.0, 0.0), complex(1.0, 0.0), complex(0.0, 0.0)]]
+    res = qsharp.utils.dump_operation("qs => CCNOT(qs[0], qs[1], qs[2])", 3)
+    assert res == [[complex(1.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0)],
+                   [complex(0.0, 0.0), complex(1.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0)],
+                   [complex(0.0, 0.0), complex(0.0, 0.0), complex(1.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0)],
+                   [complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(1.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0)],
+                   [complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(1.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0)],
+                   [complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(1.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0)],
+                   [complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(1.0, 0.0)],
+                   [complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(1.0, 0.0), complex(0.0, 0.0)]]
     qsharp.eval("operation ApplySWAP(qs : Qubit[]) : Unit { SWAP(qs[0], qs[1]); }")
     res = qsharp.utils.dump_operation("ApplySWAP", 2)
     assert res == [[complex(1.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0)],
                    [complex(0.0, 0.0), complex(0.0, 0.0), complex(1.0, 0.0), complex(0.0, 0.0)],
                    [complex(0.0, 0.0), complex(1.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0)],
                    [complex(0.0, 0.0), complex(0.0, 0.0), complex(0.0, 0.0), complex(1.0, 0.0)]]
+    res = qsharp.utils.dump_operation("qs => ()", 8)
+    for i in range(8):
+        for j in range(8):
+            if i == j:
+                assert res[i][j] == complex(1.0, 0.0)
+            else:
+                assert res[i][j] == complex(0.0, 0.0)
 
 
 def test_compile_qir_input_data() -> None: