diffcb 0.1.4__tar.gz → 0.1.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
diffcb-0.1.5/LICENSE ADDED
@@ -0,0 +1,186 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship made available under
36
+ the License, as indicated by a copyright notice that is included in
37
+ or attached to the work (an example is provided in the Appendix below).
38
+
39
+ "Derivative Works" shall mean any work, whether in Source or Object
40
+ form, that is based on (or derived from) the Work and for which the
41
+ editorial revisions, annotations, elaborations, or other modifications
42
+ represent, as a whole, an original work of authorship. For the purposes
43
+ of this License, Derivative Works shall not include works that remain
44
+ separable from, or merely link (or bind by name) to the interfaces of,
45
+ the Work and Derivative Works thereof.
46
+
47
+ "Contribution" shall mean, as submitted to the Licensor for inclusion
48
+ in the Work by the copyright owner or by an individual or Legal Entity
49
+ authorized to submit on behalf of the copyright owner. For the purposes
50
+ of this definition, "submitted" means any form of electronic, verbal,
51
+ or written communication sent to the Licensor or its representatives,
52
+ including but not limited to communication on electronic mailing lists,
53
+ source code control systems, and issue tracking systems that are managed
54
+ by, or on behalf of, the Licensor for the purpose of discussing and
55
+ improving the Work, but excluding communication that is conspicuously
56
+ marked or designated in writing by the copyright owner as "Not a
57
+ Contribution."
58
+
59
+ "Contributor" shall mean Licensor and any Legal Entity on behalf of
60
+ whom a Contribution has been received by the Licensor and included
61
+ within the Work.
62
+
63
+ 2. Grant of Copyright License. Subject to the terms and conditions of
64
+ this License, each Contributor hereby grants to You a perpetual,
65
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
66
+ copyright license to reproduce, prepare Derivative Works of,
67
+ publicly display, publicly perform, sublicense, and distribute the
68
+ Work and such Derivative Works in Source or Object form.
69
+
70
+ 3. Grant of Patent License. Subject to the terms and conditions of
71
+ this License, each Contributor hereby grants to You a perpetual,
72
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
73
+ (except as stated in this section) patent license to make, have made,
74
+ use, offer to sell, sell, import, and otherwise transfer the Work,
75
+ where such license applies only to those patent claims licensable
76
+ by such Contributor that are necessarily infringed by their
77
+ Contribution(s) alone or by combination of their Contribution(s)
78
+ with the Work to which such Contribution(s) was submitted. If You
79
+ institute patent litigation against any entity (including a cross-claim
80
+ or counterclaim in a lawsuit) alleging that the Work or any
81
+ Contribution embodied within the Work constitutes direct or
82
+ contributory patent infringement, then any patent licenses granted
83
+ to You under this License for that Work shall terminate as of the
84
+ date such litigation is filed.
85
+
86
+ 4. Redistribution. You may reproduce and distribute copies of the
87
+ Work or Derivative Works thereof in any medium, with or without
88
+ modifications, and in Source or Object form, provided that You
89
+ meet the following conditions:
90
+
91
+ (a) You must give any other recipients of the Work or Derivative
92
+ Works a copy of this License; and
93
+
94
+ (b) You must cause any modified files to carry prominent notices
95
+ stating that You changed the files; and
96
+
97
+ (c) You must retain, in the Source form of any Derivative Works
98
+ that You distribute, all copyright, patent, trademark, and
99
+ attribution notices from the Source form of the Work,
100
+ excluding those notices that do not pertain to any part of
101
+ the Derivative Works; and
102
+
103
+ (d) If the Work includes a "NOTICE" text file as part of its
104
+ distribution, You must include a readable copy of the
105
+ attribution notices contained within such NOTICE file, in
106
+ at least one of the following places: within a NOTICE text
107
+ file distributed as part of the Derivative Works; within
108
+ the Source form or documentation, if provided along with the
109
+ Derivative Works; or, within a display generated by the
110
+ Derivative Works, if and wherever such third-party notices
111
+ normally appear. The contents of the NOTICE file are for
112
+ informational purposes only and do not modify the License.
113
+ You may add Your own attribution notices within Derivative
114
+ Works that You distribute, alongside or as an addendum to
115
+ the NOTICE text from the Work, provided that such additional
116
+ attribution notices cannot be construed as modifying the
117
+ License.
118
+
119
+ You may add Your own license statement for Your modifications and
120
+ may provide additional grant of rights to use, copy, modify, merge,
121
+ publish, distribute, sublicense, and/or sell copies of Your
122
+ modifications, and to permit persons to whom Your modifications are
123
+ furnished to do so, as long as such licensing does not conflict with
124
+ the terms of this License.
125
+
126
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
127
+ any Contribution intentionally submitted for inclusion in the Work
128
+ by You to the Licensor shall be under the terms and conditions of
129
+ this License, without any additional terms or conditions.
130
+ Notwithstanding the above, nothing herein shall supersede or modify
131
+ the terms of any separate license agreement you may have executed
132
+ with Licensor regarding such Contributions.
133
+
134
+ 6. Trademarks. This License does not grant permission to use the trade
135
+ names, trademarks, service marks, or product names of the Licensor,
136
+ except as required for reasonable and customary use in describing the
137
+ origin of the Work and reproducing the content of the NOTICE file.
138
+
139
+ 7. Disclaimer of Warranty. Unless required by applicable law or
140
+ agreed to in writing, Licensor provides the Work (and each
141
+ Contributor provides its Contributions) on an "AS IS" BASIS,
142
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
143
+ implied, including, without limitation, any warranties or conditions
144
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
145
+ PARTICULAR PURPOSE. You are solely responsible for determining the
146
+ appropriateness of using or reproducing the Work and assume any
147
+ risks associated with Your exercise of permissions under this License.
148
+
149
+ 8. Limitation of Liability. In no event and under no legal theory,
150
+ whether in tort (including negligence), contract, or otherwise,
151
+ unless required by applicable law (such as deliberate and grossly
152
+ negligent acts) or agreed to in writing, shall any Contributor be
153
+ liable to You for damages, including any direct, indirect, special,
154
+ incidental, or exemplary damages of any character arising as a
155
+ result of this License or out of the use or inability to use the
156
+ Work (including but not limited to damages for loss of goodwill,
157
+ work stoppage, computer failure or malfunction, or all other
158
+ commercial damages or losses), even if such Contributor has been
159
+ advised of the possibility of such damages.
160
+
161
+ 9. Accepting Warranty or Liability. While redistributing the Work or
162
+ Derivative Works thereof, You may choose to offer, and charge a fee
163
+ for, acceptance of support, warranty, indemnity, or other liability
164
+ obligations and/or rights consistent with this License. However, in
165
+ accepting such obligations, You may offer such obligations only on
166
+ Your own behalf and on Your sole responsibility, not on behalf of
167
+ any other Contributor, and only if You agree to indemnify, defend,
168
+ and hold each Contributor harmless for any liability incurred by,
169
+ or claims asserted against, such Contributor by reason of your
170
+ accepting any such warranty or additional liability.
171
+
172
+ END OF TERMS AND CONDITIONS
173
+
174
+ Copyright 2026 Ruiyu Zhang
175
+
176
+ Licensed under the Apache License, Version 2.0 (the "License");
177
+ you may not use this file except in compliance with the License.
178
+ You may obtain a copy of the License at
179
+
180
+ http://www.apache.org/licenses/LICENSE-2.0
181
+
182
+ Unless required by applicable law or agreed to in writing, software
183
+ distributed under the License is distributed on an "AS IS" BASIS,
184
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
185
+ See the License for the specific language governing permissions and
186
+ limitations under the License.
diffcb-0.1.5/PKG-INFO ADDED
@@ -0,0 +1,317 @@
1
+ Metadata-Version: 2.4
2
+ Name: diffcb
3
+ Version: 0.1.5
4
+ Summary: Differentiable Critical Bandwidth: Silverman's modality test as a differentiable PyTorch layer with IFT backward pass.
5
+ Project-URL: Homepage, https://github.com/ryZhangHason/differentiable-critical-bandwidth
6
+ Project-URL: Repository, https://github.com/ryZhangHason/differentiable-critical-bandwidth
7
+ Project-URL: Documentation, https://github.com/ryZhangHason/differentiable-critical-bandwidth#readme
8
+ Project-URL: Bug Tracker, https://github.com/ryZhangHason/differentiable-critical-bandwidth/issues
9
+ Author-email: Ruiyu Zhang <dhhhason@gmail.com>
10
+ License: Apache License
11
+ Version 2.0, January 2004
12
+ http://www.apache.org/licenses/
13
+
14
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
15
+
16
+ 1. Definitions.
17
+
18
+ "License" shall mean the terms and conditions for use, reproduction,
19
+ and distribution as defined by Sections 1 through 9 of this document.
20
+
21
+ "Licensor" shall mean the copyright owner or entity authorized by
22
+ the copyright owner that is granting the License.
23
+
24
+ "Legal Entity" shall mean the union of the acting entity and all
25
+ other entities that control, are controlled by, or are under common
26
+ control with that entity. For the purposes of this definition,
27
+ "control" means (i) the power, direct or indirect, to cause the
28
+ direction or management of such entity, whether by contract or
29
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
30
+ outstanding shares, or (iii) beneficial ownership of such entity.
31
+
32
+ "You" (or "Your") shall mean an individual or Legal Entity
33
+ exercising permissions granted by this License.
34
+
35
+ "Source" form shall mean the preferred form for making modifications,
36
+ including but not limited to software source code, documentation
37
+ source, and configuration files.
38
+
39
+ "Object" form shall mean any form resulting from mechanical
40
+ transformation or translation of a Source form, including but
41
+ not limited to compiled object code, generated documentation,
42
+ and conversions to other media types.
43
+
44
+ "Work" shall mean the work of authorship made available under
45
+ the License, as indicated by a copyright notice that is included in
46
+ or attached to the work (an example is provided in the Appendix below).
47
+
48
+ "Derivative Works" shall mean any work, whether in Source or Object
49
+ form, that is based on (or derived from) the Work and for which the
50
+ editorial revisions, annotations, elaborations, or other modifications
51
+ represent, as a whole, an original work of authorship. For the purposes
52
+ of this License, Derivative Works shall not include works that remain
53
+ separable from, or merely link (or bind by name) to the interfaces of,
54
+ the Work and Derivative Works thereof.
55
+
56
+ "Contribution" shall mean, as submitted to the Licensor for inclusion
57
+ in the Work by the copyright owner or by an individual or Legal Entity
58
+ authorized to submit on behalf of the copyright owner. For the purposes
59
+ of this definition, "submitted" means any form of electronic, verbal,
60
+ or written communication sent to the Licensor or its representatives,
61
+ including but not limited to communication on electronic mailing lists,
62
+ source code control systems, and issue tracking systems that are managed
63
+ by, or on behalf of, the Licensor for the purpose of discussing and
64
+ improving the Work, but excluding communication that is conspicuously
65
+ marked or designated in writing by the copyright owner as "Not a
66
+ Contribution."
67
+
68
+ "Contributor" shall mean Licensor and any Legal Entity on behalf of
69
+ whom a Contribution has been received by the Licensor and included
70
+ within the Work.
71
+
72
+ 2. Grant of Copyright License. Subject to the terms and conditions of
73
+ this License, each Contributor hereby grants to You a perpetual,
74
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
75
+ copyright license to reproduce, prepare Derivative Works of,
76
+ publicly display, publicly perform, sublicense, and distribute the
77
+ Work and such Derivative Works in Source or Object form.
78
+
79
+ 3. Grant of Patent License. Subject to the terms and conditions of
80
+ this License, each Contributor hereby grants to You a perpetual,
81
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
82
+ (except as stated in this section) patent license to make, have made,
83
+ use, offer to sell, sell, import, and otherwise transfer the Work,
84
+ where such license applies only to those patent claims licensable
85
+ by such Contributor that are necessarily infringed by their
86
+ Contribution(s) alone or by combination of their Contribution(s)
87
+ with the Work to which such Contribution(s) was submitted. If You
88
+ institute patent litigation against any entity (including a cross-claim
89
+ or counterclaim in a lawsuit) alleging that the Work or any
90
+ Contribution embodied within the Work constitutes direct or
91
+ contributory patent infringement, then any patent licenses granted
92
+ to You under this License for that Work shall terminate as of the
93
+ date such litigation is filed.
94
+
95
+ 4. Redistribution. You may reproduce and distribute copies of the
96
+ Work or Derivative Works thereof in any medium, with or without
97
+ modifications, and in Source or Object form, provided that You
98
+ meet the following conditions:
99
+
100
+ (a) You must give any other recipients of the Work or Derivative
101
+ Works a copy of this License; and
102
+
103
+ (b) You must cause any modified files to carry prominent notices
104
+ stating that You changed the files; and
105
+
106
+ (c) You must retain, in the Source form of any Derivative Works
107
+ that You distribute, all copyright, patent, trademark, and
108
+ attribution notices from the Source form of the Work,
109
+ excluding those notices that do not pertain to any part of
110
+ the Derivative Works; and
111
+
112
+ (d) If the Work includes a "NOTICE" text file as part of its
113
+ distribution, You must include a readable copy of the
114
+ attribution notices contained within such NOTICE file, in
115
+ at least one of the following places: within a NOTICE text
116
+ file distributed as part of the Derivative Works; within
117
+ the Source form or documentation, if provided along with the
118
+ Derivative Works; or, within a display generated by the
119
+ Derivative Works, if and wherever such third-party notices
120
+ normally appear. The contents of the NOTICE file are for
121
+ informational purposes only and do not modify the License.
122
+ You may add Your own attribution notices within Derivative
123
+ Works that You distribute, alongside or as an addendum to
124
+ the NOTICE text from the Work, provided that such additional
125
+ attribution notices cannot be construed as modifying the
126
+ License.
127
+
128
+ You may add Your own license statement for Your modifications and
129
+ may provide additional grant of rights to use, copy, modify, merge,
130
+ publish, distribute, sublicense, and/or sell copies of Your
131
+ modifications, and to permit persons to whom Your modifications are
132
+ furnished to do so, as long as such licensing does not conflict with
133
+ the terms of this License.
134
+
135
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
136
+ any Contribution intentionally submitted for inclusion in the Work
137
+ by You to the Licensor shall be under the terms and conditions of
138
+ this License, without any additional terms or conditions.
139
+ Notwithstanding the above, nothing herein shall supersede or modify
140
+ the terms of any separate license agreement you may have executed
141
+ with Licensor regarding such Contributions.
142
+
143
+ 6. Trademarks. This License does not grant permission to use the trade
144
+ names, trademarks, service marks, or product names of the Licensor,
145
+ except as required for reasonable and customary use in describing the
146
+ origin of the Work and reproducing the content of the NOTICE file.
147
+
148
+ 7. Disclaimer of Warranty. Unless required by applicable law or
149
+ agreed to in writing, Licensor provides the Work (and each
150
+ Contributor provides its Contributions) on an "AS IS" BASIS,
151
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
152
+ implied, including, without limitation, any warranties or conditions
153
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
154
+ PARTICULAR PURPOSE. You are solely responsible for determining the
155
+ appropriateness of using or reproducing the Work and assume any
156
+ risks associated with Your exercise of permissions under this License.
157
+
158
+ 8. Limitation of Liability. In no event and under no legal theory,
159
+ whether in tort (including negligence), contract, or otherwise,
160
+ unless required by applicable law (such as deliberate and grossly
161
+ negligent acts) or agreed to in writing, shall any Contributor be
162
+ liable to You for damages, including any direct, indirect, special,
163
+ incidental, or exemplary damages of any character arising as a
164
+ result of this License or out of the use or inability to use the
165
+ Work (including but not limited to damages for loss of goodwill,
166
+ work stoppage, computer failure or malfunction, or all other
167
+ commercial damages or losses), even if such Contributor has been
168
+ advised of the possibility of such damages.
169
+
170
+ 9. Accepting Warranty or Liability. While redistributing the Work or
171
+ Derivative Works thereof, You may choose to offer, and charge a fee
172
+ for, acceptance of support, warranty, indemnity, or other liability
173
+ obligations and/or rights consistent with this License. However, in
174
+ accepting such obligations, You may offer such obligations only on
175
+ Your own behalf and on Your sole responsibility, not on behalf of
176
+ any other Contributor, and only if You agree to indemnify, defend,
177
+ and hold each Contributor harmless for any liability incurred by,
178
+ or claims asserted against, such Contributor by reason of your
179
+ accepting any such warranty or additional liability.
180
+
181
+ END OF TERMS AND CONDITIONS
182
+
183
+ Copyright 2026 Ruiyu Zhang
184
+
185
+ Licensed under the Apache License, Version 2.0 (the "License");
186
+ you may not use this file except in compliance with the License.
187
+ You may obtain a copy of the License at
188
+
189
+ http://www.apache.org/licenses/LICENSE-2.0
190
+
191
+ Unless required by applicable law or agreed to in writing, software
192
+ distributed under the License is distributed on an "AS IS" BASIS,
193
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
194
+ See the License for the specific language governing permissions and
195
+ limitations under the License.
196
+ License-File: LICENSE
197
+ Keywords: PyTorch,anomaly detection,critical bandwidth,differentiable programming,generative models,kernel density estimation,mode counting,nonparametric statistics
198
+ Classifier: Development Status :: 3 - Alpha
199
+ Classifier: Intended Audience :: Science/Research
200
+ Classifier: License :: OSI Approved :: Apache Software License
201
+ Classifier: Programming Language :: Python :: 3
202
+ Classifier: Programming Language :: Python :: 3.9
203
+ Classifier: Programming Language :: Python :: 3.10
204
+ Classifier: Programming Language :: Python :: 3.11
205
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
206
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
207
+ Requires-Python: >=3.9
208
+ Requires-Dist: matplotlib>=3.7.0
209
+ Requires-Dist: numpy>=1.24.0
210
+ Requires-Dist: scikit-learn>=1.3.0
211
+ Requires-Dist: scipy>=1.10.0
212
+ Requires-Dist: torch>=2.0.0
213
+ Provides-Extra: dev
214
+ Requires-Dist: black>=23.0.0; extra == 'dev'
215
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
216
+ Requires-Dist: pytest>=7.4.0; extra == 'dev'
217
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
218
+ Provides-Extra: notebooks
219
+ Requires-Dist: ipywidgets>=8.0.0; extra == 'notebooks'
220
+ Requires-Dist: jupyter>=1.0.0; extra == 'notebooks'
221
+ Description-Content-Type: text/markdown
222
+
223
+ # DCB — Differentiable Critical Bandwidth
224
+
225
+ [![PyPI](https://img.shields.io/pypi/v/diffcb.svg)](https://pypi.org/project/diffcb/)
226
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
227
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/)
228
+
229
+ A PyTorch package that makes **Silverman's critical bandwidth test (1981)** fully differentiable, enabling end-to-end gradient-based optimization over the modal structure of continuous distributions.
230
+
231
+ ## Overview
232
+
233
+ The critical bandwidth `h_crit` is the minimum KDE bandwidth at which a distribution appears to have at most `m` modes — a classical nonparametric statistic for modality testing. DCB replaces every non-differentiable operation in its computation with a smooth surrogate, then uses the **Implicit Function Theorem** to compute exact gradients through the root-finding step at O(1) memory cost.
234
+
235
+ ```python
236
+ import torch
237
+ from dcb import DCBLayer
238
+
239
+ X = torch.randn(1000, requires_grad=True) # 1D samples
240
+ layer = DCBLayer(target_modes=1)
241
+ h_crit = layer(X) # differentiable scalar
242
+ h_crit.backward() # exact IFT gradients
243
+ ```
244
+
245
+ ## Installation
246
+
247
+ ```bash
248
+ pip install diffcb
249
+ ```
250
+
251
+ Or from source:
252
+
253
+ ```bash
254
+ git clone https://github.com/ryZhangHason/differentiable-critical-bandwidth
255
+ cd differentiable-critical-bandwidth
256
+ pip install -e ".[dev]"
257
+ ```
258
+
259
+ ## Accuracy vs R's `bw.crit`
260
+
261
+ DCB is validated against R's `multimode::bw.crit(data, mod0=1)` — the standard reference implementation of Hall & York (2001). On **identical data**:
262
+
263
+ | n | DCB vs R (same sample) | DCB vs R (independent samples) |
264
+ |---|---|---|
265
+ | 100K | **0.004%** | ~0.5% (MC noise from independent RNG) |
266
+ | 1M | **0.005%** | ~0.2% |
267
+ | 10M | **0.004%** | ~0.1% |
268
+
269
+ The independent-sample figures reflect natural sampling variability (two unbiased estimators drawing different data), not algorithmic error. On identical data, DCB agrees with R to within **0.005%** at all tested n. DCB is 43× faster than R at n=100M (1.1 s vs 50 s) and handles n=2B in 24 s while R OOMs.
270
+
271
+ ## Key Parameters
272
+
273
+ ```python
274
+ DCBLayer(
275
+ target_modes=1, # target number of modes
276
+ G=512, # IFT evaluation grid points
277
+ use_fft=True, # FFT forward (default); eliminates subsampling bias for n>50K
278
+ max_n_exact=1_000_000,# sketch to sketch_size when n exceeds this (None = always exact)
279
+ sketch_size=500_000, # sketch target; 500K matches full-n accuracy (O(n^{-2/9}) rate)
280
+ safe_backward=False, # clamp IFT denominator near bifurcations
281
+ )
282
+ ```
283
+
284
+ ## Confirmed Experimental Results
285
+
286
+ All GPU results produced on Kaggle (T4 / P100) — see `experiments/` and `outputs/`.
287
+
288
+ | Experiment | Result | Criterion |
289
+ |---|---|---|
290
+ | **Accuracy vs R (same data, n=100K)** | **0.004%** | < 0.01% ✓ |
291
+ | **Validation (m≥2, Marron-Wand)** | R²=0.91, MAE=0.07, ρ=0.89 | R²≥0.85 ✓ |
292
+ | **Speedup vs scipy (CUDA T4, n=8192)** | **10.5×** | ≥3× ✓ |
293
+ | **GAN mode preservation** | h_crit=1.232 >> 0.3 | h_crit>0.3 ✓ |
294
+ | **Anomaly AUC (KDDCup99)** | DCB=**0.9982** vs IF=0.9867 | DCB≥IF ✓ |
295
+
296
+ ## Repository Structure
297
+
298
+ ```
299
+ dcb/ Core PyTorch package
300
+ layer.py DCBLayer nn.Module + DCBFunction autograd
301
+ solver.py IFT root-finder and backward pass
302
+ fft_kde.py FFT-based mode counter (MPS-safe, float64, G=16384)
303
+ kde.py Direct KDE derivatives (small-n path)
304
+ utils.py Grid, Silverman bandwidth, sg() stabilizer
305
+ experiments/ Reproduction scripts for all paper figures and tables
306
+ phase1_*.py Validation, speedup, ablation (Figures 1–2, S1–S2)
307
+ phase2_gan.py GAN mode-collapse prevention (Figure 3)
308
+ phase3_anomaly.py Anomaly detection (Table 2, Figure 5)
309
+ round20_*.py Large-n R comparison and streaming benchmarks
310
+ round21_*.py Accuracy improvement experiments
311
+ tests/ Unit tests (pytest, 45 passed, 1 xfailed)
312
+ outputs/ All generated figures and tables (PDFs, PNGs, CSVs)
313
+ ```
314
+
315
+ ## License
316
+
317
+ Apache 2.0 — see [LICENSE](LICENSE).
@@ -1,7 +1,7 @@
1
1
  # DCB — Differentiable Critical Bandwidth
2
2
 
3
3
  [![PyPI](https://img.shields.io/pypi/v/diffcb.svg)](https://pypi.org/project/diffcb/)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
4
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
5
5
  [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/)
6
6
 
7
7
  A PyTorch package that makes **Silverman's critical bandwidth test (1981)** fully differentiable, enabling end-to-end gradient-based optimization over the modal structure of continuous distributions.
@@ -71,18 +71,6 @@ All GPU results produced on Kaggle (T4 / P100) — see `experiments/` and `outpu
71
71
  | **GAN mode preservation** | h_crit=1.232 >> 0.3 | h_crit>0.3 ✓ |
72
72
  | **Anomaly AUC (KDDCup99)** | DCB=**0.9982** vs IF=0.9867 | DCB≥IF ✓ |
73
73
 
74
- ## Changelog
75
-
76
- ### v0.1.1 (2026-05-29)
77
- - **MPS fix:** `torch.histc` on MPS allocated an n×bins intermediate (OOM at n≥5M). Replaced with `bucketize+bincount` on CPU — MPS-safe and numerically identical.
78
- - **Sketch API:** `DCBLayer(max_n_exact=1_000_000, sketch_size=500_000)` — silently sketches to 500K when n exceeds threshold. Justified by O(n⁻²/⁹) convergence of h_crit; 500K sketch matches full-n accuracy.
79
- - **Consistent bisection domain:** Pre-computed domain passed to all `fft_mode_count` calls in a single bisection, eliminating per-step drift.
80
- - **Bias warning direction:** Corrected "expected upward bias" to "expected downward bias" on legacy `use_fft=False` path.
81
- - **Test fixes:** Updated 8 pre-existing test failures (tuple unpacking, bounds, deprecation API).
82
-
83
- ### v0.1.0 (2026-05-28)
84
- - Initial PyPI release. FFT forward (O(n + G log G)), IFT backward, MPS support.
85
-
86
74
  ## Repository Structure
87
75
 
88
76
  ```
@@ -102,28 +90,6 @@ tests/ Unit tests (pytest, 45 passed, 1 xfailed)
102
90
  outputs/ All generated figures and tables (PDFs, PNGs, CSVs)
103
91
  ```
104
92
 
105
- ## Reproducing Paper Results
106
-
107
- ```bash
108
- # Phase 1: validation, speedup, ablation
109
- python experiments/phase1_validation.py
110
- python experiments/phase1_speedup.py
111
-
112
- # Phase 2: GAN mode collapse experiment
113
- python experiments/phase2_gan.py
114
-
115
- # Phase 3: anomaly detection benchmark
116
- python experiments/phase3_anomaly.py
117
- ```
118
-
119
- For GPU runs use the Kaggle kernels:
120
- - Phase 1–2: `hsingle/dcb-full-experiments`
121
- - Phase 3: `hsingle/dcb-phase-3-anomaly-detection`
122
-
123
- ## Paper
124
-
125
- > Ruiyu Zhang. "Differentiable Critical Bandwidth: Making Silverman's Modality Test End-to-End Trainable." *Journal of Machine Learning Research*, 2026 (in preparation).
126
-
127
93
  ## License
128
94
 
129
- MIT — see [LICENSE](LICENSE).
95
+ Apache 2.0 — see [LICENSE](LICENSE).
@@ -19,4 +19,4 @@ __all__ = [
19
19
  "DCBLayer", "DifferentiableCriticalBandwidth",
20
20
  "anneal_eps_tau", "soft_mode_count_cross", "soft_mode_count",
21
21
  ]
22
- __version__ = "0.1.4"
22
+ __version__ = "0.1.5"
@@ -138,16 +138,55 @@ def mode_count_from_C(
138
138
  return 1 # degenerate single-point distribution
139
139
 
140
140
  K_deriv = 1j * omega * torch.exp(-0.5 * (omega * h) ** 2)
141
- f_prime_padded = torch.fft.irfft(C * K_deriv, n=N).real
142
- f_prime = f_prime_padded[:G]
141
+ f_prime = torch.fft.irfft(C * K_deriv, n=N).real[:G]
142
+ # Exact zeros are measure-zero for smooth KDE on non-degenerate data; strict
143
+ # inequalities match the masked path in all practical cases while avoiding
144
+ # one host sync (.any()) and two allocations (nonzero_mask, s) per call.
145
+ return int(((f_prime[:-1] > 0) & (f_prime[1:] < 0)).sum().item())
143
146
 
144
- nonzero_mask = f_prime != 0
145
- if not nonzero_mask.any():
146
- return 0
147
147
 
148
- s = f_prime[nonzero_mask]
149
- transitions = int(((s[:-1] > 0) & (s[1:] < 0)).sum().item())
150
- return transitions
148
+ def mode_count_from_C_batch(
149
+ C: Tensor,
150
+ omega: Tensor,
151
+ h_batch: list,
152
+ G: int,
153
+ N: int,
154
+ ) -> list:
155
+ """Evaluate mode count for B bandwidths in one batched irfft — O(B × G log G).
156
+
157
+ Stacks B kernel vectors into a (B, N//2+1) complex tensor and calls a
158
+ single batched irfft, amortising per-call dispatch overhead across the
159
+ batch. Used by the trisection bisection loop to evaluate two interior
160
+ points per round, cutting the number of Python dispatches by ~35 %.
161
+
162
+ Parameters
163
+ ----------
164
+ C : Tensor, shape (N//2+1,), complex
165
+ rfft of the zero-padded histogram (from `precompute_fft`).
166
+ omega : Tensor, shape (N//2+1,), float
167
+ Frequency grid (from `precompute_fft`).
168
+ h_batch : list of float, length B
169
+ Bandwidths to evaluate.
170
+ G, N : int
171
+ Histogram bin count and padded FFT length.
172
+
173
+ Returns
174
+ -------
175
+ list of int, length B
176
+ Mode counts for each bandwidth in h_batch.
177
+ """
178
+ if C.numel() == 0:
179
+ return [1] * len(h_batch)
180
+
181
+ B = len(h_batch)
182
+ h_t = torch.tensor(h_batch, dtype=omega.dtype, device=omega.device) # (B,)
183
+ # Build (B, M) kernel matrix in one vectorised op
184
+ omega_h = omega.unsqueeze(0) * h_t.unsqueeze(1) # (B, M)
185
+ K_batch = 1j * omega.unsqueeze(0) * torch.exp(-0.5 * omega_h ** 2) # (B, M)
186
+ # One batched irfft dispatch instead of B separate calls
187
+ f_prime_batch = torch.fft.irfft(C.unsqueeze(0) * K_batch, n=N)[:, :G] # (B, G)
188
+ counts = ((f_prime_batch[:, :-1] > 0) & (f_prime_batch[:, 1:] < 0)).sum(dim=1)
189
+ return counts.tolist()
151
190
 
152
191
 
153
192
  def fft_mode_count(
@@ -202,6 +241,8 @@ def _refine_hcrit(
202
241
  domain: tuple[float, float],
203
242
  target_modes: int = 1,
204
243
  pad_factor: int = 2, # Worker 5: pad_factor=2 (was 4) — safe for h ≤ 3σ, halves irfft size
244
+ C_external: Tensor | None = None,
245
+ omega_external: Tensor | None = None,
205
246
  ) -> float:
206
247
  """Sub-bin quadratic refinement of h_crit after bisection converges.
207
248
 
@@ -223,6 +264,13 @@ def _refine_hcrit(
223
264
  h_lo, h_hi : float — final bisection bracket; fft_mode_count(X,h_lo) > target,
224
265
  fft_mode_count(X,h_hi) <= target
225
266
  G, domain, target_modes, pad_factor — same as fft_mode_count
267
+ C_external : Tensor or None
268
+ If provided, reuse the rfft of the zero-padded histogram from the
269
+ bisection step (Worker 23-4: avoids a duplicate O(n) histogram + rfft).
270
+ Must be the same G and pad_factor as the bisection.
271
+ omega_external : Tensor or None
272
+ Frequency grid matching C_external. Must be provided together with
273
+ C_external or both left as None.
226
274
 
227
275
  Returns
228
276
  -------
@@ -240,19 +288,24 @@ def _refine_hcrit(
240
288
  N = pad_factor * G
241
289
  bw = bin_width # histogram bin width
242
290
 
243
- # Pre-compute histogram once; reuse C (FFT of counts) for all h evaluations.
244
- with torch.no_grad():
245
- counts = _histogram_on_device(X, G, lo_d, hi_d).cpu()
246
- counts_padded = torch.zeros(N, dtype=torch.float64)
247
- counts_padded[:G] = counts.double()
248
- C = torch.fft.rfft(counts_padded)
249
- k = torch.arange(N // 2 + 1, dtype=torch.float64)
250
- omega_base = 2 * math.pi * k / (N * bw)
291
+ if C_external is not None and omega_external is not None:
292
+ # Reuse the bisection's precomputed C and omega — no extra O(n) histogram.
293
+ C_ref = C_external
294
+ omega_base = omega_external
295
+ else:
296
+ # Fallback: build float64 histogram + rfft (original behaviour).
297
+ with torch.no_grad():
298
+ counts = _histogram_on_device(X, G, lo_d, hi_d).cpu()
299
+ counts_padded = torch.zeros(N, dtype=torch.float64)
300
+ counts_padded[:G] = counts.double()
301
+ C_ref = torch.fft.rfft(counts_padded)
302
+ k = torch.arange(N // 2 + 1, dtype=torch.float64)
303
+ omega_base = 2 * math.pi * k / (N * bw)
251
304
 
252
305
  def fprime(h: float) -> Tensor:
253
- """Compute f′ array (shape G,) for bandwidth h using cached C (float64)."""
306
+ """Compute f′ array (shape G,) for bandwidth h using cached C."""
254
307
  K_deriv = 1j * omega_base * torch.exp(-0.5 * (omega_base * h) ** 2)
255
- return torch.fft.irfft(C * K_deriv, n=N).float()[:G]
308
+ return torch.fft.irfft(C_ref * K_deriv, n=N).real[:G]
256
309
 
257
310
  with torch.no_grad():
258
311
  # If the bracket is tighter than bin_width, expand it so that the
@@ -36,13 +36,14 @@ class DCBFunction(torch.autograd.Function):
36
36
  @staticmethod
37
37
  def forward(ctx, X, grid, eps, tau, target_modes, delta, formula, chunk_size,
38
38
  brentq_n_max, g_brentq, use_hard_bisection, safe_backward, use_fft, fft_G_min,
39
- fft_dtype):
39
+ fft_dtype, use_richardson):
40
40
  """Locate h_crit and save state for the backward pass."""
41
41
  h_crit, cond_num = find_h_crit(
42
42
  X, grid, eps, tau, target_modes,
43
43
  formula=formula, brentq_n_max=brentq_n_max, chunk_size=chunk_size,
44
44
  g_brentq=g_brentq, use_hard_bisection=use_hard_bisection,
45
45
  use_fft=use_fft, G_min=fft_G_min, fft_dtype=fft_dtype,
46
+ use_richardson=use_richardson,
46
47
  )
47
48
  ctx.save_for_backward(X, grid)
48
49
  ctx.h_crit = h_crit
@@ -68,8 +69,8 @@ class DCBFunction(torch.autograd.Function):
68
69
  ctx.denom_abs = ift_gradient.last_denom_abs
69
70
  # Gradients for: X, grid, eps, tau, target_modes, delta, formula,
70
71
  # chunk_size, brentq_n_max, g_brentq, use_hard_bisection,
71
- # safe_backward, use_fft, fft_G_min, fft_dtype
72
- return grad_X, None, None, None, None, None, None, None, None, None, None, None, None, None, None
72
+ # safe_backward, use_fft, fft_G_min, fft_dtype, use_richardson
73
+ return grad_X, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None
73
74
 
74
75
 
75
76
  class DCBLayer(nn.Module):
@@ -172,6 +173,7 @@ class DCBLayer(nn.Module):
172
173
  sketch_size: int = 500_000,
173
174
  fft_G_min: int = 16384,
174
175
  fft_dtype: torch.dtype = torch.float32,
176
+ use_richardson: bool = True,
175
177
  ):
176
178
  super().__init__()
177
179
  self.target_modes = target_modes
@@ -192,6 +194,7 @@ class DCBLayer(nn.Module):
192
194
  self.sketch_size = sketch_size
193
195
  self.fft_G_min = fft_G_min
194
196
  self.fft_dtype = fft_dtype
197
+ self.use_richardson = use_richardson
195
198
  if use_fft and brentq_n_max != 50_000:
196
199
  raise TypeError(
197
200
  f"brentq_n_max={brentq_n_max} is meaningless when use_fft=True: the FFT path "
@@ -263,6 +266,7 @@ class DCBLayer(nn.Module):
263
266
  X, grid, eps_eff, tau_eff, self.target_modes, self.delta, self.formula,
264
267
  self.chunk_size, self.brentq_n_max, self.g_brentq, self.use_hard_bisection,
265
268
  self.safe_backward, self.use_fft, self.fft_G_min, self.fft_dtype,
269
+ self.use_richardson,
266
270
  )
267
271
 
268
272
 
@@ -37,7 +37,10 @@ from dcb.kde import (
37
37
  soft_mode_count_cross_from_derivs,
38
38
  kde_derivatives_chunked,
39
39
  )
40
- from dcb.fft_kde import fft_mode_count, adaptive_fft_G, precompute_fft, mode_count_from_C
40
+ from dcb.fft_kde import (
41
+ fft_mode_count, adaptive_fft_G, precompute_fft,
42
+ mode_count_from_C, mode_count_from_C_batch,
43
+ )
41
44
 
42
45
  _AUTO_FFT_THRESHOLD = 50_000 # n above which FFT bisection activates (use_fft_effective)
43
46
 
@@ -76,6 +79,7 @@ def find_h_crit_hard(
76
79
  use_fft: bool = False,
77
80
  G_min: int = 16384,
78
81
  fft_dtype: torch.dtype = torch.float32,
82
+ use_richardson: bool = True,
79
83
  ) -> tuple[float, float]:
80
84
  """Find h_crit via hard-mode-count bisection (monotone, no false roots).
81
85
 
@@ -184,32 +188,104 @@ def find_h_crit_hard(
184
188
  if mode_count_from_C(C, omega, h_hi, G_fft, N) <= target_modes:
185
189
  break
186
190
 
187
- # Adaptive bisection: stop when bracket is localised (relative width < 1e-3)
188
- # _refine_hcrit provides sub-bin precision afterwards no need to over-bisect.
191
+ # Trisection with batched irfft (Worker 23-3): evaluate two interior
192
+ # h-values per round in one batched irfft call, shrinking the bracket
193
+ # by 3× per round instead of 2× per step. This cuts the number of
194
+ # Python dispatch calls by ~35 % (≈15 rounds vs ≈22 bisection steps
195
+ # to reach relative width 1e-7) while each batched round costs only
196
+ # marginally more than a single bisection step.
189
197
  lo, hi = h_lo, h_hi
190
- for _ in range(50):
191
- mid = (lo + hi) / 2.0
192
- count = mode_count_from_C(C, omega, mid, G_fft, N)
193
- if count <= target_modes:
194
- hi = mid
195
- else:
196
- lo = mid
198
+ for _ in range(32):
197
199
  if (hi - lo) < tol:
198
200
  break
199
- # Worker 4: adaptive termination — stop when relative bracket width
200
- # is small enough that further bisection cannot meaningfully shift
201
- # _refine_hcrit's quadratic fit. Empirically 1e-7 preserves h_crit
202
- # to within 1e-6 of the 50-step tol=1e-6 baseline while saving ~10
203
- # bisection steps in typical cases.
204
201
  if hi > 0 and (hi - lo) / hi < 1e-7:
205
202
  break
203
+ width = hi - lo
204
+ h1 = lo + width / 3.0
205
+ h2 = lo + 2.0 * width / 3.0
206
+ c1, c2 = mode_count_from_C_batch(C, omega, [h1, h2], G_fft, N)
207
+ if c1 <= target_modes:
208
+ hi = h1
209
+ elif c2 <= target_modes:
210
+ lo = h1
211
+ hi = h2
212
+ else:
213
+ lo = h2
206
214
 
207
215
  h_crit = float(hi) # smallest h with count <= target_modes
208
216
 
209
217
  # Sub-bin refinement: quadratic interpolation on the disappearing f′ lobe
210
218
  # to locate h_crit below the bin-width precision limit.
219
+ # Worker 23-4: pass C and omega from bisection to avoid duplicate O(n)
220
+ # histogram + rfft inside _refine_hcrit (saves ~80 ms at n=10M).
211
221
  from dcb.fft_kde import _refine_hcrit
212
- h_crit = _refine_hcrit(X, lo, hi, G_fft, _domain, target_modes)
222
+ h_crit = _refine_hcrit(
223
+ X, lo, hi, G_fft, _domain, target_modes,
224
+ C_external=C, omega_external=omega,
225
+ )
226
+
227
+ # Worker 5: Richardson extrapolation on h_crit.
228
+ # h_crit(G) = h_crit_true + C·(data_range/G)² + O(G⁻⁴), so
229
+ # h_rich = (4·h_crit(G) − h_crit(G/2)) / 3 cancels the leading
230
+ # O(G⁻²) bin-discretisation error, dropping the same-sample R-vs-DCB
231
+ # discrepancy from ~0.004% to ~0.001% (R's noise floor).
232
+ if use_richardson:
233
+ G_half = G_fft // 2
234
+ if G_half >= 4096:
235
+ try:
236
+ C_half, omega_half, _ = precompute_fft(
237
+ X, G=G_half, domain=_domain,
238
+ pad_factor=pad_factor, fft_dtype=fft_dtype,
239
+ )
240
+ N_half = pad_factor * G_half
241
+
242
+ # Narrow bracket around the fine-grid h_crit.
243
+ h_lo_r = h_crit * 0.95
244
+ h_hi_r = h_crit * 1.05
245
+
246
+ def _bracket_valid(lo_r, hi_r):
247
+ c_lo = mode_count_from_C(C_half, omega_half, lo_r, G_half, N_half)
248
+ c_hi = mode_count_from_C(C_half, omega_half, hi_r, G_half, N_half)
249
+ return (c_lo > target_modes) and (c_hi <= target_modes)
250
+
251
+ valid = _bracket_valid(h_lo_r, h_hi_r)
252
+ if not valid:
253
+ h_lo_r = h_crit * 0.80
254
+ h_hi_r = h_crit * 1.20
255
+ valid = _bracket_valid(h_lo_r, h_hi_r)
256
+
257
+ if valid:
258
+ lo_r, hi_r = h_lo_r, h_hi_r
259
+ for _ in range(12):
260
+ if hi_r > 0 and (hi_r - lo_r) / hi_r < 1e-5:
261
+ break
262
+ width = hi_r - lo_r
263
+ h1 = lo_r + width / 3.0
264
+ h2 = lo_r + 2.0 * width / 3.0
265
+ c1, c2 = mode_count_from_C_batch(
266
+ C_half, omega_half, [h1, h2], G_half, N_half,
267
+ )
268
+ if c1 <= target_modes:
269
+ hi_r = h1
270
+ elif c2 <= target_modes:
271
+ lo_r = h1
272
+ hi_r = h2
273
+ else:
274
+ lo_r = h2
275
+
276
+ h_crit_half = _refine_hcrit(
277
+ X, lo_r, hi_r, G_half, _domain, target_modes,
278
+ C_external=C_half, omega_external=omega_half,
279
+ )
280
+
281
+ h_rich = (4.0 * h_crit - h_crit_half) / 3.0
282
+ # Sanity guard: reject implausible extrapolations.
283
+ if h_crit * 0.5 < h_rich < h_crit * 1.5:
284
+ h_crit = h_rich
285
+ except Exception:
286
+ # If the Richardson pass fails for any reason, keep the
287
+ # pre-Richardson estimate — never degrade the baseline.
288
+ pass
213
289
 
214
290
  else:
215
291
  with torch.no_grad():
@@ -318,6 +394,7 @@ def find_h_crit(
318
394
  use_fft: bool = True,
319
395
  G_min: int = 16384,
320
396
  fft_dtype: torch.dtype = torch.float32,
397
+ use_richardson: bool = True,
321
398
  ) -> tuple[float, float]:
322
399
  """Find h_crit and return (h_crit, condition_number).
323
400
 
@@ -372,6 +449,7 @@ def find_h_crit(
372
449
  X, grid, target_modes, chunk_size, brentq_n_max,
373
450
  h_lo, h_hi, formula=formula, eps=eps, tau=tau,
374
451
  use_fft=use_fft, G_min=G_min, fft_dtype=fft_dtype,
452
+ use_richardson=use_richardson,
375
453
  )
376
454
 
377
455
  from scipy.optimize import brentq
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "diffcb"
7
- version = "0.1.4"
7
+ version = "0.1.5"
8
8
  description = "Differentiable Critical Bandwidth: Silverman's modality test as a differentiable PyTorch layer with IFT backward pass."
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -25,7 +25,7 @@ keywords = [
25
25
  classifiers = [
26
26
  "Development Status :: 3 - Alpha",
27
27
  "Intended Audience :: Science/Research",
28
- "License :: OSI Approved :: MIT License",
28
+ "License :: OSI Approved :: Apache Software License",
29
29
  "Programming Language :: Python :: 3",
30
30
  "Programming Language :: Python :: 3.9",
31
31
  "Programming Language :: Python :: 3.10",
diffcb-0.1.4/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Ruiyu Zhang
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
diffcb-0.1.4/PKG-INFO DELETED
@@ -1,186 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: diffcb
3
- Version: 0.1.4
4
- Summary: Differentiable Critical Bandwidth: Silverman's modality test as a differentiable PyTorch layer with IFT backward pass.
5
- Project-URL: Homepage, https://github.com/ryZhangHason/differentiable-critical-bandwidth
6
- Project-URL: Repository, https://github.com/ryZhangHason/differentiable-critical-bandwidth
7
- Project-URL: Documentation, https://github.com/ryZhangHason/differentiable-critical-bandwidth#readme
8
- Project-URL: Bug Tracker, https://github.com/ryZhangHason/differentiable-critical-bandwidth/issues
9
- Author-email: Ruiyu Zhang <dhhhason@gmail.com>
10
- License: MIT License
11
-
12
- Copyright (c) 2026 Ruiyu Zhang
13
-
14
- Permission is hereby granted, free of charge, to any person obtaining a copy
15
- of this software and associated documentation files (the "Software"), to deal
16
- in the Software without restriction, including without limitation the rights
17
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
- copies of the Software, and to permit persons to whom the Software is
19
- furnished to do so, subject to the following conditions:
20
-
21
- The above copyright notice and this permission notice shall be included in all
22
- copies or substantial portions of the Software.
23
-
24
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
- SOFTWARE.
31
- License-File: LICENSE
32
- Keywords: PyTorch,anomaly detection,critical bandwidth,differentiable programming,generative models,kernel density estimation,mode counting,nonparametric statistics
33
- Classifier: Development Status :: 3 - Alpha
34
- Classifier: Intended Audience :: Science/Research
35
- Classifier: License :: OSI Approved :: MIT License
36
- Classifier: Programming Language :: Python :: 3
37
- Classifier: Programming Language :: Python :: 3.9
38
- Classifier: Programming Language :: Python :: 3.10
39
- Classifier: Programming Language :: Python :: 3.11
40
- Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
41
- Classifier: Topic :: Scientific/Engineering :: Mathematics
42
- Requires-Python: >=3.9
43
- Requires-Dist: matplotlib>=3.7.0
44
- Requires-Dist: numpy>=1.24.0
45
- Requires-Dist: scikit-learn>=1.3.0
46
- Requires-Dist: scipy>=1.10.0
47
- Requires-Dist: torch>=2.0.0
48
- Provides-Extra: dev
49
- Requires-Dist: black>=23.0.0; extra == 'dev'
50
- Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
51
- Requires-Dist: pytest>=7.4.0; extra == 'dev'
52
- Requires-Dist: ruff>=0.1.0; extra == 'dev'
53
- Provides-Extra: notebooks
54
- Requires-Dist: ipywidgets>=8.0.0; extra == 'notebooks'
55
- Requires-Dist: jupyter>=1.0.0; extra == 'notebooks'
56
- Description-Content-Type: text/markdown
57
-
58
- # DCB — Differentiable Critical Bandwidth
59
-
60
- [![PyPI](https://img.shields.io/pypi/v/diffcb.svg)](https://pypi.org/project/diffcb/)
61
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
62
- [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/)
63
-
64
- A PyTorch package that makes **Silverman's critical bandwidth test (1981)** fully differentiable, enabling end-to-end gradient-based optimization over the modal structure of continuous distributions.
65
-
66
- ## Overview
67
-
68
- The critical bandwidth `h_crit` is the minimum KDE bandwidth at which a distribution appears to have at most `m` modes — a classical nonparametric statistic for modality testing. DCB replaces every non-differentiable operation in its computation with a smooth surrogate, then uses the **Implicit Function Theorem** to compute exact gradients through the root-finding step at O(1) memory cost.
69
-
70
- ```python
71
- import torch
72
- from dcb import DCBLayer
73
-
74
- X = torch.randn(1000, requires_grad=True) # 1D samples
75
- layer = DCBLayer(target_modes=1)
76
- h_crit = layer(X) # differentiable scalar
77
- h_crit.backward() # exact IFT gradients
78
- ```
79
-
80
- ## Installation
81
-
82
- ```bash
83
- pip install diffcb
84
- ```
85
-
86
- Or from source:
87
-
88
- ```bash
89
- git clone https://github.com/ryZhangHason/differentiable-critical-bandwidth
90
- cd differentiable-critical-bandwidth
91
- pip install -e ".[dev]"
92
- ```
93
-
94
- ## Accuracy vs R's `bw.crit`
95
-
96
- DCB is validated against R's `multimode::bw.crit(data, mod0=1)` — the standard reference implementation of Hall & York (2001). On **identical data**:
97
-
98
- | n | DCB vs R (same sample) | DCB vs R (independent samples) |
99
- |---|---|---|
100
- | 100K | **0.004%** | ~0.5% (MC noise from independent RNG) |
101
- | 1M | **0.005%** | ~0.2% |
102
- | 10M | **0.004%** | ~0.1% |
103
-
104
- The independent-sample figures reflect natural sampling variability (two unbiased estimators drawing different data), not algorithmic error. On identical data, DCB agrees with R to within **0.005%** at all tested n. DCB is 43× faster than R at n=100M (1.1 s vs 50 s) and handles n=2B in 24 s while R OOMs.
105
-
106
- ## Key Parameters
107
-
108
- ```python
109
- DCBLayer(
110
- target_modes=1, # target number of modes
111
- G=512, # IFT evaluation grid points
112
- use_fft=True, # FFT forward (default); eliminates subsampling bias for n>50K
113
- max_n_exact=1_000_000,# sketch to sketch_size when n exceeds this (None = always exact)
114
- sketch_size=500_000, # sketch target; 500K matches full-n accuracy (O(n^{-2/9}) rate)
115
- safe_backward=False, # clamp IFT denominator near bifurcations
116
- )
117
- ```
118
-
119
- ## Confirmed Experimental Results
120
-
121
- All GPU results produced on Kaggle (T4 / P100) — see `experiments/` and `outputs/`.
122
-
123
- | Experiment | Result | Criterion |
124
- |---|---|---|
125
- | **Accuracy vs R (same data, n=100K)** | **0.004%** | < 0.01% ✓ |
126
- | **Validation (m≥2, Marron-Wand)** | R²=0.91, MAE=0.07, ρ=0.89 | R²≥0.85 ✓ |
127
- | **Speedup vs scipy (CUDA T4, n=8192)** | **10.5×** | ≥3× ✓ |
128
- | **GAN mode preservation** | h_crit=1.232 >> 0.3 | h_crit>0.3 ✓ |
129
- | **Anomaly AUC (KDDCup99)** | DCB=**0.9982** vs IF=0.9867 | DCB≥IF ✓ |
130
-
131
- ## Changelog
132
-
133
- ### v0.1.1 (2026-05-29)
134
- - **MPS fix:** `torch.histc` on MPS allocated an n×bins intermediate (OOM at n≥5M). Replaced with `bucketize+bincount` on CPU — MPS-safe and numerically identical.
135
- - **Sketch API:** `DCBLayer(max_n_exact=1_000_000, sketch_size=500_000)` — silently sketches to 500K when n exceeds threshold. Justified by O(n⁻²/⁹) convergence of h_crit; 500K sketch matches full-n accuracy.
136
- - **Consistent bisection domain:** Pre-computed domain passed to all `fft_mode_count` calls in a single bisection, eliminating per-step drift.
137
- - **Bias warning direction:** Corrected "expected upward bias" to "expected downward bias" on legacy `use_fft=False` path.
138
- - **Test fixes:** Updated 8 pre-existing test failures (tuple unpacking, bounds, deprecation API).
139
-
140
- ### v0.1.0 (2026-05-28)
141
- - Initial PyPI release. FFT forward (O(n + G log G)), IFT backward, MPS support.
142
-
143
- ## Repository Structure
144
-
145
- ```
146
- dcb/ Core PyTorch package
147
- layer.py DCBLayer nn.Module + DCBFunction autograd
148
- solver.py IFT root-finder and backward pass
149
- fft_kde.py FFT-based mode counter (MPS-safe, float64, G=16384)
150
- kde.py Direct KDE derivatives (small-n path)
151
- utils.py Grid, Silverman bandwidth, sg() stabilizer
152
- experiments/ Reproduction scripts for all paper figures and tables
153
- phase1_*.py Validation, speedup, ablation (Figures 1–2, S1–S2)
154
- phase2_gan.py GAN mode-collapse prevention (Figure 3)
155
- phase3_anomaly.py Anomaly detection (Table 2, Figure 5)
156
- round20_*.py Large-n R comparison and streaming benchmarks
157
- round21_*.py Accuracy improvement experiments
158
- tests/ Unit tests (pytest, 45 passed, 1 xfailed)
159
- outputs/ All generated figures and tables (PDFs, PNGs, CSVs)
160
- ```
161
-
162
- ## Reproducing Paper Results
163
-
164
- ```bash
165
- # Phase 1: validation, speedup, ablation
166
- python experiments/phase1_validation.py
167
- python experiments/phase1_speedup.py
168
-
169
- # Phase 2: GAN mode collapse experiment
170
- python experiments/phase2_gan.py
171
-
172
- # Phase 3: anomaly detection benchmark
173
- python experiments/phase3_anomaly.py
174
- ```
175
-
176
- For GPU runs use the Kaggle kernels:
177
- - Phase 1–2: `hsingle/dcb-full-experiments`
178
- - Phase 3: `hsingle/dcb-phase-3-anomaly-detection`
179
-
180
- ## Paper
181
-
182
- > Ruiyu Zhang. "Differentiable Critical Bandwidth: Making Silverman's Modality Test End-to-End Trainable." *Journal of Machine Learning Research*, 2026 (in preparation).
183
-
184
- ## License
185
-
186
- MIT — see [LICENSE](LICENSE).
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes