monopoly-polysome 0.1.0__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 (52) hide show
  1. monopoly_polysome-0.1.0/.gitignore +16 -0
  2. monopoly_polysome-0.1.0/CHANGELOG.md +11 -0
  3. monopoly_polysome-0.1.0/CITATION.cff +12 -0
  4. monopoly_polysome-0.1.0/CONTRIBUTING.md +21 -0
  5. monopoly_polysome-0.1.0/LICENSE +21 -0
  6. monopoly_polysome-0.1.0/PKG-INFO +188 -0
  7. monopoly_polysome-0.1.0/README.md +155 -0
  8. monopoly_polysome-0.1.0/STYLE.md +45 -0
  9. monopoly_polysome-0.1.0/benchmarks/README.md +73 -0
  10. monopoly_polysome-0.1.0/benchmarks/__init__.py +2 -0
  11. monopoly_polysome-0.1.0/benchmarks/configs/preflight.json +68 -0
  12. monopoly_polysome-0.1.0/benchmarks/configs/publication_count_models.json +162 -0
  13. monopoly_polysome-0.1.0/benchmarks/configs/publication_equal_n20.json +32 -0
  14. monopoly_polysome-0.1.0/benchmarks/configs/publication_fracfixr.json +96 -0
  15. monopoly_polysome-0.1.0/benchmarks/configs/smoke.json +55 -0
  16. monopoly_polysome-0.1.0/benchmarks/methods.py +349 -0
  17. monopoly_polysome-0.1.0/benchmarks/metrics.py +499 -0
  18. monopoly_polysome-0.1.0/benchmarks/r/install.R +132 -0
  19. monopoly_polysome-0.1.0/benchmarks/r/requirements.tsv +6 -0
  20. monopoly_polysome-0.1.0/benchmarks/r/run_comparators.R +449 -0
  21. monopoly_polysome-0.1.0/benchmarks/results/README.md +31 -0
  22. monopoly_polysome-0.1.0/benchmarks/run.py +820 -0
  23. monopoly_polysome-0.1.0/benchmarks/simulate.py +482 -0
  24. monopoly_polysome-0.1.0/docs/BENCHMARKS.md +114 -0
  25. monopoly_polysome-0.1.0/docs/DATA_CONTRACT.md +41 -0
  26. monopoly_polysome-0.1.0/docs/HPC_BENCHMARKS.md +121 -0
  27. monopoly_polysome-0.1.0/docs/METHODS.md +99 -0
  28. monopoly_polysome-0.1.0/docs/RELEASING.md +42 -0
  29. monopoly_polysome-0.1.0/docs/VALIDATION.md +33 -0
  30. monopoly_polysome-0.1.0/pyproject.toml +101 -0
  31. monopoly_polysome-0.1.0/src/monopoly/__init__.py +24 -0
  32. monopoly_polysome-0.1.0/src/monopoly/_design.py +364 -0
  33. monopoly_polysome-0.1.0/src/monopoly/_normalization.py +132 -0
  34. monopoly_polysome-0.1.0/src/monopoly/_provenance.py +87 -0
  35. monopoly_polysome-0.1.0/src/monopoly/_validation.py +38 -0
  36. monopoly_polysome-0.1.0/src/monopoly/dataset.py +412 -0
  37. monopoly_polysome-0.1.0/src/monopoly/exceptions.py +19 -0
  38. monopoly_polysome-0.1.0/src/monopoly/model.py +605 -0
  39. monopoly_polysome-0.1.0/src/monopoly/py.typed +1 -0
  40. monopoly_polysome-0.1.0/src/monopoly/results.py +136 -0
  41. monopoly_polysome-0.1.0/tests/conftest.py +51 -0
  42. monopoly_polysome-0.1.0/tests/test_api_contract.py +60 -0
  43. monopoly_polysome-0.1.0/tests/test_benchmark_methods.py +208 -0
  44. monopoly_polysome-0.1.0/tests/test_benchmark_metrics.py +190 -0
  45. monopoly_polysome-0.1.0/tests/test_benchmark_run.py +174 -0
  46. monopoly_polysome-0.1.0/tests/test_benchmark_simulate.py +109 -0
  47. monopoly_polysome-0.1.0/tests/test_dataset.py +413 -0
  48. monopoly_polysome-0.1.0/tests/test_design.py +339 -0
  49. monopoly_polysome-0.1.0/tests/test_model.py +590 -0
  50. monopoly_polysome-0.1.0/tests/test_normalization.py +184 -0
  51. monopoly_polysome-0.1.0/tests/test_provenance.py +25 -0
  52. monopoly_polysome-0.1.0/tests/test_results.py +105 -0
@@ -0,0 +1,16 @@
1
+ .coverage
2
+ .development/
3
+ .ipython/
4
+ .jupyter*/
5
+ .mplconfig/
6
+ .pytest_cache/
7
+ .ruff_cache/
8
+ .venv/
9
+ .ipynb_checkpoints/
10
+ __pycache__/
11
+ build/
12
+ benchmark-results/
13
+ dist/
14
+ htmlcov/
15
+ *.egg-info/
16
+ *.py[cod]
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Added validated three-fraction datasets and paired condition-by-fraction models.
6
+ - Made within-fraction median-of-ratios normalization the default.
7
+ - Added explicit polysome, monosome, and input contrasts with stable effect direction.
8
+ - Added gene-level diagnostics, input fingerprints, and provenance sidecars.
9
+ - Defined the shared-dispersion Wald model as a transparent working model.
10
+ - Added a frozen, resumable simulation benchmark against R DESeq2, edgeR,
11
+ a log-ratio baseline, and FracFixR's native tests and fraction reconstruction.
@@ -0,0 +1,12 @@
1
+ cff-version: 1.2.0
2
+ message: 'If you use Monopoly, please cite the software and associated manuscript.'
3
+ title: 'Monopoly: paired differential analysis of polysome profiling RNA-seq data'
4
+ type: software
5
+ authors:
6
+ - family-names: 'DeGroat'
7
+ given-names: 'William'
8
+ - family-names: 'Kreimer'
9
+ given-names: 'Anat'
10
+ version: 0.1.0
11
+ license: MIT
12
+ repository-code: 'https://github.com/WillDeGroat/monopoly'
@@ -0,0 +1,21 @@
1
+ # Contributing
2
+
3
+ Create an isolated Python 3.11 or later environment and install the development
4
+ dependencies:
5
+
6
+ ```console
7
+ python -m pip install -e ".[dev]"
8
+ ```
9
+
10
+ Before submitting a change, run:
11
+
12
+ ```console
13
+ python -m ruff check .
14
+ python -m pytest --cov=monopoly
15
+ python -m build
16
+ ```
17
+
18
+ Public API changes require tests and corresponding updates to the README and method
19
+ documentation. Statistical changes must define their estimand, assumptions, failure
20
+ modes, and validation criteria before implementation. Do not promote a method based
21
+ only on additional discoveries in one biological dataset.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 William DeGroat and Anat Kreimer
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,188 @@
1
+ Metadata-Version: 2.4
2
+ Name: monopoly-polysome
3
+ Version: 0.1.0
4
+ Summary: Paired differential analysis of polysome profiling RNA-seq data.
5
+ Project-URL: Repository, https://github.com/WillDeGroat/monopoly
6
+ Project-URL: Documentation, https://github.com/WillDeGroat/monopoly#readme
7
+ Project-URL: Changelog, https://github.com/WillDeGroat/monopoly/blob/main/CHANGELOG.md
8
+ Project-URL: Issues, https://github.com/WillDeGroat/monopoly/issues
9
+ Author: William DeGroat, Anat Kreimer
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: RNA-seq,bioinformatics,polysome profiling,translation
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: formulaic<2,>=1.0
23
+ Requires-Dist: numpy<3,>=1.26
24
+ Requires-Dist: pandas<4,>=2.2
25
+ Requires-Dist: pydeseq2<0.6,>=0.5.4
26
+ Provides-Extra: dev
27
+ Requires-Dist: build<2,>=1; extra == 'dev'
28
+ Requires-Dist: pytest-cov<8,>=5; extra == 'dev'
29
+ Requires-Dist: pytest<10,>=8; extra == 'dev'
30
+ Requires-Dist: ruff<1,>=0.9; extra == 'dev'
31
+ Requires-Dist: scipy<2,>=1.12; extra == 'dev'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # Monopoly
35
+
36
+ Monopoly is a Python package for paired differential analysis of input, monosome,
37
+ and polysome RNA-seq count data. It estimates condition-associated changes in RNA
38
+ distribution between experimental fractions while accounting for the matched
39
+ participant design.
40
+
41
+ Monopoly is designed for experiments in which every participant contributes all
42
+ three fractions. It supports larger two-condition studies with unequal condition
43
+ sizes; no study-specific sample size is hardcoded.
44
+
45
+ ## Install
46
+
47
+ ```console
48
+ python -m pip install monopoly-polysome
49
+ ```
50
+
51
+ The distribution is named `monopoly-polysome`; the Python import is `monopoly`.
52
+ Python 3.11 or later is required.
53
+
54
+ ## Input
55
+
56
+ Provide one participant-by-gene count matrix for each fraction and a participant
57
+ metadata table containing a lowercase `condition` column. The three matrices must
58
+ contain identical participant and gene identifiers.
59
+
60
+ Counts must be finite, nonnegative whole numbers. For Salmon gene quantification,
61
+ round `lengthScaledTPM` count-scale values once before constructing the dataset. Do
62
+ not use TPM as counts. Filtering and transcript aggregation remain upstream analysis
63
+ decisions; see the
64
+ [data contract](https://github.com/WillDeGroat/monopoly/blob/main/docs/DATA_CONTRACT.md).
65
+
66
+ ## Workflow
67
+
68
+ ```python
69
+ from monopoly import MonopolyDataset, MonopolyModel
70
+
71
+ dataset = MonopolyDataset(
72
+ input = input_counts,
73
+ monosome = monosome_counts,
74
+ polysome = polysome_counts,
75
+ metadata = metadata,
76
+ gene_metadata = gene_metadata,
77
+ )
78
+
79
+ model = MonopolyModel(
80
+ dataset = dataset,
81
+ reference = 'Control',
82
+ ).fit(
83
+ normalization = 'fraction',
84
+ )
85
+
86
+ results = model.contrast('poly', 'mono')
87
+ results.write('polyVsMono.tsv')
88
+ ```
89
+
90
+ `poly` and `mono` are accepted contrast aliases for `polysome` and `monosome`.
91
+ `input` is used without an alias. Version 0.1 requires exactly two conditions and
92
+ infers the test condition as the level other than `reference`.
93
+
94
+ A positive polysome-versus-monosome `log2FoldChange` means that the normalized
95
+ polysome-to-monosome ratio is higher in the test condition than in the reference
96
+ condition. `poly` versus `input` and `mono` versus `input` provide secondary
97
+ decompositions.
98
+
99
+ ## Model
100
+
101
+ Monopoly fits a negative-binomial generalized linear model with participant fixed
102
+ effects, fraction effects, and condition-by-fraction interactions. Participant fixed
103
+ effects make each contrast a within-participant fraction comparison. The released
104
+ model uses one moderated dispersion per gene across fractions and asymptotic Wald
105
+ tests through PyDESeq2.
106
+
107
+ Fraction-wise median-of-ratios normalization is the default. It avoids treating
108
+ input, monosome, and polysome libraries as one compositionally homogeneous source.
109
+ Joint normalization remains available as an explicit sensitivity analysis:
110
+
111
+ ```python
112
+ model.fit(normalization = 'joint')
113
+ ```
114
+
115
+ The complete mathematical specification and assumptions are in
116
+ [method specification](https://github.com/WillDeGroat/monopoly/blob/main/docs/METHODS.md).
117
+
118
+ ## Interpretation boundary
119
+
120
+ Monopoly estimates relative redistribution among sequenced RNA pools. It does not
121
+ measure absolute protein synthesis, absolute RNA allocation, or ribosome number per
122
+ transcript. Monosome fractions may contain actively translated RNA and must not be
123
+ described as untranslated RNA.
124
+
125
+ The Wald test assumes that one moderated dispersion per gene adequately represents
126
+ all modeled fractions. Strong fraction-specific mean-variance differences can
127
+ invalidate that assumption. Small studies can still use Monopoly for effect
128
+ estimation, ranking, diagnostics, and sensitivity analyses, but the package does not
129
+ claim that gene-level false-discovery rates are calibrated merely because a model
130
+ can be fit. Larger studies should also inspect fraction-specific variability and
131
+ perform design-appropriate sensitivity analyses.
132
+
133
+ Participant fixed effects leave each modeled library cell unique, so automatic
134
+ Cook's filtering and outlier replacement are disabled. Cook's distances and explicit
135
+ fit statuses remain available as diagnostics.
136
+
137
+ ## Results
138
+
139
+ `MonopolyResults.table` contains stable camelCase columns:
140
+
141
+ - `geneId` and optional `geneName`;
142
+ - `baseMean`, `log2FoldChange`, and `log2FoldChangeSE`;
143
+ - `statistic`, `pValue`, and `adjustedPValue`.
144
+
145
+ `MonopolyResults.diagnostics` reports fit status, dispersion, maximum Cook's
146
+ distance, Cook's-outlier status, and independent-filter status for every gene.
147
+ `MonopolyResults.provenance` records package versions, input fingerprints, design,
148
+ normalization, fit settings, contrast direction, and inferential scope.
149
+
150
+ `results.write('polyVsMono.tsv')` writes three files:
151
+
152
+ - `polyVsMono.tsv`;
153
+ - `polyVsMono.diagnostics.tsv`; and
154
+ - `polyVsMono.provenance.json`.
155
+
156
+ Existing files are replaced.
157
+
158
+ ## Development
159
+
160
+ ```console
161
+ python -m pip install -e ".[dev]"
162
+ python -m ruff check .
163
+ python -m pytest --cov=monopoly
164
+ python -m build
165
+ ```
166
+
167
+ The release test suite covers validation, design identifiability, normalization,
168
+ contrast direction, invariance, diagnostics, output schemas, provenance, and package
169
+ construction. See the
170
+ [validation scope](https://github.com/WillDeGroat/monopoly/blob/main/docs/VALIDATION.md).
171
+
172
+ The public simulation benchmark independently compares Monopoly with R DESeq2,
173
+ edgeR, a log-ratio test, and FracFixR. Its estimands, frozen configurations, and run
174
+ instructions are in the
175
+ [benchmark README](https://github.com/WillDeGroat/monopoly/blob/main/benchmarks/README.md);
176
+ the [HPC guide](https://github.com/WillDeGroat/monopoly/blob/main/docs/HPC_BENCHMARKS.md)
177
+ covers publication-scale execution. FracFixR
178
+ results are not presented as the same contrast when its native test targets a
179
+ different single-fraction proportion.
180
+
181
+ Monopoly is developed by William DeGroat and Anat Kreimer and released under the
182
+ [MIT License](https://github.com/WillDeGroat/monopoly/blob/main/LICENSE).
183
+
184
+ ## Publication benchmark
185
+
186
+ The complete frozen study benchmarks and the additional equal-dispersion run
187
+ are linked from [benchmark results](benchmarks/results/README.md).
188
+ See [release instructions](docs/RELEASING.md) for PyPI Trusted Publishing.
@@ -0,0 +1,155 @@
1
+ # Monopoly
2
+
3
+ Monopoly is a Python package for paired differential analysis of input, monosome,
4
+ and polysome RNA-seq count data. It estimates condition-associated changes in RNA
5
+ distribution between experimental fractions while accounting for the matched
6
+ participant design.
7
+
8
+ Monopoly is designed for experiments in which every participant contributes all
9
+ three fractions. It supports larger two-condition studies with unequal condition
10
+ sizes; no study-specific sample size is hardcoded.
11
+
12
+ ## Install
13
+
14
+ ```console
15
+ python -m pip install monopoly-polysome
16
+ ```
17
+
18
+ The distribution is named `monopoly-polysome`; the Python import is `monopoly`.
19
+ Python 3.11 or later is required.
20
+
21
+ ## Input
22
+
23
+ Provide one participant-by-gene count matrix for each fraction and a participant
24
+ metadata table containing a lowercase `condition` column. The three matrices must
25
+ contain identical participant and gene identifiers.
26
+
27
+ Counts must be finite, nonnegative whole numbers. For Salmon gene quantification,
28
+ round `lengthScaledTPM` count-scale values once before constructing the dataset. Do
29
+ not use TPM as counts. Filtering and transcript aggregation remain upstream analysis
30
+ decisions; see the
31
+ [data contract](https://github.com/WillDeGroat/monopoly/blob/main/docs/DATA_CONTRACT.md).
32
+
33
+ ## Workflow
34
+
35
+ ```python
36
+ from monopoly import MonopolyDataset, MonopolyModel
37
+
38
+ dataset = MonopolyDataset(
39
+ input = input_counts,
40
+ monosome = monosome_counts,
41
+ polysome = polysome_counts,
42
+ metadata = metadata,
43
+ gene_metadata = gene_metadata,
44
+ )
45
+
46
+ model = MonopolyModel(
47
+ dataset = dataset,
48
+ reference = 'Control',
49
+ ).fit(
50
+ normalization = 'fraction',
51
+ )
52
+
53
+ results = model.contrast('poly', 'mono')
54
+ results.write('polyVsMono.tsv')
55
+ ```
56
+
57
+ `poly` and `mono` are accepted contrast aliases for `polysome` and `monosome`.
58
+ `input` is used without an alias. Version 0.1 requires exactly two conditions and
59
+ infers the test condition as the level other than `reference`.
60
+
61
+ A positive polysome-versus-monosome `log2FoldChange` means that the normalized
62
+ polysome-to-monosome ratio is higher in the test condition than in the reference
63
+ condition. `poly` versus `input` and `mono` versus `input` provide secondary
64
+ decompositions.
65
+
66
+ ## Model
67
+
68
+ Monopoly fits a negative-binomial generalized linear model with participant fixed
69
+ effects, fraction effects, and condition-by-fraction interactions. Participant fixed
70
+ effects make each contrast a within-participant fraction comparison. The released
71
+ model uses one moderated dispersion per gene across fractions and asymptotic Wald
72
+ tests through PyDESeq2.
73
+
74
+ Fraction-wise median-of-ratios normalization is the default. It avoids treating
75
+ input, monosome, and polysome libraries as one compositionally homogeneous source.
76
+ Joint normalization remains available as an explicit sensitivity analysis:
77
+
78
+ ```python
79
+ model.fit(normalization = 'joint')
80
+ ```
81
+
82
+ The complete mathematical specification and assumptions are in
83
+ [method specification](https://github.com/WillDeGroat/monopoly/blob/main/docs/METHODS.md).
84
+
85
+ ## Interpretation boundary
86
+
87
+ Monopoly estimates relative redistribution among sequenced RNA pools. It does not
88
+ measure absolute protein synthesis, absolute RNA allocation, or ribosome number per
89
+ transcript. Monosome fractions may contain actively translated RNA and must not be
90
+ described as untranslated RNA.
91
+
92
+ The Wald test assumes that one moderated dispersion per gene adequately represents
93
+ all modeled fractions. Strong fraction-specific mean-variance differences can
94
+ invalidate that assumption. Small studies can still use Monopoly for effect
95
+ estimation, ranking, diagnostics, and sensitivity analyses, but the package does not
96
+ claim that gene-level false-discovery rates are calibrated merely because a model
97
+ can be fit. Larger studies should also inspect fraction-specific variability and
98
+ perform design-appropriate sensitivity analyses.
99
+
100
+ Participant fixed effects leave each modeled library cell unique, so automatic
101
+ Cook's filtering and outlier replacement are disabled. Cook's distances and explicit
102
+ fit statuses remain available as diagnostics.
103
+
104
+ ## Results
105
+
106
+ `MonopolyResults.table` contains stable camelCase columns:
107
+
108
+ - `geneId` and optional `geneName`;
109
+ - `baseMean`, `log2FoldChange`, and `log2FoldChangeSE`;
110
+ - `statistic`, `pValue`, and `adjustedPValue`.
111
+
112
+ `MonopolyResults.diagnostics` reports fit status, dispersion, maximum Cook's
113
+ distance, Cook's-outlier status, and independent-filter status for every gene.
114
+ `MonopolyResults.provenance` records package versions, input fingerprints, design,
115
+ normalization, fit settings, contrast direction, and inferential scope.
116
+
117
+ `results.write('polyVsMono.tsv')` writes three files:
118
+
119
+ - `polyVsMono.tsv`;
120
+ - `polyVsMono.diagnostics.tsv`; and
121
+ - `polyVsMono.provenance.json`.
122
+
123
+ Existing files are replaced.
124
+
125
+ ## Development
126
+
127
+ ```console
128
+ python -m pip install -e ".[dev]"
129
+ python -m ruff check .
130
+ python -m pytest --cov=monopoly
131
+ python -m build
132
+ ```
133
+
134
+ The release test suite covers validation, design identifiability, normalization,
135
+ contrast direction, invariance, diagnostics, output schemas, provenance, and package
136
+ construction. See the
137
+ [validation scope](https://github.com/WillDeGroat/monopoly/blob/main/docs/VALIDATION.md).
138
+
139
+ The public simulation benchmark independently compares Monopoly with R DESeq2,
140
+ edgeR, a log-ratio test, and FracFixR. Its estimands, frozen configurations, and run
141
+ instructions are in the
142
+ [benchmark README](https://github.com/WillDeGroat/monopoly/blob/main/benchmarks/README.md);
143
+ the [HPC guide](https://github.com/WillDeGroat/monopoly/blob/main/docs/HPC_BENCHMARKS.md)
144
+ covers publication-scale execution. FracFixR
145
+ results are not presented as the same contrast when its native test targets a
146
+ different single-fraction proportion.
147
+
148
+ Monopoly is developed by William DeGroat and Anat Kreimer and released under the
149
+ [MIT License](https://github.com/WillDeGroat/monopoly/blob/main/LICENSE).
150
+
151
+ ## Publication benchmark
152
+
153
+ The complete frozen study benchmarks and the additional equal-dispersion run
154
+ are linked from [benchmark results](benchmarks/results/README.md).
155
+ See [release instructions](docs/RELEASING.md) for PyPI Trusted Publishing.
@@ -0,0 +1,45 @@
1
+ # Monopoly style
2
+
3
+ Monopoly should remain small, explicit, and unsurprising.
4
+
5
+ Scientific terminology and assumptions are defined in `docs/METHODS.md`.
6
+
7
+ ## Design
8
+
9
+ - Keep the public workflow centered on `MonopolyDataset`, `MonopolyModel`, and
10
+ `MonopolyResults`.
11
+ - Prefer plain functions to inheritance, registries, factories, or plugin systems.
12
+ - Add an abstraction only when it removes repeated behavior that already exists.
13
+ - Use established numerical libraries whenever they can express the required method.
14
+ - Use PyDESeq2 for negative-binomial estimation and established scientific
15
+ libraries for numerical work.
16
+ - Validate at public boundaries. Internal functions may rely on validated inputs.
17
+ - Never guess identifiers, silently remove observations, or mutate user objects.
18
+
19
+ ## Source
20
+
21
+ - Use single-quoted strings. Docstrings use triple double quotes.
22
+ - Limit lines to 88 characters.
23
+ - Include spaces around keyword and default assignments.
24
+ - Use concise one-line docstrings ending in periods for public APIs.
25
+ - Use descriptive phrases for classes and imperative sentences for functions and
26
+ methods.
27
+ - Add comments only when they explain scientific or non-obvious algorithmic reasoning.
28
+ - Use `snake_case` internally and camelCase in exported scientific tables.
29
+ - Use Ruff for linting and line-length enforcement. Format manually because standard
30
+ Python formatters remove the approved spaces around keyword assignments.
31
+
32
+ ## Errors
33
+
34
+ - Raise a dedicated Monopoly exception with an actionable message.
35
+ - Stop for invalid experiments or global model failures.
36
+ - Preserve valid genome-wide fits when isolated genes fail, and report those failures
37
+ through diagnostics.
38
+
39
+ ## Tests
40
+
41
+ - Test public contracts, scientific invariants, and failure modes.
42
+ - Do not reproduce dependency test suites or preserve tests for rejected methods.
43
+ - Use fixed random seeds only for deterministic numerical contract tests.
44
+ - A successful test suite establishes implementation behavior, not universal
45
+ statistical calibration.
@@ -0,0 +1,73 @@
1
+ # Monopoly benchmark
2
+
3
+ This benchmark evaluates the released three-pool model against independently
4
+ generated truth. It is not a search for settings that maximize discoveries.
5
+
6
+ ## Methods
7
+
8
+ - `monopoly`: fraction-wise median-ratio normalization and the released PyDESeq2
9
+ participant-by-fraction interaction model.
10
+ - `deseq2`: an independent R implementation of the same design and normalization.
11
+ This is a numerical reference, not a method Monopoly is expected to outperform.
12
+ - `edgeR`: the corresponding robust quasi-likelihood interaction model with
13
+ fraction-wise TMM normalization.
14
+ - `ratioWelch`: a deliberately simple pseudocount log-ratio followed by a Welch
15
+ test. It represents a common analysis that does not model count uncertainty.
16
+ - `fracfixr`: FracFixR 1.1.0 fraction reconstruction followed by its native GLM,
17
+ Logit, and beta-binomial Wald tests.
18
+
19
+ Monopoly, DESeq2, edgeR, and `ratioWelch` target the condition difference in the
20
+ log2 polysome-to-monosome ratio. FracFixR's Logit and Wald tests target the odds of
21
+ one reconstructed fraction against the remainder. Its GLM returns a log2 ratio of
22
+ mean reconstructed proportions while testing a binomial condition coefficient.
23
+ All are reported under separate estimands. FracFixR fraction-weight recovery is
24
+ evaluated in a separate table.
25
+
26
+ ## Run
27
+
28
+ Install Monopoly with its development dependencies, then install the frozen R
29
+ comparators:
30
+
31
+ ```console
32
+ python -m pip install -e ".[dev]"
33
+ Rscript benchmarks/r/install.R
34
+ ```
35
+
36
+ Run the three-replicate software smoke test:
37
+
38
+ ```console
39
+ python -m benchmarks.run benchmarks/configs/smoke.json benchmark-results/smoke \
40
+ --rscript Rscript --workers 1
41
+ ```
42
+
43
+ `preflight.json` is a small scientific diagnostic. The two `publication_*.json`
44
+ files contain the larger count-model and FracFixR grids. They are separate because
45
+ FracFixR fits gene-wise models for a related but non-identical estimand and is much
46
+ more computationally expensive. Run the smoke test and preflight locally before
47
+ submitting either publication grid. See the
48
+ [HPC walkthrough](../docs/HPC_BENCHMARKS.md) for a reproducible Slurm workflow.
49
+
50
+ Runs are resumable. A result directory cannot be resumed after its configuration,
51
+ method set, or benchmark source changes. Each run records seeds, configurations,
52
+ software versions, simulation hashes, result hashes, failures, and runtime.
53
+ The frozen R versions in `r/requirements.tsv` are enforced before any replicate is
54
+ run. Completed replicate parts are written atomically, and progress is printed at
55
+ approximately one-percent intervals. A missing or empty R result is retried once.
56
+ If a replicate still fails, the runner reports its scenario and replicate, retains
57
+ all other completed parts, and exits after the remaining submitted work finishes.
58
+ Resume an interrupted run with the identical command and output directory. Never
59
+ run multiple benchmark processes against the same output directory.
60
+
61
+ ## Outputs
62
+
63
+ - `manifest.json`: run identity, environment, hashes, and completion state.
64
+ - `seedManifest.tsv`: one immutable configuration and seed per replicate.
65
+ - `replicateResults.tsv`: operating characteristics for each method and estimand.
66
+ - `metricSummary.tsv`: means and Monte Carlo standard errors across replicates.
67
+ - `benchmarkAssessment.tsv`: prespecified pass/fail calibration checks.
68
+ - `fractionRecovery.tsv`: FracFixR fraction-weight recovery metrics.
69
+ - `parts/`: atomic replicate outputs used for resumption and auditing.
70
+
71
+ The smoke and preflight configurations are engineering checks, not validation
72
+ evidence. Publication claims must use the frozen publication grids and report every
73
+ prespecified scenario, including failures.
@@ -0,0 +1,2 @@
1
+ """Public benchmark utilities for Monopoly."""
2
+
@@ -0,0 +1,68 @@
1
+ {
2
+ "alpha": 0.05,
3
+ "benchmarkId": "monopoly-preflight-2026-09",
4
+ "methods": [
5
+ "monopoly",
6
+ "ratioWelch",
7
+ "deseq2",
8
+ "edgeR",
9
+ "fracfixr"
10
+ ],
11
+ "minimumMean": 20,
12
+ "scenarios": [
13
+ {
14
+ "dispersionCorrelation": 1.0,
15
+ "dispersionLogSd": 0.5,
16
+ "fractionDispersions": [0.15, 0.15, 0.15],
17
+ "genes": 500,
18
+ "participantsPerCondition": 3,
19
+ "redistributedGenes": 0,
20
+ "replicates": 4,
21
+ "scenarioId": "equalNullN3",
22
+ "seedStart": 41107000,
23
+ "transcriptionGenes": 0
24
+ },
25
+ {
26
+ "dispersionCorrelation": 0.7,
27
+ "dispersionLogSd": 0.5,
28
+ "fractionDispersions": [0.05, 0.15, 0.5],
29
+ "genes": 500,
30
+ "participantsPerCondition": 3,
31
+ "redistributedGenes": 0,
32
+ "replicates": 4,
33
+ "scenarioId": "unequalNullN3",
34
+ "seedStart": 41108000,
35
+ "transcriptionGenes": 0
36
+ },
37
+ {
38
+ "dispersionCorrelation": 1.0,
39
+ "dispersionLogSd": 0.5,
40
+ "fractionDispersions": [0.15, 0.15, 0.15],
41
+ "genes": 500,
42
+ "participantsPerCondition": 3,
43
+ "redistributedGenes": 100,
44
+ "redistributionLog2Effect": 2,
45
+ "redistributionPositiveFraction": 0.5,
46
+ "replicates": 4,
47
+ "scenarioId": "equalEffect2N3",
48
+ "seedStart": 41109000,
49
+ "transcriptionGenes": 100,
50
+ "transcriptionLog2Effect": 1
51
+ },
52
+ {
53
+ "dispersionCorrelation": 0.7,
54
+ "dispersionLogSd": 0.5,
55
+ "fractionDispersions": [0.05, 0.15, 0.5],
56
+ "genes": 500,
57
+ "participantsPerCondition": 3,
58
+ "redistributedGenes": 100,
59
+ "redistributionLog2Effect": 2,
60
+ "redistributionPositiveFraction": 0.5,
61
+ "replicates": 4,
62
+ "scenarioId": "unequalEffect2N3",
63
+ "seedStart": 41110000,
64
+ "transcriptionGenes": 100,
65
+ "transcriptionLog2Effect": 1
66
+ }
67
+ ]
68
+ }