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.
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]).
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.
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.
| Survey panel | LLM panel | Smallville-style [7] | Holon | |
|---|---|---|---|---|
| Wall-clock | 2–6 weeks | ~2 min | hours | ~9 ms |
| Cost / run | $5k–20k | ~$0.50 | $$$ | ~$0 |
| LLM calls | 0 | ~n | ~n² | 0 |
| Models network | no | no | partial | yes |
| Reproducible | no | no | no | seed→hash |
* Holon wall-clock and cost measured on a single CPU core; LLM panel cost assumes GPT-4-class API at typical 2026 pricing.
- 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:
| Seed | Adoption | Loud detractors | End-state hash |
|---|---|---|---|
| 42 | 24.2% | 0.4% | d927bc3d |
| 43 | 27.7% | 0.4% | a0d03d0a |
| 77 | 24.8% | 0.6% | d2034fa4 |
| 128 | 25.1% | 0.2% | 81c7e1f |
| 256 | 22.9% | 0.2% | 7785ee88 |
| 999 | 24.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.
| Property | Measured result |
|---|---|
| Determinism | 200/200 — same (scenario, seed) → identical end-state hash; a different seed diverges |
| Run time · 1,000 agents · 90 ticks | 9 ms median · 10 ms p90 · single core |
| Scaling · 1k / 5k / 10k agents | 9 ms / 53 ms / 106 ms |
| Adoption spread · feature · 200 seeds | p10 21% · p50 25% · p90 28% (of in-market) |
| Loud-detractor band · 200 seeds | 0.1% – 1.3% (p10–p90) |
| LLM calls · per tick · per run | 0 |
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.
| Product | p | q | Peak tick · engine / Bass | MAPE vs closed-form |
|---|---|---|---|---|
| Colour TV | 0.021 | 0.583 | 6 / 5.5 | 0.94% |
| B&W TV | 0.065 | 0.335 | 5 / 4.1 | 0.36% |
| Cellular phones | 0.008 | 0.421 | 10 / 9.2 | 1.13% |
| Cable TV | 0.0000061 | 0.5012 | 23 / 22.6 | 3.50% |
| Microwave oven | 0.018 | 0.337 | 9 / 8.3 | 0.64% |
The scorecard, one frozen calibration against many independent public references at once — no per-case tuning (that would be overfitting):
| Check | Engine | Public reference | Type |
|---|---|---|---|
| Bass diffusion · 5 classics | 0.4–3.5% MAPE | closed-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/ent | 73% / 87% / 93% /yr | vendor bands 70–82 / 85–90 / 90–95% [14] | in band |
| Net dollar retention · SMB/mid/ent | 90% / 105% / 115% | public-SaaS median 112% — Blossom St, 32 cos [13] | brackets the market median |
| Bundling overtake (Teams↔Office) | flips at ~85% suite reach | documented Teams-over-Slack outcome [15] | directional |
| Per-account MAPE on your cohort | — harness ready | your private data | roadmap |
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
Methodology builds on peer-reviewed research from the venues above. See references for exact papers.
References
- [1]Bass, F. (1969). A New Product Growth Model for Consumer Durables. Management Science 15(5).origin of p (innovation) and q (imitation)
- [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]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]Granovetter, M. (1978). Threshold Models of Collective Behavior. AJS 83(6).thresholds for collective adoption
- [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]Baumeister, R. et al. (2001). Bad Is Stronger Than Good. Review of General Psychology 5(4).the 2.5× negativity-asymmetry constant
- [7]Park, J. S. et al. (2023). Generative Agents: Interactive Simulacra of Human Behavior. UIST.the 'Smallville' precedent we explicitly diverge from
- [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]Synthetic Market Research Buyers Guide 2026 (Minds/Lakmoos/Aaru meta-review).category gap: social contagion + NWOM
- [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]Holon Field Note 02 — Twelve citizens. Built to say no.the cast, friction, β-split, and complex-contagion gate are detailed there
- [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]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]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]European Commission (2024). Statement of Objections: Microsoft tied Teams to Office / Microsoft 365 (Slack complaint).the bundling/distribution advantage our Teams-into-Office case reproduces
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
