Stochastic assembly

theory

This page runs stochastic community assembly with regnans and the full plant FF16 model. The maximum-fitness method always adds the mutant with the highest invasion fitness. Stochastic assembly instead draws a new mutant at random from a Gaussian mutation distribution, controlled by vcv.

The sequence of mutants therefore resembles a noisy evolutionary walk rather than a direct climb towards the highest-fitness strategy. The first example allows leaf mass per area (lma) to vary. The second allows both lma and height at maturation (hmat) to vary.

library(plant)
library(regnans)
library(tidyverse)

model_support <- list(
  p = plant_default_assembly_pars(),
  plant_control = plant_default_assembly_control()
)

Leaf mass per area (lma)

Assembly

control <- assembler_control(
  list(
    run_type = "single_step",
    birth_type = "stochastic",
    vcv = diag(1)
  )
)

community0 <- community_start(bounds(lma = c(0.01, 1)), model_support = model_support)
obj <- assembler_run(assembler_start(community0, control = control), nsteps = 60)

obj$community$traits
            lma
[1,] 0.31224478
[2,] 0.09486322
[3,] 0.30855811
obj$community$birth_rate
[1] 0.443440488 4.652990951 0.004294305

The assembly walk

tidy_assembly() gives one row for each resident strategy at each step. After unnesting the trait values, we can plot how the community changes through time. Larger points represent strategies with higher birth rates:

tidy_assembly(obj) |>
  filter(resident) |>
  unnest(traits) |>
  mutate(step = as.integer(step)) |>
  ggplot(aes(step, lma, size = births)) +
  geom_point(alpha = 0.4, colour = "forestgreen") +
  scale_y_log10() +
  labs(x = "assembly step", y = "lma", size = "birth rate") +
  theme_classic()

Fitness landscape

Stochastic runs do not calculate a complete fitness landscape by default. We can evaluate it on a grid for the final community. Resident strategies are marked in red; they should lie at or near zero fitness, with no large positive invasion peaks remaining:

obj$community <- community_fitness_landscape(obj$community, method = "grid", n_evals = 100)
fp <- obj$community$fitness_points
ggplot(fp, aes(lma, fitness)) +
  geom_line() +
  geom_hline(yintercept = 0, linetype = "dashed") +
  geom_point(data = filter(fp, resident), colour = "red", size = 2) +
  scale_x_log10() +
  theme_classic()

Leaf mass per area and height at maturation

With a two-dimensional mutation distribution, both lma and hmat can change at each step. The community is then assembled across a two-dimensional trait space.

control2 <- assembler_control(
  list(
    run_type = "single_step",
    birth_type = "stochastic",
    compute_viable_fitness = FALSE,
    vcv = diag(2)
  )
)

community0 <- community_start(bounds(lma = c(0.01, 1), hmat = c(0.5, 30)),
                             model_support = model_support)
obj2 <- assembler_run(assembler_start(community0, control = control2), nsteps = 10)

obj2$community$traits
            lma      hmat
[1,] 0.06078574 10.889714
[2,] 0.30697190  5.545609
obj2$community$birth_rate
[1] 137.56505  61.09276

The final plot shows the assembled strategies in the lma-hmat trait space. Larger points represent strategies with higher birth rates:

as_tibble(obj2$community$traits) |>
  mutate(birth_rate = obj2$community$birth_rate) |>
  ggplot(aes(lma, hmat, size = birth_rate)) +
  geom_point(colour = "forestgreen", alpha = 0.6) +
  scale_x_log10() +
  labs(size = "birth rate") +
  theme_classic()