---
myst:
html_meta:
"description lang=en": |
How PANORAMA's pangenome comparison workflows work for spots and systems.
"keywords": |
Pangenomics, Python, Developer, Comparison, Spots, Systems, GFRR
---
# Compare Workflow
PANORAMA provides two comparison subcommands that share a common graph-based strategy:
| Subcommand | Entry point | What it compares |
|---|---|---|
| `compare_spots` | {py:func}`panorama.compare.spots.launch` | Genomic islands (RGP spots) across pangenomes |
| `compare_systems` | {py:func}`panorama.compare.systems.launch` | Detected biological systems across pangenomes |
Both rely on a **Gene Family Relatedness Ratio (GFRR)** metric computed in
{py:mod}`panorama.compare.utils` and follow the same three-phase structure:
build a graph → score edges → cluster into conserved groups.
---
## Shared infrastructure ({py:mod}`panorama.compare.utils`)
### GFRR — the similarity metric
The GFRR between two objects (spots or systems) from different pangenomes is the fraction
of their gene families that are considered equivalent (via {py:class}`Akin `
links established by `panorama align` or `panorama cluster`):
```
GFRR(A, B) = |shared Akin families| / aggregation(|families in A|, |families in B|)
```
The aggregation function is controlled by `--gfrr_metrics` (`min_gfrr`, `max_gfrr`,
`mean_gfrr`). `--gfrr_cutoff` sets the threshold below which an edge is dropped.
{py:func}`panorama.compare.utils.compute_gfrr` computes the score for a single pair;
it is called from both comparison modules.
### Clustering
{py:func}`panorama.compare.utils.cluster_on_gfrr` applies connected-component clustering
on the filtered graph. Each connected component becomes one
{py:class}`ConservedSpots ` or
{py:class}`ClusterSystems ` group.
---
## Spots comparison ({py:mod}`panorama.compare.spots`)
(validate_load_spot)=
### 1. Validate and load
{py:func}`panorama.compare.spots.check_compare_spots_args` validates arguments and
returns the `need_info` dict. Pangenomes are loaded in parallel by
{py:func}`panorama.format.read_binaries.load_pangenomes` with spots and RGPs enabled.
### 2. Build per-pangenome spot graphs
{py:func}`panorama.compare.spots.create_pangenome_spots_graph` builds a `networkx`
graph for each pangenome where nodes are {py:class}`Spot ` objects
and edges connect spots that share gene families within that pangenome.
(build_cross_spot)=
### 3. Build the cross-pangenome graph
{py:func}`panorama.compare.spots.create_spots_graph` merges the per-pangenome graphs
and adds cross-pangenome edges by calling
{py:func}`panorama.compare.spots.compute_gfrr_edges`, which iterates over every pair of
pangenomes and scores each spot–spot combination using GFRR.
#### Optional: overlay systems
If `--systems` is passed, {py:func}`panorama.compare.spots.add_systems_info` annotates
each spot node with the systems detected within it, and
{py:func}`panorama.compare.spots.create_pangenome_system_graph` /
{py:func}`panorama.compare.spots.create_systems_graph` build a parallel systems layer
linked to the spot graph via
{py:func}`panorama.compare.spots.graph_systems_link_with_conserved_spots`.
(write_outputs_spots)=
### 4. Cluster and write outputs
{py:func}`panorama.compare.utils.cluster_on_gfrr` groups connected spots into
{py:class}`ConservedSpots ` objects.
{py:func}`panorama.compare.spots.write_conserved_spots` writes the TSV and optional
graph outputs.
(data_flow_spots)=
### Data-flow summary
```
load_pangenomes()
│
├─ create_pangenome_spots_graph() per-pangenome graph
├─ create_spots_graph() merge + GFRR cross-pangenome edges
│ └─ compute_gfrr_edges() score every spot pair
│
├─ [optional] add_systems_info() overlay systems on spots
│
├─ cluster_on_gfrr() connected components → ConservedSpots
└─ write_conserved_spots() TSV + graph outputs
```
---
## Systems comparison ({py:mod}`panorama.compare.systems`)
(validate_load_systems)=
### 1. Validate and load
{py:func}`panorama.compare.systems.check_compare_systems_args` validates that the number
of `--sources` matches `--models` and returns the `need_info` dict. Pangenomes are loaded
with systems and family metadata enabled.
### 2. Build per-pangenome system graphs
{py:func}`panorama.compare.systems.create_pangenome_system_graph` builds a graph for
each pangenome where nodes are {py:class}`System ` objects
and edges connect systems that share gene families within that pangenome.
{py:func}`panorama.compare.systems.add_system_metadata_to_graph` annotates nodes with
system metadata for downstream output.
(build_cross_systems)=
### 3. Build the cross-pangenome graph
{py:func}`panorama.compare.systems.create_systems_graph` merges per-pangenome graphs
and {py:func}`panorama.compare.systems.compute_gfrr_edges` scores every system–system
pair across pangenomes using GFRR.
(write_outputs_systems)=
### 4. Cluster and write outputs
{py:func}`panorama.compare.utils.cluster_on_gfrr` groups connected systems into
{py:class}`ClusterSystems ` objects.
{py:func}`panorama.compare.systems.write_conserved_systems` writes TSV outputs.
Optionally, {py:func}`panorama.compare.systems.generate_heatmap` and
{py:func}`panorama.compare.systems.create_pangenome_systems_heatmaps` produce Bokeh
HTML heatmaps of system distributions across pangenomes.
(data_flow_systems)=
### Data-flow summary
```
load_pangenomes()
│
├─ create_pangenome_system_graph() per-pangenome graph
├─ create_systems_graph() merge + GFRR cross-pangenome edges
│ └─ compute_gfrr_edges() score every system pair
│
├─ cluster_on_gfrr() connected components → ClusterSystems
├─ write_conserved_systems() TSV outputs
└─ [optional] generate_heatmap() Bokeh HTML visualisation
```
---
## Parallel execution
Both workflows wrap their per-pangenome graph construction in a `ThreadPoolExecutor`
and use a shared `multiprocessing.Manager().Lock()` (via {py:func}`panorama.utils.init_lock`)
to serialise concurrent HDF5 reads. The number of workers is controlled by `--cpus`.
```{note}
Alignment or clustering (`panorama align` / `panorama cluster`) must be run before
comparison so that {py:class}`Akin ` links exist between
gene families from different pangenomes. Without them every GFRR score is 0.
```