Anti-money laundering
Declare the laundering rate, the typology mix and the hard negatives, then prove nothing else in the file looks like a crime.
Financial crime teams, model validators, and anyone benchmarking a detector
The problem
Real transaction data cannot leave the bank, and the part that could would not help anyway: nobody knows which of its rows are laundering. Institutions have alerts, and alerts are a detector's opinion, not the truth.
So the number every AML programme is judged on, the false positive rate, is measured against labels that were themselves produced by a detector. The measurement and the thing being measured share an error.
Why the usual answer falls short
The established answer is simulation. AMLworld, from IBM Research and ETH Zurich (NeurIPS 2023), grows an agent-based economy and labels what its agents do. It is careful work and it produces complete labels, which is a real advance over alerts.
But a simulator measures its illicit ratio after the fact rather than accepting it as an input. That is why its public releases are bucketed into higher- and lower-illicit sets rather than parameterised: the rate is an outcome. You cannot ask for exactly 0.40% split 40/20/20/10/10 across typologies.
The deeper problem is the one nobody can see. Emergent background traffic will sometimes contain, by chance, the exact shapes a detector hunts. When the screen fires on one, there is no way to tell whether the detector erred or the simulation happened to draw a ring. The false positive rate inherits that ambiguity.
Uncontrolled Accidental Cycles
Standard agentic or probabilistic graph generators let edges form at random. When loops inevitably close due to random chance, detection models cannot distinguish real criminal patterns from noise.
Strictly Acyclic Background + Explicit Motifs
The background network is enforced as a strict topologically ranked DAG where edges only flow from low to high rank. Zero accidental loops can ever close. Every ring has an explicit Case ID.
Head to head
The illicit rate is whatever the agents produce. Releases ship as higher- and lower-illicit buckets because the ratio is an outcome.
The rate is an input. 0.40% of two million rows is 8,000 transactions, and 8,000 is what comes out.
The typology mix is emergent. You take the blend the model happens to generate.
The mix is stated and allocated by largest remainder, so 40/20/20/10/10 lands as 3,200/1,600/1,600/800/800 exactly.
Background traffic can grow the same shapes a detector hunts. A hit may be the detector's error or the simulation's coincidence, and nothing distinguishes them.
The background is ranked and acyclic, so an accidental ring cannot form. Every hit sorts into a declared bucket.
Confounders cannot be commissioned. You cannot request 800 innocent rings that look guilty.
Hard negatives are declared like anything else: 4,000 rows across 872 real cycles, labeled legitimate and never flagged.
What was declared
0.40% of two million transfers are laundering, split 40% rings, 20% fan-out, 20% fan-in, 10% scatter-gather and 10% layering chains, plus 0.20% declared hard negatives: real rings that are entirely legitimate.
Primitives used: graph_motifs, dag_edges. Seed 20260905, engine 0.9.6.46.
Circular Ring
cycleFunds traverse a closed loop of accounts, returning to source minus intermediary cuts.
Fan-Out Smurfing
fan_outOne central originator splits a large aggregate sum into micro-transactions under reporting thresholds.
Fan-In Layering
fan_inDozens of small straw-purchaser accounts aggregate balances into a single destination vault.
Scatter-Gather
scatter_gatherFunds split across mule relays, layer through shell accounts, then reconverge at a clean entity.
Multi-Hop Layering Chain
chainLinear sequential hops across jurisdictions and entities to obscure provenance.
How it is built
- Stage 01Pipeline Step
Declare
Rate, typology mix, hard negatives. Shares must sum to one or the schema is refused.
rate: 0.004 shares: {cycle: .4, fan_out: .2, fan_in: .2, scatter_gather: .2} - Stage 02Pipeline Step
Rank & Lay Background
Nodes receive a strict topological rank. Every background edge runs strictly from low to high, so no cycle can close at any depth.
dag_edges → strictly acyclic background graph - Stage 03Pipeline Step
Inject Labeled Motifs
Targeted subgraphs are rewritten into declared laundering shapes, each tagged with an immutable case ID. Unaffected edges remain acyclic.
graph_motifs → 8,000 edges, 1,578 certified cases - Stage 04Pipeline Step
Independent Oracle Audit
A separate verification pass recalculates all connected components and loop structures. It throws hard errors on any unintended cycle.
largest un-cased strongly-connected component = 1 node
What came out, and how it was checked
| Transfers generated over 60,000 accounts | 2,000,000 in 7.2 seconds |
| Declared laundering transactions | 8,000 declared, 8,000 found |
| Typology mix, exact | 3,200 ring / 1,600 fan-out / 1,600 fan-in / 800 chain / 800 scatter-gather |
| Declared rings that are genuinely closed rings | 1,578 cases, 0 malformed |
| Declared hard negatives, labeled and never flagged | 4,000 rows across 872 real cycles, 0 flagged |
| Background subgraph carrying no case id | 1,988,000 edges over 60,000 nodes, largest strongly connected component = 1 node |
The last row is the one that matters. A strongly connected component of one node means no cycle exists anywhere outside a declared case, so an accidental pattern is not unlikely, it is impossible.
Running a detector against it
A standard directed-triangle screen, the kind deployed as a first-pass graph filter, run over the same file:
| Directed triangles found | 609 |
| Edges flagged | 1,792 |
| Flags that are a declared motif | 1,341 |
| Flags that are a background edge adjacent to a declared motif | 451 |
| Triangles made entirely of background edges | 0 |
| Recall on the three-hop rings this screen can see | 1,089 / 1,089 |
Every false positive is attributable. Some are confounders declared on purpose, the rest are background edges pulled in by a declared structure, counted separately. None are accidents, and that is proven rather than assumed. A generator that grows its graph cannot produce this table, because it cannot separate its own noise from the detector's error.
What this does not claim
- The acyclic background is a designed property, not a claim about banking. Real ledgers contain innocent loops. Declare hard negatives to put them back, so they stay accounted for.
- Names and free text are structural placeholders. AML detection reads amounts, timing, counterparties and graph shape, none of which is prose, so this is not a limitation for the task. Do not use the file for anything language-shaped.
- This does not replace AMLworld. It answers a different question. They simulate and measure, which is what you want for training on emergent realism. This declares and proves, which is what you want for measuring a detector against known conditions.
- A certificate proves the data matches the declaration. It never proves the declaration matches reality. That judgement stays with the person writing the schema.
Questions
How is this different from AMLworld or AMLSim?
What does "the background is provably acyclic" actually buy me?
Can I control the typology mix precisely?
What are declared hard negatives?
Is the data reproducible?
Can I use this for model validation or a regulator conversation?
Build this yourself
Everything above is a schema and a seed. Install the engine, declare what has to be true, and check the result with whatever you trust.

