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.
Cycles emerge on their own. Some were the simulation acting out a crime, some were traffic. Nothing in the file says which.
Background edges run forward along a rank, so no loop can close. The only ring is one somebody asked for, and it carries a 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.
cycleValue returns to where it started, minus a skim at each hop.
fan_outOne account splits a sum across many, to stay under notice.
fan_inMany accounts converge on one, reassembling the total.
scatter_gatherSplit across intermediaries, then collected at a second account.
chainHop after hop, each one putting distance from the source.
How it is built
- 01
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, …} - 02
Rank and lay the background
Nodes get a topological rank. Every background edge runs low to high, so a cycle cannot close at any depth.
dag_edges → acyclic by construction
- 03
Inject the motifs
A slice of rows is rewritten into the declared shapes, each labeled with a case id. Rows left alone stay as the DAG put them.
graph_motifs → 8,000 edges, 1,578 cases
- 04
Certify
An independent pass recomputes the counts and the components. It fails loudly rather than warning.
largest un-cased SCC = 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?
- Those simulate agents and measure the illicit ratio that emerges, which is why their datasets ship as higher- and lower-illicit buckets rather than as a parameter. Misata takes the rate and the typology mix as inputs and hits them exactly, then certifies the result. Different guarantee for a different job: simulation is better for training on emergent realism, declaration is better for measuring a detector against known conditions.
- What does "the background is provably acyclic" actually buy me?
- It means an accidental laundering pattern cannot exist in the data. Every cycle belongs to a case that was declared, so when your detector fires, the hit sorts into a known bucket instead of an unknown one. On emergent data you can never tell a detector's mistake from a coincidence in the traffic, and that ambiguity lands directly in your false positive rate.
- Can I control the typology mix precisely?
- Yes, that is the point. Shares must sum to one and are hit exactly by largest-remainder allocation, so 40/20/20/10/10 over 8,000 transactions gives 3,200/1,600/1,600/800/800 and not approximately that.
- What are declared hard negatives?
- Real motifs of the same shapes, labeled as legitimate rather than as laundering. Without them a detector is only measured on whether it can find loops. With them it is measured on whether it can tell a laundering ring from an innocent one, which is the thing that actually drives alert volume.
- Is the data reproducible?
- Yes. The same schema, seed and Misata version produce byte-identical output. The seed and version for the run above are printed on this page.
- Can I use this for model validation or a regulator conversation?
- It is built for exactly that: measuring detector behaviour under conditions you stated in advance and can show on paper. It is not a substitute for testing against your own production data, and the honest limits are listed on this page rather than buried.
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.

