sparc-pc 0.5.3__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.
Files changed (97) hide show
  1. sparc_pc-0.5.3/LICENSE +21 -0
  2. sparc_pc-0.5.3/MANIFEST.in +3 -0
  3. sparc_pc-0.5.3/PKG-INFO +205 -0
  4. sparc_pc-0.5.3/README.md +161 -0
  5. sparc_pc-0.5.3/pyproject.toml +100 -0
  6. sparc_pc-0.5.3/setup.cfg +4 -0
  7. sparc_pc-0.5.3/setup.py +73 -0
  8. sparc_pc-0.5.3/sparc/__init__.py +114 -0
  9. sparc_pc-0.5.3/sparc/_graph.cpp +44853 -0
  10. sparc_pc-0.5.3/sparc/_graph.pxd +123 -0
  11. sparc_pc-0.5.3/sparc/_graph.pyx +869 -0
  12. sparc_pc-0.5.3/sparc/_mathutils.pxd +56 -0
  13. sparc_pc-0.5.3/sparc/builders/__init__.py +21 -0
  14. sparc_pc-0.5.3/sparc/builders/_factory.py +64 -0
  15. sparc_pc-0.5.3/sparc/builders/embedding.py +252 -0
  16. sparc_pc-0.5.3/sparc/builders/region_graph.py +116 -0
  17. sparc_pc-0.5.3/sparc/circuit.py +206 -0
  18. sparc_pc-0.5.3/sparc/eval.cpp +36340 -0
  19. sparc_pc-0.5.3/sparc/eval.pxd +15 -0
  20. sparc_pc-0.5.3/sparc/eval.pyx +250 -0
  21. sparc_pc-0.5.3/sparc/grad.cpp +36997 -0
  22. sparc_pc-0.5.3/sparc/grad.pxd +6 -0
  23. sparc_pc-0.5.3/sparc/grad.pyx +377 -0
  24. sparc_pc-0.5.3/sparc/io/__init__.py +12 -0
  25. sparc_pc-0.5.3/sparc/io/learned_pc.py +47 -0
  26. sparc_pc-0.5.3/sparc/io/serializer.py +265 -0
  27. sparc_pc-0.5.3/sparc/metrics.cpp +11445 -0
  28. sparc_pc-0.5.3/sparc/metrics.pxd +11 -0
  29. sparc_pc-0.5.3/sparc/metrics.pyx +58 -0
  30. sparc_pc-0.5.3/sparc/nodes.cpp +37522 -0
  31. sparc_pc-0.5.3/sparc/nodes.pxd +107 -0
  32. sparc_pc-0.5.3/sparc/nodes.pyx +588 -0
  33. sparc_pc-0.5.3/sparc/optim.py +233 -0
  34. sparc_pc-0.5.3/sparc/py.typed +0 -0
  35. sparc_pc-0.5.3/sparc/queries/__init__.py +71 -0
  36. sparc_pc-0.5.3/sparc/queries/_engine.cpp +14798 -0
  37. sparc_pc-0.5.3/sparc/queries/_engine.pxd +58 -0
  38. sparc_pc-0.5.3/sparc/queries/_engine.pyx +220 -0
  39. sparc_pc-0.5.3/sparc/queries/cw.cpp +47302 -0
  40. sparc_pc-0.5.3/sparc/queries/cw.pyx +784 -0
  41. sparc_pc-0.5.3/sparc/queries/esd.cpp +35244 -0
  42. sparc_pc-0.5.3/sparc/queries/esd.pyx +522 -0
  43. sparc_pc-0.5.3/sparc/queries/expectation.cpp +58335 -0
  44. sparc_pc-0.5.3/sparc/queries/expectation.pyx +1248 -0
  45. sparc_pc-0.5.3/sparc/queries/gcw.cpp +57765 -0
  46. sparc_pc-0.5.3/sparc/queries/gcw.pyx +1953 -0
  47. sparc_pc-0.5.3/sparc/solvers/__init__.py +0 -0
  48. sparc_pc-0.5.3/sparc/solvers/assignment.cpp +9665 -0
  49. sparc_pc-0.5.3/sparc/solvers/assignment.pxd +9 -0
  50. sparc_pc-0.5.3/sparc/solvers/assignment.pyx +134 -0
  51. sparc_pc-0.5.3/sparc/solvers/northwest.cpp +6022 -0
  52. sparc_pc-0.5.3/sparc/solvers/northwest.pxd +38 -0
  53. sparc_pc-0.5.3/sparc/solvers/northwest.pyx +160 -0
  54. sparc_pc-0.5.3/sparc/solvers/transport.cpp +12306 -0
  55. sparc_pc-0.5.3/sparc/solvers/transport.pxd +12 -0
  56. sparc_pc-0.5.3/sparc/solvers/transport.pyx +404 -0
  57. sparc_pc-0.5.3/sparc/structures/__init__.py +43 -0
  58. sparc_pc-0.5.3/sparc/structures/_blocks.py +105 -0
  59. sparc_pc-0.5.3/sparc/structures/_chowliu.py +168 -0
  60. sparc_pc-0.5.3/sparc/structures/distributions.py +138 -0
  61. sparc_pc-0.5.3/sparc/structures/hclt.py +143 -0
  62. sparc_pc-0.5.3/sparc/structures/hmm.py +108 -0
  63. sparc_pc-0.5.3/sparc/structures/pd.py +298 -0
  64. sparc_pc-0.5.3/sparc/structures/rat_spn.py +112 -0
  65. sparc_pc-0.5.3/sparc_pc.egg-info/PKG-INFO +205 -0
  66. sparc_pc-0.5.3/sparc_pc.egg-info/SOURCES.txt +95 -0
  67. sparc_pc-0.5.3/sparc_pc.egg-info/dependency_links.txt +1 -0
  68. sparc_pc-0.5.3/sparc_pc.egg-info/requires.txt +21 -0
  69. sparc_pc-0.5.3/sparc_pc.egg-info/top_level.txt +1 -0
  70. sparc_pc-0.5.3/tests/test_bernoulli_integration.py +84 -0
  71. sparc_pc-0.5.3/tests/test_circuit_api.py +118 -0
  72. sparc_pc-0.5.3/tests/test_circuit_serializer.py +158 -0
  73. sparc_pc-0.5.3/tests/test_compiled_eval.py +155 -0
  74. sparc_pc-0.5.3/tests/test_compiled_queries.py +56 -0
  75. sparc_pc-0.5.3/tests/test_cw_distance.py +290 -0
  76. sparc_pc-0.5.3/tests/test_distributions.py +120 -0
  77. sparc_pc-0.5.3/tests/test_embedding_builder.py +135 -0
  78. sparc_pc-0.5.3/tests/test_esd_grad.py +163 -0
  79. sparc_pc-0.5.3/tests/test_exp_query.py +398 -0
  80. sparc_pc-0.5.3/tests/test_gcw_coupling.py +436 -0
  81. sparc_pc-0.5.3/tests/test_gcw_crossterm.py +100 -0
  82. sparc_pc-0.5.3/tests/test_gcw_grad.py +250 -0
  83. sparc_pc-0.5.3/tests/test_likelihood.py +137 -0
  84. sparc_pc-0.5.3/tests/test_likelihood_grad.py +204 -0
  85. sparc_pc-0.5.3/tests/test_log_exp_grad.py +133 -0
  86. sparc_pc-0.5.3/tests/test_metrics.py +78 -0
  87. sparc_pc-0.5.3/tests/test_nodes.py +69 -0
  88. sparc_pc-0.5.3/tests/test_nodes_extended.py +83 -0
  89. sparc_pc-0.5.3/tests/test_optim.py +133 -0
  90. sparc_pc-0.5.3/tests/test_properties.py +124 -0
  91. sparc_pc-0.5.3/tests/test_query_compatibility.py +103 -0
  92. sparc_pc-0.5.3/tests/test_refresh_parameters.py +34 -0
  93. sparc_pc-0.5.3/tests/test_region_graph.py +45 -0
  94. sparc_pc-0.5.3/tests/test_sampling.py +116 -0
  95. sparc_pc-0.5.3/tests/test_solvers.py +140 -0
  96. sparc_pc-0.5.3/tests/test_sparc_extras.py +251 -0
  97. sparc_pc-0.5.3/tests/test_structures.py +152 -0
sparc_pc-0.5.3/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Adrian Ciotinga
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.
@@ -0,0 +1,3 @@
1
+ include LICENSE README.md pyproject.toml setup.py
2
+ recursive-include sparc *.pyx *.pxd
3
+ include sparc/py.typed
@@ -0,0 +1,205 @@
1
+ Metadata-Version: 2.4
2
+ Name: sparc-pc
3
+ Version: 0.5.3
4
+ Summary: SparC: fast, modular sparse probabilistic circuits in Cython (CPU-only)
5
+ Author: Adrian Ciotinga
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/aciotinga/sparc
8
+ Project-URL: Documentation, https://sparc-docs.readthedocs.io
9
+ Project-URL: Repository, https://github.com/aciotinga/sparc
10
+ Project-URL: Issues, https://github.com/aciotinga/sparc/issues
11
+ Keywords: probabilistic-circuits,sum-product-networks,cython,wasserstein,machine-learning
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Programming Language :: Cython
21
+ Classifier: Topic :: Scientific/Engineering
22
+ Requires-Python: <3.15,>=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: numpy>=1.20
26
+ Provides-Extra: gurobi
27
+ Requires-Dist: gurobipy>=11.0; extra == "gurobi"
28
+ Provides-Extra: dev
29
+ Requires-Dist: build>=1.0; extra == "dev"
30
+ Requires-Dist: pytest>=7.0; extra == "dev"
31
+ Requires-Dist: pytest-randomly>=3.15; extra == "dev"
32
+ Requires-Dist: scipy>=1.7; extra == "dev"
33
+ Requires-Dist: twine>=4.0; extra == "dev"
34
+ Provides-Extra: docs
35
+ Requires-Dist: Cython>=3.0; extra == "docs"
36
+ Requires-Dist: numpy>=1.20; extra == "docs"
37
+ Requires-Dist: mkdocs<2,>=1.6; extra == "docs"
38
+ Requires-Dist: mkdocs-material<10,>=9.5; extra == "docs"
39
+ Requires-Dist: mkdocstrings-python; extra == "docs"
40
+ Requires-Dist: mkdocs-gen-files; extra == "docs"
41
+ Requires-Dist: mkdocs-literate-nav; extra == "docs"
42
+ Requires-Dist: mkdocs-section-index; extra == "docs"
43
+ Dynamic: license-file
44
+
45
+ # SparC
46
+
47
+ [![PyPI](https://img.shields.io/pypi/v/sparc-pc)](https://pypi.org/project/sparc-pc/)
48
+
49
+ **Spar**se **C**ircuits — a fast, modular, CPU-only library for probabilistic
50
+ circuits (PCs) written in Cython.
51
+
52
+ Documentation: [https://sparc-docs.readthedocs.io](https://sparc-docs.readthedocs.io)
53
+ (build locally with `pip install -e ".[docs]" && mkdocs serve`).
54
+
55
+ ## Highlights
56
+
57
+ - **Speed**: typed Cython with C++ STL containers, `nogil` inner loops, and a
58
+ compiled/batched evaluation path.
59
+ - **Modularity**: nodes dispatch through a C-level vtable, and all pairwise
60
+ queries share one tape/gradient engine. New leaf types and new queries are
61
+ added without editing existing files.
62
+ - **Minimal deps**: `numpy` only at runtime. Transportation and assignment
63
+ problems are solved by built-in pure-Cython solvers.
64
+
65
+ ## Supported queries
66
+
67
+ | Query | Math | Gradients w.r.t. | Docs |
68
+ |-------|------|------------------|------|
69
+ | CW | $W_p^p$ (Circuit-Wasserstein objective) | circuit2 | [queries](docs/guides/queries.md) |
70
+ | GCW cross-term | Gromov-Circuit-Wasserstein cross-term | circuit2 | [queries](docs/guides/queries.md) |
71
+ | Expectation | $E_Q[P(X)]$ | both circuits | [queries](docs/guides/queries.md) |
72
+ | Log expectation | $\log E_Q[P(X)]$ | both circuits | [queries](docs/guides/queries.md) |
73
+ | ESD | $E[d(X,X')^p]$ (two i.i.d. draws) | single circuit | [queries](docs/guides/queries.md) |
74
+
75
+ CW and GCW gradient variants return gradients with respect to the **second**
76
+ circuit only. Expectation queries return gradients for both circuits.
77
+
78
+ ## Supported structures
79
+
80
+ | Constructor | Description | Docs |
81
+ |-------------|-------------|------|
82
+ | `HMM` / `GeneralizedHMM` | Latent-chain sequence models | [structures](docs/guides/structures.md) |
83
+ | `HCLT` | Hidden tree from data (MI + MST) | [structures](docs/guides/structures.md) |
84
+ | `PD` / `PDHCLT` | Recursive grid decompositions | [structures](docs/guides/structures.md) |
85
+ | `RAT_SPN` | Randomized tensorized sum-product network | [structures](docs/guides/structures.md) |
86
+ | `EmbeddingBuilder` | Random recursive PC with node reuse | [builders](docs/guides/builders.md) |
87
+
88
+ Import structures from `sparc.structures` and builders from `sparc.builders`.
89
+
90
+ ## Install
91
+
92
+ Prebuilt wheels are available for Linux, Windows, and macOS (Python 3.10–3.14):
93
+
94
+ ```bash
95
+ pip install sparc-pc
96
+ ```
97
+
98
+ The PyPI package name is `sparc-pc`; import as `sparc`:
99
+
100
+ ```python
101
+ import sparc
102
+ from sparc import Circuit
103
+ ```
104
+
105
+ ### From source (developers)
106
+
107
+ SparC ships Cython/C++17 extensions, so a C++ compiler is required when
108
+ installing from source or in editable mode:
109
+
110
+ ```bash
111
+ pip install -e . # build extensions in place
112
+ pip install -e ".[dev]" # + pytest, scipy, build tools
113
+ pip install -e ".[docs]" # + MkDocs documentation builder
114
+ pip install -e ".[gurobi]" # optional Gurobi extra (unused by core library)
115
+ ```
116
+
117
+ See [Releasing](docs/releasing.md) for maintainer release steps.
118
+
119
+ ## Quick start
120
+
121
+ ```python
122
+ import numpy as np
123
+ from sparc import CategoricalInputNode, SumNode, ProductNode, Circuit
124
+
125
+ x0 = CategoricalInputNode(id=0, scope_var=0, probabilities=[0.7, 0.3])
126
+ x1 = CategoricalInputNode(id=1, scope_var=1, probabilities=[0.5, 0.5])
127
+ prod = ProductNode(id=2, children=[x0, x1])
128
+ root = SumNode(id=3, children=[prod], parameters=[1.0])
129
+
130
+ circuit = Circuit(root)
131
+ point = np.array([0, 1], dtype=np.int32)
132
+ circuit.log_likelihood(point)
133
+ circuit.sample(5, seed=0) # ndarray (5, max_var+1)
134
+
135
+ data = np.random.randint(0, 2, size=(1000, 2)).astype(np.int32)
136
+ circuit.compile().log_likelihood(data)
137
+ ```
138
+
139
+ ### Training
140
+
141
+ ```python
142
+ from sparc.optim import MLETrainer
143
+
144
+ trainer = MLETrainer(circuit, lr=0.5)
145
+ trainer.fit(dataset, epochs=100)
146
+ ```
147
+
148
+ ### Circuit distances
149
+
150
+ ```python
151
+ from sparc import cw_distance, gcw_crossterm, gcw_coupling_circuit, PNormMetric
152
+
153
+ cw_distance(p, q, metric=PNormMetric(p=2.0, scale=1.0))
154
+ gcw_crossterm(circuit1, circuit2)
155
+ gcw_coupling_circuit(circuit1, circuit2).sample(1000)
156
+ ```
157
+
158
+ See the [documentation](docs/index.md) for compatibility rules, all leaf types,
159
+ and extension points.
160
+
161
+ ## Package layout
162
+
163
+ ```
164
+ sparc/
165
+ circuit.py # high-level Circuit wrapper
166
+ nodes.pyx # CircuitNode / Sum / Product / leaf nodes (+ vtable)
167
+ eval.pyx # likelihood / sampling + CompiledCircuit
168
+ grad.pyx # GradBundle + mean_log_likelihood_and_grad
169
+ metrics.pyx # GroundMetric, PNormMetric
170
+ solvers/ # transport, assignment, northwest
171
+ queries/ # CW, GCW, expectation, ESD
172
+ builders/ # region graphs, embedding builders
173
+ structures/ # HMM, HCLT, PD, RAT-SPN, ...
174
+ io/ # gcw-circuit-v1 JSON serializer
175
+ optim.py # simplex_step, apply_grads, MLETrainer
176
+ docs/ # MkDocs site (guides, handbook, API reference)
177
+ examples/ # runnable demo scripts
178
+ tests/
179
+ ```
180
+
181
+ ## Examples
182
+
183
+ ```bash
184
+ PYTHONPATH=. python examples/mle.py
185
+ PYTHONPATH=. python examples/cw_minimization.py
186
+ PYTHONPATH=. python examples/gcw_optimization.py --direction max
187
+ PYTHONPATH=. python examples/dro.py
188
+ ```
189
+
190
+ Full list: [docs/examples/overview.md](docs/examples/overview.md).
191
+
192
+ ## Development
193
+
194
+ ```bash
195
+ pip install -e ".[dev]"
196
+ pytest
197
+
198
+ pip install -e ".[docs]"
199
+ mkdocs serve # browse docs at http://127.0.0.1:8000
200
+ mkdocs build # static site in site/
201
+ ```
202
+
203
+ ## License
204
+
205
+ MIT
@@ -0,0 +1,161 @@
1
+ # SparC
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/sparc-pc)](https://pypi.org/project/sparc-pc/)
4
+
5
+ **Spar**se **C**ircuits — a fast, modular, CPU-only library for probabilistic
6
+ circuits (PCs) written in Cython.
7
+
8
+ Documentation: [https://sparc-docs.readthedocs.io](https://sparc-docs.readthedocs.io)
9
+ (build locally with `pip install -e ".[docs]" && mkdocs serve`).
10
+
11
+ ## Highlights
12
+
13
+ - **Speed**: typed Cython with C++ STL containers, `nogil` inner loops, and a
14
+ compiled/batched evaluation path.
15
+ - **Modularity**: nodes dispatch through a C-level vtable, and all pairwise
16
+ queries share one tape/gradient engine. New leaf types and new queries are
17
+ added without editing existing files.
18
+ - **Minimal deps**: `numpy` only at runtime. Transportation and assignment
19
+ problems are solved by built-in pure-Cython solvers.
20
+
21
+ ## Supported queries
22
+
23
+ | Query | Math | Gradients w.r.t. | Docs |
24
+ |-------|------|------------------|------|
25
+ | CW | $W_p^p$ (Circuit-Wasserstein objective) | circuit2 | [queries](docs/guides/queries.md) |
26
+ | GCW cross-term | Gromov-Circuit-Wasserstein cross-term | circuit2 | [queries](docs/guides/queries.md) |
27
+ | Expectation | $E_Q[P(X)]$ | both circuits | [queries](docs/guides/queries.md) |
28
+ | Log expectation | $\log E_Q[P(X)]$ | both circuits | [queries](docs/guides/queries.md) |
29
+ | ESD | $E[d(X,X')^p]$ (two i.i.d. draws) | single circuit | [queries](docs/guides/queries.md) |
30
+
31
+ CW and GCW gradient variants return gradients with respect to the **second**
32
+ circuit only. Expectation queries return gradients for both circuits.
33
+
34
+ ## Supported structures
35
+
36
+ | Constructor | Description | Docs |
37
+ |-------------|-------------|------|
38
+ | `HMM` / `GeneralizedHMM` | Latent-chain sequence models | [structures](docs/guides/structures.md) |
39
+ | `HCLT` | Hidden tree from data (MI + MST) | [structures](docs/guides/structures.md) |
40
+ | `PD` / `PDHCLT` | Recursive grid decompositions | [structures](docs/guides/structures.md) |
41
+ | `RAT_SPN` | Randomized tensorized sum-product network | [structures](docs/guides/structures.md) |
42
+ | `EmbeddingBuilder` | Random recursive PC with node reuse | [builders](docs/guides/builders.md) |
43
+
44
+ Import structures from `sparc.structures` and builders from `sparc.builders`.
45
+
46
+ ## Install
47
+
48
+ Prebuilt wheels are available for Linux, Windows, and macOS (Python 3.10–3.14):
49
+
50
+ ```bash
51
+ pip install sparc-pc
52
+ ```
53
+
54
+ The PyPI package name is `sparc-pc`; import as `sparc`:
55
+
56
+ ```python
57
+ import sparc
58
+ from sparc import Circuit
59
+ ```
60
+
61
+ ### From source (developers)
62
+
63
+ SparC ships Cython/C++17 extensions, so a C++ compiler is required when
64
+ installing from source or in editable mode:
65
+
66
+ ```bash
67
+ pip install -e . # build extensions in place
68
+ pip install -e ".[dev]" # + pytest, scipy, build tools
69
+ pip install -e ".[docs]" # + MkDocs documentation builder
70
+ pip install -e ".[gurobi]" # optional Gurobi extra (unused by core library)
71
+ ```
72
+
73
+ See [Releasing](docs/releasing.md) for maintainer release steps.
74
+
75
+ ## Quick start
76
+
77
+ ```python
78
+ import numpy as np
79
+ from sparc import CategoricalInputNode, SumNode, ProductNode, Circuit
80
+
81
+ x0 = CategoricalInputNode(id=0, scope_var=0, probabilities=[0.7, 0.3])
82
+ x1 = CategoricalInputNode(id=1, scope_var=1, probabilities=[0.5, 0.5])
83
+ prod = ProductNode(id=2, children=[x0, x1])
84
+ root = SumNode(id=3, children=[prod], parameters=[1.0])
85
+
86
+ circuit = Circuit(root)
87
+ point = np.array([0, 1], dtype=np.int32)
88
+ circuit.log_likelihood(point)
89
+ circuit.sample(5, seed=0) # ndarray (5, max_var+1)
90
+
91
+ data = np.random.randint(0, 2, size=(1000, 2)).astype(np.int32)
92
+ circuit.compile().log_likelihood(data)
93
+ ```
94
+
95
+ ### Training
96
+
97
+ ```python
98
+ from sparc.optim import MLETrainer
99
+
100
+ trainer = MLETrainer(circuit, lr=0.5)
101
+ trainer.fit(dataset, epochs=100)
102
+ ```
103
+
104
+ ### Circuit distances
105
+
106
+ ```python
107
+ from sparc import cw_distance, gcw_crossterm, gcw_coupling_circuit, PNormMetric
108
+
109
+ cw_distance(p, q, metric=PNormMetric(p=2.0, scale=1.0))
110
+ gcw_crossterm(circuit1, circuit2)
111
+ gcw_coupling_circuit(circuit1, circuit2).sample(1000)
112
+ ```
113
+
114
+ See the [documentation](docs/index.md) for compatibility rules, all leaf types,
115
+ and extension points.
116
+
117
+ ## Package layout
118
+
119
+ ```
120
+ sparc/
121
+ circuit.py # high-level Circuit wrapper
122
+ nodes.pyx # CircuitNode / Sum / Product / leaf nodes (+ vtable)
123
+ eval.pyx # likelihood / sampling + CompiledCircuit
124
+ grad.pyx # GradBundle + mean_log_likelihood_and_grad
125
+ metrics.pyx # GroundMetric, PNormMetric
126
+ solvers/ # transport, assignment, northwest
127
+ queries/ # CW, GCW, expectation, ESD
128
+ builders/ # region graphs, embedding builders
129
+ structures/ # HMM, HCLT, PD, RAT-SPN, ...
130
+ io/ # gcw-circuit-v1 JSON serializer
131
+ optim.py # simplex_step, apply_grads, MLETrainer
132
+ docs/ # MkDocs site (guides, handbook, API reference)
133
+ examples/ # runnable demo scripts
134
+ tests/
135
+ ```
136
+
137
+ ## Examples
138
+
139
+ ```bash
140
+ PYTHONPATH=. python examples/mle.py
141
+ PYTHONPATH=. python examples/cw_minimization.py
142
+ PYTHONPATH=. python examples/gcw_optimization.py --direction max
143
+ PYTHONPATH=. python examples/dro.py
144
+ ```
145
+
146
+ Full list: [docs/examples/overview.md](docs/examples/overview.md).
147
+
148
+ ## Development
149
+
150
+ ```bash
151
+ pip install -e ".[dev]"
152
+ pytest
153
+
154
+ pip install -e ".[docs]"
155
+ mkdocs serve # browse docs at http://127.0.0.1:8000
156
+ mkdocs build # static site in site/
157
+ ```
158
+
159
+ ## License
160
+
161
+ MIT
@@ -0,0 +1,100 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61", "wheel", "Cython>=3.0", "numpy>=1.20"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "sparc-pc"
7
+ version = "0.5.3"
8
+ description = "SparC: fast, modular sparse probabilistic circuits in Cython (CPU-only)"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10,<3.15"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Adrian Ciotinga" }]
14
+ keywords = [
15
+ "probabilistic-circuits",
16
+ "sum-product-networks",
17
+ "cython",
18
+ "wasserstein",
19
+ "machine-learning",
20
+ ]
21
+ classifiers = [
22
+ "Development Status :: 3 - Alpha",
23
+ "Intended Audience :: Science/Research",
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3.10",
26
+ "Programming Language :: Python :: 3.11",
27
+ "Programming Language :: Python :: 3.12",
28
+ "Programming Language :: Python :: 3.13",
29
+ "Programming Language :: Python :: 3.14",
30
+ "Programming Language :: Cython",
31
+ "Topic :: Scientific/Engineering",
32
+ ]
33
+ dependencies = [
34
+ "numpy>=1.20",
35
+ ]
36
+
37
+ [project.optional-dependencies]
38
+ gurobi = ["gurobipy>=11.0"]
39
+ dev = [
40
+ "build>=1.0",
41
+ "pytest>=7.0",
42
+ "pytest-randomly>=3.15",
43
+ "scipy>=1.7",
44
+ "twine>=4.0",
45
+ ]
46
+ docs = [
47
+ "Cython>=3.0",
48
+ "numpy>=1.20",
49
+ "mkdocs>=1.6,<2",
50
+ "mkdocs-material>=9.5,<10",
51
+ "mkdocstrings-python",
52
+ "mkdocs-gen-files",
53
+ "mkdocs-literate-nav",
54
+ "mkdocs-section-index",
55
+ ]
56
+
57
+ [project.urls]
58
+ Homepage = "https://github.com/aciotinga/sparc"
59
+ Documentation = "https://sparc-docs.readthedocs.io"
60
+ Repository = "https://github.com/aciotinga/sparc"
61
+ Issues = "https://github.com/aciotinga/sparc/issues"
62
+
63
+ [tool.setuptools]
64
+ include-package-data = true
65
+
66
+ [tool.setuptools.packages.find]
67
+ where = ["."]
68
+ include = ["sparc*"]
69
+
70
+ [tool.setuptools.package-data]
71
+ "sparc" = ["*.pyx", "*.pxd", "py.typed"]
72
+
73
+ [tool.cibuildwheel]
74
+ build = "cp310-* cp311-* cp312-* cp313-* cp314-*"
75
+ skip = "*-musllinux_* cp*t-*"
76
+ test-command = "python -c \"import sparc; from sparc import Circuit; print(sparc.__version__)\""
77
+
78
+ [tool.cibuildwheel.macos]
79
+ archs = ["x86_64", "arm64"]
80
+
81
+ [tool.pytest.ini_options]
82
+ testpaths = ["tests"]
83
+ markers = [
84
+ "gurobi: requires gurobipy and a valid Gurobi license",
85
+ "gcw: GCW coupling / materialization tests (order- and platform-sensitive)",
86
+ "gcw_stress: heap/order stress tests for GCW memoization isolation",
87
+ "solver: direct unit tests for OT / assignment solvers",
88
+ "metrics: ground metric and metric-driven query behavior",
89
+ "eval: likelihood, sampling, and compiled batched evaluation",
90
+ "optim: simplex_step, apply_grads, MLETrainer",
91
+ "circuit: Circuit wrapper (clone, save/load, normalization)",
92
+ "queries: CW / GCW / exp / log_exp query behavior",
93
+ "integration: cross-module leaf-type integration tests",
94
+ "property: cross-cutting invariants (normalization, determinism)",
95
+ "nodes: node construction and validation",
96
+ "no_dual_path: opt out of object-graph vs compiled parametrization",
97
+ ]
98
+ filterwarnings = [
99
+ "error::pytest.PytestUnraisableExceptionWarning",
100
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,73 @@
1
+ import os
2
+ import sys
3
+ import sysconfig
4
+
5
+ import numpy as np
6
+ from Cython.Build import cythonize
7
+ from setuptools import Extension, setup
8
+
9
+ _fast_build = os.environ.get("SPARC_FAST_BUILD", "").lower() in ("1", "true", "yes")
10
+
11
+ _cc = sysconfig.get_config_var("CC") or ""
12
+ _is_msvc = (
13
+ sys.platform == "win32"
14
+ and "gcc" not in _cc.lower()
15
+ and "clang" not in _cc.lower()
16
+ )
17
+
18
+ if _is_msvc:
19
+ extra_compile_args = ["/std:c++17", "/Ox", "/fp:fast"]
20
+ extra_link_args = []
21
+ if not _fast_build:
22
+ extra_compile_args.append("/GL")
23
+ extra_link_args.append("/LTCG")
24
+ else:
25
+ # Avoid -ffast-math: it breaks log-sum-exp guards (isfinite(-inf), -inf
26
+ # comparisons) and causes log_exp_query to return -1 instead of -inf on Linux.
27
+ extra_compile_args = ["-std=c++17", "-O3", "-funroll-loops"]
28
+ extra_link_args = []
29
+ if not _fast_build:
30
+ extra_compile_args.append("-flto")
31
+ extra_link_args.append("-flto")
32
+
33
+ _numpy_include = np.get_include()
34
+
35
+ # Every Cython extension module in SparC. All are C++ (libcpp containers,
36
+ # C++ <random>, etc.).
37
+ _pyx_modules = [
38
+ "sparc.nodes",
39
+ "sparc._graph",
40
+ "sparc.eval",
41
+ "sparc.grad",
42
+ "sparc.metrics",
43
+ "sparc.solvers.northwest",
44
+ "sparc.solvers.transport",
45
+ "sparc.solvers.assignment",
46
+ "sparc.queries._engine",
47
+ "sparc.queries.esd",
48
+ "sparc.queries.expectation",
49
+ "sparc.queries.cw",
50
+ "sparc.queries.gcw",
51
+ ]
52
+
53
+
54
+ def _to_path(dotted: str) -> str:
55
+ return dotted.replace(".", "/") + ".pyx"
56
+
57
+
58
+ ext_modules = cythonize(
59
+ [
60
+ Extension(
61
+ name,
62
+ [_to_path(name)],
63
+ language="c++",
64
+ include_dirs=[_numpy_include],
65
+ extra_compile_args=extra_compile_args,
66
+ extra_link_args=extra_link_args,
67
+ )
68
+ for name in _pyx_modules
69
+ ],
70
+ compiler_directives={"language_level": "3", "embedsignature": True},
71
+ )
72
+
73
+ setup(ext_modules=ext_modules)
@@ -0,0 +1,114 @@
1
+ """SparC: fast, modular sparse probabilistic circuits in Cython (CPU-only).
2
+
3
+ SparC implements probabilistic circuits (PCs) with typed Cython evaluation,
4
+ differentiable Wasserstein-type queries, and composable structure builders.
5
+ Import the high-level :class:`~sparc.circuit.Circuit` wrapper and query
6
+ functions from this package; see subpackages :mod:`sparc.optim`,
7
+ :mod:`sparc.builders`, :mod:`sparc.structures`, and :mod:`sparc.io` for
8
+ training, random construction, built-in structures, and serialization.
9
+
10
+ Install from PyPI with ``pip install sparc-pc`` (import name: ``sparc``).
11
+ Full documentation: https://sparc-docs.readthedocs.io
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import importlib
17
+ from importlib.metadata import PackageNotFoundError, version
18
+
19
+ try:
20
+ __version__ = version("sparc-pc")
21
+ except PackageNotFoundError:
22
+ __version__ = "0.0.0+dev"
23
+
24
+ __all__ = [
25
+ "Circuit",
26
+ "CircuitNode",
27
+ "SumNode",
28
+ "ProductNode",
29
+ "InputNode",
30
+ "FiniteDiscreteInputNode",
31
+ "CategoricalInputNode",
32
+ "BernoulliInputNode",
33
+ "IndicatorInputNode",
34
+ "LiteralInputNode",
35
+ "DiscreteLogisticInputNode",
36
+ "Evidence",
37
+ "RandomState",
38
+ "likelihood",
39
+ "log_likelihood",
40
+ "sample",
41
+ "CompiledCircuit",
42
+ "mean_log_likelihood_and_grad",
43
+ "GradBundle",
44
+ "GroundMetric",
45
+ "PNormMetric",
46
+ "CircuitSerializer",
47
+ "load_learned_pc",
48
+ "cw_distance",
49
+ "cw_distance_and_grad",
50
+ "expected_squared_distance",
51
+ "expected_squared_distance_and_grad",
52
+ "exp_query",
53
+ "exp_query_and_grad",
54
+ "log_exp_query",
55
+ "log_exp_query_and_grad",
56
+ "gcw_crossterm",
57
+ "gcw_crossterm_and_grad",
58
+ "gcw_coupling_circuit",
59
+ ]
60
+
61
+ _LAZY_EXPORTS = {
62
+ "Circuit": ("sparc.circuit", "Circuit"),
63
+ "CompiledCircuit": ("sparc._graph", "CompiledCircuit"),
64
+ "likelihood": ("sparc.eval", "likelihood"),
65
+ "log_likelihood": ("sparc.eval", "log_likelihood"),
66
+ "sample": ("sparc.eval", "sample"),
67
+ "GradBundle": ("sparc.grad", "GradBundle"),
68
+ "mean_log_likelihood_and_grad": ("sparc.grad", "mean_log_likelihood_and_grad"),
69
+ "CircuitSerializer": ("sparc.io", "CircuitSerializer"),
70
+ "load_learned_pc": ("sparc.io", "load_learned_pc"),
71
+ "GroundMetric": ("sparc.metrics", "GroundMetric"),
72
+ "PNormMetric": ("sparc.metrics", "PNormMetric"),
73
+ "CircuitNode": ("sparc.nodes", "CircuitNode"),
74
+ "SumNode": ("sparc.nodes", "SumNode"),
75
+ "ProductNode": ("sparc.nodes", "ProductNode"),
76
+ "InputNode": ("sparc.nodes", "InputNode"),
77
+ "FiniteDiscreteInputNode": ("sparc.nodes", "FiniteDiscreteInputNode"),
78
+ "CategoricalInputNode": ("sparc.nodes", "CategoricalInputNode"),
79
+ "BernoulliInputNode": ("sparc.nodes", "BernoulliInputNode"),
80
+ "IndicatorInputNode": ("sparc.nodes", "IndicatorInputNode"),
81
+ "LiteralInputNode": ("sparc.nodes", "LiteralInputNode"),
82
+ "DiscreteLogisticInputNode": ("sparc.nodes", "DiscreteLogisticInputNode"),
83
+ "Evidence": ("sparc.nodes", "Evidence"),
84
+ "RandomState": ("sparc.nodes", "RandomState"),
85
+ "cw_distance": ("sparc.queries", "cw_distance"),
86
+ "cw_distance_and_grad": ("sparc.queries", "cw_distance_and_grad"),
87
+ "expected_squared_distance": ("sparc.queries", "expected_squared_distance"),
88
+ "expected_squared_distance_and_grad": (
89
+ "sparc.queries",
90
+ "expected_squared_distance_and_grad",
91
+ ),
92
+ "exp_query": ("sparc.queries", "exp_query"),
93
+ "exp_query_and_grad": ("sparc.queries", "exp_query_and_grad"),
94
+ "log_exp_query": ("sparc.queries", "log_exp_query"),
95
+ "log_exp_query_and_grad": ("sparc.queries", "log_exp_query_and_grad"),
96
+ "gcw_crossterm": ("sparc.queries", "gcw_crossterm"),
97
+ "gcw_crossterm_and_grad": ("sparc.queries", "gcw_crossterm_and_grad"),
98
+ "gcw_coupling_circuit": ("sparc.queries", "gcw_coupling_circuit"),
99
+ }
100
+
101
+
102
+ def __getattr__(name: str):
103
+ spec = _LAZY_EXPORTS.get(name)
104
+ if spec is None:
105
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
106
+ module_name, attr_name = spec
107
+ module = importlib.import_module(module_name)
108
+ value = getattr(module, attr_name)
109
+ globals()[name] = value
110
+ return value
111
+
112
+
113
+ def __dir__():
114
+ return sorted(set(globals()) | set(__all__))