Skip to content

(not-to-merge) kicking the tires: dense subset sum in btsim#86

Draft
bc1cindy wants to merge 1 commit into
payjoin:masterfrom
bc1cindy:count
Draft

(not-to-merge) kicking the tires: dense subset sum in btsim#86
bc1cindy wants to merge 1 commit into
payjoin:masterfrom
bc1cindy:count

Conversation

@bc1cindy

@bc1cindy bc1cindy commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

wires the dense-subset-sum 4-path subset-sum/coinjoin-mapping counts (brute, radix, sparse, sasamoto) into btsim two ways:

  • as agent cost: WLowerBoundMetric (W lower bound via brute/sparse) and RadixMappingMetric (k×m! denomination mappings, gated by radix density) plug into the existing PrivacyBundle. With a subset_sum_threshold / radix_threshold set in ScorerConfig, each candidate plan is scored by a linear deficit to the threshold, so agents favor coinjoins with more subset-sum ambiguity.
  • as measurement: four_counts runs all four paths over the sim's confirmed txs recording CPU, peak RAM (alloc-probe feature), and density regime (kappa via the L bracket). Plus a correctness benchmark against the dep's oracle, a kappa/L sweep, a switch_sweep locating the sparse->Sasamoto handoff, and a CJA comparison (W vs Maurer sub-transaction mappings).

adds denominated funding (standard-denomination UTXOs in a band, via mine_denominated) so the sim can produce dense coinjoins, and fixes a latent over-spend in coin selection that the dense funding exposed (select_all now returns None unless inputs cover the target; session contribution is gated on coverage).

    N | κ    | sparse     | sparse_us | sasa_err% | spearman
    8 | 0.50 | Exact      |    152    |   18.0    |   0.810
   12 | 0.33 | Exact      |    469    |   19.1    |   0.936
   14 | 0.29 | LowerBound |   1179    |    5.3    |   1.000
   22 | 0.18 | LowerBound |   5957    |    2.4    |   1.000


N=14 (κ≈0.29) the sparse convolution stops being exact (its sumset exceeds the budget, CPU climbs 152→5957µs) and precisely there the Sasamoto error drops to ≤5% (spearman→1.0). In other words: where sparse becomes too costly, Sasamoto becomes the good estimator.

the metric is wired + unit-tested, but it doesn't yet steer the sim, emergent coinjoins are still sparse (same-seed runs are identical with the threshold on or off). Driving the sim to produce dense coinjoins is a separate work

BTSIM_CORRECTNESS=1 cargo run - 4 paths vs oracle, κ/L sweep, switch_sweep, CJA
(sparse_mem_budget=4096 entries, dp_max=4000000 cells, radix_max_size=6)
family | N | ground_truth | sparse_kind | sasamoto_err% | spearman | brute_us | radix_us | sparse_us | sasa_us | peakB | lb_ok | aborts | radix_dense | regime
   pow2 |  9 |  exhaustive |    Exact |          7.3 |    0.997 |      144 |       69 |       158 |      88 |         0 |    ok |        - |         yes | κ ∈ [0.3333, 5.3033], κ_c ∈ [0.9849, 0.0000], regime = Transitional
   pow2 | 12 |  exhaustive |    Exact |          6.0 |    0.999 |     1821 |       70 |       356 |     114 |         0 |    ok |        - |         yes | κ ∈ [0.3333, 3.9429], κ_c ∈ [0.9979, 0.0000], regime = Transitional
   pow2 | 15 |  exhaustive |    Exact |          5.5 |    1.000 |        0 |       44 |       687 |      83 |         0 |    ok |        b |         yes | κ ∈ [0.3333, 3.1328], κ_c ∈ [0.9723, 0.0000], regime = Transitional
   pow2 | 18 |  exhaustive |    Exact |          5.4 |    1.000 |        0 |       56 |      2747 |      90 |         0 |    ok |        b |         yes | κ ∈ [0.3333, 2.5961], κ_c ∈ [0.9343, 0.0000], regime = Transitional
   pow2 | 21 |  exhaustive |    Exact |          5.4 |    1.000 |        0 |       57 |      5228 |      87 |         0 |    ok |        b |         yes | κ ∈ [0.3333, 2.2146], κ_c ∈ [0.8939, 0.0000], regime = Transitional
   pow2 | 24 |          dp | LowerBnd |          5.3 |    1.000 |        0 |       51 |     18466 |      93 |         0 |    ok |        b |         yes | κ ∈ [0.3333, 1.9298], κ_c ∈ [0.8549, 0.0000], regime = Transitional
   pow2 | 27 |          dp | LowerBnd |          5.3 |    1.000 |        0 |       51 |     48313 |      89 |         0 |    ok |        b |         yes | κ ∈ [0.3333, 1.7091], κ_c ∈ [0.8187, 0.0000], regime = Transitional
   pow2 | 30 |          dp | LowerBnd |          5.3 |    1.000 |        0 |       54 |    127480 |     106 |         0 |    ok |        b |         yes | κ ∈ [0.3333, 1.5331], κ_c ∈ [0.7857, 0.0000], regime = Transitional
   pow2 | 33 |          dp | LowerBnd |          5.3 |    1.000 |        0 |       79 |    277263 |      93 |         0 |    ok |        b |         yes | κ ∈ [0.3333, 1.3895], κ_c ∈ [0.7557, 0.0000], regime = Transitional
   pow2 | 36 |          dp | LowerBnd |          5.3 |    1.000 |        0 |      234 |    294995 |      93 |         0 |    ok |        b |         yes | κ ∈ [0.3333, 1.2703], κ_c ∈ [0.7284, 0.0000], regime = Transitional
   pow2 | 39 |          dp | LowerBnd |          5.3 |    1.000 |        0 |      269 |    368619 |      93 |         0 |    ok |        b |         yes | κ ∈ [0.3333, 1.1696], κ_c ∈ [0.7036, 0.0000], regime = Transitional
   pow2 | 42 |          dp | LowerBnd |          5.3 |    1.000 |        0 |      654 |    605093 |     114 |         0 |    ok |        b |         yes | κ ∈ [0.3333, 1.0835], κ_c ∈ [0.6810, 0.0000], regime = Transitional
  radix |  9 |  exhaustive |    Exact |         98.7 |    0.277 |      138 |       57 |       137 |      89 |         0 |    ok |        - |         yes | κ ∈ [1.1073, 5.3033], κ_c ∈ [0.8599, 0.0000], regime = Sparse
  radix | 12 |  exhaustive |    Exact |         97.4 |    0.355 |     1371 |       63 |       316 |     128 |         0 |    ok |        - |         yes | κ ∈ [0.8333, 3.9429], κ_c ∈ [0.7907, 0.0000], regime = Sparse
  radix | 15 |  exhaustive |    Exact |         95.4 |    0.346 |        0 |       93 |       869 |      87 |         0 |    ok |        b |         yes | κ ∈ [0.7007, 3.1328], κ_c ∈ [0.9517, 0.0000], regime = Transitional
  radix | 18 |  exhaustive |    Exact |         92.8 |    0.322 |        0 |       93 |      3850 |      86 |         0 |    ok |        b |         yes | κ ∈ [0.6092, 2.5961], κ_c ∈ [0.9921, 0.0000], regime = Transitional
  radix | 21 |  exhaustive | LowerBnd |         90.5 |    0.329 |        0 |       88 |     15555 |     104 |         0 |    ok |        b |         yes | κ ∈ [0.5238, 2.2146], κ_c ∈ [0.9726, 0.0000], regime = Transitional
  radix | 24 |          dp | LowerBnd |         88.8 |    0.430 |        0 |      419 |     39919 |     102 |         0 |    ok |        b |         yes | κ ∈ [0.4623, 1.9298], κ_c ∈ [0.9650, 0.0000], regime = Transitional
  radix | 27 |          dp | LowerBnd |         83.7 |    0.419 |        0 |      731 |    128886 |      95 |         0 |    ok |        b |         yes | κ ∈ [0.4444, 1.7091], κ_c ∈ [0.9817, 0.0000], regime = Transitional
  radix | 30 |          dp | LowerBnd |         88.1 |    0.273 |        0 |     1116 |    369665 |     109 |         0 |    ok |        b |         yes | κ ∈ [0.4032, 1.5331], κ_c ∈ [0.9932, 0.0000], regime = Transitional
arbitrary |  3 |  exhaustive |    Exact |            - |        - |        4 |   124344 |        33 |     101 |         0 |    ok |        - |          no | κ ∈ [7.1722, 16.4381], κ_c ∈ [0.9383, 0.0001], regime = Sparse
arbitrary |  4 |  exhaustive |    Exact |        100.0 |      NaN |        5 |      827 |        27 |      90 |         0 |    ok |        - |          no | κ ∈ [5.4829, 12.2248], κ_c ∈ [0.9657, 0.0001], regime = Sparse
arbitrary |  5 |  exhaustive |    Exact |        100.0 |    0.000 |        8 |   229100 |        67 |     112 |         0 |    ok |        - |          no | κ ∈ [4.4507, 9.7155], κ_c ∈ [0.9782, 0.0002], regime = Sparse
arbitrary |  6 |  exhaustive |    Exact |        100.0 |    0.506 |       16 |    14082 |        71 |      92 |         0 |    ok |        - |          no | κ ∈ [3.7528, 8.0524], κ_c ∈ [0.9849, 0.0002], regime = Sparse
arbitrary |  7 |  exhaustive |    Exact |        100.0 |    0.502 |       32 |   342637 |       122 |      95 |         0 |    ok |        - |          no | κ ∈ [3.2484, 6.8703], κ_c ∈ [0.9889, 0.0002], regime = Sparse
arbitrary |  8 |  exhaustive |    Exact |        100.0 |    0.490 |       72 |       63 |       158 |      92 |         0 |    ok |        - |          no | κ ∈ [2.8664, 5.9874], κ_c ∈ [0.9915, 0.0003], regime = Sparse
arbitrary |  9 |  exhaustive |    Exact |        100.0 |    0.522 |      156 |   469217 |       316 |      98 |         0 |    ok |        - |          no | κ ∈ [2.5668, 5.3033], κ_c ∈ [0.9933, 0.0003], regime = Sparse
arbitrary | 10 |  exhaustive |    Exact |        100.0 |    0.531 |      339 |   518922 |       419 |     116 |         0 |    ok |        - |          no | κ ∈ [2.3253, 4.7577], κ_c ∈ [0.9946, 0.0003], regime = Sparse
arbitrary | 11 |  exhaustive |    Exact |        100.0 |    0.531 |      701 |   567600 |       542 |      98 |         0 |    ok |        - |          no | κ ∈ [2.1265, 4.3127], κ_c ∈ [0.9955, 0.0004], regime = Sparse
arbitrary | 12 |  exhaustive |    Exact |        100.0 |    0.535 |     1503 |      973 |       712 |     102 |         0 |    ok |        - |          no | κ ∈ [1.9597, 3.9429], κ_c ∈ [0.9962, 0.0004], regime = Sparse
random_dense |  8 |  exhaustive |    Exact |         18.0 |    0.810 |       70 |       47 |       137 |      92 |         0 |    ok |        - |         yes | κ ∈ [0.5000, 5.9874], κ_c ∈ [0.9979, 0.0000], regime = Transitional
random_dense | 10 |  exhaustive |    Exact |          9.1 |    0.989 |      324 |       46 |       283 |      93 |         0 |    ok |        - |         yes | κ ∈ [0.3700, 4.7577], κ_c ∈ [0.9988, 0.0000], regime = Transitional
random_dense | 12 |  exhaustive |    Exact |         19.1 |    0.936 |     1503 |       47 |       426 |      92 |         0 |    ok |        - |         yes | κ ∈ [0.3333, 3.9429], κ_c ∈ [0.8686, 0.0000], regime = Transitional
random_dense | 14 |  exhaustive | LowerBnd |          5.3 |    1.000 |     6834 |       47 |      1315 |      94 |         0 |    ok |        - |         yes | κ ∈ [0.2857, 3.3637], κ_c ∈ [0.9859, 0.0000], regime = Transitional
random_dense | 16 |  exhaustive | LowerBnd |          2.9 |    0.999 |        0 |       48 |      2302 |      90 |         0 |    ok |        b |          no | κ ∈ [0.2500, 2.9312], κ_c ∈ [0.9343, 0.0000], regime = Transitional
random_dense | 18 |  exhaustive | LowerBnd |          2.6 |    1.000 |        0 |       53 |      3395 |     103 |         0 |    ok |        b |         yes | κ ∈ [0.2222, 2.5961], κ_c ∈ [0.9915, 0.0000], regime = Transitional
random_dense | 20 |  exhaustive | LowerBnd |          2.7 |    1.000 |        0 |       50 |      3366 |      99 |         0 |    ok |        b |         yes | κ ∈ [0.2000, 2.3289], κ_c ∈ [0.9691, 0.0000], regime = Transitional
random_dense | 22 |  exhaustive | LowerBnd |          2.4 |    1.000 |        0 |       55 |      6241 |      93 |         0 |    ok |        b |         yes | κ ∈ [0.1818, 2.1109], κ_c ∈ [0.9930, 0.0000], regime = Transitional
random_dense | 24 |          dp | LowerBnd |          2.7 |    1.000 |        0 |       59 |     11700 |      93 |         0 |    ok |        b |         yes | κ ∈ [0.1667, 1.9298], κ_c ∈ [0.9979, 0.0000], regime = Transitional

kappa sweep @ N=20 (Sasamoto error vs oracle as density falls):
kappa | regime | sasamoto_err% | spearman
0.15 | κ ∈ [0.1500, 2.3289], κ_c ∈ [0.9584, 0.0000], regime = Transitional |          2.6 |    1.000
0.20 | κ ∈ [0.2000, 2.3289], κ_c ∈ [0.9808, 0.0000], regime = Transitional |          2.7 |    1.000
0.25 | κ ∈ [0.2477, 2.3289], κ_c ∈ [0.9767, 0.0000], regime = Transitional |          2.7 |    1.000
0.30 | κ ∈ [0.2977, 2.3289], κ_c ∈ [0.9809, 0.0000], regime = Transitional |          3.9 |    0.999
0.35 | κ ∈ [0.3471, 2.3289], κ_c ∈ [0.9806, 0.0000], regime = Transitional |          3.0 |    0.999
0.40 | κ ∈ [0.3968, 2.3289], κ_c ∈ [0.9809, 0.0000], regime = Transitional |          3.6 |    0.999

switch sweep @ l_max=16, sparse budget=512 (where sparse stops being exact, Sasamoto takes over):
N | kappa | sparse_kind | sparse_us | sparse_peakB | sasamoto_err% | spearman
8 |  0.50 |       Exact |       180 |            0 |          18.0 |    0.810
10 |  0.40 |       Exact |       340 |            0 |           9.1 |    0.989
12 |  0.33 |       Exact |       498 |            0 |          19.1 |    0.936
14 |  0.29 |    LowerBnd |      1481 |            0 |           5.3 |    1.000
16 |  0.25 |    LowerBnd |      2605 |            0 |           2.9 |    0.999
18 |  0.22 |    LowerBnd |      3791 |            0 |           2.6 |    1.000
20 |  0.20 |    LowerBnd |      3550 |            0 |           2.7 |    1.000
22 |  0.18 |    LowerBnd |      7195 |            0 |           2.4 |    1.000

CJA: W vs sub-transaction mappings (both anonymity facets) on small coinjoins:
label | n_in | n_out | non_derived | entropy_bits | sasamoto_log | w_lookup_log
cja_0 |    4 |     4 |           1 |         0.00 |         -inf |         -inf
cja_1 |    4 |     4 |           7 |         2.81 |         1.19 |         1.10
cja_2 |    3 |     3 |           1 |         0.00 |         0.33 |         0.00
BTSIM_FOUR_COUNTS=1 CONFIG_FILE=dense_small.toml cargo run - 4 paths over the sim's confirmed txs
tx_index | primitive | log2W/count | lower-bound? | status | cpu_us | peak_bytes
     0 |    brute |        0.00 |         true | Computed |     685 |          0
     0 |    radix |       12.08 |         true | Computed |     130 |          0
     0 |   sparse |        0.00 |         true | Computed |     461 |          0
     0 | sasamoto |           - |        false | Computed |   16828 |          0
     1 |    brute |           - |         true | Computed |       6 |          0
     1 |    radix |           - |         true | Computed | 1137067 |          0
     1 |   sparse |           - |         true | Computed |      28 |          0
     1 | sasamoto |           - |        false | Computed |       4 |          0
     2 |    brute |           - |         true | Computed |       6 |          0
     2 |    radix |           - |         true | Computed |   16993 |          0
     2 |   sparse |           - |         true | Computed |      18 |          0
     2 | sasamoto |           - |        false | Computed |     173 |          0
primitive | runs | aborted | with_count | med_cpu_us | max_cpu_us | med_peak_B | max_peak_B
 brute |    3 |       0 |          3 |          6 |        723 |          0 |         0
 radix |    3 |       0 |          3 |      16936 |    1149301 |          0 |         0
sparse |    3 |       0 |          3 |         32 |        459 |          0 |         0
sasamoto |    3 |       0 |          0 |        173 |      16964 |          0 |         0
tx_index | density regime (best/worst-L bracket at midpoint E)
     0 | κ ∈ [8.0548, 12.2248], κ_c ∈ [0.8561, 0.0031], regime = Sparse
     1 | κ ∈ [33.1300, 50.8993], κ_c ∈ [0.0000, 0.0043], regime = Sparse
     2 | κ ∈ [18.0000, 50.8993], κ_c ∈ [0.0000, 0.0000], regime = Sparse
Total payment obligations: 8
Missed payment obligations percentage: 0.625
Total block weight used (wu): 14086
Average fee cost (sats): 1466707716

related to #84

Wires the dense-subset-sum 4-path subset-sum/coinjoin-mapping counts
(brute, radix, sparse, Sasamoto) into btsim two ways:

- As agent cost: WLowerBoundMetric (W lower bound via brute/sparse) and
  RadixMappingMetric (k×m! denomination mappings, gated by radix density)
  plug into the existing PrivacyBundle. With a subset_sum_threshold /
  radix_threshold set in ScorerConfig, each candidate plan is scored by a
  linear deficit to the threshold, so agents favor coinjoins with more
  subset-sum ambiguity.
- As measurement: four_counts runs all four paths over the sim's confirmed
  txs recording CPU, peak RAM (alloc-probe feature), and density regime
  (kappa via the L bracket). Plus a correctness benchmark against the dep's
  oracle, a kappa/L sweep, a switch_sweep locating the sparse->Sasamoto
  handoff, and a CJA comparison (W vs Maurer sub-transaction mappings).

Adds denominated funding (standard-denomination UTXOs in a band, via
mine_denominated) so the sim can produce dense coinjoins, and fixes a
latent over-spend in coin selection that the dense funding exposed
(select_all now returns None unless inputs cover the target; session
contribution is gated on coverage).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant