Click any figure for the code that produces it. Every example simulates with ZOMBI2 and plots with Phylustrator, a separate package — so to run one you need both:
pip install zombi2 phylustrator
Pure birth, no extinction — a forward tree of 100 lineages.
pure birth (Yule)The full history behind 50 survivors — their branches solid, extinct lineages dashed.
birth–deathA pulse at t = 3 culls 75% of lineages — the skyline drops sharply at the dashed line, then recovers.
mass extinction · + skylineSpeciation runs slow, then fast, then slow — the burst packs branches between the two dashed regime lines.
time-varying birthSpeciation slows as diversity fills up; the skyline rises and plateaus at the cap of 100.
birth–death · + skylineTwo thousand trees under each of two processes. zombi2 tools tree --gamma separates them almost perfectly.
A genome as a ring — genes evenly spaced by rank, coloured by family, arrows by strand. plot(g, layout="circular") + genes().
Two genomes, one per row; ribbons link same-family genes and cross where the order was rearranged. stack([a,b]) + synteny().
Every tip's gene order beside the tree. Genes are coloured by their ancestral position, so each rearrangement is a break in the gradient.
phylustrator · syntenyOne family's history on the species tree: duplications (squares), losses (crosses), transfers (arrows, donor→recipient).
phylustrator · eventsA family × genome copy-number heatmap, its rows locked to the tips. beside(tree, heatmap(profiles)).
A real bacterium: Mycoplasma genitalium, 546 genes at their true base positions. The forward/reverse switch marks the replication origin.
phylustrator · real GFFOne inversion on a circular genome: the segment is reversed and its strands flip. The band marks it in both rings.
phylustrator · circularTransfers steered to run between two clades, by topology rather than by a trait. The barplot counts them by clade pair, so A↔B towers over within-clade.
clades · transfer_toTwo runs at the same mean rates. Every family alike gives no core at all; letting families differ gives 28 core families and a U-shaped spectrum.
phylustrator · heterogeneityEvery lineage draws its own rate, with no memory of its parent, so the colour is salt-and-pepper. substitution = PerSite().varying_among('lineages', LogNormal(0.0, 0.55)).
The same independent draw with a gamma instead of a lognormal. varying_among('lineages', Gamma(shape=3.31, scale=0.302)).
A daughter starts at its parent's rate and is nudged, so the colour moves in clades rather than branch to branch. substitution = PerSite().varying_among('lineages', Drift(LogNormal(0.0, 0.4))).
The same inherited drift in steps: the rate takes one of a few values and a daughter moves to a neighbouring one. varying_among('lineages', Drift(LogNormal(0.0, 0.45), bins=6)).
A small tree with its internal nodes numbered, and beside it the sequence at each. The rows are one per node, not aligned to the tips.
phylustrator · ancestralA single-copy family across 20 species, residues coloured (with a nucleotide key), each row locked to its tip. beside(tree, alignment(aln)).
Free diffusion — sister lineages drift apart with time.
continuousPulled to an optimum: a high start (yellow) converges to blue.
continuousA two-state trait hops between habitats; each branch is painted by its state history.
discrete · MkTwo traits evolve together (r = 0.9) — two trees, coloured by each trait, and the tip scatter.
continuous · + scatterTwo binary characters where one's flip rate depends on the other's state. X in green, Y in purple, so you can see Y is present where X is; the 2×2 chain is the model.
discrete · dependentA habitat trait sets how fast body size diffuses: twenty times faster where the habitat fluctuates. rate = PerLineage(0.25).scaled_by(habitat, {…}).
A lifestyle trait drives gene loss. Endosymbionts shed genes faster and gain fewer, so their genomes end up a fifth the size.
trait → lossA selection trait drives duplication. Under relaxed selection duplicates accumulate and the genomes grow fivefold.
trait → duplicationA competence trait drives who receives a transfer rather than a rate. Competent lineages take up DNA more often.
trait → transfer uptakeA diffusing trait drives gene gain. A Curve turns each value into a factor, so genome size follows the trait.
The same run with a different Curve. Gene gain rises with the trait and then levels off, so the factor is bounded.
The response is any function you write. Here it peaks at an intermediate value, which no table of per-state multipliers can express.
continuous trait → originationA temperature trait is grown first; body size then diffuses at a rate that reads it. The scale is centred on where it started, so white means it has not moved.
trait → traitCarrying a toxin family makes a lineage become pathogenic forty times faster. 74% of the tips with the gene end up pathogenic, against none of those without.
gene → traitHow much of a four-gene module a lineage keeps decides its metabolism, through a step: lambda f: 20.0 if f > 0.5 else 1.0.
A two-state trait drives speciation — the fast state's clades take over; the inset is the state Markov chain.
trait → speciationOne state dies far faster; the doomed lineages (dashed) drop out.
trait → extinctionThree graded speciation rates with constant death — the fastest state fills the tree, extinct lineages dashed.
trait → speciation