agentleFS
pasqal-iopasqal-io/agent-marketplace/skills/noise-emulate/SKILL.md

noise-emulate

Skill9 starsChanged 58 days agoApache-2.0View on GitHub

Emulate a neutral-atom Pulser sequence's time evolution under the target Rydberg device's live noise model, producing noiseless and noisy trajectory curves with a quantile envelope. Asks where to run — this machine, a GPU cluster, or Pasqal Cloud emulators. Use this to study how noise shapes a signal over time; use validate-emu to decide whether an experiment is worth submitting. Triggered by phrases like "run a noise emulation of this sequence", "emu-mps with noise", "noise envelope".

  • Reads credentials
  • Installs packages

The file

We split this skill into the rules it gives an agent

6 rules. About 0 are common, 2 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Not even under "just run the noise study".

    line 34
  2. Distinctive:

    After about three failed attempts at the same obstacle — a builder that will not import, jobs that keep erroring, an out-of-memory at every …

    line 42
  3. Very specific:

    If a suitable venv already exists, point the skill at it with export PULSER_VENV=<path>.

    line 186
  4. Very specific:

    Write seq_builder.py with this signature (fully built, not parametric):

    line 195
  5. Very specific:

    Use AnalogDevice for emulation.

    line 209
  6. Very specific:

    If --cal-offsets was used and the calibration band is much wider than the stochastic band, flag that calibration precision matters more …

    line 374

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

noise-emulate

Emulate a Pulser sequence's time evolution with the target device's noise model (fetched live from the Pasqal Cloud — includes SPAM, amplitude, dephasing, detuning, Doppler, relaxation).

Choosing the device — all modes accept --device-name (default FRESNEL_CAN1):

  • FRESNEL_CAN1 (default): no extra setup.
  • SA1: pass --device-name SA1 and set PASQAL_REGION=sa (SA1 lives in the sa cloud region and is invisible without it); your project must have SA1 access. Verify the exact device key with sdk.get_device_specs_dict().keys().
  • Devices not on the cloud SDK (an on-premise QPU reached through a cluster, for instance): no live noise model is available. Build against AnalogDevice constraints instead, and submit through submit-to-cea.

The user never needs to touch the Python scripts: this skill reads their sequence, writes the builder, runs everything, and shows the result.

Decisions that are not yours

Not even under "just run the noise study". Ask, recommend, and wait:

  • where it runs — this machine, the cluster, or the cloud (Step 0), and for the cloud, which project pays
  • the budget — N, the bond dimension χ, the number of trajectories, the number of time points (Step 0's budget question)
  • whose noise model runs, if the spec carries one of its own (--noise-source)

After about three failed attempts at the same obstacle — a builder that will not import, jobs that keep erroring, an out-of-memory at every χ — stop. Report what was tried, what failed and what is known, then offer a smaller system, a different mode, or abandoning the study. It is a diagnostic, not a gate: "we could not run this affordably" is an acceptable outcome, and a better one than a week of GPU time.

Where the outputs land

Beside the experiment they belong to, as they are produced:

experiments/<name>/results/noise/    .npz or .json data, logs, run figures
experiments/<name>/figures/          figures made for the report
experiments/<name>/analysis/         any script you write to read these files
experiments/<name>/NOTEBOOK.md       one appended block per run

Each script prints where it runs as its first line (RUNS ON: …) — say the same to the user before starting, because "the envelope is ±0.03" means one thing from 40 GPU trajectories and another from 5 cloud batches.

Support scripts

Paths like support/… below are relative to this skill's directory — expand them to the skill's absolute location when running commands from your project directory (keep outputs like --out-dir in your project, not the plugin).

support/
  run_noise_emu.py          ← MPS trajectory runner (local + cluster modes)
  plot_noise_emu.py         ← trajectory envelope figure
  submit_slurm.sh           ← turnkey SLURM launcher (one GPU job per trajectory)
  run_noise_emu_cloud.py    ← Pasqal Cloud EMU_MPS runner (cloud mode)
  plot_noise_emu_cloud.py   ← cloud results figure
  requirements.txt          ← Python dependencies (local/cluster modes)
  spec_noise.py             ← the source's own noise model, if it has one (shared)
  batch_tags.py             ← the labels every batch carries (shared, do not edit here)
  pasqal_auth.py            ← credentials, projects and credits (shared, do not edit here)

Step 0 — Ask the user how to run

Always ask this first. Present the three options as a choice — use your harness's interactive question mechanism if it has one, plain text otherwise — with the recommended option marked:

Where should the emulation run?

  1. Locally — on this machine. Fine for small systems / quick tests; a CUDA GPU helps a lot. Trajectories run sequentially in one process.
  2. On a GPU cluster (recommended) — one GPU job per trajectory, all trajectories in parallel. By far the fastest for full 40-trajectory envelopes. Requires sbatch on the current machine.
  3. Via Pasqal Cloud — runs on the Pasqal Cloud EMU_MPS emulator fleet. No GPU or cluster needed, only Pasqal Cloud credentials. Best when you have no local compute; queue times apply and quality degrades for large registers (N ≳ 60–100).

Quick heuristics if the user has no preference: which sbatch succeeds → cluster; otherwise a CUDA GPU present (nvidia-smi) → local; otherwise → cloud.

The budget question — N, χ and trajectories

Users arrive with a register size and expect the emulator to either handle it or refuse. It does neither: it runs whatever it is given, and how much of the answer is real depends on --max-chi. Explain the three knobs before agreeing to a run, because they trade against each other:

KnobWhat it costsWhat too little looks like
N (atoms)bond dimension needed grows with entanglement, not with N alonenothing — the failure shows up as χ being too small
--max-chi (χ)memory and time, steeplya smooth, plausible, wrong curve
--n-trajlinear; each trajectory is an independent GPU job on SLURMa noise band too jagged to read

χ is an accuracy knob, not a size knob. Lowering it to fit 60 atoms on one GPU does not trade size for a coarser answer, it trades size for an answer whose error is invisible: MPS truncation does not warn, it just returns a state that cannot hold the entanglement the dynamics generated. An adiabatic sweep through a transition is the worst case, since that is where entanglement peaks.

Do not go hunting for the right χ. It is the most tempting way to burn a week here, and it does not converge on an answer: at a size where χ matters, no χ this GPU can hold is ever large enough, and each attempt costs more than the last. The decision has two branches and no search:

  • Inside the calibrated envelope → run once, no convergence study. The --max-chi default of 128 was validated against χ=200 for S(π,π), local and two-point observables on systems up to 6×6, agreeing to ~0.05% (t_fall sweep, 2026-06). That class of observable, at that size, needs nothing further.
  • Outside it → change the register, not χ. Take a sub-system: a smaller register with the same geometry motif, the same R_b/a and the same protocol, small enough to sit inside the envelope, and check that it reproduces something approaching the expected signal. That validates the approach. The full-size number is then the QPU's job, not the emulator's.

One step up in χ, if the user asks for it, is fine. A sweep over χ is not: it answers a question the QPU is going to answer anyway, and a curve defended by "we tried several χ and picked the one that looked stable" is not defensible.

Which observable is asked for decides which branch you are in: ⟨n⟩, S(K) and correlators need far less χ than entanglement entropy, full bitstring distributions or higher moments — those leave the calibrated envelope immediately, whatever N is.

--n-traj 5 --max-chi 64 stays useful as a smoke test, whose curve nobody should quote.

Then read the user's sequence carefully:

  • Register layout? (N×N square, chain, custom)
  • Channel? (EOM or standard rydberg_global)
  • Parametric variable? (hx, T, R, …)

First-time setup (all modes)

Pasqal Cloud credentials (needed in every mode — the noise model is fetched live). Each field is resolved independently: environment variables first, then the system keyring (password only), then ~/.pasqal_credentials.json.

export PASQAL_USERNAME=... PASQAL_PASSWORD=... PASQAL_PROJECT_ID=...

On a shared machine or in a cluster job script, prefer the environment variables — they are the only option that keeps the password off disk. Alternatively run any of the scripts below in a terminal with nothing configured: it offers a one-time setup that puts the password in the OS keyring and only the username and project ID in ~/.pasqal_credentials.json (chmod 600). Storing the password in that file works too, and the scripts will warn you that it is plaintext.

Python environment (local and cluster modes):

python3 -m venv ~/pulser-venv
source ~/pulser-venv/bin/activate
pip install -r support/requirements.txt

CI pins pulser 1.9.0 and checks that sequences build and observables are correct against it. The full local stack — emu-mps 2.7.5, torch 2.9.0, pasqal-cloud 0.22.0 — was last exercised end-to-end in 2026-06 against pulser 1.8.0, and has not been re-run since; treat a failure on a newer pulser as a version problem before assuming your sequence is wrong. For GPU, install PyTorch for your CUDA version from pytorch.org first. If a suitable venv already exists, point the skill at it with export PULSER_VENV=<path>. Cloud mode only needs pulser, pulser-pasqal and pasqal-cloud (no emu-mps/torch).


Step 1 — Write the sequence builder

Local / cluster modes

Write seq_builder.py with this signature (fully built, not parametric):

def build_sequence(
    N:            int   = 6,
    hx:           float = 4.0,
    t:            int   = 4000,     # pulse duration in ns
    omega_offset: float = 1.0,      # multiplicative Ω scale
    delta_offset: float = 0.0,      # additive δ in MHz
    R_offset:     float = 1.0,      # multiplicative lattice spacing scale
    **kwargs,
) -> pulser.Sequence: ...

Use AnalogDevice for emulation; call with_automatic_layout(AnalogDevice) if spacing < 10 µm. For EOM/quench sequences use the mean-field detuning at the central site:

import numpy as np, pulser
from pulser import Sequence
from pulser.devices import AnalogDevice
from pulser.register.special_layouts import SquareLatticeLayout

def build_sequence(N=6, hx=4.0, t=4000,
                   omega_offset=1.0, delta_offset=0.0, R_offset=1.0, **kwargs):
    omega = 2 * np.pi * 2.0
    C6    = AnalogDevice.interaction_coeff
    R     = (hx * 2 * C6 / (4 * omega)) ** (1/6)
    coords = SquareLatticeLayout(N, N, R * R_offset).coords
    register = pulser.Register.from_coordinates(coords, prefix="q")
    if R * R_offset < 10:
        register = register.with_automatic_layout(AnalogDevice)
    ci   = (N//2)*N + N//2
    dist = np.linalg.norm(coords - coords[ci], axis=-1); dist[ci] = np.inf
    delta = C6 * np.sum(1.0 / dist**6) + 2 * np.pi * delta_offset
    seq = Sequence(register, AnalogDevice)
    seq.declare_channel("ising", "rydberg_global")
    seq.enable_eom_mode("ising", amp_on=omega*omega_offset, detuning_on=delta)
    seq.add_eom_pulse("ising", duration=t, phase=0.0)
    seq.disable_eom_mode("ising")
    return seq

Cloud mode

The cloud runner follows the spec-to-sequence contract instead: the file must export build_sequence(device=None, **params) (observation time in ns as a kwarg, default name t) and compute_observable(counts) -> float. If the user brings a raw sequence, wrap it into this contract; default to mean Rydberg density if no observable is specified, and say so.


Step 2 — Run

Mode 1 — Local

source "${PULSER_VENV:-$HOME/pulser-venv}/bin/activate"

python support/run_noise_emu.py \
    --seq-file    experiments/<name>/<name>_sequence.py \
    --fn-name     build_sequence \
    --seq-kwargs  '{"N": 6, "hx": 4.0, "t": 4000}' \
    --n-traj      40 \
    --max-chi     128 \
    --n-times     75 \
    --out-dir     experiments/<name>/results/noise/ \
    --cal-offsets '{"omega_offset": 0.03, "delta_offset": 0.2, "R_offset": 0.01}' \
    [--spec experiments/<name>/<name>_spec.json --noise-source device|paper]

Free and local: no batch is submitted and nothing is billed — the one cloud call reads the device's noise model. Pass --spec if the source described its own noise model: the script prints the difference against the device's field by field, and --noise-source paper runs the source's instead. It never substitutes it silently.

--cal-offsets adds a sensitivity band: ±3% on ω, ±0.2 MHz on δ, ±1% on R (6 extra noiseless runs). If a QPU manifest from qpu-submit exists, ask the user whether to add --qpu-manifest <path> — this runs the noisy trajectories at the measured calibrated parameters for a fair comparison with QPU data.

Typical runtime on one A100: 3–8 min per noisy trajectory, so a full 40+1 run is hours — run it with nohup/background for large systems, or use --n-traj 5 --max-chi 64 for quick tests. The last printed line is the output .npz path.

Mode 2 — cluster (recommended)

SEQFILE=experiments/<name>/<name>_sequence.py \
SEQKWARGS='{"N":6,"hx":4.0,"t":4000}' \
OUTDIR=experiments/<name>/results/noise \
NTRAJ=40 CHI=128 \
PARTITION=<your_partition> GRES=gpu:a100:1 ACCOUNT=<your_account> \
bash support/submit_slurm.sh

The launcher (1) fetches the noise model once on the login node (compute nodes are usually offline) → OUTDIR/noise_model.json, (2) submits a SLURM array 0..NTRAJ — one GPU per trajectory, id 0 = noiseless — and (3) submits a dependent merge+plot job. Set PARTITION/GRES/ACCOUNT/TIME to your site's values; on a shared cluster cap concurrency with ARRAY_MAX=10. Watch with squeue -u $USER | grep nemu; figure appears at OUTDIR/noise_plot.png.

Wall time per trajectory (N=6, χ=128, dt=10): ~8–14 min on A100; the whole array finishes in roughly one trajectory's wall time when GPUs are free. For larger N or entanglement/full-distribution observables, raise CHI (200+) and re-validate convergence at the longest evolution time.

Mode 3 — Pasqal Cloud

python support/pasqal_auth.py --whoami     # free, read-only: account, projects, credits

python support/run_noise_emu_cloud.py \
    --seq-file   experiments/<name>/<name>_sequence.py \
    --spec       experiments/<name>/<name>_spec.json \
    --seq-kwargs '{"N": 5, "hx": 6.0}' \
    --t-max      4000 \
    --n-times    15 \
    --shots      500 \
    --project-id <the project the user picked> \
    --out-dir    experiments/<name>/results/noise/ \
    [--tag "envelope run 2"] [--noise-source device|paper]

Emulator time is billed to a project, so the user picks it. --whoami is free and read-only: it prints which account was found, where each credential came from, and the QPU and EMU credits each of their projects has left — no password, no token. Show it, ask which project pays, and pass --project-id. The script refuses without it: the id in PASQAL_PROJECT_ID is the last one somebody exported, not a decision.

--spec is optional but worth passing: it supplies the batch labels (exp:<name>, stage:noise-emulate, backend:…, t:…, noise:…, plus your --tags) and, if the source described its own noise model, it is what --noise-source paper switches to. Without it the labels fall back to the sequence file's name and only the device's model is available.

The cloud can't sample mid-evolution states, so this builds one sequence per observation time (what the real QPU does) and submits noiseless + noisy EMU_MPS batches per point. Batch budget = n_times × (2 + n_envelope); warn the user beyond ~100 batches. --n-envelope K adds K independent noisy batches per point for a client-side quantile band. Batch IDs are saved immediately; if the session dies mid-poll, re-run with --resume and the same --out-dir; a re-run without --resume into a directory that already records batches is refused.


Step 3 — Plot

NOISE=experiments/<name>/results/noise

# local / cluster (.npz):
python support/plot_noise_emu.py \
    --result $NOISE/FCAN1_*.npz --out $NOISE/noise_plot.png --coverage 0.75

# cloud (.json):
python support/plot_noise_emu_cloud.py \
    --results $NOISE/noise_emu_cloud.json \
    --out     $NOISE/noise_emu_cloud.png

Save figures as .png only, next to the data they came from. A figure you make for the report — annotated, or comparing two runs — goes in experiments/<name>/figures/, never in the working directory's root.


Step 4 — Report to the user

  1. The noise model values printed during the run (copy the table) — these come as-shipped from the live device spec.
  2. Key observations: how wide is the noise band, does the noiseless curve lie within it, signal retention at the peak.
  3. If --cal-offsets was used and the calibration band is much wider than the stochastic band, flag that calibration precision matters more than noise.
  4. Paths to the saved data file and figure, and where the numbers you are quoting came from.
  5. Where it ran and what that limits — 40 GPU trajectories and 5 cloud batches do not license the same sentence about the envelope.
  6. Which χ produced it, and whether the register was inside the calibrated envelope. If it was not, say so in the same sentence as the result — an unqualified curve from a possibly truncated run is the one output of this skill that can mislead silently.
  7. Which noise model produced it, and if the spec carried another one, that it exists and how it differs.
  8. Next step. This skill diagnoses; it does not authorise anything. If the user came from a validate-emu NO-GO, the next step is the protocol change the envelope suggests, then re-running validate-emu — its cloud verdict is still the only thing that gates a QPU submission.
  9. The appended NOTEBOOK.md block.

Troubleshooting

IssueFix
Pasqal Cloud credentials incompleteSee First-time setup above; the message names the missing fields
<device> not in available devicesCloud SDK connection failed or device hidden from the project (for SA1: is PASQAL_REGION=sa set?); retry / check project
build_sequence not foundPass --fn-name <name>
Builder returned a parametric sequenceCall .build(...) inside the builder
GPU OOM (local/cluster)Reduce N, not χ. Lowering --max-chi to make it fit buys a curve whose truncation error is invisible, and no χ that fits will settle it — emulate a sub-system that stays inside the calibrated envelope instead
emu_mps not foundWrong virtualenv; install from requirements.txt
partial_*.npz missing after SLURM jobCheck the .err log: time limit, OOM, or missing venv
Cloud batches stuck PENDINGQueue congestion — normal; re-poll later with --resume
Cloud batch ERROR on large NCloud EMU_MPS degrades for N ≳ 60–100; reduce N or use the cluster or local mode

Output files

experiments/<name>/results/noise/
# local / cluster
  FCAN1_<kwargs>_ntraj40_chi128_<timestamp>.npz   ← raw data (self-documenting:
  noise_plot.png                                     embeds noise model + kwargs)
# cloud
  batch_ids.json          ← submitted batches, their labels (for --resume)
  noise_emu_cloud.json    ← per-time records
  noise_emu_cloud.png

Then append the block to experiments/<name>/NOTEBOOK.md: where it ran, the budget actually used (N, χ, trajectories, time points), which noise model, the files written, and what the envelope says. A .npz whose provenance lives only in a chat log is a file nobody will trust in a month.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.