← Field notes
Note 01·Methodology · Engine·2026-06·v0.5 · draft·12 min

Not another panel. A market simulator.

The hybrid Bass-SIR-D engine: math, vectorisation, calibration, reproducibility — and why it runs in milliseconds with zero LLM calls per tick.

by Holon Research

ABSTRACT

We describe Holon's simulation engine, a continuous-time hazard model with four states (Susceptible, Adopter, Recovered, Detractor) operating over a directed weighted graph of 600–10,000 agents. Adoption combines an exogenous innovation term with endogenous social pressure (Bass [1]); churn is socially contagious (Nitzan & Libai [3]); negative signals carry a 2.5× weight (Baumeister [6]). Because every quantity reduces to a sparse matrix-vector product, a 90-tick run on 1,000 agents executes in ~9 ms on a single core (measured — §7). Runs are deterministic functions of (scenario, seed) and hashable end-to-end — the precondition for publishing backtests other people can verify.

1. The problem with synthetic panels

Most synthetic-user tools prompt a language model to roleplay a persona, then ask it questions one at a time. It is a chat window wearing a costume — fine for a first gut-check, useless for the thing that actually decides a launch: how an opinion moves through a room. Adoption is not an average of isolated answers. It is a process on a network.

The 2026 buyer guides for synthetic research are explicit on the gap: the category is weakest precisely where it is most often sold — modelling social contagion, negative word-of-mouth, and the cascades that determine whether a feature lands or a price change rebounds [9]. Holon was built against that gap.

Per 90-day run
~9 ms
1,000 agents · single core · measured
LLM calls
0
per tick · per run · per agent
Edges modeled
~7k
1k-agent city · directed · weighted · sparse

2. The four-state model

Each of n agents occupies one of four states at discrete time t:

  • S — Susceptible. Has not adopted. Receives both positive and negative pressure.
  • I — Adopter (Infectious). Has adopted. Emits a persuasion signal η to its out-neighbours.
  • R — Recovered. Has churned silently. Emits nothing.
  • D — Detractor. Has churned loudly. Emits a negative signal weighted ν ≈ 2.5·η.

Transitions S→I, I→R, I→D, and D→R are stochastic and depend on the agent's role, budget, and the pressure arriving from its in-neighbours in a directed weighted graph G = (V, W). The split between R and D is governed by a role-specific loud-exit probability β (Power Users churn loudly; Lurkers vanish silent — §3 of Note 02 [11]).

Schema
Hazards drive every transition. β splits churn into loud (D) vs. silent (R); δ decays detractors back to a quiet state — a short, tunable half-life consistent with how online firestorms burn out [5].

The fourth state is the one most models skip, and it is the one that matters. A churned customer who simply stops logging in (R) and a churned customer who writes the thread that warns forty peers (D) look identical on a retention dashboard — but they do very different things to a market. Splitting them is what lets the engine reproduce a bad-news cascade instead of a smooth, gentle decay. Everything downstream — the hazards, the pressure term, the speed — exists to move agents between these four boxes honestly.

3. Adoption hazard

We use a continuous-time hazard formulation discretised at Δt = 1 day. The probability that susceptible agent i adopts during a tick composes an exogenous innovation term p (advertising, discovery) with endogenous imitation q·φ⁺, divided by a role-based friction coefficient f, and dampened by negative pressure φ⁻:

S → I — show
λᵢ(t) = 1 − exp( −Δt · [ pᵢ + qᵢ · φ⁺ᵢ(t) / fᵢ⁽ᵉ⁾ − r · φ⁻ᵢ(t) ] )

The exponential form keeps λ in [0, 1] for any pressure, avoids double-counting when multiple neighbours push simultaneously, and matches the standard continuous-time hazard convention used in epidemic and diffusion models [2]. The novelty is f (friction, see Note 02 [11]) and the explicit negative term r · φ⁻ that lets aversion suppress adoption even when social proof is high.

Prior values for (p, q) are taken from the Sultan-Farley-Lehmann meta-analysis [2], rescaled from annual category penetration to community-response hazards. Calibrated cities (Scale tier) fit per-agent (p, q, μ) to client historical curves and report a per-account MAPE on a held-out window.

4. Social pressure on the graph

Pressure is computed from the graph, not from a prompt. Let W ∈ ℝⁿˣⁿ₊ be the column-normalised weighted adjacency matrix where Wⱼᵢ > 0 means j influences i. Active adopters emit a signal u(t) = η ⊙ i(t); detractors emit a negative signal u⁻(t) = ν ⊙ d(t) with ν ≈ 2.5 η. The pressure received by every node is a single matrix product:

positive & negative pressure — show
φ⁺(t) = a ⊙ ( Wᵀ u(t) ) u(t) = η ⊙ i(t) φ⁻(t) = a ⊙ ( Wᵀ u⁻(t) ) u⁻(t) = ν ⊙ d(t)

Because each step is a sparse matrix-vector multiply, the whole city updates simultaneously. There is no per-agent loop, no per-agent API call. a is the receiver's affinity; ⊙ denotes element-wise product. Cluster-level ("tribal") pressure can be added with a membership matrix C ∈ {0,1}ⁿˣᴷ to model community contagion [4].

The intuition is worth stating plainly: at each tick every adopter "pushes" on everyone it is connected to, every detractor pushes the other way, and the matrix product just totals up what lands on each person at once. No agent waits its turn, no agent calls an API to decide — the whole city resolves one step in a single multiply. That is the entire reason the thing runs in milliseconds rather than minutes.

Schema
Pressure on every node = one sparse matrix-vector product. Filled cells = non-zero edge weights; the black column = adopter mask; the orange bars = the resulting pressure vector φ⁺.

5. Contagious churn

Naïve models treat churn as an independent coin flip. Holon makes it social: an adopter's churn hazard rises with personal aversion av and with the negative pressure from neighbours who already defected — but only if the agent is itself exposed (Nitzan & Libai's neighbour-defection effect [3]):

I → R or D — show
γᵢ(t) = 1 − exp( −Δt · [ μᵢ + μ_av·avᵢ + s · φ⁻ᵢ(t) · (0.15 + 1.3·avᵢ) ] )

The (0.15 + 1.3·av) factor encodes our modelling choice that social churn bites the already-dissatisfied and barely touches the satisfied — consistent with Nitzan & Libai's neighbour-defection result [3]. When churn fires, a role-specific β decides whether the agent exits quietly (R) or loudly (D). Detractors decay back to quiet at rate δ ≈ 0.07/tick — a short half-life we set to match how online firestorms burn out [5].

6. Why it's fast

Every quantity above is a vector or a sparse matrix. A full 90-tick run on 1,000 agents is ~90 sparse matrix-vector products — milliseconds on a single core. Below is the inner loop in NumPy, the same code that powers the reference engine; the production TypeScript engine is a direct transliteration.

Show implementation — python
def step(state, W, p, q, r, mu, f, eta, nu, a, dt=1.0, rng=None):
    rng = rng or np.random.default_rng()
    I  = (state == 1).astype(float)
    D  = (state == 3).astype(float)
    u_pos =  eta * I
    u_neg =  nu  * D
    phi_pos = a * (W.T @ u_pos)          # positive social pressure
    phi_neg = a * (W.T @ u_neg)          # negative pressure (negativity bias)
    # adoption hazard (Bass-style with friction and aversion)
    lam = 1 - np.exp(-dt * np.maximum(0, p + q*phi_pos/f - r*phi_neg))
    # churn hazard, gated by personal aversion (Nitzan & Libai)
    gam = 1 - np.exp(-dt * (mu + 0.04*aversion + 0.14*phi_neg*(0.15+1.3*aversion)))
    return advance(state, lam, gam, beta_role, delta=0.07, rng=rng)

Zero LLM calls per tick means: no rate limits, no latency, no per-run bill. You can sweep 500 random seeds and read a distribution rather than a single hopeful point.

PER VERDICT · 1,000 AGENTS · 90 DAYS
Survey panelLLM panelSmallville-style [7]Holon
Wall-clock2–6 weeks~2 minhours~9 ms
Cost / run$5k–20k~$0.50$$$~$0
LLM calls0~n~n²0
Models networknonopartialyes
Reproduciblenononoseed→hash

* Holon wall-clock and cost measured on a single CPU core; LLM panel cost assumes GPT-4-class API at typical 2026 pricing.

Engine scaling · wall-clock per 90-tick run · measuredmax ≈ 106 ms
  • 1,000 agentsdemo city · |E|≈7k
    ~9 ms
  • 5,000 agentsmid · |E|≈34k
    ~53 ms
  • 10,000 agentslarge · |E|≈69k
    ~106 ms

7. Reproducibility

A run is a pure function of (scenario, seed). The engine uses a seeded Mulberry32 PRNG; Date.now() and Math.random() are forbidden inside it (linted at CI). We hash the final state, so any shared URL replays byte-for-byte — the precondition for publishing backtests that other people can independently verify.

Reproducible does not mean identical. Each seed is a fixed, replayable world — and different seeds are genuinely different worlds. Here is the same city under the same feature-release scenario, on six seeds:

SeedAdoptionLoud detractorsEnd-state hash
4224.2%0.4%d927bc3d
4327.7%0.4%a0d03d0a
7724.8%0.6%d2034fa4
12825.1%0.2%81c7e1f
25622.9%0.2%7785ee88
99924.7%0.5%b022f4ef

Adoption (of the in-market pool) stays in a tight 22.9–27.7% band — a feature reaches its addressable subset, not the whole market — but the loud-detractor share swings from 0.2% to 0.6%, a 3× difference in how much public backlash the same launch produces, depending only on which world you happened to land in. Re-run any row and it returns its exact hash; report a single row as "the answer" and you are gambling on the seed. That is precisely why every verdict is a p10·p50·p90 over 200–500 seeds, never one number.

Below is the full measured battery — every row is the live output of our measurement harness, not a target we hope to hit.

PropertyMeasured result
Determinism200/200 — same (scenario, seed) → identical end-state hash; a different seed diverges
Run time · 1,000 agents · 90 ticks9 ms median · 10 ms p90 · single core
Scaling · 1k / 5k / 10k agents9 ms / 53 ms / 106 ms
Adoption spread · feature · 200 seedsp10 21% · p50 25% · p90 28% (of in-market)
Loud-detractor band · 200 seeds0.1% – 1.3% (p10–p90)
LLM calls · per tick · per run0

Internal reproducibility is necessary but not sufficient — a model can be perfectly reproducible and still wrong. So we also check the diffusion core against the outside world. In its homogeneous mean-field limit, Holon's adoption hazard should collapse to the classic Bass model [1]. It does: across five documented products, the engine's curve tracks the closed-form Bass solution on each product's published (p, q) to within 0.4–3.5% MAPE, with the adoption-rate peak landing on the right period.

ProductpqPeak tick · engine / BassMAPE vs closed-form
Colour TV0.0210.5836 / 5.50.94%
B&W TV0.0650.3355 / 4.10.36%
Cellular phones0.0080.42110 / 9.21.13%
Cable TV0.00000610.501223 / 22.63.50%
Microwave oven0.0180.3379 / 8.30.64%

The scorecard, one frozen calibration against many independent public references at once — no per-case tuning (that would be overfitting):

CheckEnginePublic referenceType
Bass diffusion · 5 classics0.4–3.5% MAPEclosed-form Bass on documented p,q [1]reproduces documented math
Price-hike churn curve+10%→3% · +20%→8% · +30%→16%ProfitWell n≈14k · OpenView ranges [12]vs population benchmark
Gross-logo retention · SMB/mid/ent73% / 87% / 93% /yrvendor bands 70–82 / 85–90 / 90–95% [14]in band
Net dollar retention · SMB/mid/ent90% / 105% / 115%public-SaaS median 112% — Blossom St, 32 cos [13]brackets the market median
Bundling overtake (Teams↔Office)flips at ~85% suite reachdocumented Teams-over-Slack outcome [15]directional
Per-account MAPE on your cohort— harness readyyour private dataroadmap

8. What the model misses

High-stakes decisions dominated by emotion or identity (politics, sensitive medical, security). Novel categories with no historical precedent. Rare-event tail risk. We log these limits per backtest in a mandatory "what the model misses" section. The model is a pre-filter, not an oracle — designed to kill bad ideas quickly so research budget can be spent on the survivors.

Built on research published in

arXiv
Science
INFORMS — Management Science
SAGE
Univ. of Chicago Press

Methodology builds on peer-reviewed research from the venues above. See references for exact papers.

References

  1. [1]
    Bass, F. (1969). A New Product Growth Model for Consumer Durables. Management Science 15(5).origin of p (innovation) and q (imitation)
  2. [2]
    Sultan, F., Farley, J. & Lehmann, D. (1990). A Meta-Analysis of Diffusion Models. Journal of Marketing Research.p̄≈0.03, q̄≈0.38 across categories — used as priors, rescaled to community-response hazards
  3. [3]
    Nitzan, I. & Libai, B. (2011). Social Effects on Customer Retention. Journal of Marketing 75(6): 24–38. DOI 10.1509/jm.75.6.24.a defecting neighbour raises churn risk ~1.8× (80%); we additionally gate the effect by satisfaction in the model
  4. [4]
    Granovetter, M. (1978). Threshold Models of Collective Behavior. AJS 83(6).thresholds for collective adoption
  5. [5]
    Pfeffer, J., Zorbach, T. & Carley, K. (2014). Understanding online firestorms: negative word-of-mouth dynamics in social media networks. Journal of Marketing Communications 20(1–2): 117–128.documents firestorms as short-lived NWOM bursts; the detractor-decay δ≈0.07/tick is our model parameter, not a figure taken from this paper
  6. [6]
    Baumeister, R. et al. (2001). Bad Is Stronger Than Good. Review of General Psychology 5(4).the 2.5× negativity-asymmetry constant
  7. [7]
  8. [8]
    Aral, S. & Walker, D. (2012). Identifying Influential and Susceptible Members of Social Networks. Science 337(6092).asymmetric influence/susceptibility — informs η variation by role
  9. [9]
    Synthetic Market Research Buyers Guide 2026 (Minds/Lakmoos/Aaru meta-review).category gap: social contagion + NWOM
  10. [10]
    Centola, D. & Macy, M. (2007). Complex Contagions and the Weakness of Long Ties. AJS 113(3).complex contagion → multi-source gate (see Note 02)
  11. [11]
    Holon Field Note 02 — Twelve citizens. Built to say no.the cast, friction, β-split, and complex-contagion gate are detailed there
  12. [12]
    ProfitWell / Paddle price-increase analysis (n≈14,000 SaaS).a ~10% increase adds ~10% revenue at ~1–2% incremental churn when well-executed (vendor, large-sample); our worst-case no-moderator curve sits just above this
  13. [13]
    Blossom Street Ventures (Q2 2024). Net dollar retention across public SaaS companies IPO'd since 2017.112% median across the 32 of 64 companies that reported NDR — a compiled public dataset, not a single case
  14. [14]
    Drexus (2024) · QuantLedger (2025) · KPITree (2025) — B2B SaaS gross-logo retention benchmarks by ACV segment.vendor benchmarks (no peer-reviewed B2B gross-logo study exists): SMB 70–82% / mid 85–90% / enterprise 90–95% per year
  15. [15]

Reproducibility

Every figure in this note is reproducible from a fixed seed = 4711. Run hashes ship with each release; deviations from the published hash are reportable bugs. The TypeScript and Python reference implementations are tested for hash parity at CI.

Want to run this on your market?

Request access
NEXT · Note 02
Twelve citizens. Built to say no.