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]).
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].
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 captures Nitzan & Libai's finding that the social-churn effect is near-zero for satisfied customers. 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 ~14-day half-life calibrated on online-firestorm lifecycle studies [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 TypeScript port in /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.
Below is the measured battery — every row is the live output of the harness in scripts/engine-bench.ts, 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 94% · p50 96% · p90 98% |
| Loud-detractor band · 200 seeds | 2.3% – 4.7% (p10–p90) |
| LLM calls · per tick · per run | 0 |
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).neighbour-defection raises churn ~1.3–1.8×, near-zero when satisfied
- [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. Journal of Marketing Communications.outrage decay ~7–21 days → δ≈0.07/tick
- [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 — Built to say no.the friction, β-split, and complex-contagion gate are detailed there
Reproducibility
Every figure in this note is reproducible from seed = 4711 on the engine in /engine. 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
