> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.conductorquantum.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.conductorquantum.com/_mcp/server.

# Using the Anticrossing Detector

> Learn how to detect anticrossings and resonance features

## Input Format

The detector expects a **visualization-ready** tensor shaped `(2, n_freqs, n_sweeps)`:

| Axis    | Index           | Dimension  | Description                          |
| ------- | --------------- | ---------- | ------------------------------------ |
| Channel | `data[0]`       | -          | Magnitude (dB)                       |
| Channel | `data[1]`       | -          | Phase (degrees)                      |
| Rows    | `data[c, :, :]` | `n_freqs`  | Frequency bins (y-axis when plotted) |
| Columns | `data[c, :, :]` | `n_sweeps` | Sweep voltages (x-axis when plotted) |

This is the same layout you'd pass to `imshow()` or `pcolormesh()` - frequency on the vertical axis, sweep voltage on the horizontal axis.

> **Tip**
>
> **Coming from acquisition-format data?** If your data is stored as `(n_sweeps, n_freqs)` - one frequency trace per sweep point - transpose before calling the API. This matches the typical visualization convention with sweep voltage on the x-axis and frequency on the y-axis:
>
> ```python
> # If mags.shape == (n_sweeps, n_freqs):
> api_input = np.stack([mags.T, phases.T], axis=0)  # → (2, n_freqs, n_sweeps)
> ```

## Output Format

All indices are returned as `[freq_idx, sweep_idx]` pairs - matching `[row, col]` in your input array:

| Key                     | Type              | Description                                                             |
| ----------------------- | ----------------- | ----------------------------------------------------------------------- |
| `frequency_fit_indices` | `list[list[int]]` | Fitted resonance position at each sweep: `[[freq_idx, sweep_idx], ...]` |
| `anticrossing_indices`  | `list[list[int]]` | Detected anticrossing centers                                           |
| `peak_indices`          | `list[list[int]]` | Local frequency maxima near anticrossings                               |
| `valley_indices`        | `list[list[int]]` | Local frequency minima near anticrossings                               |
| `score`                 | `float`           | Quality score (0–1, higher = stronger anticrossing signal)              |

## Usage Example

**`Python`**

```python title="Python"
import os
import numpy as np
from dotenv import load_dotenv
from conductorquantum import ConductorQuantum

load_dotenv()
client = ConductorQuantum(token=os.getenv("CONDUCTOR_API_KEY"))

# Load your measurement data: shape (2, n_freqs, n_sweeps)
data = np.load("path/to/your/data.npy")
magnitudes = data[0]  # shape: (n_freqs, n_sweeps)
phases = data[1]

results = client.control.models.run(
    model="anticrossing-detector-v0",
    data=data,
)

print(f"Results output: {results.output}")
output = results.output
frequency_fit_indices = np.asarray(output["frequency_fit_indices"])
anticrossing_indices = np.asarray(output["anticrossing_indices"])
peak_indices = np.asarray(output["peak_indices"])
valley_indices = np.asarray(output["valley_indices"])
```

**`Sample output`**

```python title="Sample output"
Results output: {'frequency_fit_indices': [[392, 0], [392, 1], [393, 2], [391, 3], [394, 4], [390, 5], [391, 6], [391, 7], [393, 8], [392, 9], [391, 10], [393, 11], [392, 12], [390, 13], [392, 14], [390, 15], [391, 16], [389, 17], [390, 18], [387, 19], [389, 20], [390, 21], [389, 22], [389, 23], [385, 24], [385, 25], [385, 26], [383, 27], [381, 28], [379, 29], [375, 30], [364, 31], [359, 32], [326, 33], [489, 34], [425, 35], [420, 36], [412, 37], [415, 38], [412, 39], [408, 40], [408, 41], [409, 42], [409, 43], [409, 44], [410, 45], [411, 46], [409, 47], [411, 48], [409, 49], [416, 50], [414, 51], [414, 52], [415, 53], [416, 54], [415, 55], [417, 56], [418, 57], [417, 58], [417, 59], [422, 60], [419, 61], [420, 62], [421, 63], [424, 64], [423, 65], [427, 66], [430, 67], [432, 68], [430, 69], [435, 70], [442, 71], [444, 72], [450, 73], [459, 74], [465, 75], [471, 76], [481, 77], [493, 78], [525, 79], [572, 80], [647, 81], [767, 82], [800, 83], [176, 84], [231, 85], [267, 86], [298, 87], [306, 88], [322, 89], [332, 90], [340, 91], [344, 92], [349, 93], [354, 94], [361, 95], [364, 96], [365, 97], [368, 98], [369, 99], [370, 100], [374, 101], [374, 102], [373, 103], [376, 104], [376, 105], [377, 106], [380, 107], [379, 108], [380, 109], [380, 110], [382, 111], [382, 112], [381, 113], [383, 114], [384, 115], [384, 116], [386, 117], [384, 118], [385, 119], [386, 120], [385, 121], [386, 122], [388, 123], [388, 124], [387, 125], [388, 126]], 'anticrossing_indices': [[489, 34], [800, 83]], 'peak_indices': [[489, 34], [800, 83]], 'valley_indices': [[326, 33], [176, 84]], 'score': 0.6434958298396819}
```

### Visualize the detections

With the arrays from the previous step, overlay the detector outputs on the magnitude channel:

**`Python`**

```python title="Python"
import matplotlib.pyplot as plt

freq_axis = np.arange(magnitudes.shape[0])
sweep_axis = np.arange(magnitudes.shape[1])

fig, ax = plt.subplots(figsize=(10, 6))
im = ax.imshow(
    magnitudes,
    origin="lower",
    aspect="auto",
    extent=[sweep_axis.min(), sweep_axis.max(), freq_axis.min(), freq_axis.max()],
    cmap="viridis",
)
fig.colorbar(im, ax=ax, label="Magnitude (a.u.)")


def scatter_indices(indices_array, color, label, marker="o"):
    if indices_array.size == 0:
        return
    rows = indices_array[:, 0]
    cols = indices_array[:, 1]
    ax.scatter(
        cols,
        rows,
        s=80,
        c=color,
        marker=marker,
        edgecolor="white",
        linewidth=2,
        label=label,
    )


scatter_indices(frequency_fit_indices, color="red", label="Frequency fit", marker=".")
scatter_indices(peak_indices, color="cyan", label="Peak", marker="^")
scatter_indices(valley_indices, color="magenta", label="Valley", marker="v")

if anticrossing_indices.size:
    unique_columns = np.unique(anticrossing_indices[:, 1])
    for idx, column in enumerate(unique_columns):
        ax.axvline(
            x=sweep_axis[column],
            color="black",
            linestyle="--",
            linewidth=2,
            alpha=0.9,
            label="Anticrossing" if idx == 0 else "_nolegend_",
        )

ax.set_xlabel("Sweep voltage (a.u.)")
ax.set_ylabel("Frequency (a.u.)")
ax.set_title(f"Anticrossing Detector v0\nScore = {output['score']:.3f}")
ax.legend(loc="upper right", frameon=False)
plt.tight_layout()
plt.show()
```

Scatter overlays highlight each feature family while dashed vertical lines mark anticrossings, making it easy to validate frequency fits alongside neighboring peak and valley structures directly on the measurement.

### Using physical units

To plot with real voltage and frequency values, use `extent` and convert indices:

**`Python`**

```python title="Python"
# Your physical axes
sweep_voltages = np.linspace(-3.5, -3.3, magnitudes.shape[1])  # (n_sweeps,)
frequencies_ghz = np.linspace(7.1, 7.2, magnitudes.shape[0])   # (n_freqs,)

fig, ax = plt.subplots(figsize=(10, 6))
im = ax.imshow(
    magnitudes,
    origin="lower",
    aspect="auto",
    extent=[sweep_voltages.min(), sweep_voltages.max(),
            frequencies_ghz.min(), frequencies_ghz.max()],
    cmap="viridis",
)

# Convert indices to physical coordinates
if peak_indices.size:
    peak_freqs = frequencies_ghz[peak_indices[:, 0]]
    peak_volts = sweep_voltages[peak_indices[:, 1]]
    ax.scatter(peak_volts, peak_freqs, c="cyan", marker="^", s=150,
               edgecolor="black", label="Peak")

ax.set_xlabel("Sweep Voltage (V)")
ax.set_ylabel("Frequency (GHz)")
```