U-Probe is a universal and agentic probe design platform tailored for imaging-based spatial-omics. It overcomes the architectural limitations of existing tools and lowers the expertise barrier by combining an innovative declarative configuration system with LLM-based AI agents.
Whether you are designing standard probes for established protocols or developing entirely novel architectures, U-Probe provides a comprehensive, automated workflow from target sequence extraction to rigorous thermodynamic filtering.
-
Universal Probe Architecture: Features a declarative configuration system and a directed acyclic graph (DAG)-based assembly engine. Easily design arbitrary and complex multi-part probe structures (e.g., DNA-FISH, smFISH, MERFISH, seqFISH,
$\pi$ -FISH, MiP-seq, RCA) without modifying any source code. -
Agentic AI Workflow: An Agent Skill (
uprobe/skills/u-probe/) lets AI coding agents such as Claude Code or Codex design probes with U-Probe, and the web server offers conversational design through hierarchical LLM-based AI agents (via PantheonOS, an optional extra). Users can describe experimental goals in plain language or provide scRNA-seq data, and the agents will autonomously construct configurations, select parameters, and execute the design pipeline. - Comprehensive Quality Filtering: Automatically computes and filters candidates based on GC content, melting temperature, secondary structure stability (via ViennaRNA), off-target mapping (via Bowtie2, or exhaustively within a number of mismatches via Bowtie 1), and k-mer frequency (via Jellyfish).
- End-to-End Automation: Streamlines the entire process from target sequence extraction to advanced post-processing, including overlap removal and equal spacing for tiling designs.
- Extensible API & Web UI: Offers a clean Python API for programmatic integration, a command-line interface, and an interactive web server for visual workflow management and result analysis.
Requires Python 3.10+. Use Linux or WSL2 for backend development and bioinformatics tools.
pip install uprobegit clone https://github.com/u-science-lab/U-Probe.git
cd U-Probe
pip install -e .git clone https://github.com/u-science-lab/U-Probe.git
cd U-Probe
conda env create -f environments.yaml
conda activate uprobe
pip install -e .The conda environment also installs the command-line tools U-Probe calls (Bowtie2, Bowtie 1, BLAST+, MMseqs2, Jellyfish). With a pip install, install the ones you use from bioconda, e.g. conda install -c bioconda bowtie2 bowtie jellyfish. Bowtie 1 (bioconda package bowtie) runs the exhaustive off-target search, which short probes need: the smFISH, seqFISH+, MERFISH and HybISS templates use it, and so does the generated off-target attribute of a protocol without attributes when the genome enables it. uprobe genome fetch adds bowtie to a new genome's align_index when Bowtie 1 is installed.
U-Probe ships an Agent Skill in uprobe/skills/u-probe/. Its SKILL.md gives a coding agent the design workflow (clarify the request, choose the technology and template, fetch the genome, edit, validate, run, verify, deliver) and the rules behind it, and sends the agent to reference notes on each technology, on U-Probe's YAML and on probe-design concepts when it needs them; scripts/check_probes.py checks a result table before delivery. Install U-Probe and the aligners, then copy the skill to where your agent looks for skills:
pip install uprobe
conda install -c bioconda bowtie bowtie2 jellyfish
uprobe skill install --target codex # ./.agents/skills/u-probe (run it at the repository root)
uprobe skill install --target claude --user # ~/.claude/skills/u-probe
uprobe skill path # the copy inside the installed package| Agent | Project (default, working directory) | --user |
Source |
|---|---|---|---|
| Codex | .agents/skills/u-probe |
~/.agents/skills/u-probe |
Codex docs: Agent Skills |
| Claude Code | .claude/skills/u-probe |
~/.claude/skills/u-probe |
Claude Code docs: Skills |
Without --target the skill is installed for both agents; --target DIR installs it as DIR/u-probe for any other agent that reads Agent Skills. After upgrading U-Probe, run the install again with --force so the skill matches the CLI. Then describe the experiment to the agent in plain language, e.g. "Design smFISH probes against human GAPDH mRNA for HeLa cells".
U-Probe provides three main entry points: Command Line Interface (CLI), Python API, and the HTTP/Web UI.
For source installs, install the package and runtime dependencies before running examples:
pip install -e .Most workflow commands require two YAML files:
genomes.yaml: genome paths such as FASTA and GTF.protocol.yaml: probe design parameters.
The CLI is perfect for running tasks quickly in the terminal or shell scripts. A typical session, also the path for scripts and AI agents, fetches a reference genome, starts from a published template, checks the protocol and runs it; each step is described below.
uprobe genome fetch --species mus_musculus --release latest --genomes genomes.yaml
uprobe templates show padlock_iss -o protocol.yaml # then set genome, targets and encoding
uprobe validate -p protocol.yaml -g genomes.yaml --check-targets
uprobe run -p protocol.yaml -g genomes.yaml -o ./results --jsonThe recommended way to use U-Probe with an AI agent such as Claude Code or Codex is the Agent Skill under uprobe/skills/u-probe/; install it with uprobe skill install (see Use U-Probe with an AI agent). U-Probe no longer ships a terminal agent of its own: the uprobe agent command has been removed. The web server keeps its chat agent as an optional extra (see below).
Run the FastAPI backend in WSL/Linux and the separate React frontend in Windows. Copy config.example.ini to the Git-ignored config.ini for optional local overrides. Leave [Paths] commented to use portable defaults, or set your own paths locally. Never commit credentials or machine-specific settings. When upgrading from a version that tracked config.ini, back up your local file before pulling and restore it afterward as an untracked file; keep existing custom settings instead of replacing them with the example.
cd /path/to/U-Probe
conda activate uprobe # or your existing Python 3.10+ environment
uprobe server --env development --host 127.0.0.1 --port 8005 --workers 1Development mode reloads Python changes automatically. Alternatively, run APP_ENV=development PORT=8005 python -m uprobe.http.server from the project root. Running without overrides uses config.ini, which may select production mode.
The web chat (the /agent endpoints) is the optional web-agent extra: install it with pip install 'uprobe[web-agent]', or pip install -e '.[web-agent]' from source. Without it the server starts and those endpoints answer 503; deployments that serve the chat, such as app.u-probe.org, must install it.
In a separate Windows PowerShell terminal:
cd /path/to/probe-web-ui
pnpm install # first run or after dependency changes
pnpm dev --host 127.0.0.1 --port 5173- UI: http://localhost:5173
- API docs: http://localhost:8005/docs
- Connection check: http://localhost:5173/api/ should return
{"message":"Hello, World!"}.
The frontend proxies /api to backend port 8005. No separate database service is needed; users and tasks are stored locally. Create a test account through POST /auth/register in the API docs; this endpoint needs no email verification. Full probe workflows also require FASTA/GTF data and the relevant bioinformatics tools.
Download an Ensembl genome FASTA and GTF, verify them against Ensembl's CHECKSUMS, and register them in genomes.yaml:
uprobe genome fetch --species homo_sapiens --release latest --out ./genomes --genomes genomes.yaml
uprobe genome fetch --species arabidopsis_thaliana --division plants --build-index -t 10
uprobe genome search mouse # find Ensembl species names
uprobe genome list -g genomes.yaml # entries, file and index status (--json for scripts)--speciestakes an Ensembl name (homo_sapiens), display name (human) or taxonomy id (9606).--release latestis resolved to a number, which is recorded in the entry.- Sources: Ensembl (vertebrates plus yeast, worm and fly) and the Ensembl Genomes divisions
plants,fungi,metazoaandprotists(--division; they have their own release numbers). Ensembl Bacteria is not supported. - The unmasked primary assembly FASTA is used when Ensembl provides one, otherwise the toplevel FASTA. Files are decompressed (U-Probe's tools need plain FASTA) into
<out>/<name>/; the entry name defaults to<species>_<assembly>_e<release>(_eg<release>for Ensembl Genomes). --soft-maskedfetches the soft-masked FASTA instead (Ensembldna_sm: repeats in lowercase; the name gets the suffix_sm). Every template can use it, since the aligners and Jellyfish ignore case; the Oligopaint template needs it, because itsextracts.target_region.masked: dropskips the genome windows with a soft-masked or N base, as OligoMiner mines a repeat-masked genome.- Re-running skips files that are already present and verified, and resumes interrupted downloads.
--dry-runprints the resolved URLs without downloading;--mirrorreplaces the Ensembl FTP base URL. - A new entry gets
align_index: [bowtie2], plusbowtiewhen Bowtie 1 (bowtie-build) is installed, for the exhaustive off-target search (the log says so when it is not), andjellyfish: truewhen Jellyfish is installed.--align-indexand--jellyfish/--no-jellyfishset them; re-fetching keeps the settings of an existing entry. --build-indexbuilds every index of the entry'salign_index, asuprobe build-index --alldoes.
When you have transcript sequences and a table of transcripts instead of a genome and its GTF, register them directly:
uprobe genome add human_tx --transcriptome transcripts.fa --records transcripts.tsv --align-index bowtie--transcriptomeis a plain-text FASTA with one record per transcript (the header up to the first space is the transcript id, matched with or without a.Nversion).--recordsis a TSV (CSV when the name contains.csv) with a transcript id column (transcript_id,transcript,enst, ...), a gene name column (gene_name,gene,symbol, ...), and optionally a gene id (gene_id,ensg; default: the gene name), tags and CDS start/end within the transcript (cds_start,cds_end). MANE Select and Ensembl canonical come from atag/tagscolumn (comma-separated GTF tags), amane_select/mane/mane_statuscolumn (1/true/yes, or a value containingSelect) and anensembl_canonical/canonicalcolumn (1/true/yes); name other columns with--transcript-column,--gene-column,--gene-id-column,--tag-column,--mane-columnand--canonical-column.- Each FASTA record becomes a single-exon, plus-strand "contig" of a synthetic GTF (
<out>/<name>/annotation.gtf;transcript,exonand, with CDS columns,CDSlines carryinggene_name,gene_id,transcript_idand thetags), sosource: mRNA(andexon,CDS) withtranscripts: maneorcanonicaland gene-name targets work as with a genome.<out>/<name>/transcript.faholds the transcripts named<gene_id>_<transcript_id>, the names the off-target search reads (a_in a gene id is written as-), so every transcript of the table is in the off-target index. FASTA records missing from the table are left out (--unlisted includekeeps each as a gene of its own); the command logs how many. - The entry's
fastais a link in<out>/<name>/to your FASTA, and itsindex_diris that directory (or--index-dir), so nothing is written next to your files. Only the indexes a protocol searches are built (--align-index bowtieis enough for the exhaustive off-target search of windows up to 30 nt; no genome-scale index is built).--dry-runreads the inputs and prints the entry only. - Running
genome addagain for the same name rewrites the annotation andtranscript.fa. When the transcript sequences or their names (e.g. a changed gene id) differ from the previoustranscript.fa, the indexes built from it in the entry'sindex_dir(<aligner>_transcript/) are removed, and so are those of the FASTA (<aligner>_genome/,jf_genome/) when the FASTA differs; runs rebuild the ones they need. An old index would otherwise report the target's own gene under its previous id as an off-target gene.
Any entry can also move or reuse its indexes with two optional keys:
human_tx:
fasta: /data/transcripts.fa
gtf: /data/annotation.gtf
align_index: [bowtie]
index_dir: /fast/disk/human_tx # <aligner>_genome/, <aligner>_transcript/, jf_genome/ and transcript.fa live here, not next to the FASTA
indexes: # an index that already exists: the prefix given to bowtie / bowtie2
bowtie_transcript: /data/bowtie/transcripts # used as it is, never built; `uprobe validate` errors when it is incompleteuprobe genome add ... --existing-index bowtie_transcript=/data/bowtie/transcripts writes the second key. An index listed under indexes must name the transcriptome (transcript) or genome (genome) index of an aligner in align_index; with the default layout the prefix is <index_dir>/<aligner>_<kind>/<FASTA stem>.
A transcriptome index given under indexes must be built from records named <gene_id>_<transcript_id>, with the gene ids of the entry's GTF: the transcript.fa U-Probe writes (genome add writes it to index_dir; for a genome it is extracted from the GTF). mapped_genes reads the gene of each hit from the record name, up to the first _. An index of an ordinary transcript FASTA, named by transcript ids, gives wrong counts: the target's own isoforms (ENST00000229239.10, ENST00000396861.5) count as off-target genes, and RefSeq records (NM_002046.7, NM_001256799.3) all count as one gene, NM. genome add --existing-index, uprobe validate and the run list the records of such an index (bowtie-inspect -n or bowtie2-inspect -n) and stop with an error, naming examples, when a record's gene is not a gene of the GTF; when the inspect program is not on PATH the names are not checked and validate warns. Build the index from that transcript.fa (e.g. bowtie-build <index_dir>/transcript.fa <prefix>), or leave the key out and let U-Probe build it.
U-Probe ships complete protocol templates for published probe technologies: smFISH (Stellaris-style), MERFISH, seqFISH+, HCR v3.0, STARmap, padlock ISS (HybISS), MiP-seq, Oligopaint/OligoMiner and the double-hybridization RCA design. Each template declares the source protocol's hybridization conditions; where a paper gives a Tm window together with the conditions it was computed under (MERFISH, Oligopaint, HybISS), the Tm attribute is computed under those conditions with the design code's own formula (tm_method: merfish_design, oligominer, multi_padlock_design) and the window is applied as a filter; smFISH, HCR v3 and MiP-seq have a Tm floor and STARmap a Tm window taken from their published probe sets under the template's conditions (meta.parameter_sources). A meta block cites the source papers, maps every parameter to its source and lists what you must replace (genome, targets, barcodes/readouts). Every template passes uprobe validate. Every RNA template designs on the reference transcript (transcripts: mane: MANE Select, the Ensembl canonical transcript without it) and takes the region its source designed on (meta.parameter_sources quotes the evidence): the whole mRNA for smFISH, MERFISH, HCR v3, HybISS and MiP-seq, whose published probes also lie in UTRs, and the CDS for seqFISH+ and STARmap, which used the coding region, and for the double-hybridization RCA design. Raj et al. 2008 tiled the coding region of each smFISH target; the smFISH template takes the whole mRNA, as the Stellaris designer suggests for expanding a set, so that genes sharing coding sequence with paralogs or pseudogenes still reach 48 probes (source: CDS for the coding region alone). Templates whose target windows are 30 nt or shorter (smFISH, seqFISH+, MERFISH, HybISS) count off-target genes with the exhaustive Bowtie 1 search, and so do the templates of units of several oligos (HCR v3, STARmap, MiP-seq), per oligo (oligos), so their genome needs bowtie in align_index; the smFISH template keeps only probes without an off-target gene, exempts copies of the target (exempt_copies, as the MERFISH and seqFISH+ templates do), recommends (commented out) an expression table, by transcript when you have one, with min_fraction_of_target, and offers off_target_pool as a commented-out option for genes with many paralogs; see Off-target search.
uprobe templates list
uprobe templates show hcr_v3 -o protocol.yamlCheck a protocol (and optionally the genome config) before running it. Every problem is reported with its location in the YAML and a suggested fix; the exit status is 1 when there are errors.
uprobe validate -p protocol.yaml
uprobe validate -p protocol.yaml -g genomes.yaml --check-targets --jsonProbe expressions are checked with the grammar of the safe evaluator that uprobe run uses, hybridization blocks with the same rules as the run, and the probes are then built on synthetic target regions, so expressions that would fail at run time are reported too. -g adds genome checks (files, aligner indexes required by the attributes, installed aligners), --check-targets looks the targets up in the GTF or FASTA, and --json prints the findings with a summary for scripts and agents.
To run the entire pipeline from genome index construction to final probe generation in one go:
uprobe run -p protocol.yaml -g genomes.yaml -o ./results --threads 10Useful flags:
uprobe run -p protocol.yaml -g genomes.yaml -o ./results --continue-invalid --raw --threads 10--continue-invalid: a gene target that is not agene_nameof the GTF stops the run before anything is designed (statuserror, exit 1), with an error that lists those targets. With--continue-invalid, they are skipped and reported instead: logged, listed insummary.json(inputs.invalid_targetsand aninvalid_targetswarning), and the run endspartial(orno_valid_targetsif no target is left).uprobe validate --check-targetsfinds them before a run. Tasks submitted through the web server skip invalid targets.--raw: also write every candidate with its attributes, before filtering (<name>_<time>_raw.xlsx).--threads(default 10): threads for building the indexes and for every threaded step of the attributes (Bowtie, Bowtie2, Jellyfish) whose ownthreadsoption is not set; an attribute'sthreadstakes precedence.uprobe post-processtakes--threadstoo; web-server tasks use the server'stask_threads.
Every run writes summary.json to the output directory, also when it stops early or fails. It gives the outcome (status: ok, partial, no_probes, no_valid_targets, no_target_sequences, no_probes_constructed or error, with a message; partial means some targets have no probes, or fewer than equal_space requested), how many candidates each target kept after every post-processing stage and which stage removed the most, statistics of the summary attributes, the off-target genes found by mapped_genes attributes (off_targets), the transcripts each gene target's windows came from (targets.<name>.transcripts, see transcripts below), the output file names, warnings, and the inputs, including the Tm/fold conditions in effect and the genome's Ensembl source when it is recorded. The final probe table is written as <name>_<time>.xlsx and as plain tab-separated <name>_<time>_probes.tsv. Excel limits never fail a run or lose data: a cell longer than Excel's 32,767 characters (e.g. the off-target list of a repeat window) is cut in the .xlsx and ends with ...[truncated: N characters; full value in <file>.tsv], and a table with more rows than a worksheet holds (1,048,576 with the header) is written only as TSV; the raw table then gets a <name>_<time>_raw.tsv too, and summary.json lists the affected tables in an excel_limits warning (output_files.raw_tsv). Web-server result archives include the TSV files. With --json, uprobe run prints the summary to stdout and sends logs and tool output to stderr.
Exit status of uprobe run (summary.json and the --json output are written before it exits):
| Code | Meaning | status |
|---|---|---|
| 0 | Probes were designed | ok, partial |
| 1 | The run failed | error |
| 2 | Invalid command-line usage (missing or bad options) | none; no summary is written |
| 3 | The run finished without any probe | no_probes, no_probes_constructed, no_target_sequences, no_valid_targets |
For advanced users who need intermediate results or custom workflows, you can execute each step individually:
# 1. Build genome index
uprobe build-index -p protocol.yaml -g genomes.yaml -t 10
# 2. Validate target genes against the GTF file
uprobe validate-targets -p protocol.yaml -g genomes.yaml --continue-invalid
# 3. Extract target region sequences
uprobe generate-targets -p protocol.yaml -g genomes.yaml -o ./results --continue-invalid
# 4. Construct initial probes from target sequences
uprobe construct-probes -p protocol.yaml -g genomes.yaml --targets ./results/target_sequences.csv -o ./results
# 5. Post-process probes (requires target + probe columns)
uprobe post-process -p protocol.yaml -g genomes.yaml --probes ./results/constructed_probes_combined.csv -o ./results
# 6. Generate HTML analysis report
uprobe generate-report -p protocol.yaml -g genomes.yaml --probes ./results/probes_*.csv -o ./resultsuprobe generate-barcodes \
--strategy max_orthogonality \
--name barcodes \
--num-barcodes 16 \
--length 8 \
--alphabet ACT \
--output ./results/barcodesThis writes barcodes.csv (column sequence) and barcodes.txt.
If you are developing a web backend or data analysis pipeline, we recommend directly using UProbeAPI. It returns Pandas DataFrames, making it easy to process further.
import uprobe
from pathlib import Path
# Initialize API
api = uprobe.UProbeAPI(
protocol_config=Path("protocol.yaml"),
genomes_config=Path("genomes.yaml"),
output_dir=Path("./results")
)
# --- Method 1: Complete Workflow ---
df_final = api.run_workflow(threads=10)
# --- Method 2: Step-by-Step Execution ---
api.build_genome_index(threads=10)
api.validate_targets() # raises on a target not in the GTF; continue_on_invalid=True skips it
df_targets = api.generate_target_seqs()
df_probes = api.construct_probes(df_targets)
import pandas as pd
df_combined = pd.concat([df_targets, df_probes], axis=1)
df_final = api.post_process_probes(df_combined)
# Generate HTML report
reports = api.generate_report(df_final)
print(reports["html_reports"])
# Quick barcode generation
barcodes = api.quick_generate_barcodes(num_barcodes=16, length=8, alphabet="ACT")U-Probe relies on two main configuration files to run. Here is a breakdown of how to structure them.
This file maps a genome name to its corresponding file paths. It tells U-Probe where to find the reference genome and annotation files, and which aligner indices to build.
# Example genomes.yaml
GRCh38:
fasta: "/path/to/GRCh38.fasta"
gtf: "/path/to/GRCh38.gtf"
align_index:
- bowtie2
- bowtie # Bowtie 1 transcriptome index for mapped_genes with aligner: bowtie (exhaustive off-target search)
- blast
jellyfish: false uprobe build-index builds the indexes of the aligners in align_index that the protocol's attributes search, as uprobe run does before designing: mapped_genes searches its aligner's transcriptome index (transcript.fa, spliced from the GTF), mapped_sites the genome index. An RNA design that searches only the transcriptome therefore never builds the Bowtie2 genome index (13.5 min and 3.5 GB for mouse GRCm39). uprobe build-index --all builds every index of align_index: genome indexes for bowtie2, blast and mmseqs, transcriptome indexes for bowtie2, blast and bowtie, which has no genome index.
Entries written by uprobe genome fetch also record their origin under source (Ensembl division, release, assembly and URLs), which each run copies into summary.json; the verified checksums are kept in genome_source.json next to the files. The workflow ignores keys it does not use.
This is the core configuration file that defines all parameters for a specific probe design run. It is highly customizable.
# Example protocol.yaml snippet
name: my_rna_probe_design
genome: GRCh38
targets:
- CD4
extracts:
target_region:
source: mRNA # genome / mRNA / exon / CDS / UTR
length: 30
overlap: 15
# Define how genes map to specific barcodes
encoding:
CD4:
BC1: ACGAGCCTTCCA
BC2: CGGTAATGGACT
# Define the structure of your probes
probes:
probe_1:
template: "{part1}{part2}"
parts:
part1:
expr: "rc(target_region[0:20])"
part2:
template: "CC{barcode1}TGCGTCTATTT{barcode2}TAGTGGAGCCT"
parts:
barcode1:
expr: "encoding[target]['BC1']"
barcode2:
expr: "encoding[target]['BC2']"
probe_2:
template: "{part1}AGGCTCCACTA"
parts:
part1:
expr: "rc(target_region[-10:])"
# Optional. If omitted or empty, the CLI auto-generates attributes,
# post_process, and summary based on source mode and probe structure.
attributes:
target_gc:
target: target_region
type: gc_content
target_tm:
target: target_region
type: annealing_temperature
target_fold:
target: target_region
type: fold_score
post_process:
filters:
target_tm:
condition: target_tm >= 50 & target_tm <= 90
sorts:
is_ascending:
- target_gc
- target_fold # lower fold_score = less structure
summary:
report_name: rna_report # rna_report / dna_report
attributes:
- target_gc
- target_tm
- target_fold-
name&genome: Basic metadata. Thegenomemust match a key in yourgenomes.yaml. -
targets: A list of target gene names or IDs you want to design probes for. -
extracts: Parameters for extracting target sequences: the sliding windowlengthandstep(oroverlap), and thesourcethe windows are taken from:genome: targets are regions such aschr1:1000-2000, tiled along the genomic sequence (the reference, + strand). Regions are 1-based and inclusive, as genome browsers and Ensembl show them:chr1:1000-2000is the 1,001 bases from position 1,000 to 2,000, and the first base of a chromosome is 1.avoid_otptarget_regionsuse the same notation. A window'ssub_regions-eis its position along the region, so it covers the genomic basesstart + s - 1tostart + e - 1. Earlier versions read region targets as 0-based (chr1:1000-2000was bases 1,001-2,000, whileavoid_otpalready read them 1-based), so a DNA design made from the same protocol now starts one base earlier; a start of 0 is an error.mRNA: the whole mature RNA of each target gene, i.e. all itsexonfeatures: 5' UTR, CDS and 3' UTR of a protein-coding gene, the processed RNA of a non-coding gene. Use it when probes may bind anywhere on the mRNA, as smFISH probe sets do.exon: the coding sequence of a protein-coding gene (itsCDSfeatures, without the UTRs), and all exons only of a gene without CDS. Despite its name it is not the whole mRNA of a protein-coding gene;uprobe validatewarns and points tomRNA.CDS: the same asexon, under the name that says what it extracts.UTR: the UTR features (UTRin GENCODE GTFs,five_prime_utrandthree_prime_utrin Ensembl GTFs).
With
transcripts: all(the default), each window lies within one exon (or CDS or UTR segment), so no window spans a splice junction. Withexon,CDSandUTR, segments shorter than the window are left out, and a target left with none gets no windows (a log warning andno_candidate_windowsinsummary.json) while the other targets are designed as usual; withmRNA, short exons give no window but still count in the positions along the RNA (target_start, see Probes per target). Whentranscriptsselects one transcript of a target,mRNA,exonandCDStile its spliced sequence instead, so windows also span its exon junctions; see Windows across exon junctions.transcripts(RNA sources): which transcripts of each gene the windows come from.all(the default) takes the exons of all its transcripts, the merged exon model, so windows can lie on exons that only rare isoforms, or retained-intron transcripts, contain.canonicaltakes the transcript the GTF tagsEnsembl_canonical;manethe one taggedMANE_Select(human), and for a gene without one (other species, most non-coding genes) itsEnsembl_canonicaltranscript, with a log note; a list of transcript ids, e.g.transcripts: [ENST00000646664], takes those (with or without version), and every target needs at least one of them. Ensembl and GENCODE GTFs carry both tags. An unknown id, an id of a gene that is not a target, a target without a listed id, or a gene without the tag stops the run with an error;uprobe validate --check-targetsreports them beforehand. With one transcript, the windows tile that spliced transcript (mRNA) or its spliced CDS (exon,CDS), across its exon junctions,target_startis the position on it, andexempt_copiesjudges copies against it.summary.jsonlists each target's transcripts:selection,chosen_by(all,MANE_Select,Ensembl_canonicalorlisted) andids.masked(source: genome):keep(the default) upper-cases the windows;dropskips every window with a soft-masked (lowercase) or N base, as OligoMiner mines a repeat-masked genome. It needs a soft-masked FASTA (uprobe genome fetch --soft-masked, or UCSChg38.fa) or amask; the run log anduprobe validate --check-targetswarn when a target has no lowercase base (with amask: and none of its bases is in the mask). The Oligopaint template usesdrop.mask(source: genome, withmasked: drop): the path of a file that marks repeats on the genome apart from the FASTA, so an unmasked genome can be used and no soft-masked copy has to be made: a BED file (chrom start end, 0-based and end-exclusive, in the coordinates of the genome FASTA, optionally.gz; e.g. RepeatMasker or UCSCrmskintervals), or a FASTA of soft-masked loci whose record names are regions (>chr7:1000-52000, repeats in lowercase). A window that contains a base of the mask, a soft-masked base of the genome FASTA or anNis dropped. It is read once and applied to the extracted locus.uprobe validatechecks that the file reads and thatmasked: dropis set, and warns, without--check-targets, when the mask has no interval on a target's chromosome (chr1vs1: a mask with other chromosome names masks nothing). The run log warns when a target has neither a soft-masked base nor a base in the mask, and names the chromosome when the mask has no interval on it.
-
encoding: Mapping of specific genes to custom barcodes or identifiers (e.g., assigningBC1andBC2toCD4). -
probes: The core of the design, powered by a Directed Acyclic Graph (DAG) architecture. This allows for complex, modular probe construction where parts and probes can reference each other.template: Construct sequences using placeholders (e.g.,{part1}{part2}).expr: Apply Python-like expressions. You can use built-in functions likerc()(reverse complement), slice sequences ([0:20]), or fetch from theencodingmap (encoding[target]['BC1']). Expressions are evaluated safely (never with Pythoneval): only string/integer literals, the namestarget_region,target,encoding,target_parts,probe_indexand probe/part names (e.g.probe_1.part1), subscripts and slices,+concatenation,%(remainder of two integers) andrc()are allowed; anything else (other function calls, attribute or method access, comprehensions, lambdas, ...) is rejected with an error naming the probe part.probe_index: the number of the probe within its target, 0, 1, 2, ... along the target (5' to 3' on an RNA, along the region on DNA), so a part can vary from probe to probe, e.g. rotate readouts with an encoding list:rc(encoding[target]['readouts'][(probe_index + 1) % 4])(an encoding value may be a list of sequences). The final probes are numbered after post-processing, the parts that readprobe_indexare then rebuilt and their attributes recomputed, and the final table gets aprobe_indexcolumn; filters and sorts see each candidate numbered in extraction order (uprobe validatewarns about attributes of such parts). Themerfishtemplate uses it to put 3 of each gene's 4 readouts on every probe.- DAG References: Because of the DAG structure, subsequent probes or parts can dynamically reference the sequences of previously defined probes/parts in their expressions.
Chemical modifications belong to a complete probes.<name> entry, independently of its DNA sequence:
probes:
padlock:
template: "{binding}"
parts:
binding:
expr: "rc(target_region)"
modifications:
5prime: phosphate
linker_probe:
template: "ACGTACGT"
parts: {}
modifications:
internal:
- type: disulfide
after_base: 4
3prime: {type: fluorophore, name: Cy5}Terminal 5prime/3prime entries support phosphate, hydroxyl and named fluorophore; internal lists support disulfide and phosphorothioate. after_base: n denotes the bond between bases n and n+1 (1-based; 1 β€ n < generated sequence length). A bond accepts one modification. Validation rejects unknown types, invalid positions, conflicting declarations and chemical symbols in DNA. Modifications do not affect assembly, reverse complement, scoring or selection.
The Python API adds <probe>.modifications only for probes with annotations, as a list of records such as {"probe":"linker_probe","type":"disulfide","position":"internal","after_base":4}. Raw and final TSV/XLSX tables encode this column as JSON. DNA columns remain plain sequences; unmodified protocols retain their existing columns.
attributes: Define the biochemical or physical properties you want to calculate for specific targets or probe parts. Available attribute types include:gc_content: Calculates the GC ratio of the sequence.base_fraction,max_homopolymer,max_window_count: base composition.base_fractionis the fraction of the sequence's bases that are one ofbases(A,C,GC, ...; U counts as T);max_homopolymerthe longest run of one base ofbases(default: any base), so<= 3excludes AAAA;max_window_countthe highest number ofbasesin anywindowconsecutive bases. Each takespositions: [start, end], 1-based and inclusive from the 5' end of the sequence as written (negative: from the 3' end,-1= the last base), to look at part of it only. Together they express sequence rules such as those of Xu et al. 2009 (PNAS 106:2289, doi:10.1073/pnas.0812506106), which Oligostan applies to smiFISH probes: A < 28 % (base_fraction,bases: A, filter< 0.28), no AAAA, C 22-28 %, no CCCC, and no 4 C in any 6 consecutive bases within the first 12 (max_window_count,bases: C,window: 6,positions: [1, 12], filter<= 3). Filters run beforeremove_overlap, so the overlap removal chooses among windows that pass them.annealing_temperature: Calculates the melting temperature (Tm) using Primer3, under the protocol'shybridizationconditions. Withtm_method: multi_padlock_design,merfish_designoroligominerit uses the formula of the HybISS padlock, MERFISH or OligoMiner design code instead (see Hybridization conditions).fold_score: The negated minimum free energy, βMFE in kcal/mol (β₯ 0), of the sequence folded alone as RNA by ViennaRNA, athybridization.temperature_cwhen set. Higher means more stable secondary structure, so lower is better: filter with<=and sort it inis_ascending.rna_dna_dg37: The RNA/DNA hybrid free energy ΞGΒ°37 (kcal/mol; more negative = more stable) of a DNA probe on its RNA target, computed as the Oligostan script does (Tsanov et al. 2016, NAR 44:e165, doi:10.1093/nar/gkw784;Oligostan.rin bitbucket.org/muellerflorian/fish_quant): the sum of 16 tabulated dinucleotide values of the probe read 5'β3' plus a constant salt term of -0.1785 kcal/mol (0.115 M), with no initiation or terminal term; a dinucleotide with an unknown base counts 0. Pointtargetat the probe or its binding part (default,sequence: probe), or attarget_regionwithsequence: target, which reverse-complements the RNA first.target_region(or atarget_partslayout part) withsequence: probereads the RNA as if it were the probe and gives the value of the other strand (up to 8.8 kcal/mol off, median 2.0, on the 68 Oligostan XPO1 probes), souprobe validateand the run warn about it. It does not usehybridization. Filter on it like any attribute, e.g.condition: dg37 >= -33 & dg37 <= -31(filter conditions accept negative numbers). It reproduces thedG37column of the script's own tutorial output (Oligostan_Tutorial_Data_160527.zip, all 68 XPO1 probes of 26-32 nt) to 1e-9. The script gives no reference for its 16 values; the paper cites Wu et al. 2002 (Eur J Biochem 269:2821, doi:10.1046/j.1432-1033.2002.02970.x) for the temperature dependence of RNA/DNA stability, and the values were not checked against Sugimoto et al. 1995 or Wu et al. 2002 (not freely available). Oligostan's own choice of probes (one target ΞGΒ°37 for the whole set, probes within Β±1 kcal/mol of it, chosen by sweeping -36 to -28 kcal/mol) is not implemented; the attribute gives the filter, not the sweep.self_match: Counts 4-mer matches between the sequence and its reverse complement, a measure of self-dimerization; lower is better.mapped_sites: Aligns the sequence to the genome (Bowtie2--very-sensitive-local -k 100, both strands) and lists the sites, with their number in<attribute>_num;<attribute>_num == 1keeps sequences with one site, OligoMiner's Unique Mode. A window across an exon junction has no contiguous genomic site (Windows across exon junctions).mapped_genes: Counts the number of unique genes the sequence aligns to in the transcriptome, via Bowtie2 or, exhaustively withinmax_mismatchessubstitutions, via Bowtie 1. See Off-target search; withexempt_copies, genes that copy most of the target and are not protein-coding are not counted (Copies of the target).difference: The absolute difference of two attributes defined before it,of: [a, b](notarget); the STARmap template uses it for the Tm difference of its two halves.kmer_count: Counts k-mer occurrences in the genome (via Jellyfish) to evaluate specificity: the highest genome count among the sequence's k-mers, so lower is better. Eachkmer_len(default 35) has its own index,jf_genome/<fasta stem>.k<kmer_len>.jfnext to the FASTA, built at its first use.strand: both(the default) counts canonical k-mers, i.e. on both strands;strand: sensecounts the sequence's k-mers as written on the FASTA's forward strand, as OligoMiner's and PaintSHOP's dictionaries do (index<fasta stem>.k<kmer_len>.stranded.jf), andantisensetheir reverse complements. For windows across exon junctions see Windows across exon junctions. Withlocus_only: trueit counts only the k-mers of the candidate windows of the table: U-Probe lists them (both strands) in a FASTA and runsjellyfish count --if <that FASTA> <genome FASTA>(Jellyfish 2.x; MarΓ§ais & Kingsford 2011, doi:10.1093/bioinformatics/btr011), which counts, in one pass over the genome, only the k-mers of that file. The temporary index is a few MB for a locus-sized target instead of the whole-genome table (about 18 GB for human 18-mers), is not kept, andjellyfish: trueis not needed in the genome entry. The counts equal those of the whole-genome index forstrand: both,senseandantisense(tested with Jellyfish 2.3.0);sizedefaults to twice the number of listed k-mers.
hybridization(optional): The experimental conditions (temperature, salt, probe concentration, formamide) thatannealing_temperatureandfold_scoreare computed under. See Hybridization conditions.gene_expression(optional): An expression table of your sample, by gene or by transcript;mapped_genesattributes then ignore off-target genes that are not expressed, or optionally expressed far below the target. See Expression-aware off-target counting.post_process: Define strictfilters(e.g., Tm ranges) based on the calculated attributes, andsortsto rank the best probes (ascending or descending). Lower is better forfold_score,self_match,kmer_countandmapped_genes: list them underis_ascending(uprobe validatewarns when they are sorted descending). Whenpost_processis generated, every sort is ascending: GC, Tm, mapped genes, then the fold, self-match and k-mer scores. Its specificity filter istarget_mapped_genes <= 4in RNA mode and, in DNA mode (source: genome),target_kmer_count <= 100on the generatedtarget_kmer_countattribute: the highest genome count of the 35-mers of the genomic window (Jellyfish; the genome needsjellyfish: true).remove_overlap: Optional post-processing step for controlling spacing between probes. Put it underpost_processwhen you need it, e.g.post_process: { remove_overlap: { location_interval: 0 } }. Of the candidates that share a start (alengthrange or alayoutwith ranges) it keeps the shortest, or withprefer: <attribute>the one with the lowest value of that attribute. It visits each target's candidates in order of position and keeps one when it starts more thanlocation_intervalbases after the end of the last kept one. In RNA mode the positions are those along the target (target_start/target_end: on the gene's merged exon model, or on its one selected transcript; see Probes per target), so windows from alternative exons of different transcripts are compared too, and a site that several exon entries contain is kept once, with the transcripts of all of them intranscript_names(and their number inn_trans). Windows of a target that have the same sequence (an internal repeat, e.g. tandem copies of a domain) are one oligo that binds several sites: it is kept once, only if none of its sites is withinlocation_intervalof a kept site, and then occupies all of them (a layout is part of the oligo).equal_spacelikewise counts such an oligo once. When a kept oligo has several sites, its row getsn_sitesandoligo_sites(start-end;start-end, along the target) andsummary.jsonamulti_site_oligoswarning listing them; tables without repeats are unchanged. The warning and the columns describe the final table: each of its oligos with several sites is listed once, with all its sites (itscountis the number of rows withn_sites> 1), and an oligo that a later step (equal_space) leaves out is not listed; the columns are left out when no final row has several sites.off_target_pool: Optional post-processing step that lets at mostmax_probes_per_geneprobes of a target's set hit the same off-target gene, e.g.post_process: { off_target_pool: { attribute: target_mapped_genes, max_probes_per_gene: 2 } }. See Off-target pool limit.equal_space: Optional post-processing step that keepsnumber_desiredprobes per target, spread evenly along the target, e.g.post_process: { equal_space: { number_desired: 48 } }, or per target:equal_space: { GAPDH: { number_desired: 48 } }. See Probes per target.summary: Define thereport_name(e.g.,rna_reportordna_report) and the specificattributesyou want to visualize and output in the final report. The HTML report embeds the final table, a 100-row preview of the raw candidates and a link to the raw table that--rawwrites;summary: {report: {embed_raw: true}}also embeds the whole raw table (63 MB for the 581,793 candidates of a 200-kb Oligopaint design).meta(optional): Free-form documentation that the workflow ignores; the built-in templates use it for citations and parameter sources.
The post_process steps always run in this order, whatever their order in the protocol: filters, avoid_otp (DNA mode), remove_overlap, off_target_pool, equal_space, sorts.
When extracts.target_region.transcripts selects exactly one transcript of a target (canonical, mane, or a list with one of its ids), U-Probe tiles the spliced sequence of that transcript, as one would tile its cDNA: source: mRNA joins all its exons, exon and CDS its CDS pieces (its exons if it has no CDS), 5' to 3', every one however short. Windows therefore also span its exon-exon junctions. They are valid windows of the spliced RNA and bind only the mature RNA, not the pre-mRNA. On Ensembl 113, the MANE Select mRNA of ACTB (ENST00000646664, 6 exons) gives 1,793 20-nt windows instead of the 1,698 that lie within one exon, exactly the 20-mers of its Ensembl cDNA; ACTB, EEF1A1, GAPDH, HSPA8, RPL13A and TUBB give 12,401 instead of 11,679 (19 more per junction). transcripts: all (the merged exon model), several selected transcripts of a target, and source: UTR keep one window per exon or segment.
target_start/target_end: the window's position on the spliced transcript (or spliced CDS), whichremove_overlap,equal_space,off_target_poolandexempt_copiesuse as for any window.exon_name: the exon (or CDS piece) the window lies on,chrom_start_end_strand, or, for a window across junctions, the exons it spans, 5' to 3', joined by;, e.g.7_5530524_5530601_-;7_5529535_5529663_-.start/endandsub_region: the window's position along the exons thatexon_namenames, joined; for a window within one exon, its position on that exon, as for every other window. ACTB's window at 70-89, on the exons above, covers the last 9 nt of the 78-nt first exon and the first 11 of the second.exon_nameandstart/endthus place every window on the genome.transcript_names: the transcript;n_trans: 1.- Attributes:
mapped_genesandexempt_copiessearch the spliced transcripts (transcript.fa), so they find a junction window on its own transcript like any other window; the sequence attributes (Tm, GC, folding, self-match) read the window itself.mapped_sitesandkmer_countsearch the genome, where an intron interrupts a junction window:mapped_sitesfinds the window's own site in part (a local alignment) or not at all, andkmer_countfinds its k-mers that cross the junction only in processed copies of the gene, so their values for junction windows do not compare with those of other windows. Usemapped_genesto judge the specificity of RNA probes.
equal_space works in DNA (source: genome) and RNA mode (mRNA, exon, CDS, UTR). It runs after the filters, remove_overlap and off_target_pool, orders each target's remaining candidates along the target and keeps every k-th one (evenly spaced ranks, first and last included), so the kept probes follow the candidates from one end of the target to the other. Use it together with remove_overlap; otherwise overlapping windows can be kept (uprobe validate warns).
-
Identical windows count once: windows of a target with the same sequence are one oligo, so
equal_spaceranks each distinct oligo once, at its first site. This includes a window that several transcripts or exon entries share at one position (transcripts: allwithoutremove_overlap): earlier versions ranked each copy and could space the probes by duplicates; now it is one candidate, whose row lists the transcripts of all the copies intranscript_names(and their number inn_trans), asremove_overlapdoes. Withoutremove_overlapthe kept probes of atranscripts: alldesign can therefore differ from earlier versions (GAPDH, which has no repeat: 77 of 92 MERFISH windows). Withremove_overlapbefore it, as in every template, the candidates are already distinct and the result is unchanged. -
Along the target: in DNA mode, along the genomic region. In RNA mode, windows number
start/endwithin their exon or UTR, so U-Probe also writestarget_start/target_end: the window's position along the target. Whentranscriptsselects one transcript of the target, that is the position on the spliced transcript (mRNA) or its spliced CDS (exon,CDS); see Windows across exon junctions. Otherwise it is the position on the gene's merged exon model, the union of the extracted exons (CDS or UTRs) of its transcripts with the introns removed, read 5' to 3': for a gene with one transcript andsource: mRNAthe position on the spliced RNA, while exons of different transcripts that overlap share positions, and withexon,CDSandUTR, segments shorter than the window length are not extracted and do not count. Direct sequences use their own coordinates. -
Fewer candidates than requested: the target keeps all of them.
summary.jsonlistsrequested_probesandfinalfor every target and afewer_probes_than_requestedwarning with the counts, the run status ispartial, anduprobe runlogs the per-target counts asfinal/requested.
By default, Tm is computed under PCR-like conditions (legacy defaults: 50 mM Na+, 0.6 mM dNTP, 50 nM oligo, no formamide). FISH hybridizations usually run in SSC buffer with formamide, so declare the conditions of your experiment with a top-level hybridization block:
hybridization:
temperature_c: 37 # hybridization temperature (Β°C); used by fold_score and auto-generated Tm filters
na_mM: 390 # monovalent cations; 2x SSC (0.3 M NaCl + 0.03 M Na3-citrate) = 390 mM Na+
mg_mM: 0 # divalent cations
dntp_mM: 0 # dNTPs (chelate Mg2+; no effect when mg_mM is 0)
probe_nM: 50 # concentration of each probe oligo
formamide_pct: 30 # formamide, % (v/v)
attributes:
probe_1_tm:
target: probe_1.part1
type: annealing_temperature # uses the block above
readout_tm:
target: probe_2
type: annealing_temperature
hybridization: # per-attribute override, merged over the block
formamide_pct: 10| Field | Unit | Default when unset | Used by |
|---|---|---|---|
temperature_c |
Β°C | 37 (ViennaRNA default) | fold_score, auto-generated Tm filters |
na_mM |
mM | 50 | annealing_temperature |
mg_mM |
mM | 0 | annealing_temperature |
dntp_mM |
mM | 0.6 | annealing_temperature |
probe_nM |
nM | 50 | annealing_temperature |
formamide_pct |
% (v/v) | 0 | annealing_temperature |
- Every field is optional. Without the block (or with an empty one) results are identical to earlier versions. An
annealing_temperatureorfold_scoreattribute may carry its ownhybridizationmapping with the same fields, overriding the top-level values for that attribute only. - Values must be numbers and
>= 0;na_mMandprobe_nMmust be> 0,formamide_pctandtemperature_cat most 100, and unknown field names are rejected. - Tm model: Primer3 nearest-neighbor Tm (SantaLucia 1998, PNAS 95:1460-65) with SantaLucia salt correction; Mg2+ is converted to a Na+ equivalent as in von Ahsen et al. 2001 (Clin Chem 47:1956-61). Primer3 parameters describe DNA/DNA duplexes, so Tm against RNA targets is an approximation.
tm_method: multi_padlock_design(on anannealing_temperatureattribute): the Tm formula of Moldia/multi_padlock_design (lib/screenseq.py), the code that designed the HybISS padlock probes (Gyllborg et al. 2020, Nucleic Acids Res 48:e112, doi:10.1093/nar/gkaa792): Sugimoto et al. 1996 DNA/DNA nearest-neighbor parameters (Nucleic Acids Res 24:4501-4505, doi:10.1093/nar/24.22.4501) with one initiation term, the probe concentration as strand concentration, + 16.6 log10([Na+] + 3.795 sqrt([Mg2+])) (mol/l) and - 0.72 Β°C per % formamide;dntp_mMis not used. It reproduces the repository's values (to 1e-12 Β°C) under its fixed conditions, 75 mM Na+, 10 mM Mg2+, 100 nM probe and 20% formamide, and lets thepadlock_isstemplate apply the published 65-75 Β°C window: on the 1,195 published HybISS targets Primer3 gives 3.4 Β± 1.5 Β°C less, and only 327 of them fall within 65-75 Β°C by Primer3.tm_method: merfish_design: the Tm formula of the MERFISH probe design code (Moffitt et al. 2016, PNAS 113:11046, doi:10.1073/pnas.1612826113; ZhuangLab/MERFISH_analysis,probe_construction/TRDesigner.m,GetRegionTm): SantaLucia & Hicks 2004 nearest-neighbor sums (the 1998 values except AA/TT, -7.6 kcal/mol and -21.3 cal/K/mol), + 0.2 / -5.7 initiation and + 2.2 / + 6.9 per terminal A or T, 0.368 (N-1) ln[Na+], and Tm = 1000 dH / (dS + R ln C) - 273.15 with C the probe concentration itself, where Primer3 uses C/4 (the code's own comment says the correct term would be probe minus half the target concentration). So the formula is about 1.4 Β°C warmer than Primer3 for a 30-mer at 5 nM. Usesna_mMandprobe_nMonly; the published 66-76 Β°C window of 30-nt targets is computed at 300 mM Na+ and 5 nM, with no formamide. The code is ported from reading; the paper and repository give no per-probe Tm values to compare with, so the formula is checked against a hand calculation of the code's terms, and against Primer3, whose difference is the concentration term.tm_method: oligominer: the Tm of OligoMiner (Beliveau et al. 2018, PNAS 115:E2183, doi:10.1073/pnas.1714530115; brianbeliveau/OligoMiner,blockParse.py/probeTm.py): BiopythonTm_NNwithDNA_NN3(SantaLucia 1998), salt correction 5 and strand concentrationdnac1 - dnac2/2, rounded to 2 decimals, minus 0.65 Β°C per % formamide (chem_correction).probe_nMis bothdnac1anddnac2(OligoMiner's-cand-C, 25 nM by default),na_mMis-s(390 mM) andformamide_pctis-F(50); Mg2+ and dNTP are not used. It reproduces the Tm column of OligoMiner'sExampleFiles/3_probes.bed(12 probes, 42.08-45.79 Β°C) to within its 0.01 Β°C rounding. OligoMiner's default window, 42-47 Β°C (-t,-T), applies to this formamide-corrected value. Example:hybridization: {na_mM: 390, probe_nM: 25, formamide_pct: 50}on the attribute.merfish_designandoligominertake only declared conditions; a field they use that is not declared (protocol block or the attribute's override) gets U-Probe's legacy PCR default (50 mM Na+, 50 nM probe, 0% formamide), far from the design codes' conditions: OligoMiner's first example probe gets 64.86 Β°C instead of 42.85 Β°C.uprobe validateand the run warn and suggest the design code's values (OligoMiner: 390 mM, 25 nM, 50%; MERFISH: 300 mM, 5 nM).- Formamide: U-Probe uses Primer3's correction, which lowers Tm by (2.88 β 0.453 Γ GC fraction) Β°C per mol/l formamide (Blake & Delcourt 1996, Nucleic Acids Res 24:2095-2103, doi:10.1093/nar/24.11.2095).
formamide_pctis converted to mol/l with formamide's density (1.1334 g/ml at 20 Β°C) and molar mass (45.04 g/mol), so 1% (v/v) = 0.252 M and Tm drops 0.61-0.72 Β°C per percent depending on GC content, close to the linear 0.60 Β°C/% measured by Hutton 1977 (Nucleic Acids Res 4:3537-3555, doi:10.1093/nar/4.10.3537). It is preferred over a fixed linear factor because it accounts for GC content and is applied inside the same Primer3 call. - Folding:
fold_scorefolds attemperature_c; ViennaRNA does not model formamide, and salt is left at its default. - A per-attribute override suits a published Tm window that was computed under other conditions than the experiment's. The MERFISH template, for example, hybridizes in 2x SSC with 30% formamide (top-level block) but computes
binding_tmwith the MERFISH design code's formula (tm_method: merfish_design) at 300 mM Na+ and 5 nM probe, with no formamide term, the conditions of its 66-76 Β°C window. uprobe validatereports an invalid block or override with its path. The conditions in effect are logged for every Tm/fold attribute and recorded insummary.json(inputs.hybridization_conditions). When a protocol declares them, they are also shown in the HTML report header and stored in the result.xlsxas custom document properties (hybridization:<attribute>).- Auto-generated Tm filters (when
post_processis left out) follow the hybridization temperature: each Tm attribute keeps [temperature_c,temperature_c+ 40] Β°C, the 40 Β°C width of the legacy window, since Tm under declared FISH conditions (e.g. 50% formamide) can fall tens of degrees below the legacy 50-90 Β°C. An attribute's owntemperature_cwins; an override that changes salt, probe or formamide without its owntemperature_c(such as the MERFISH design conditions above) keeps 50-90 Β°C. Conditions declared withouttemperature_cuse the default 37 Β°C (37-77 Β°C), with a warning fromuprobe runanduprobe validate. Without the block (or with an empty one) the filters stay at 50-90 Β°C. The chosen windows are logged.
A mapped_genes attribute aligns a sequence to the transcriptome and counts the distinct genes it hits, the target gene included, so target_mapped_genes <= 1 keeps sequences that hit no other gene. Choose the search with aligner:
aligner |
Search | Needs |
|---|---|---|
bowtie2 |
Bowtie2 --very-sensitive-local -k 100: local alignments, which may cover only part of the sequence, found by a seed heuristic |
bowtie2 in align_index |
bowtie |
Bowtie 1 -v <max_mismatches> -a: every end-to-end site with at most max_mismatches substitutions (0-3, default 2); indels are not considered |
bowtie in align_index and Bowtie 1 installed |
attributes:
target_mapped_genes:
target: target_region
type: mapped_genes
aligner: bowtie
max_mismatches: 2
strand: sensestrand(both aligners):both(default) counts sites on either transcript strand;senseonly sites where the sequence itself occurs in a transcript, right fortarget_region, whose probes bind exactly there;antisenseonly sites of its reverse complement, right for a probe's binding sequence. A probe does not bind an RNA that contains its own sequence, sobothalso counts antisense transcripts the probe cannot bind.- Bowtie2's local mode requires a score of at least 20 + 8 ln(length), more than a perfect match scores (2 per base) below 22 nt: shorter sequences never align and count 0 genes, so a filter on them keeps every candidate.
uprobe runlogs a warning anduprobe validatereports it; usealigner: bowtiefor smFISH-length oligos.uprobe validatealso warns when Bowtie2 aligns sequences of 22-30 nt, whose off-target sites it finds only in part (see below). - On the Ensembl 113 human transcriptome (386,885 primary-assembly transcripts), for all 15,295 18-22-mers of the ACTB and GAPDH MANE Select transcripts:
aligner: bowtie(max_mismatches: 2) reported exactly the sites a brute-force scan of every transcript window found, for a random sample of 1,000 of them (147,426 sites);bowtie2found 1.5% of the off-target genes with a site within 2 substitutions (none for 18-21-mers; for 30- and 40-mers it found 56% and 82%). The Bowtie 1 search took 4.6 s for the 15,295 sequences with 8 threads (2.5 s withstrand: sense; Bowtie2 1.6 s); its index took about 5 minutes to build and 650 MB of disk. - Per window, which is what a filter such as
target_mapped_genes <= 1sees: for 1,500 random windows of each length from 400 random MANE Select mRNAs, searched in the 410,909 Ensembl 113 human cDNA and ncRNA transcripts (patches included, genes counted by name) withstrand: sense, Bowtie2 flagged none of the 20-mers, 69% of the 25-mers, 88% of the 28-mers, 87% of the 30-mers and 97-99% of the 40-, 46- and 52-mers that have a site within 2 substitutions in another gene, listing on average 0%, 49%, 71%, 70% and 87-94% of those genes. It reports at most 100 alignments, so it misses most genes of windows in repeats; from 40 nt on it also reports partial matches (about 25 nt or more), indels and sites with 3 or more mismatches, which a search within 2 substitutions cannot see (3-5% of the windows had only such hits). The templates therefore usealigner: bowtiefor windows of 30 nt or less (smFISH, seqFISH+, MERFISH, HybISS) and Bowtie2 for the 40-52-nt windows of MiP-seq, STARmap, the double-hybridization RCA design and HCR v3, all withstrand: sense. - Protocols without
attributes: in RNA mode the generatedtarget_mapped_genesusesaligner: bowtie(max_mismatches: 2,strand: sense) when the genome'salign_indexincludesbowtieand Bowtie 1 is installed (the run builds the index if it is missing), andbowtie2otherwise; the log says which and why, with a warning when the windows are shorter than 22 nt.uprobe validate -gmakes the same choice. - Units of several oligos: where a signal needs several oligos bound together on one RNA (the two probes of an HCR v3 split-initiator pair, Choi et al. 2018; a STARmap SNAIL padlock and primer, Wang et al. 2018; a MiP-seq padlock and RCA primer), a search of the whole window counts the mismatches of all oligos together, so a transcript where each 25-nt half of an HCR pair has 2 substitutions (4 over the 52-nt window) passes.
oligos(withaligner: bowtieandtarget: target_regionsplit by alayout) searches each oligo's binding site on its own instead and counts a gene when every oligo has a site withinmax_mismatcheson one of its transcripts; the_off_targetscolumn gives the worst oligo's mismatches. Each entry is a layout part or a list of adjacent parts joined into one oligo, e.g.oligos: [left, right](HCR v3) oroligos: [[arm5_site, arm3_site], primer_site](a padlock's two arm sites and a primer).max_gap: <nt>also requires the sites to lie on one strand, not overlapping, within that distance of the longest oligo's site; without it, sites anywhere on the same transcript count. The HCR v3, STARmap and MiP-seq templates useoligos(withoutmax_gap), so their genome needsbowtieinalign_index. The longest oligo of a unit is searched first and the others keep only sites on the transcripts it hits, so short oligos (a 13-nt primer site has thousands of sites within 2 substitutions) stay cheap. - Each
mapped_genesattribute also writes<attribute>_off_targetsto the result tables (.xlsxand_probes.tsv, next to the attribute): the genes other than the row's target that the sequence hits, each with its fewest mismatches, e.g.ACTG1:0, POTEF:2(names only withbowtie2). Genes are named after the GTF'sgene_name; a name that several gene ids share is writtenname(gene_id). The count includes the row's target once, however many gene ids carry its name: a copy of the gene on an assembly patch or alternate haplotype (Ensembl cDNA includes them, each with a gene id of its own) is the target, not a second gene, sotarget_mapped_genes <= 1keeps a window that hits both. Genes that an expression table rules out go to<attribute>_unexpressed, copies of the target to<attribute>_copies, and with a table of transcripts<attribute>_hit_transcriptsgives the transcripts each off-target gene was valued by (see below).
A probe can only bind RNAs that are present. Supply an expression table of your sample, and mapped_genes attributes no longer count off-target genes that are not expressed, or are expressed far below the target:
gene_expression:
table: hek293_tpm.tsv # your table: tab-separated (comma-separated when the name contains .csv), may be gzipped
level: gene # default; transcript for a table of transcripts (recommended, see below)
gene_column: gene_id # Ensembl gene ids (with or without version) or gene names; default: the first column
value_column: TPM # default: the only numeric column
min_value: 1 # off-target genes with a lower value are not counted
min_fraction_of_target: 0.05 # optional: nor those below 5% of the target's value
absent: count # default; ignore: off-target genes missing from the table are not counted- Genes are looked up by Ensembl gene id, then by gene name;
gene_columnmay list several columns, e.g.[gene_id, gene_name]. A gene listed more than once counts with its highest value. Leading#comment lines are skipped, and GTEx.gctfiles can be used directly (choose the tissue withvalue_column). - An off-target gene below the threshold is not counted in
<attribute>and is listed in<attribute>_unexpressedinstead of<attribute>_off_targets. The threshold ismin_value, or withmin_fraction_of_targetthe higher of the two below. The target gene always counts, so<attribute> <= 1still means "no (expressed) off-target gene". min_fraction_of_target(optional, 0-1) makes the threshold relative to the target: an off-target gene of a target counts only if its value is at least max(min_value,min_fraction_of_targetx the target's value). A spot needs many probes on one RNA, and an RNA at a few percent of the target's level adds at most a few percent of false spots, so low-level pseudogenes and paralogs of a highly expressed target no longer remove its probes, while a gene expressed at a similar level still does. The target's value is read from the same table (the highest of its gene ids). A target that the table does not list keepsmin_valuealone: the log warns, andsummary.jsonlists it inoff_targets.gene_expression.targets_not_in_table(warningtargets_not_in_expression_table), next to the value and threshold of every listed target (off_targets.gene_expression.targets). Per attribute,below_target_fractioncounts the off-target genes that only the fraction removed, for at least one target.- Off-target genes that the table does not list count as expressed (
absent: count, the conservative default). Withabsent: ignorethey are not counted and are listed in<attribute>_unexpressed, for a table you trust to list every gene the sample expresses. Either way the log andsummary.json(off_targets:missing_from_tableper attribute, with a warningoff_target_genes_not_in_expression_table) report how many there were, with examples, and which of them the final probes hit. A table that matches no gene of the annotation stops the run: its identifiers or species do not fit the genome. - The table applies to every
mapped_genesattribute. Relative paths are resolved from the directory where uprobe runs.uprobe validatechecks the block, the file and its columns.
Use a table of transcripts when you have one, e.g. salmon's quant.sf, kallisto's abundance.tsv or RSEM's isoforms.results. False signal comes only from RNA molecules that carry a probe's site, so an off-target gene's value for a sequence is then the sum of the values of the gene's transcripts that the sequence hits, not the whole gene's. A gene expressed on isoforms without the site no longer counts.
gene_expression:
table: quant.sf
level: transcript
transcript_column: Name # default: the first column
value_column: TPM
min_value: 1
min_fraction_of_target: 0.05- Transcripts are looked up by id with or without version (
ENSMUST00000073605.15matchesENSMUST00000073605; a GENCODE FASTA header such asENST...|ENSG...|...by its first field). The genome's GTF, from which the searched transcriptome is built, assigns them to genes, sogene_columndoes not apply. - The target's value, for
min_fraction_of_target, is the sum over all its transcripts in the table. - A hit transcript missing from the table makes its gene count (
absent: count) or adds nothing to the gene's value (absent: ignore).missing_from_tablethen counts the genes with such transcripts, andmissing_transcriptsthe transcripts. <attribute>_hit_transcriptslists, for each off-target gene the table valued, the transcripts the sequence hits with their values (NAwhen not listed), e.g.Mir100hg: ENSMUST00000212079=0.014, ENSMUST00000362798=0.<attribute>_off_targetsandoff_target_poolstill work by gene, andsummary.jsonreports the level (off_targets.gene_expression.level).- Both aligners report the transcripts they hit. Bowtie2 reports at most 100 alignments per sequence, so for genes with many isoforms some hit transcripts may be missing from the sum.
- A table whose identifiers fit the other level (gene ids with
level: transcript, or transcript ids withlevel: gene) stops the run with a hint, anduprobe validatereports it.
Measured with template runs of the smFISH template as in Copies of the target (reference mRNA, 20-mers, Bowtie 1 within 2 substitutions, exempt_copies, target_mapped_genes <= 1, 48 probes per gene) with TPM >= 1 and min_fraction_of_target: 0.05. The tables are salmon quantifications of HeLa-S3 and NIH/3T3 on the Ensembl 113 transcriptomes, by gene and by transcript; a gene's TPM is the sum of its transcripts' TPM, so both tables come from one quantification. Final probes (genes with 48), candidate windows without a counted off-target gene, and off-target genes counted for at least one window:
| Table | Human ACTB, EEF1A1, GAPDH, HSPA8, RPL13A, TUBB | Human, 30 random genes | Mouse Actb, Eef1a1, Gapdh, Hspa8, Rpl13a, Rpl3, Tubb5 | Mouse, 30 random genes |
|---|---|---|---|---|
| By gene | 288 (6); 9,314; 2,777 | 1,337 (26); 64,631; 12,894 | 313 (5); 9,015; 1,840 | 1,426 (28); 84,142; 11,245 |
| By transcript | 288 (6); 10,233; 1,078 | 1,350 (26); 74,107; 11,589 | 335 (6); 9,984; 724 | 1,427 (28); 91,248; 9,999 |
- Mouse Gapdh went from 26 to 48 probes. Its 20-mers hit two lncRNA genes mostly on isoforms that NIH/3T3 hardly expresses. Of the 689 windows that hit ENSMUSG00000141751 (90 TPM), 495 hit only isoforms that sum to 1.5 TPM or less; the 194 that also hit its 89-TPM isoform still count. Mir100hg (129 TPM over 169 isoforms) is hit by 272 windows, all on isoforms at 0.11 TPM or less.
- Elsewhere the table of transcripts counted 61% fewer off-target genes for the housekeeping genes and 10-11% fewer for the random genes, and 8-15% more windows passed, but few final probes changed, since most genes reach 48 either way. Among the random genes, TEX54 went from 13 to 23 probes.
A processed pseudogene, a retrocopy, or a non-coding gene that overlaps the target can hold most of the target's sequence. Every probe of the target then hits it too, so no probe set avoids it, and counting it as an off-target leaves such a target with few or no probes, although probe sets for it work in practice. With exempt_copies, a mapped_genes attribute does not count these genes, as long as they are not protein-coding:
attributes:
target_mapped_genes:
target: target_region
type: mapped_genes
aligner: bowtie
max_mismatches: 2
strand: sense
exempt_copies: # or `exempt_copies: true` for these defaults
kmer: 30 # k-mer length, 12-32
min_shared: 0.5 # share of the target's k-mers that one transcript of the gene must hold- An off-target gene is a copy of a target when one of its transcripts contains, exactly and in the same orientation, at least
min_sharedof the distinctkmer-mers of the target's sequence. A copy is not counted in<attribute>and is listed in<attribute>_copies(name and fewest mismatches, as in_off_targets) instead of<attribute>_off_targets, sooff_target_pooldoes not limit it either. It is exempt whatever its expression;gene_expressionapplies to the other off-target genes. Paralogs share few exact 30-mers and stay counted. - Only copies whose biotype is not coding are exempt: processed and unprocessed pseudogenes, lncRNA retrocopies. Coding is
protein_codingor an expressed immunoglobulin or T-cell receptor segment (IG_C_gene,TR_C_gene, ...: mouse Trbc2 for Trbc1); their pseudogenes stay eligible. The biotype is the GTF'sgene_biotypeattribute (Ensembl), orgene_type(GENCODE). A protein-coding copy is a separately quantified transcript, and binding it is a real off-target: mouse Ifit3b holds 62.5% of the 30-mers of Ifit3, and stays counted. So does a copy whose biotype the GTF does not give, with a log warning naming how many such genes there are.summary.jsonlists both undercopies_not_exempt, each with its share andnot_exempt: protein_coding(orunknown_biotype). - The target's sequence is what its candidate windows cover, placed by
target_start/target_end: in RNA mode its merged exon model (the extracted segments of its transcripts, all of them or thosetranscriptsselects, with the introns removed: the coordinatesremove_overlapandequal_spaceuse), so the share is that of the sequence the probes are drawn from, e.g. of the MANE Select mRNA withsource: mRNAandtranscripts: mane; for a direct sequence, the sequence itself. Withsource: mRNAthat is the whole mRNA; withexonorCDS, U-Probe extracts the CDS of protein-coding genes, so their copies are judged on the coding sequence. Bases that no window covers are unknown, so keepstepat most the window length (uprobe validatewarns). - The transcripts of the genes the alignment hit are read from
transcript.fa, the FASTA the transcriptome index is built from (extracted again if it is missing), in one pass, with the k-mers packed into 64-bit integers. Checking every gene of the Ensembl 113 human transcriptome (410,909 transcripts, 597 Mb) against 4 targets took 9 s; in the runs below the check added 3-13 s to a run. - It needs no expression table, since a copy is a matter of sequence, and works with either aligner, so it can be enabled on its own.
- The log and
summary.jsonname every exempt gene: per attribute,copy_genes,copies(each target's exempt copies with their shares) andfinal_copy_genes(those the final probes hit), and every copy that is not exempt (copies_not_exempt). A target whose windows give no k-mer keeps all its off-target genes and is reported (targets_without_sequence, warningcopies_not_checked). - Near-identical gene families cannot be told apart by any probe: SPDYE8-15 hold 84-89% of the 30-mers of SPDYE17. Members annotated
protein_codingare not exempt, so they count against the SPDYE17 probes and appear undercopies_not_exempt; members annotated as pseudogenes are exempt and appear in<attribute>_copies.
Shares of the target's 30-mers on Ensembl 113, for the canonical mRNA and for the merged CDS that source: exon extracts; at min_shared: 0.5 the paralog and the human GAPDH pseudogenes stay counted:
| Target | Off-target gene | Canonical mRNA | Merged CDS |
|---|---|---|---|
| ACTB | ACTG1 (paralog) | 4.8% | 7.4% |
| GAPDH | GAPDHP1 (the closest of its ~70 pseudogenes) | 23.5% | 29.9% |
| EEF1A1 | EEF1A1P5 (processed pseudogene) | 35.6% | 81.2% |
| RPL13A | RPL13AP5 (processed pseudogene) | 44.9% | 64.9% |
| mouse Gapdh | Gm13394 (retrocopy, 668 TPM in NIH/3T3) | 75.6% |
Measured before exempt_copies was limited to genes that are not protein-coding, so protein-coding copies (e.g. mouse Vmn2r92 for Vmn2r91) were exempt in these runs. Measured with template runs of the smFISH template on Ensembl 113 (GRCh38 and GRCm39 primary assemblies with their GTFs): windows from the whole reference mRNA, across its exon junctions (source: mRNA, transcripts: mane: MANE Select in human, Ensembl canonical in mouse), 20-mers, Bowtie 1 within 2 substitutions, target_mapped_genes <= 1, remove_overlap 2, 48 probes per gene, with HeLa-S3 and NIH/3T3 tables (TPM, min_value: 1). Final probes (genes with 48); each row adds its option to the row above, except exempt_copies alone, which is the template's default:
| Setting | Human ACTB, EEF1A1, GAPDH, HSPA8, RPL13A, TUBB | Human, 30 random genes | Mouse Actb, Eef1a1, Gapdh, Hspa8, Rpl13a, Rpl3, Tubb5 | Mouse, 30 random genes |
|---|---|---|---|---|
| No expression table | 119 (1) | 1,198 (24) | 120 (1) | 1,288 (23) |
exempt_copies alone (template default) |
119 (1) | 1,231 (24) | 125 (1) | 1,300 (23) |
| Table, TPM >= 1 | 180 (2) | 1,255 (25) | 125 (1) | 1,394 (27) |
+ min_fraction_of_target: 0.05 |
193 (2) | 1,285 (25) | 203 (2) | 1,394 (27) |
+ exempt_copies (recommended) |
193 (2) | 1,333 (26) | 275 (4) | 1,394 (27) |
+ absent: ignore |
264 (5) | 1,337 (26) | 335 (6) | 1,426 (28) |
- The relative threshold helps highly expressed genes whose pseudogenes are expressed at a few percent of their level: human GAPDH 7 -> 14 probes and ACTB 38 -> 44, mouse Eef1a1 17 -> 48 and Rpl13a 17 -> 46. It changed little for random genes.
- Copies matter where a copy is expressed: mouse Rpl3 (Rpl3-ps1 holds 85% of its 30-mers) went from 7 to 48 probes and Hspa8 from 18 to 48 with both options; among the random genes the copies were lncRNA genes overlapping NDUFV2 and EIF3L on the same strand, the SPDYE family, and mouse Vmn2r92 for Vmn2r91 (73%). Human GAPDH has no copy above 30%; it reached 48 probes only when, on top of the fraction, its pseudogenes missing from the HeLa table were ignored. Mouse Gapdh has 35 retrocopies holding 50-92% of its 30-mers, all exempt, but others holding less are missing from the NIH/3T3 table and kept it at 6 probes until they were ignored.
absent: ignoretrusts the table to list every expressed gene.
Where the windows come from matters as much. Up to U-Probe 0.2 the template used source: exon, the CDS of all coding isoforms; mRNA with transcripts: all takes the exons of every isoform, retained introns included. Probes (genes with 48) and the share of final probes whose site lies on the reference mRNA, without an expression table (exempt_copies on) and with the recommended settings (TPM >= 1, min_fraction_of_target: 0.05, exempt_copies):
| Windows | Expression table | Human ACTB, EEF1A1, GAPDH, HSPA8, RPL13A, TUBB | Human, 30 random genes | Mouse Actb, Eef1a1, Gapdh, Hspa8, Rpl13a, Rpl3, Tubb5 | Mouse, 30 random genes |
|---|---|---|---|---|---|
exon (CDS), all transcripts (template up to 0.2) |
none | 11 (0), 45% | 1,021 (13), 95% | 38 (0), 68% | 1,082 (17), 98% |
exon (CDS), all transcripts (template up to 0.2) |
recommended | 125 (2), 94% | 1,119 (16), 95% | 222 (3), 95% | 1,209 (19), 99% |
mRNA, all transcripts |
none | 288 (6), 16% | 1,334 (27), 68% | 332 (5), 27% | 1,337 (26), 76% |
mRNA, all transcripts |
recommended | 288 (6), 31% | 1,376 (28), 72% | 336 (7), 38% | 1,400 (28), 77% |
mRNA, transcripts: mane (template) |
none | 119 (1), 100% | 1,231 (24), 100% | 125 (1), 100% | 1,300 (23), 100% |
mRNA, transcripts: mane (template) |
recommended | 193 (2), 100% | 1,333 (26), 100% | 275 (4), 100% | 1,394 (27), 100% |
The reference mRNA gave the six human housekeeping genes 119 probes against 11 from the CDS of all isoforms without a table, and 193 against 125 with it; paralogs and pseudogenes share their coding sequence more than their UTRs. All transcripts reach 48 probes for nearly every housekeeping gene, but mostly on exons of other isoforms and retained introns: only 16-38% of these probes lie on the reference mRNA.
In smFISH a false spot needs many probes of the set on the same off-target RNA, so a few probes that hit one off-target gene do no harm, while many do. off_target_pool limits how many probes of one target's set may hit the same off-target gene:
post_process:
filters:
target_mapped_genes:
condition: target_mapped_genes <= 2 # at most one off-target gene per probe
remove_overlap:
location_interval: 2
off_target_pool:
attribute: target_mapped_genes # a mapped_genes attribute, or a list of them
max_probes_per_gene: 2
equal_space:
number_desired: 48
sorts:
is_ascending: [target_mapped_genes]- For each target, probes are visited from best to worst rank, the order
sortsdefines (the current order when there are nosorts), and a probe is dropped when one of its off-target genes (<attribute>_off_targets, all listed attributes together) is already hit bymax_probes_per_genekept probes. With agene_expressiontable only expressed off-target genes are listed, so only they are limited; copies of the target (exempt_copies) are not listed either.max_probes_per_gene: 0keeps only probes without off-target genes. - It runs after
remove_overlap, so the limit applies to probes that can end up in the same set, and beforeequal_space, which then chooses the final N among the probes that respect it (any subset still does). Per-probe filters such astarget_mapped_genes <= 3still apply first. summary.jsonreports the stage instagesand, inoff_targets.pool, how many probes it removed and the genes that reached the limit most often.- A probe in a repeat can hit hundreds of genes, each of them by no other probe of the set, so the limit alone keeps it; combine the limit with a per-probe filter such as
target_mapped_genes <= 2(at most one off-target gene per probe). equal_spacepicks the final probes by position, not by their off-target genes, so when a target has more candidates than it needs, the limit mostly swaps clean probes for probes with an off-target gene instead of adding probes (measured below). It would pay off with anequal_spacethat prefers probes without off-target genes, which U-Probe does not have yet.- For padlock/RCA probes a single probe makes signal, so allow no expressed off-target per probe with a filter instead:
target_mapped_genes <= 1(withgene_expression).
Measured on Ensembl 113 human genes with template runs of the smFISH template (the MANE Select mRNAs, exempt_copies, off-targets within 2 substitutions by Bowtie 1, remove_overlap 2, 48 probes per gene; the genes of Copies of the target): target_mapped_genes <= 1 alone, against target_mapped_genes <= 2 with max_probes_per_gene: 2.
| Genes | Expression table | <= 1: probes (genes with 48) |
<= 2 + pool: probes (genes with 48) |
Final probes with an off-target gene, <= 2 + pool |
|---|---|---|---|---|
| 30 random protein-coding genes | none | 1,231 (24) | 1,249 (24) | 47% |
| 30 random protein-coding genes | HeLa, TPM >= 1 | 1,303 (26) | 1,312 (26) | 34% |
| ACTB, EEF1A1, GAPDH, HSPA8, RPL13A, TUBB | none | 119 (1) | 132 (1) | 45% |
| ACTB, EEF1A1, GAPDH, HSPA8, RPL13A, TUBB | HeLa, TPM >= 1 | 180 (2) | 195 (2) | 36% |
The limit added 9-18 probes per gene set while a third to almost half of the final probes came to carry an off-target gene (each such gene hit by at most 2 probes of a set). An expression table gained more: 61 and 72 probes with <= 1. The smFISH template therefore keeps target_mapped_genes <= 1, recommends gene_expression and leaves off_target_pool as a commented-out option for genes with many paralogs or pseudogenes.
For more detailed examples and advanced configurations, please refer to the tests/data/*.yaml directory and the built-in protocol templates (uprobe templates list).
- π Documentation: uprobe.readthedocs.io
- π¬ GitHub Discussions: Ask questions and share ideas
- π Bug Reports: GitHub Issues
- π Contributing: See our contributing guide
If you use U-Probe in your research, please cite our paper (or software):
@software{uprobe2026,
title={U-Probe: Universal Probe Design Tool},
author={Zhang, Qian and Xu, Weize and Cai, Huaiyuan},
year={2025},
url={https://github.com/u-science-lab/U-Probe},
version={1.0.0}
}(Note: Update the citation format with actual journal details once published.)
U-Probe is released under the MIT License. See the LICENSE file for details.
We thank the bioinformatics community for valuable feedback during development, and the authors of the following tools that U-Probe integrates:
- PantheonOS - The multi-agent framework powering the web server's chat agent
- Bowtie2 - Fast and memory-efficient sequence alignment
- BLAST+ - Sequence similarity search
- MMseqs2 - Ultra-fast and sensitive sequence search and clustering
- Jellyfish - Fast k-mer counting
- ViennaRNA - RNA secondary structure prediction
- Primer3 - Primer and probe design algorithms
- FastAPI & React - Powering our interactive Web UI