neurocvguard 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.
- neurocvguard-0.1.0/.gitignore +39 -0
- neurocvguard-0.1.0/LICENSE +29 -0
- neurocvguard-0.1.0/PKG-INFO +97 -0
- neurocvguard-0.1.0/README.md +65 -0
- neurocvguard-0.1.0/examples/01_cohort_audit.py +10 -0
- neurocvguard-0.1.0/examples/02_repeated_observations.py +11 -0
- neurocvguard-0.1.0/examples/03_site_held_out.py +11 -0
- neurocvguard-0.1.0/examples/04_preprocessing_provenance.py +11 -0
- neurocvguard-0.1.0/examples/05_explicit_feature_join.py +11 -0
- neurocvguard-0.1.0/pyproject.toml +85 -0
- neurocvguard-0.1.0/src/neurocvguard/__init__.py +199 -0
- neurocvguard-0.1.0/src/neurocvguard/__main__.py +6 -0
- neurocvguard-0.1.0/src/neurocvguard/_diagnostics.py +57 -0
- neurocvguard-0.1.0/src/neurocvguard/_evaluation_fits.py +186 -0
- neurocvguard-0.1.0/src/neurocvguard/_evaluation_inputs.py +138 -0
- neurocvguard-0.1.0/src/neurocvguard/_features.py +61 -0
- neurocvguard-0.1.0/src/neurocvguard/_inner_plans.py +50 -0
- neurocvguard-0.1.0/src/neurocvguard/_paths.py +12 -0
- neurocvguard-0.1.0/src/neurocvguard/_projection.py +554 -0
- neurocvguard-0.1.0/src/neurocvguard/_records.py +130 -0
- neurocvguard-0.1.0/src/neurocvguard/_report_tables.py +72 -0
- neurocvguard-0.1.0/src/neurocvguard/_report_writes.py +100 -0
- neurocvguard-0.1.0/src/neurocvguard/_tables.py +128 -0
- neurocvguard-0.1.0/src/neurocvguard/_tuning.py +97 -0
- neurocvguard-0.1.0/src/neurocvguard/audit.py +164 -0
- neurocvguard-0.1.0/src/neurocvguard/checks/__init__.py +1 -0
- neurocvguard-0.1.0/src/neurocvguard/checks/associations.py +267 -0
- neurocvguard-0.1.0/src/neurocvguard/checks/cohort.py +556 -0
- neurocvguard-0.1.0/src/neurocvguard/checks/partitions.py +585 -0
- neurocvguard-0.1.0/src/neurocvguard/checks/preprocessing.py +144 -0
- neurocvguard-0.1.0/src/neurocvguard/cli.py +504 -0
- neurocvguard-0.1.0/src/neurocvguard/comparison.py +174 -0
- neurocvguard-0.1.0/src/neurocvguard/config.py +244 -0
- neurocvguard-0.1.0/src/neurocvguard/demo.py +368 -0
- neurocvguard-0.1.0/src/neurocvguard/errors.py +38 -0
- neurocvguard-0.1.0/src/neurocvguard/evaluation.py +235 -0
- neurocvguard-0.1.0/src/neurocvguard/fit_boundaries.py +129 -0
- neurocvguard-0.1.0/src/neurocvguard/identity.py +214 -0
- neurocvguard-0.1.0/src/neurocvguard/io.py +499 -0
- neurocvguard-0.1.0/src/neurocvguard/metrics.py +147 -0
- neurocvguard-0.1.0/src/neurocvguard/models.py +864 -0
- neurocvguard-0.1.0/src/neurocvguard/provenance.py +132 -0
- neurocvguard-0.1.0/src/neurocvguard/py.typed +0 -0
- neurocvguard-0.1.0/src/neurocvguard/reporting.py +316 -0
- neurocvguard-0.1.0/src/neurocvguard/rules.py +503 -0
- neurocvguard-0.1.0/src/neurocvguard/schema.py +126 -0
- neurocvguard-0.1.0/src/neurocvguard/schemas/audit-report.schema.json +1336 -0
- neurocvguard-0.1.0/src/neurocvguard/schemas/comparison-summary.schema.json +474 -0
- neurocvguard-0.1.0/src/neurocvguard/schemas/config.schema.json +295 -0
- neurocvguard-0.1.0/src/neurocvguard/schemas/evaluation-result.schema.json +1087 -0
- neurocvguard-0.1.0/src/neurocvguard/schemas/preprocessing-ledger.schema.json +102 -0
- neurocvguard-0.1.0/src/neurocvguard/schemas/split-plan.schema.json +156 -0
- neurocvguard-0.1.0/src/neurocvguard/serialization.py +173 -0
- neurocvguard-0.1.0/src/neurocvguard/splitting.py +488 -0
- neurocvguard-0.1.0/src/neurocvguard/synthetic.py +172 -0
- neurocvguard-0.1.0/src/neurocvguard/templates/report.css +30 -0
- neurocvguard-0.1.0/src/neurocvguard/templates/report.html +103 -0
- neurocvguard-0.1.0/src/neurocvguard/workflows.py +83 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Isolated local environments and build products
|
|
2
|
+
.venv/
|
|
3
|
+
.venv-install/
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
*.egg-info/
|
|
7
|
+
|
|
8
|
+
# Python and development caches
|
|
9
|
+
__pycache__/
|
|
10
|
+
*.py[cod]
|
|
11
|
+
.pytest_cache/
|
|
12
|
+
.mypy_cache/
|
|
13
|
+
.ruff_cache/
|
|
14
|
+
.hypothesis/
|
|
15
|
+
.coverage
|
|
16
|
+
.coverage.*
|
|
17
|
+
.coverage-s15/
|
|
18
|
+
htmlcov/
|
|
19
|
+
docs/_build/
|
|
20
|
+
|
|
21
|
+
# Local research inputs and outputs; synthetic fixtures remain visible
|
|
22
|
+
/data/private/
|
|
23
|
+
/private_data/
|
|
24
|
+
/local_outputs/
|
|
25
|
+
/results/
|
|
26
|
+
*.private.json
|
|
27
|
+
|
|
28
|
+
# Credentials and per-user IDE settings
|
|
29
|
+
.env
|
|
30
|
+
.env.*
|
|
31
|
+
!.env.example
|
|
32
|
+
*.pem
|
|
33
|
+
*.key
|
|
34
|
+
.secrets/
|
|
35
|
+
.vscode/
|
|
36
|
+
.idea/
|
|
37
|
+
*.code-workspace
|
|
38
|
+
# Local backup copies
|
|
39
|
+
/Backup/
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Alireza Emad
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
9
|
+
this list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
22
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
23
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
24
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
25
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
26
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
27
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
28
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
29
|
+
POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: neurocvguard
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Research-only cohort audits and independently checked split plans for neuroimaging ML
|
|
5
|
+
Project-URL: Source, https://github.com/Alireza2177/NeuroCVguard
|
|
6
|
+
Author: Alireza Emad
|
|
7
|
+
Maintainer-email: Alireza Emad <Alireza221177@gmail.com>
|
|
8
|
+
License-Expression: BSD-3-Clause
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Requires-Dist: jinja2>=3.1.6
|
|
12
|
+
Requires-Dist: jsonschema>=4.23.0
|
|
13
|
+
Requires-Dist: numpy>=1.26.4
|
|
14
|
+
Requires-Dist: pandas>=2.2.3
|
|
15
|
+
Requires-Dist: scikit-learn>=1.5.2
|
|
16
|
+
Requires-Dist: scipy>=1.13.1
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: build; extra == 'dev'
|
|
19
|
+
Requires-Dist: hypothesis; extra == 'dev'
|
|
20
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
21
|
+
Requires-Dist: pandas-stubs; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
24
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
25
|
+
Requires-Dist: twine; extra == 'dev'
|
|
26
|
+
Requires-Dist: types-jsonschema; extra == 'dev'
|
|
27
|
+
Provides-Extra: docs
|
|
28
|
+
Requires-Dist: myst-parser; extra == 'docs'
|
|
29
|
+
Requires-Dist: sphinx; extra == 'docs'
|
|
30
|
+
Requires-Dist: sphinx-rtd-theme; extra == 'docs'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# NeuroCVguard
|
|
34
|
+
|
|
35
|
+
Research-only Python software for checking whether supplied cohort identities,
|
|
36
|
+
partitions and evaluation procedures match an intended generalization claim.
|
|
37
|
+
It reads local CSV/TSV/JSON, checks participant/transitive dependence and requested
|
|
38
|
+
domain separation, describes acquisition–target association, generates checked
|
|
39
|
+
splits and runs a controlled participant-level logistic baseline with optional
|
|
40
|
+
nested C selection. Offline HTML/JSON reports retain incomplete coverage and
|
|
41
|
+
limitations. No account, GPU or runtime internet connection is needed.
|
|
42
|
+
|
|
43
|
+
It does not process MRI images, provide clinical advice, authenticate upstream
|
|
44
|
+
preprocessing, prove causal confounding or certify a study as leakage-free.
|
|
45
|
+
Repeated visits alone are not leakage; inspect actual membership and objective.
|
|
46
|
+
Unknown upstream preprocessing remains unassessable even with a correct Pipeline.
|
|
47
|
+
|
|
48
|
+
## Install and try
|
|
49
|
+
|
|
50
|
+
From an authorized local source checkout, create a dedicated Python environment.
|
|
51
|
+
Use `.venv/Scripts/python.exe` on Windows or `.venv/bin/python` on Linux/macOS
|
|
52
|
+
after `python -m venv .venv`. With that interpreter selected:
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
python -m pip install -e ".[dev,docs]"
|
|
56
|
+
python -m neurocvguard demo --out local_outputs/demo
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Open `local_outputs/demo/report.html`. Inputs are fully synthetic, not patient
|
|
60
|
+
data. Choose a new output path or explicitly use `--overwrite`. Read warnings
|
|
61
|
+
beside coverage: association suggests reviewing acquisition imbalance, while
|
|
62
|
+
unknown preprocessing asks for evidence rather than a passing verdict.
|
|
63
|
+
|
|
64
|
+
**Private plans/evaluations are separate from projected reports.** Default
|
|
65
|
+
projection is not guaranteed anonymity; inspect artifacts before sharing.
|
|
66
|
+
Version: 0.1.0. See the [release procedure and evidence](docs/release.md) for
|
|
67
|
+
publication status and the actual verified installation/platform results.
|
|
68
|
+
|
|
69
|
+
## Documentation and development
|
|
70
|
+
|
|
71
|
+
- [Installation and troubleshooting](docs/installation.md)
|
|
72
|
+
- [Executable quickstart](docs/quickstart.md) and [synthetic tutorials](docs/synthetic_examples.md)
|
|
73
|
+
- [Inputs](docs/input_tables.md), [configuration](docs/configuration.md),
|
|
74
|
+
[CLI](docs/cli.md) and [Python API](docs/api.rst)
|
|
75
|
+
- [Objectives/warning actions](docs/objectives.md), [evaluation](docs/evaluation.md),
|
|
76
|
+
[report privacy](docs/reporting.md) and [limitations](docs/limitations.md)
|
|
77
|
+
- [Contributing/testing](CONTRIBUTING.md), [changelog](CHANGELOG.md),
|
|
78
|
+
[security](SECURITY.md) and [release procedure](docs/release.md)
|
|
79
|
+
|
|
80
|
+
Build the full local site with
|
|
81
|
+
`python -m sphinx -W --keep-going -b html docs docs/_build/html` and open
|
|
82
|
+
`docs/_build/html/index.html`. Run
|
|
83
|
+
`python -m pytest -q --strict-markers --strict-config` for the test suite.
|
|
84
|
+
Actual stage evidence and unrun checks are recorded under `state/handoffs/`.
|
|
85
|
+
Local Windows and Linux WSL2 checks and the Python 3.11 direct-dependency floor
|
|
86
|
+
have been exercised; see [installation evidence](docs/installation.md).
|
|
87
|
+
macOS and hosted CI remain unverified until their recorded runs pass.
|
|
88
|
+
|
|
89
|
+
## License, support and citation
|
|
90
|
+
|
|
91
|
+
Copyright 2026 Alireza Emad. Released under the [BSD-3-Clause license](LICENSE).
|
|
92
|
+
Maintainer: Alireza Emad. For private security reports, use the approved contact in
|
|
93
|
+
[SECURITY.md](SECURITY.md); share only synthetic reproductions. No response-time
|
|
94
|
+
or long-term support commitment is claimed.
|
|
95
|
+
Citation metadata will be added only after verified authorship and release details;
|
|
96
|
+
no DOI or citation badge exists. [AI assistance](AI_ASSISTANCE.md) is recorded
|
|
97
|
+
honestly. Human walkthrough, external-user testing and acceptance remain pending.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# NeuroCVguard
|
|
2
|
+
|
|
3
|
+
Research-only Python software for checking whether supplied cohort identities,
|
|
4
|
+
partitions and evaluation procedures match an intended generalization claim.
|
|
5
|
+
It reads local CSV/TSV/JSON, checks participant/transitive dependence and requested
|
|
6
|
+
domain separation, describes acquisition–target association, generates checked
|
|
7
|
+
splits and runs a controlled participant-level logistic baseline with optional
|
|
8
|
+
nested C selection. Offline HTML/JSON reports retain incomplete coverage and
|
|
9
|
+
limitations. No account, GPU or runtime internet connection is needed.
|
|
10
|
+
|
|
11
|
+
It does not process MRI images, provide clinical advice, authenticate upstream
|
|
12
|
+
preprocessing, prove causal confounding or certify a study as leakage-free.
|
|
13
|
+
Repeated visits alone are not leakage; inspect actual membership and objective.
|
|
14
|
+
Unknown upstream preprocessing remains unassessable even with a correct Pipeline.
|
|
15
|
+
|
|
16
|
+
## Install and try
|
|
17
|
+
|
|
18
|
+
From an authorized local source checkout, create a dedicated Python environment.
|
|
19
|
+
Use `.venv/Scripts/python.exe` on Windows or `.venv/bin/python` on Linux/macOS
|
|
20
|
+
after `python -m venv .venv`. With that interpreter selected:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
python -m pip install -e ".[dev,docs]"
|
|
24
|
+
python -m neurocvguard demo --out local_outputs/demo
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Open `local_outputs/demo/report.html`. Inputs are fully synthetic, not patient
|
|
28
|
+
data. Choose a new output path or explicitly use `--overwrite`. Read warnings
|
|
29
|
+
beside coverage: association suggests reviewing acquisition imbalance, while
|
|
30
|
+
unknown preprocessing asks for evidence rather than a passing verdict.
|
|
31
|
+
|
|
32
|
+
**Private plans/evaluations are separate from projected reports.** Default
|
|
33
|
+
projection is not guaranteed anonymity; inspect artifacts before sharing.
|
|
34
|
+
Version: 0.1.0. See the [release procedure and evidence](docs/release.md) for
|
|
35
|
+
publication status and the actual verified installation/platform results.
|
|
36
|
+
|
|
37
|
+
## Documentation and development
|
|
38
|
+
|
|
39
|
+
- [Installation and troubleshooting](docs/installation.md)
|
|
40
|
+
- [Executable quickstart](docs/quickstart.md) and [synthetic tutorials](docs/synthetic_examples.md)
|
|
41
|
+
- [Inputs](docs/input_tables.md), [configuration](docs/configuration.md),
|
|
42
|
+
[CLI](docs/cli.md) and [Python API](docs/api.rst)
|
|
43
|
+
- [Objectives/warning actions](docs/objectives.md), [evaluation](docs/evaluation.md),
|
|
44
|
+
[report privacy](docs/reporting.md) and [limitations](docs/limitations.md)
|
|
45
|
+
- [Contributing/testing](CONTRIBUTING.md), [changelog](CHANGELOG.md),
|
|
46
|
+
[security](SECURITY.md) and [release procedure](docs/release.md)
|
|
47
|
+
|
|
48
|
+
Build the full local site with
|
|
49
|
+
`python -m sphinx -W --keep-going -b html docs docs/_build/html` and open
|
|
50
|
+
`docs/_build/html/index.html`. Run
|
|
51
|
+
`python -m pytest -q --strict-markers --strict-config` for the test suite.
|
|
52
|
+
Actual stage evidence and unrun checks are recorded under `state/handoffs/`.
|
|
53
|
+
Local Windows and Linux WSL2 checks and the Python 3.11 direct-dependency floor
|
|
54
|
+
have been exercised; see [installation evidence](docs/installation.md).
|
|
55
|
+
macOS and hosted CI remain unverified until their recorded runs pass.
|
|
56
|
+
|
|
57
|
+
## License, support and citation
|
|
58
|
+
|
|
59
|
+
Copyright 2026 Alireza Emad. Released under the [BSD-3-Clause license](LICENSE).
|
|
60
|
+
Maintainer: Alireza Emad. For private security reports, use the approved contact in
|
|
61
|
+
[SECURITY.md](SECURITY.md); share only synthetic reproductions. No response-time
|
|
62
|
+
or long-term support commitment is claimed.
|
|
63
|
+
Citation metadata will be added only after verified authorship and release details;
|
|
64
|
+
no DOI or citation badge exists. [AI assistance](AI_ASSISTANCE.md) is recorded
|
|
65
|
+
honestly. Human walkthrough, external-user testing and acceptance remain pending.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Map generated cohort TSV, audit and create participant-disjoint CV; open report.html.
|
|
2
|
+
|
|
3
|
+
Run: python examples/01_cohort_audit.py --out local_outputs/example-01
|
|
4
|
+
All inputs are created locally by the installed package; no patient data.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from neurocvguard.demo import example_main
|
|
8
|
+
|
|
9
|
+
if __name__ == "__main__":
|
|
10
|
+
raise SystemExit(example_main(1))
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Compare row-random diagnostic and participant-disjoint synthetic evaluations.
|
|
2
|
+
|
|
3
|
+
Run: python examples/02_repeated_observations.py --out local_outputs/example-02
|
|
4
|
+
The row-random score is invalid for unseen-participant generalization.
|
|
5
|
+
No universal ordering or causal leakage amount is claimed.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from neurocvguard.demo import example_main
|
|
9
|
+
|
|
10
|
+
if __name__ == "__main__":
|
|
11
|
+
raise SystemExit(example_main(2))
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Hold sites out with explicit class-associated domains; retain undefined fold metrics.
|
|
2
|
+
|
|
3
|
+
Run: python examples/03_site_held_out.py --out local_outputs/example-03
|
|
4
|
+
Association=1 is an explicit pedagogical mechanism, not a selected favorable seed.
|
|
5
|
+
Site/participant objectives differ; these tabular data are entirely fictitious.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from neurocvguard.demo import example_main
|
|
9
|
+
|
|
10
|
+
if __name__ == "__main__":
|
|
11
|
+
raise SystemExit(example_main(3))
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Compare unknown preprocessing with a fictitious global-PCA declaration.
|
|
2
|
+
|
|
3
|
+
Run: python examples/04_preprocessing_provenance.py --out local_outputs/example-04
|
|
4
|
+
Inspect report.html and unknown.report.html. No PCA actually runs; a declaration
|
|
5
|
+
cannot authenticate historical execution, and a Pipeline cannot repair it.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from neurocvguard.demo import example_main
|
|
9
|
+
|
|
10
|
+
if __name__ == "__main__":
|
|
11
|
+
raise SystemExit(example_main(4))
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Join reversed synthetic feature rows by scan_key with explicit role/feature names.
|
|
2
|
+
|
|
3
|
+
Run: python examples/05_explicit_feature_join.py --out local_outputs/example-05
|
|
4
|
+
The installed workflow writes local TSVs and uses load_cohort's keyed join.
|
|
5
|
+
No hidden notebook state, external dataset or row-order alignment is used.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from neurocvguard.demo import example_main
|
|
9
|
+
|
|
10
|
+
if __name__ == "__main__":
|
|
11
|
+
raise SystemExit(example_main(5))
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "neurocvguard"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Research-only cohort audits and independently checked split plans for neuroimaging ML"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
authors = [{name = "Alireza Emad"}]
|
|
12
|
+
maintainers = [{name = "Alireza Emad", email = "Alireza221177@gmail.com"}]
|
|
13
|
+
license = "BSD-3-Clause"
|
|
14
|
+
license-files = ["LICENSE"]
|
|
15
|
+
dependencies = ["numpy>=1.26.4", "pandas>=2.2.3", "scipy>=1.13.1", "scikit-learn>=1.5.2", "Jinja2>=3.1.6", "jsonschema>=4.23.0"]
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
Source = "https://github.com/Alireza2177/NeuroCVguard"
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
dev = ["pytest", "pytest-cov", "hypothesis", "ruff", "mypy", "build", "twine", "pandas-stubs", "types-jsonschema"]
|
|
22
|
+
docs = ["sphinx", "myst-parser", "sphinx-rtd-theme"]
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
neurocvguard = "neurocvguard.__main__:main"
|
|
26
|
+
|
|
27
|
+
[tool.hatch.version]
|
|
28
|
+
path = "src/neurocvguard/__init__.py"
|
|
29
|
+
|
|
30
|
+
[tool.hatch.build.targets.wheel]
|
|
31
|
+
packages = ["src/neurocvguard"]
|
|
32
|
+
|
|
33
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
34
|
+
"examples" = "neurocvguard/examples"
|
|
35
|
+
|
|
36
|
+
[tool.hatch.build.targets.sdist]
|
|
37
|
+
include = ["/src/neurocvguard", "/examples", "/pyproject.toml", "/README.md", "/LICENSE"]
|
|
38
|
+
|
|
39
|
+
[tool.pytest.ini_options]
|
|
40
|
+
testpaths = ["tests"]
|
|
41
|
+
addopts = "--strict-markers --strict-config"
|
|
42
|
+
|
|
43
|
+
[tool.coverage.run]
|
|
44
|
+
branch = true
|
|
45
|
+
source = ["neurocvguard"]
|
|
46
|
+
# CLI/tutorial tests launch real Python processes. Measure those paths as well.
|
|
47
|
+
patch = ["subprocess"]
|
|
48
|
+
|
|
49
|
+
[tool.ruff]
|
|
50
|
+
target-version = "py311"
|
|
51
|
+
line-length = 100
|
|
52
|
+
# The unchanged master builder predates S00. The repaired foundation validator
|
|
53
|
+
# and all new stage helpers now receive the ordinary project lint/format checks.
|
|
54
|
+
extend-exclude = ["tools/build_master.py", ".venv-install"]
|
|
55
|
+
|
|
56
|
+
[tool.ruff.lint]
|
|
57
|
+
select = ["E", "F", "I", "UP", "B"]
|
|
58
|
+
|
|
59
|
+
[tool.mypy]
|
|
60
|
+
python_version = "3.11"
|
|
61
|
+
strict = true
|
|
62
|
+
|
|
63
|
+
# sklearn does not ship typing metadata. Restrict this exception to its import
|
|
64
|
+
# boundary; NeuroCVguard's participant adapters and public functions remain strict.
|
|
65
|
+
[[tool.mypy.overrides]]
|
|
66
|
+
module = ["sklearn", "sklearn.*"]
|
|
67
|
+
ignore_missing_imports = true
|
|
68
|
+
|
|
69
|
+
# SciPy's installed contingency module has no typing metadata. Keep this
|
|
70
|
+
# exception at the external boundary; validate finite outputs and type our adapter.
|
|
71
|
+
[[tool.mypy.overrides]]
|
|
72
|
+
module = ["scipy.stats.contingency"]
|
|
73
|
+
ignore_missing_imports = true
|
|
74
|
+
|
|
75
|
+
[tool.neurocvguard.release]
|
|
76
|
+
proposed-license = "BSD-3-Clause"
|
|
77
|
+
license-status = "OWNER_CONFIRMED"
|
|
78
|
+
copyright-owner = "Alireza Emad"
|
|
79
|
+
maintainer = "Alireza Emad"
|
|
80
|
+
security-contact = "Alireza221177@gmail.com"
|
|
81
|
+
repository-url = "https://github.com/Alireza2177/NeuroCVguard"
|
|
82
|
+
# Selected project name; registry/publisher verification is release evidence.
|
|
83
|
+
package-namespace = "neurocvguard"
|
|
84
|
+
# Publication authorization is recorded in state/PROJECT_STATUS.json, outside
|
|
85
|
+
# immutable distribution metadata, and must name the reviewed artifacts.
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""Research-only local cohort/split checks, with lazy public API imports."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
import pandas as pd
|
|
10
|
+
|
|
11
|
+
from neurocvguard.config import AuditConfig
|
|
12
|
+
from neurocvguard.models import (
|
|
13
|
+
AuditReport,
|
|
14
|
+
Cohort,
|
|
15
|
+
ComparisonResult,
|
|
16
|
+
EvaluationResult,
|
|
17
|
+
SplitPlan,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
__version__ = "0.1.0"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def compare_designs(results: dict[str, EvaluationResult]) -> ComparisonResult:
|
|
24
|
+
"""Compare private records descriptively; see neurocvguard.comparison.compare_designs."""
|
|
25
|
+
from neurocvguard.comparison import compare_designs as compare
|
|
26
|
+
|
|
27
|
+
return compare(results)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def evaluate_baseline(cohort: Cohort, plan: SplitPlan, *, config: AuditConfig) -> EvaluationResult:
|
|
31
|
+
"""Run classification with optional nested C selection; see evaluation.evaluate_baseline."""
|
|
32
|
+
from neurocvguard.evaluation import evaluate_baseline as evaluate
|
|
33
|
+
|
|
34
|
+
return evaluate(cohort, plan, config=config)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def load_config(path: str | Path) -> AuditConfig:
|
|
38
|
+
"""Read strict local JSON settings; see neurocvguard.config.load_config."""
|
|
39
|
+
from neurocvguard.config import load_config as load
|
|
40
|
+
|
|
41
|
+
return load(path)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def load_cohort(
|
|
45
|
+
metadata: str | Path | pd.DataFrame,
|
|
46
|
+
*,
|
|
47
|
+
config: AuditConfig,
|
|
48
|
+
features: str | Path | pd.DataFrame | None = None,
|
|
49
|
+
) -> Cohort:
|
|
50
|
+
"""Load strict local tables; see neurocvguard.io.load_cohort for the input contract."""
|
|
51
|
+
from neurocvguard.io import load_cohort as load
|
|
52
|
+
|
|
53
|
+
return load(metadata, config=config, features=features)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def audit_cohort(
|
|
57
|
+
cohort: Cohort,
|
|
58
|
+
*,
|
|
59
|
+
config: AuditConfig,
|
|
60
|
+
ledger: dict[str, object] | None = None,
|
|
61
|
+
) -> AuditReport:
|
|
62
|
+
"""Audit constructed cohort data and declarations without fitting or writing.
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
cohort : Cohort
|
|
67
|
+
Explicitly keyed in-memory metadata and optional aligned features.
|
|
68
|
+
config : AuditConfig
|
|
69
|
+
Matching roles and diagnostic settings.
|
|
70
|
+
ledger : dict or None, optional
|
|
71
|
+
Strict user-declaration contract; it cannot authenticate historical fitting.
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
AuditReport
|
|
76
|
+
Scoped findings and persistent upstream limitations; see neurocvguard.audit.
|
|
77
|
+
|
|
78
|
+
Examples
|
|
79
|
+
--------
|
|
80
|
+
``audit_cohort(cohort, config=config).to_dict()`` returns the public projection.
|
|
81
|
+
"""
|
|
82
|
+
from neurocvguard.audit import audit_cohort as audit
|
|
83
|
+
|
|
84
|
+
return audit(cohort, config=config, ledger=ledger)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def make_splits(cohort: Cohort, *, config: AuditConfig) -> SplitPlan:
|
|
88
|
+
"""Generate and independently audit deterministic outer assignments.
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
cohort : Cohort
|
|
93
|
+
Constructed keyed cohort with constant targets and complete protected fields.
|
|
94
|
+
config : AuditConfig
|
|
95
|
+
Explicit supported objective, splitter settings and seed.
|
|
96
|
+
|
|
97
|
+
Returns
|
|
98
|
+
-------
|
|
99
|
+
SplitPlan
|
|
100
|
+
Sensitive operational plan with separate in-memory generation_report.
|
|
101
|
+
|
|
102
|
+
Raises
|
|
103
|
+
------
|
|
104
|
+
PlanningError, UnsupportedDesignError, ConfigurationError, InputValidationError
|
|
105
|
+
Unsupported inputs, infeasible design or failed independent audit.
|
|
106
|
+
|
|
107
|
+
Examples
|
|
108
|
+
--------
|
|
109
|
+
``plan = make_splits(cohort, config=config)`` writes no files and fits no model.
|
|
110
|
+
"""
|
|
111
|
+
from neurocvguard.splitting import make_splits as make
|
|
112
|
+
|
|
113
|
+
return make(cohort, config=config)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def load_split_plan(
|
|
117
|
+
source: str | Path | dict[str, object],
|
|
118
|
+
*,
|
|
119
|
+
cohort: Cohort,
|
|
120
|
+
config: AuditConfig,
|
|
121
|
+
) -> SplitPlan:
|
|
122
|
+
"""Load and bind supplied assignments; see neurocvguard.io.load_split_plan.
|
|
123
|
+
|
|
124
|
+
Parameters
|
|
125
|
+
----------
|
|
126
|
+
source : str, Path or dict
|
|
127
|
+
Local JSON/TSV path or schema-shaped plan dictionary.
|
|
128
|
+
cohort : Cohort
|
|
129
|
+
Already validated, explicitly keyed in-memory data.
|
|
130
|
+
config : AuditConfig
|
|
131
|
+
Matching objective/roles and input limits.
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
SplitPlan
|
|
136
|
+
Bound sensitive operational plan, not a design approval.
|
|
137
|
+
|
|
138
|
+
Raises
|
|
139
|
+
------
|
|
140
|
+
InputValidationError, ConfigurationError, SplitValidationError
|
|
141
|
+
Invalid source structure, identities, mapping, objective or digest.
|
|
142
|
+
|
|
143
|
+
Examples
|
|
144
|
+
--------
|
|
145
|
+
``load_split_plan("plan.json", cohort=cohort, config=config)``
|
|
146
|
+
"""
|
|
147
|
+
from neurocvguard.io import load_split_plan as load
|
|
148
|
+
|
|
149
|
+
return load(source, cohort=cohort, config=config)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def audit_splits(cohort: Cohort, plan: SplitPlan, *, config: AuditConfig) -> AuditReport:
|
|
153
|
+
"""Audit supplied partitions; see neurocvguard.audit.audit_splits.
|
|
154
|
+
|
|
155
|
+
Parameters
|
|
156
|
+
----------
|
|
157
|
+
cohort : Cohort
|
|
158
|
+
Validated in-memory input.
|
|
159
|
+
plan : SplitPlan
|
|
160
|
+
Supplied structural plan record.
|
|
161
|
+
config : AuditConfig
|
|
162
|
+
Matching roles/objective; no diagnostic relaxation of invariants.
|
|
163
|
+
|
|
164
|
+
Returns
|
|
165
|
+
-------
|
|
166
|
+
AuditReport
|
|
167
|
+
Scoped checks and separate objective/complete-CV eligibility flags.
|
|
168
|
+
|
|
169
|
+
Raises
|
|
170
|
+
------
|
|
171
|
+
InputValidationError, ConfigurationError, SplitValidationError
|
|
172
|
+
Corrupt identities or incompatible objective, mapping or digest.
|
|
173
|
+
|
|
174
|
+
Examples
|
|
175
|
+
--------
|
|
176
|
+
``audit_splits(cohort, plan, config=config).to_dict()`` is privacy-projected.
|
|
177
|
+
"""
|
|
178
|
+
from neurocvguard.audit import audit_splits as audit
|
|
179
|
+
|
|
180
|
+
return audit(cohort, plan, config=config)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def write_report(
|
|
184
|
+
result: AuditReport | EvaluationResult | ComparisonResult,
|
|
185
|
+
*,
|
|
186
|
+
output_dir: str | Path,
|
|
187
|
+
sensitive_details: bool = False,
|
|
188
|
+
overwrite: bool = False,
|
|
189
|
+
) -> dict[str, Path]:
|
|
190
|
+
"""Write local projected JSON/HTML; see neurocvguard.reporting.write_report.
|
|
191
|
+
|
|
192
|
+
Safe defaults omit private evidence and refuse overwriting. Sensitive output
|
|
193
|
+
requires an explicit argument; no operational evaluation or split file is written.
|
|
194
|
+
"""
|
|
195
|
+
from neurocvguard.reporting import write_report as write
|
|
196
|
+
|
|
197
|
+
return write(
|
|
198
|
+
result, output_dir=output_dir, sensitive_details=sensitive_details, overwrite=overwrite
|
|
199
|
+
)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Narrow evaluation exception; audit findings and objective validity never change."""
|
|
2
|
+
|
|
3
|
+
from neurocvguard.config import AuditConfig, Objective
|
|
4
|
+
from neurocvguard.errors import UnsupportedDesignError
|
|
5
|
+
from neurocvguard.models import AuditReport, CheckStatus
|
|
6
|
+
|
|
7
|
+
DIAGNOSTIC_LIMITATION = (
|
|
8
|
+
"Diagnostic only; valid_for_objective=false. Do not report as evidence for "
|
|
9
|
+
"unseen-participant generalization. Participant-level aggregation does not remove "
|
|
10
|
+
"the training leakage already present. Every observation has one held-out prediction; "
|
|
11
|
+
"all held-out observation probabilities are averaged once per participant."
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def validate_diagnostic_request(config: AuditConfig) -> None:
|
|
16
|
+
if config.evaluation.diagnostic_allow_subject_overlap and (
|
|
17
|
+
config.study.objective != Objective.UNSEEN_PARTICIPANT or config.columns.independence
|
|
18
|
+
):
|
|
19
|
+
raise UnsupportedDesignError(
|
|
20
|
+
"Diagnostic overlap requires unseen_participant and no extra independence columns. "
|
|
21
|
+
"It cannot waive family, site or phase constraints."
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def evaluation_eligibility(audit: AuditReport, config: AuditConfig) -> bool:
|
|
26
|
+
"""Return whether the narrow outer-overlap exception was actually necessary.
|
|
27
|
+
|
|
28
|
+
Inner participant/component overlap is never waived, including during tuning.
|
|
29
|
+
Without extra dependence columns, component overlap is participant overlap.
|
|
30
|
+
All original audit failures remain in the retained report.
|
|
31
|
+
"""
|
|
32
|
+
validate_diagnostic_request(config)
|
|
33
|
+
summary = next(c for c in audit.checks if c.scope.get("field") == "split_summary")
|
|
34
|
+
if summary.evidence.get("evaluation_permitted") is True:
|
|
35
|
+
return False
|
|
36
|
+
required = {f"NCG-SPLIT-{n:03d}" for n in (1, 2, 3, 4, 5, 7, 8, 9, 10, 11)}
|
|
37
|
+
failures = [
|
|
38
|
+
c
|
|
39
|
+
for c in audit.checks
|
|
40
|
+
if c.rule_id in required and c.status in (CheckStatus.FAIL, CheckStatus.NOT_ASSESSABLE)
|
|
41
|
+
]
|
|
42
|
+
if (
|
|
43
|
+
config.evaluation.diagnostic_allow_subject_overlap
|
|
44
|
+
and summary.evidence.get("complete_cv") is True
|
|
45
|
+
and failures
|
|
46
|
+
and all(
|
|
47
|
+
c.status == CheckStatus.FAIL
|
|
48
|
+
and c.rule_id in {"NCG-SPLIT-002", "NCG-SPLIT-003"}
|
|
49
|
+
and c.scope.get("inner_fold_id") is None
|
|
50
|
+
for c in failures
|
|
51
|
+
)
|
|
52
|
+
):
|
|
53
|
+
return True
|
|
54
|
+
raise UnsupportedDesignError(
|
|
55
|
+
"Evaluation requires complete, protected and objective-valid partitions with all "
|
|
56
|
+
"training classes. Diagnostic permission cannot waive other violations; run audit."
|
|
57
|
+
)
|