From 17e4ffef8e612cda1bb25e54596398132f652a27 Mon Sep 17 00:00:00 2001 From: Chase Mateusiak Date: Tue, 30 Apr 2024 19:57:02 -0500 Subject: [PATCH] deploy as 0.0.1 (#77) * fixing readme badge order; adding codecove badge (#5) resolves #4 * removing plural from gene_populations; resolves #6 (#7) * Fix binding effects (#27) * Bump codecov/codecov-action from 3 to 4 (#13) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update torch requirement from ^1.0.2 to ^2.2.0 (#12) Updates the requirements on [torch](https://github.com/pytorch/pytorch) to permit the latest version. - [Release notes](https://github.com/pytorch/pytorch/releases) - [Changelog](https://github.com/pytorch/pytorch/blob/main/RELEASE.md) - [Commits](https://github.com/pytorch/pytorch/compare/v1.1.0a0...v2.2.0) --- updated-dependencies: - dependency-name: torch dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ben Mueller <57322122+BenMueller1@users.noreply.github.com> * Bump actions/checkout from 2 to 4 (#2) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Bump actions/setup-python from 2 to 5 (#1) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Update black requirement from ^23.12.1 to ^24.1.1 (#9) Updates the requirements on [black](https://github.com/psf/black) to permit the latest version. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.12.1...24.1.1) --- updated-dependencies: - dependency-name: black dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Bump griffe from 0.39.1 to 0.40.0 (#11) Bumps [griffe](https://github.com/mkdocstrings/griffe) from 0.39.1 to 0.40.0. - [Release notes](https://github.com/mkdocstrings/griffe/releases) - [Changelog](https://github.com/mkdocstrings/griffe/blob/main/CHANGELOG.md) - [Commits](https://github.com/mkdocstrings/griffe/compare/0.39.1...0.40.0) --- updated-dependencies: - dependency-name: griffe dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ben Mueller <57322122+BenMueller1@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Bump mkdocs-material from 9.5.5 to 9.5.7 (#14) Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.5 to 9.5.7. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.5...9.5.7) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * updating pytest CI to include multiple OS * fixing readme badge order; adding codecove badge (#5) resolves #4 * removing plural from gene_populations; resolves #6 (#7) * major re-write of probability_models and associated documentation * fixing typo in generate_pert_effects docstring; adding error handling on Callable * removing python 3.10 from CI --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ben Mueller <57322122+BenMueller1@users.noreply.github.com> * fix torch install issue for macs, closes #18 (#19) * Simple linear model, Synthetic data DataLoader, a couple scripts, lots of setup (#31) * installed lightning and outlined file structure * forgot to outline testing files * created incredibly basic linear model * made basic outline for data loader * outlined idea for processin dataset * wrote script to run simple model experiment * fixed bug in gitignore that caused tmp files to not be ignored * simple model is working * added basic script to load in model checkpoint and print off params and hyperparams * added logic for checkpointing * added support for logging with tensorboard * now supports csv and tensorboard logging * added typing to simple model * added type hints to dataLoader * added type hinting to simple_model_synthetic_data.py * rewrote to use new data generation functions * added assertions for type checking and valid ranges for params / inputs * docstrings added * added new docs to mkdocs * added tests for simple model and synthetic data loader * now passes all pre-commit checks * addressing all comments on PR * add all log folders to gitignore * formatting * Adding sanity check to tutorial (#38) * Bump codecov/codecov-action from 3 to 4 (#13) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update torch requirement from ^1.0.2 to ^2.2.0 (#12) Updates the requirements on [torch](https://github.com/pytorch/pytorch) to permit the latest version. - [Release notes](https://github.com/pytorch/pytorch/releases) - [Changelog](https://github.com/pytorch/pytorch/blob/main/RELEASE.md) - [Commits](https://github.com/pytorch/pytorch/compare/v1.1.0a0...v2.2.0) --- updated-dependencies: - dependency-name: torch dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ben Mueller <57322122+BenMueller1@users.noreply.github.com> * Bump actions/checkout from 2 to 4 (#2) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Bump actions/setup-python from 2 to 5 (#1) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Update black requirement from ^23.12.1 to ^24.1.1 (#9) Updates the requirements on [black](https://github.com/psf/black) to permit the latest version. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.12.1...24.1.1) --- updated-dependencies: - dependency-name: black dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Bump griffe from 0.39.1 to 0.40.0 (#11) Bumps [griffe](https://github.com/mkdocstrings/griffe) from 0.39.1 to 0.40.0. - [Release notes](https://github.com/mkdocstrings/griffe/releases) - [Changelog](https://github.com/mkdocstrings/griffe/blob/main/CHANGELOG.md) - [Commits](https://github.com/mkdocstrings/griffe/compare/0.39.1...0.40.0) --- updated-dependencies: - dependency-name: griffe dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ben Mueller <57322122+BenMueller1@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Bump mkdocs-material from 9.5.5 to 9.5.7 (#14) Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.5 to 9.5.7. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.5...9.5.7) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * updating pytest CI to include multiple OS * fixing readme badge order; adding codecove badge (#5) resolves #4 * removing plural from gene_populations; resolves #6 (#7) * correcting error in data generation regarding tf indexing * adding another cell to the tutorial to show how to use the function that relates binding strength to purturbation effect * improved plot for the tf effect on perturbation * changing colors for aesthetics * changing colors for aesthetics x2 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ben Mueller <57322122+BenMueller1@users.noreply.github.com> * Fixing (#68) * need to update pre commit config since were now using python3.11 * added in current generate_data file, commented out test_generate_data, also had to add relation_classes * fix lint issue * attempt to fix by downgrading default language version * new attempt to fix by changing specified torch version * try limiting to install torch beneath 2.3.0 * Adding in files (#70) * need to update pre commit config since were now using python3.11 * added in current generate_data file, commented out test_generate_data, also had to add relation_classes * fix lint issue * attempt to fix by downgrading default language version * new attempt to fix by changing specified torch version * try limiting to install torch beneath 2.3.0 * uncomment tests * pyproject now matches * fix indent error in tests * added the dataLoaders * add in models * formatting * linting * Rewrite generate_in_silico_data.ipynb to match new methods we are using, also delete old unused mean adjustment function (#73) * need to update pre commit config since were now using python3.11 * added in current generate_data file, commented out test_generate_data, also had to add relation_classes * fix lint issue * attempt to fix by downgrading default language version * new attempt to fix by changing specified torch version * try limiting to install torch beneath 2.3.0 * uncomment tests * pyproject now matches * fix indent error in tests * added the dataLoaders * add in models * formatting * linting * delete old unused adjustment function * rewrite in silico data generation tutorial notebook to match current methods * cleaning up generate_perturbation_effects * passing precommit * Cleaning up notebooks (#74) * need to update pre commit config since were now using python3.11 * added in current generate_data file, commented out test_generate_data, also had to add relation_classes * fix lint issue * attempt to fix by downgrading default language version * new attempt to fix by changing specified torch version * try limiting to install torch beneath 2.3.0 * uncomment tests * pyproject now matches * fix indent error in tests * added the dataLoaders * add in models * formatting * linting * delete old unused adjustment function * rewrite in silico data generation tutorial notebook to match current methods * cleaning up generate_perturbation_effects * passing precommit * cleaned up hyperparameter sweep notebook and added better explanations * added dataset directory and logs to gitignore * added notebook for visualizing data generation methods * finished adding in data visualization experiment * renamed loss to mse in simpleModel * finished the data generation method experiment notebook, added in experiment comparing model performance * added and cleaned up the testing_model_metrics notebook * real data loader choice of perturbation effect dataset is now a param you can pass in * added lightning crash course notebook * linting * removing experiments from src dir; updating docs md and index files (#76) * removing experiments from src dir; updating docs md and index files * fixing stupid mkdocs docs text added to readme * setting version to 0.0.1 * fixing readme badge order; adding codecove badge (#5) resolves #4 * removing plural from gene_populations; resolves #6 (#7) * Fix binding effects (#27) * Bump codecov/codecov-action from 3 to 4 (#13) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update torch requirement from ^1.0.2 to ^2.2.0 (#12) Updates the requirements on [torch](https://github.com/pytorch/pytorch) to permit the latest version. - [Release notes](https://github.com/pytorch/pytorch/releases) - [Changelog](https://github.com/pytorch/pytorch/blob/main/RELEASE.md) - [Commits](https://github.com/pytorch/pytorch/compare/v1.1.0a0...v2.2.0) --- updated-dependencies: - dependency-name: torch dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ben Mueller <57322122+BenMueller1@users.noreply.github.com> * Bump actions/checkout from 2 to 4 (#2) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Bump actions/setup-python from 2 to 5 (#1) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Update black requirement from ^23.12.1 to ^24.1.1 (#9) Updates the requirements on [black](https://github.com/psf/black) to permit the latest version. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.12.1...24.1.1) --- updated-dependencies: - dependency-name: black dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Bump griffe from 0.39.1 to 0.40.0 (#11) Bumps [griffe](https://github.com/mkdocstrings/griffe) from 0.39.1 to 0.40.0. - [Release notes](https://github.com/mkdocstrings/griffe/releases) - [Changelog](https://github.com/mkdocstrings/griffe/blob/main/CHANGELOG.md) - [Commits](https://github.com/mkdocstrings/griffe/compare/0.39.1...0.40.0) --- updated-dependencies: - dependency-name: griffe dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ben Mueller <57322122+BenMueller1@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Bump mkdocs-material from 9.5.5 to 9.5.7 (#14) Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.5 to 9.5.7. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.5...9.5.7) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * updating pytest CI to include multiple OS * fixing readme badge order; adding codecove badge (#5) resolves #4 * removing plural from gene_populations; resolves #6 (#7) * major re-write of probability_models and associated documentation * fixing typo in generate_pert_effects docstring; adding error handling on Callable * removing python 3.10 from CI --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ben Mueller <57322122+BenMueller1@users.noreply.github.com> * fix torch install issue for macs, closes #18 (#19) * Simple linear model, Synthetic data DataLoader, a couple scripts, lots of setup (#31) * installed lightning and outlined file structure * forgot to outline testing files * created incredibly basic linear model * made basic outline for data loader * outlined idea for processin dataset * wrote script to run simple model experiment * fixed bug in gitignore that caused tmp files to not be ignored * simple model is working * added basic script to load in model checkpoint and print off params and hyperparams * added logic for checkpointing * added support for logging with tensorboard * now supports csv and tensorboard logging * added typing to simple model * added type hints to dataLoader * added type hinting to simple_model_synthetic_data.py * rewrote to use new data generation functions * added assertions for type checking and valid ranges for params / inputs * docstrings added * added new docs to mkdocs * added tests for simple model and synthetic data loader * now passes all pre-commit checks * addressing all comments on PR * add all log folders to gitignore * formatting * Adding sanity check to tutorial (#38) * Bump codecov/codecov-action from 3 to 4 (#13) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update torch requirement from ^1.0.2 to ^2.2.0 (#12) Updates the requirements on [torch](https://github.com/pytorch/pytorch) to permit the latest version. - [Release notes](https://github.com/pytorch/pytorch/releases) - [Changelog](https://github.com/pytorch/pytorch/blob/main/RELEASE.md) - [Commits](https://github.com/pytorch/pytorch/compare/v1.1.0a0...v2.2.0) --- updated-dependencies: - dependency-name: torch dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ben Mueller <57322122+BenMueller1@users.noreply.github.com> * Bump actions/checkout from 2 to 4 (#2) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Bump actions/setup-python from 2 to 5 (#1) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Update black requirement from ^23.12.1 to ^24.1.1 (#9) Updates the requirements on [black](https://github.com/psf/black) to permit the latest version. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.12.1...24.1.1) --- updated-dependencies: - dependency-name: black dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Bump griffe from 0.39.1 to 0.40.0 (#11) Bumps [griffe](https://github.com/mkdocstrings/griffe) from 0.39.1 to 0.40.0. - [Release notes](https://github.com/mkdocstrings/griffe/releases) - [Changelog](https://github.com/mkdocstrings/griffe/blob/main/CHANGELOG.md) - [Commits](https://github.com/mkdocstrings/griffe/compare/0.39.1...0.40.0) --- updated-dependencies: - dependency-name: griffe dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ben Mueller <57322122+BenMueller1@users.noreply.github.com> Co-authored-by: Chase Mateusiak * Bump mkdocs-material from 9.5.5 to 9.5.7 (#14) Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.5 to 9.5.7. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.5...9.5.7) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chase Mateusiak * updating pytest CI to include multiple OS * fixing readme badge order; adding codecove badge (#5) resolves #4 * removing plural from gene_populations; resolves #6 (#7) * correcting error in data generation regarding tf indexing * adding another cell to the tutorial to show how to use the function that relates binding strength to purturbation effect * improved plot for the tf effect on perturbation * changing colors for aesthetics * changing colors for aesthetics x2 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ben Mueller <57322122+BenMueller1@users.noreply.github.com> * Fixing (#68) * need to update pre commit config since were now using python3.11 * added in current generate_data file, commented out test_generate_data, also had to add relation_classes * fix lint issue * attempt to fix by downgrading default language version * new attempt to fix by changing specified torch version * try limiting to install torch beneath 2.3.0 * Adding in files (#70) * need to update pre commit config since were now using python3.11 * added in current generate_data file, commented out test_generate_data, also had to add relation_classes * fix lint issue * attempt to fix by downgrading default language version * new attempt to fix by changing specified torch version * try limiting to install torch beneath 2.3.0 * uncomment tests * pyproject now matches * fix indent error in tests * added the dataLoaders * add in models * formatting * linting * Rewrite generate_in_silico_data.ipynb to match new methods we are using, also delete old unused mean adjustment function (#73) * need to update pre commit config since were now using python3.11 * added in current generate_data file, commented out test_generate_data, also had to add relation_classes * fix lint issue * attempt to fix by downgrading default language version * new attempt to fix by changing specified torch version * try limiting to install torch beneath 2.3.0 * uncomment tests * pyproject now matches * fix indent error in tests * added the dataLoaders * add in models * formatting * linting * delete old unused adjustment function * rewrite in silico data generation tutorial notebook to match current methods * cleaning up generate_perturbation_effects * passing precommit * Cleaning up notebooks (#74) * need to update pre commit config since were now using python3.11 * added in current generate_data file, commented out test_generate_data, also had to add relation_classes * fix lint issue * attempt to fix by downgrading default language version * new attempt to fix by changing specified torch version * try limiting to install torch beneath 2.3.0 * uncomment tests * pyproject now matches * fix indent error in tests * added the dataLoaders * add in models * formatting * linting * delete old unused adjustment function * rewrite in silico data generation tutorial notebook to match current methods * cleaning up generate_perturbation_effects * passing precommit * cleaned up hyperparameter sweep notebook and added better explanations * added dataset directory and logs to gitignore * added notebook for visualizing data generation methods * finished adding in data visualization experiment * renamed loss to mse in simpleModel * finished the data generation method experiment notebook, added in experiment comparing model performance * added and cleaned up the testing_model_metrics notebook * real data loader choice of perturbation effect dataset is now a param you can pass in * added lightning crash course notebook * linting * removing experiments from src dir; updating docs md and index files (#76) * removing experiments from src dir; updating docs md and index files * fixing stupid mkdocs docs text added to readme * setting version to 0.0.1 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ben Mueller <57322122+BenMueller1@users.noreply.github.com> --- .github/workflows/ci.yml | 26 +- .gitignore | 14 +- .pre-commit-config.yaml | 4 +- .vscode/settings.json | 9 +- README.md | 35 +- docs/data_loaders/real_data_loader.md | 1 + docs/data_loaders/synthetic_data_loader.md | 1 + docs/index.md | 95 ++- docs/ml_models/customizable_model.md | 1 + docs/ml_models/metrics_compute_nrmse.md | 1 + docs/ml_models/metrics_smse.md | 1 + docs/ml_models/simple_model.md | 1 + docs/probability_models/GenePopulation.md | 1 + ...perturbation_effect_adjustment_function.md | 1 + .../generate_perturbation_binding_data.md | 1 - ...justment_function_with_tf_relationships.md | 1 + ...ion_with_tf_relationships_boolean_logic.md | 1 + docs/probability_models/relation_classes.md | 1 + docs/tutorials/generate_in_silico_data.ipynb | 782 +++++++++++++----- docs/tutorials/hyperparameter_sweep.ipynb | 278 +++++++ docs/tutorials/lightning_crash_course.ipynb | 232 ++++++ docs/tutorials/testing_model_metrics.ipynb | 353 ++++++++ ..._and_testing_data_generation_methods.ipynb | 631 ++++++++++++++ experiments/inspect_simple_model.py | 60 ++ experiments/simple_model_synthetic_data.py | 161 ++++ mkdocs.yml | 34 +- pyproject.toml | 19 +- yeastdnnexplorer/data_loaders/__init__.py | 0 .../data_loaders/real_data_loader.py | 329 ++++++++ .../data_loaders/synthetic_data_loader.py | 322 ++++++++ yeastdnnexplorer/ml_models/__init__.py | 0 .../ml_models/customizable_model.py | 246 ++++++ yeastdnnexplorer/ml_models/metrics.py | 75 ++ yeastdnnexplorer/ml_models/simple_model.py | 147 ++++ .../probability_models/generate_data.py | 725 +++++++++++----- .../probability_models/relation_classes.py | 93 +++ .../tests/data_loaders/__init__.py | 0 .../test_synthetic_data_loader.py | 17 + yeastdnnexplorer/tests/ml_models/__init__.py | 0 .../tests/ml_models/test_simple_model.py | 36 + .../probability_models/test_generate_data.py | 276 +++---- 41 files changed, 4392 insertions(+), 619 deletions(-) create mode 100644 docs/data_loaders/real_data_loader.md create mode 100644 docs/data_loaders/synthetic_data_loader.md create mode 100644 docs/ml_models/customizable_model.md create mode 100644 docs/ml_models/metrics_compute_nrmse.md create mode 100644 docs/ml_models/metrics_smse.md create mode 100644 docs/ml_models/simple_model.md create mode 100644 docs/probability_models/GenePopulation.md create mode 100644 docs/probability_models/default_perturbation_effect_adjustment_function.md delete mode 100644 docs/probability_models/generate_perturbation_binding_data.md create mode 100644 docs/probability_models/perturbation_effect_adjustment_function_with_tf_relationships.md create mode 100644 docs/probability_models/perturbation_effect_adjustment_function_with_tf_relationships_boolean_logic.md create mode 100644 docs/probability_models/relation_classes.md create mode 100644 docs/tutorials/hyperparameter_sweep.ipynb create mode 100644 docs/tutorials/lightning_crash_course.ipynb create mode 100644 docs/tutorials/testing_model_metrics.ipynb create mode 100644 docs/tutorials/visualizing_and_testing_data_generation_methods.ipynb create mode 100644 experiments/inspect_simple_model.py create mode 100644 experiments/simple_model_synthetic_data.py create mode 100644 yeastdnnexplorer/data_loaders/__init__.py create mode 100644 yeastdnnexplorer/data_loaders/real_data_loader.py create mode 100644 yeastdnnexplorer/data_loaders/synthetic_data_loader.py create mode 100644 yeastdnnexplorer/ml_models/__init__.py create mode 100644 yeastdnnexplorer/ml_models/customizable_model.py create mode 100644 yeastdnnexplorer/ml_models/metrics.py create mode 100644 yeastdnnexplorer/ml_models/simple_model.py create mode 100644 yeastdnnexplorer/probability_models/relation_classes.py create mode 100644 yeastdnnexplorer/tests/data_loaders/__init__.py create mode 100644 yeastdnnexplorer/tests/data_loaders/test_synthetic_data_loader.py create mode 100644 yeastdnnexplorer/tests/ml_models/__init__.py create mode 100644 yeastdnnexplorer/tests/ml_models/test_simple_model.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed141b9..c6c627b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,12 +29,25 @@ jobs: uses: pre-commit/action@v3.0.0 pytest: - runs-on: ubuntu-latest + strategy: + matrix: + # see https://github.com/actions/runner-images + os: + [ + ubuntu-22.04, + ubuntu-20.04, + macos-13, + macos-12, + windows-2022, + windows-2019, + ] + python-version: ["3.11"] + runs-on: ${{ matrix.os }} steps: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: ${{ matrix.python-version }} - name: Checkout Code Repository uses: actions/checkout@v4 @@ -45,13 +58,8 @@ jobs: pip install poetry poetry install - - name: Run tests and collect coverage - run: poetry run pytest --cov=./ --cov-report=xml - - name: Upload coverage reports to Codecov with GitHub Action - uses: codecov/codecov-action@v4 - - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v4 + if: matrix.os == 'ubuntu-20.04' + uses: codecov/codecov-action@v3 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 29fc722..0bc6f11 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,11 @@ +# Dataset directory +data/ + +# logs +logs/ + # local tmp files -./tmp/* +tmp/* # But do not ignore README.md in the tmp directory !/tmp/README.md @@ -31,6 +37,10 @@ share/python-wheels/ *.egg MANIFEST +# ignore any log folders anywhere in repo +**/logs/ +**/lightning_logs/ + # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. @@ -157,6 +167,8 @@ dmypy.json # Cython debug symbols cython_debug/ +logs + # PyCharm # JetBrains specific template is maintained in a separate JetBrains.gitignore that can # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0e8ab55..9ee5fcd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,8 +1,8 @@ -exclude: '^docs/|devcontainer.json' +exclude: "^docs/|devcontainer.json" default_stages: [commit] default_language_version: - python: python3.10 + python: python3.11 repos: - repo: https://github.com/pre-commit/pre-commit-hooks diff --git a/.vscode/settings.json b/.vscode/settings.json index 870f1a8..d3bcb7a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,12 +1,19 @@ { "editor.formatOnSave": true, "cSpell.words": [ + "arange", "dtype", + "ndim", "pval", "pvalues", "randperm" ], "[markdown]": { "editor.formatOnSave": false - } + }, + "python.testing.pytestArgs": [ + "." + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true } diff --git a/README.md b/README.md index fdf25a8..5324e89 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,26 @@ # yeastdnnexplorer -[![gh-pages](https://github.com/BrentLab/yeastdnnexplorer/actions/workflows/docs.yml/badge.svg)](https://github.com/BrentLab/yeastdnnexplorer/actions/workflows/docs.yml) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![style](https://img.shields.io/badge/%20style-sphinx-0a507a.svg)](https://www.sphinx-doc.org/en/master/usage/index.html) [![Pytest](https://github.com/BrentLab/yeastdnnexplorer/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/BrentLab/yeastdnnexplorer/actions/workflows/ci.yml) +[![codecov](https://codecov.io/gh/BrentLab/yeastdnnexplorer/graph/badge.svg?token=D2AB7IUY7F)](https://codecov.io/gh/BrentLab/yeastdnnexplorer) [![gh-pages](https://github.com/BrentLab/yeastdnnexplorer/actions/workflows/docs.yml/badge.svg)](https://github.com/BrentLab/yeastdnnexplorer/actions/workflows/docs.yml) ## Documentation -See [here]() for more complete documentation +See [here](https://brentlab.github.io/yeastdnnexplorer/) for more complete documentation ## Installation -(no user installation instructions yet) +This repo has not yet been added to PyPI. See the developer installation below. ### Development 1. git clone the repo 1. `cd` into the local version of the repo -1. choose one (or more) of the following +1. choose one (or more) of the following (only poetry currently supported) -#### vscode + #### poetry @@ -50,7 +51,7 @@ After cloning and `cd`ing into the repo, you can install the dependencies with: poetry install ``` -#### docker compose + + +#### mkdocs + +The documentation is build with mkdocs: + +##### Commands + +* `mkdocs new [dir-name]` - Create a new project. +* `mkdocs serve` - Start the live-reloading docs server. +* `mkdocs build` - Build the documentation site. +* `mkdocs -h` - Print help message and exit. + +##### Project layout + +* mkdocs.yml # The configuration file. +* docs/ + * index.md # The documentation homepage. + * ... # Other markdown pages, images and other files. diff --git a/docs/data_loaders/real_data_loader.md b/docs/data_loaders/real_data_loader.md new file mode 100644 index 0000000..32faf2f --- /dev/null +++ b/docs/data_loaders/real_data_loader.md @@ -0,0 +1 @@ +::: yeastdnnexplorer.data_loaders.real_data_loader.RealDataLoader diff --git a/docs/data_loaders/synthetic_data_loader.md b/docs/data_loaders/synthetic_data_loader.md new file mode 100644 index 0000000..7df8923 --- /dev/null +++ b/docs/data_loaders/synthetic_data_loader.md @@ -0,0 +1 @@ +::: yeastdnnexplorer.data_loaders.synthetic_data_loader.SyntheticDataLoader diff --git a/docs/index.md b/docs/index.md index 000ea34..833fa27 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,102 @@ -# Welcome to MkDocs +# yeastdnnexplorer -For full documentation visit [mkdocs.org](https://www.mkdocs.org). +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![style](https://img.shields.io/badge/%20style-sphinx-0a507a.svg)](https://www.sphinx-doc.org/en/master/usage/index.html) +[![Pytest](https://github.com/BrentLab/yeastdnnexplorer/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/BrentLab/yeastdnnexplorer/actions/workflows/ci.yml) +[![codecov](https://codecov.io/gh/BrentLab/yeastdnnexplorer/graph/badge.svg?token=D2AB7IUY7F)](https://codecov.io/gh/BrentLab/yeastdnnexplorer) +[![gh-pages](https://github.com/BrentLab/yeastdnnexplorer/actions/workflows/docs.yml/badge.svg)](https://github.com/BrentLab/yeastdnnexplorer/actions/workflows/docs.yml) -## Commands +## Introduction + +`yeastdnnexplorer` is intended to serve as a development environment for exploring +different DNN models to infer the relationship between transcription factors and +target genes using binding and perturbation expression data. + +## Installation + +This repo has not yet been added to PyPI. See the developer installation below. + +### Development + +1. git clone the repo +1. `cd` into the local version of the repo +1. choose one (or more) of the following (only poetry currently supported) + + + +#### poetry + +You can also install the dependencies using poetry. I prefer setting the following: + +```bash +poetry config virtualenvs.in-project true +``` + +So that the virtual environments are installed in the project directory as `.venv` + +After cloning and `cd`ing into the repo, you can install the dependencies with: + +```bash +poetry install +``` + + + +#### mkdocs + +The documentation is build with mkdocs: + +##### Commands + +After building the environment with poetry, you can use `poetry run` or a poetry shell +to execute the following: * `mkdocs new [dir-name]` - Create a new project. * `mkdocs serve` - Start the live-reloading docs server. * `mkdocs build` - Build the documentation site. * `mkdocs -h` - Print help message and exit. -## Project layout +##### Project layout mkdocs.yml # The configuration file. docs/ index.md # The documentation homepage. - ... # Other markdown pages, images and other files. + ... # Other markdown pages, images and other files. \ No newline at end of file diff --git a/docs/ml_models/customizable_model.md b/docs/ml_models/customizable_model.md new file mode 100644 index 0000000..4f5f13a --- /dev/null +++ b/docs/ml_models/customizable_model.md @@ -0,0 +1 @@ +::: yeastdnnexplorer.ml_models.customizable_model.CustomizableModel \ No newline at end of file diff --git a/docs/ml_models/metrics_compute_nrmse.md b/docs/ml_models/metrics_compute_nrmse.md new file mode 100644 index 0000000..a4299c4 --- /dev/null +++ b/docs/ml_models/metrics_compute_nrmse.md @@ -0,0 +1 @@ +::: yeastdnnexplorer.ml_models.metrics.compute_nrmse \ No newline at end of file diff --git a/docs/ml_models/metrics_smse.md b/docs/ml_models/metrics_smse.md new file mode 100644 index 0000000..395edcc --- /dev/null +++ b/docs/ml_models/metrics_smse.md @@ -0,0 +1 @@ +::: yeastdnnexplorer.ml_models.metrics.SMSE \ No newline at end of file diff --git a/docs/ml_models/simple_model.md b/docs/ml_models/simple_model.md new file mode 100644 index 0000000..0ee6f12 --- /dev/null +++ b/docs/ml_models/simple_model.md @@ -0,0 +1 @@ +::: yeastdnnexplorer.ml_models.simple_model \ No newline at end of file diff --git a/docs/probability_models/GenePopulation.md b/docs/probability_models/GenePopulation.md new file mode 100644 index 0000000..6347580 --- /dev/null +++ b/docs/probability_models/GenePopulation.md @@ -0,0 +1 @@ +::: yeastdnnexplorer.probability_models.generate_data.GenePopulation \ No newline at end of file diff --git a/docs/probability_models/default_perturbation_effect_adjustment_function.md b/docs/probability_models/default_perturbation_effect_adjustment_function.md new file mode 100644 index 0000000..88fe4f7 --- /dev/null +++ b/docs/probability_models/default_perturbation_effect_adjustment_function.md @@ -0,0 +1 @@ +::: yeastdnnexplorer.probability_models.generate_data.default_perturbation_effect_adjustment_function \ No newline at end of file diff --git a/docs/probability_models/generate_perturbation_binding_data.md b/docs/probability_models/generate_perturbation_binding_data.md deleted file mode 100644 index 8824cf1..0000000 --- a/docs/probability_models/generate_perturbation_binding_data.md +++ /dev/null @@ -1 +0,0 @@ -::: yeastdnnexplorer.probability_models.generate_data.generate_perturbation_binding_data \ No newline at end of file diff --git a/docs/probability_models/perturbation_effect_adjustment_function_with_tf_relationships.md b/docs/probability_models/perturbation_effect_adjustment_function_with_tf_relationships.md new file mode 100644 index 0000000..0c95cef --- /dev/null +++ b/docs/probability_models/perturbation_effect_adjustment_function_with_tf_relationships.md @@ -0,0 +1 @@ +::: yeastdnnexplorer.probability_models.generate_data.perturbation_effect_adjustment_function_with_tf_relationships \ No newline at end of file diff --git a/docs/probability_models/perturbation_effect_adjustment_function_with_tf_relationships_boolean_logic.md b/docs/probability_models/perturbation_effect_adjustment_function_with_tf_relationships_boolean_logic.md new file mode 100644 index 0000000..b812193 --- /dev/null +++ b/docs/probability_models/perturbation_effect_adjustment_function_with_tf_relationships_boolean_logic.md @@ -0,0 +1 @@ +::: yeastdnnexplorer.probability_models.generate_data.perturbation_effect_adjustment_function_with_tf_relationships_boolean_logic \ No newline at end of file diff --git a/docs/probability_models/relation_classes.md b/docs/probability_models/relation_classes.md new file mode 100644 index 0000000..0ffaac8 --- /dev/null +++ b/docs/probability_models/relation_classes.md @@ -0,0 +1 @@ +::: yeastdnnexplorer.probability_models.relation_classes \ No newline at end of file diff --git a/docs/tutorials/generate_in_silico_data.ipynb b/docs/tutorials/generate_in_silico_data.ipynb index 9782c95..26de65e 100644 --- a/docs/tutorials/generate_in_silico_data.ipynb +++ b/docs/tutorials/generate_in_silico_data.ipynb @@ -1,246 +1,626 @@ { "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Generating in silico data" + ] + }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ + "from yeastdnnexplorer.probability_models.relation_classes import And, Or\n", "from yeastdnnexplorer.probability_models.generate_data import (generate_gene_population, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t generate_perturbation_binding_data)\n" + " generate_binding_effects,\n", + " generate_pvalues,\n", + " generate_perturbation_effects,\n", + " perturbation_effect_adjustment_function_with_tf_relationships,\n", + " perturbation_effect_adjustment_function_with_tf_relationships_boolean_logic)\n", + "\n", + "import torch\n", + "import matplotlib.pyplot as plt\n", + "\n", + "torch.manual_seed(42) # For CPU\n", + "torch.cuda.manual_seed_all(42) # For all CUDA devices\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 1:\n", + "\n", + "The first step is to generate a gene population, or set of gene populations.\n", + "A gene population is simply a class that stores a 1D tensor called `labels`.\n", + "`labels` is a boolean vector where 1 means the gene is part of the signal group\n", + "(a gene which is both bound and responsive to the TF) while 0 means the gene is\n", + "part of the background or noise group. The length of `labels` is the number of\n", + "genes in the population, and the index should be considered the unique gene\n", + "identifier. In other words, the indicies should never change." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, + "outputs": [], + "source": [ + "n_genes = 1000\n", + "signal = [0.1, 0.15, 0.2, 0.25, 0.3]\n", + "n_sample = [1, 1, 2, 2, 4]\n", + "\n", + "# this will be a list of length 10 with a GenePopulation object in each element\n", + "gene_populations_list = []\n", + "for signal_proportion, n_draws in zip(signal, n_sample):\n", + " for _ in range(n_draws):\n", + " gene_populations_list.append(generate_gene_population(n_genes, signal_proportion))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 2:\n", + "\n", + "The second step is to generate binding data from the gene population(s)." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of the binding data tensor: torch.Size([1000, 10, 3])\n" + ] + } + ], + "source": [ + "# Generate binding data for each gene population\n", + "binding_effect_list = [generate_binding_effects(gene_population)\n", + " for gene_population in gene_populations_list]\n", + "\n", + "\n", + "# Calculate p-values for binding data\n", + "binding_pvalue_list = [generate_pvalues(binding_data) for binding_data in binding_effect_list]\n", + "\n", + "binding_data_combined = [torch.stack((gene_population.labels, binding_effect, binding_pval), dim=1)\n", + " for gene_population, binding_effect, binding_pval\n", + " in zip (gene_populations_list, binding_effect_list, binding_pvalue_list)]\n", + "\n", + "# Stack along a new dimension (dim=1) to create a tensor of shape [num_genes, num_TFs, 3]\n", + "binding_data_tensor = torch.stack(binding_data_combined, dim=1)\n", + "\n", + "# Verify the shape\n", + "print(\"Shape of the binding data tensor:\", binding_data_tensor.shape)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 3: Generate perturbation data.\n", + "\n", + "It is important to understand that there are four possible ways we provide for you to generate perturbation data.\n", + "1. No Mean Adjustment\n", + "2. Standard Mean Adjustment\n", + "3. Mean adjustment dependent on all TFs bound to gene in question\n", + "4. Mean adjustment dependent on binary relationships between bound and unbound TFs to gene in question." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Method 1: Generating perturbation data with no mean adjustment\n", + "\n", + "If you don't pass in a value for `max_mean_adjustment` to `generate_perturbation_effects` it will default to zero, meaning the means of the perturbation effects will not be adjusted in any way and will all be equal to `signal_mean` (deault is 3.0) for bound TF-gene pairs and `noise_mean` (default is 0.0) for unbound TF-gene pairs." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# See `generate_perturbation_effects()` in the help or the documentation for more details.\n", + "perturbation_effects_list_no_mean_adjustment = [generate_perturbation_effects(binding_data_tensor[:, tf_index, :].unsqueeze(1), tf_index=0) \n", + " for tf_index in range(sum(n_sample))]\n", + "perturbation_pvalue_list_no_mean_adjustment = [generate_pvalues(perturbation_effects) for perturbation_effects in perturbation_effects_list_no_mean_adjustment]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Method 2: Generating perturbation data with a simple mean adjustment\n", + "If you do pass in a nonzero value for `max_mean_adjustment`, the means of bound gene-TF pairs will be adjusted by up to a maximum of `max_mean_adjustment`. Note that instead of passing in one column (corresponding to one TF) of the binding data tensor at a time, we instead pass in the entire binding data tensor at once. This syntactic difference is just a result of how our mean adjustment functions requires the entire matrix of all genes and TFs as opposed to being able to operate on one column at once. Using this data generation method, we adust the mean of any TF that is bound to a gene." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# if you want to modify the default mean for bound genes, you can pass in the 'signal_mean' parameter\n", + "perturbation_effects_list_normal_mean_adjustment = generate_perturbation_effects(\n", + " binding_data_tensor, \n", + " max_mean_adjustment=10.0\n", + ")\n", + "\n", + "# since the p-value generation function operates on one column at a time, we must iterate over the columns of our perturb effects\n", + "# list and generate p-values for each column\n", + "perturbation_effects_list_normal_mean_adjustment_pvalues = torch.zeros_like(perturbation_effects_list_normal_mean_adjustment)\n", + "for col_idx in range(perturbation_effects_list_normal_mean_adjustment.shape[1]):\n", + " col = perturbation_effects_list_normal_mean_adjustment[:, col_idx]\n", + " col_pvals = generate_pvalues(col)\n", + " perturbation_effects_list_normal_mean_adjustment_pvalues[:, col_idx] = col_pvals" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Method 3: Generating Perturbation Data with a mean adjustment dependent on which TFs are bound to gene\n", + "You are also able to specify a dictionary of TF relationships. Passing in this dictionary in combination with using our `perturbation_effect_adjustment_function_with_tf_relationships` mean adjustment function alows for you to only adjust the means of perturbation effects if the TF in the TF-gene pair in question is bound AND all other TFs associated with that TF are bound to the same gene. To associate a TF with another TF, put its index in the list of TFs corresponding to the other TF's index in the tf_relationships dictionary.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# define our dictionary of TF relationships\n", + "# For each gene, if TF 0 is bound, then we only adjust its mean if TF 1 is also bound\n", + "# similarly, if TF 7 is bound, we still only adjust its mean if TFs 1 and 4 are bound\n", + "tf_relationships = {\n", + " 0: [1],\n", + " 1: [8],\n", + " 2: [5, 6],\n", + " 3: [4],\n", + " 4: [5],\n", + " 5: [9],\n", + " 6: [4],\n", + " 7: [1, 4],\n", + " 8: [6],\n", + " 9: [4],\n", + "}\n", + "\n", + "perturbation_effects_list_dep_mean_adjustment = generate_perturbation_effects(\n", + " binding_data_tensor, \n", + " tf_relationships=tf_relationships,\n", + " adjustment_function=perturbation_effect_adjustment_function_with_tf_relationships,\n", + " max_mean_adjustment=10.0,\n", + ")\n", + "perturbation_effects_list_dep_mean_adjustment_pvalues = torch.zeros_like(perturbation_effects_list_dep_mean_adjustment)\n", + "for col_idx in range(perturbation_effects_list_dep_mean_adjustment.shape[1]):\n", + " col = perturbation_effects_list_dep_mean_adjustment[:, col_idx]\n", + " col_pvals = generate_pvalues(col)\n", + " perturbation_effects_list_dep_mean_adjustment_pvalues[:, col_idx] = col_pvals" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Method 4: Generating Perturbation Data with a mean adjustment dependent on boolean relationships between TFs\n", + "(see the documentation in `yeastdnnexplorer/probability_models/relation_classes.py` for more information on `And()` and `Or()`) \n", + "\n", + "This is a more advanced version of method 3 where instead of only specifying direct dependencies you can specify logical relations that must be satisfied for a gene-TF pair's perturbation effect value to be adjusted. For example, in the below example we only adjust the mean of TF 3 for each gene if TF 3 is bound and (7 || 9) && (6 && 7) are bound. " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# note that Or(1,1) is used to enforce a unary contraint\n", + "tf_relationships_dict_boolean_logic = {\n", + " 0: [And(3, 4, 8), Or(3, 7), Or(1, 1)],\n", + " 1: [And(5, Or(7, 8))],\n", + " 2: [],\n", + " 3: [Or(7, 9), And(6, 7)],\n", + " 4: [And(1, 2)],\n", + " 5: [Or(0, 1, 2, 8, 9)],\n", + " 6: [And(0, Or(1, 2))],\n", + " 7: [Or(2, And(5, 6, 9))],\n", + " 8: [],\n", + " 9: [And(6, And(3, Or(0, 9)))],\n", + "}\n", + "\n", + "perturbation_effects_list_boolean_logic = generate_perturbation_effects(\n", + " binding_data_tensor, \n", + " adjustment_function=perturbation_effect_adjustment_function_with_tf_relationships_boolean_logic,\n", + " tf_relationships=tf_relationships_dict_boolean_logic,\n", + " max_mean_adjustment=10.0,\n", + ")\n", + "perturbation_effects_list_boolean_logic_pvalues = torch.zeros_like(perturbation_effects_list_boolean_logic)\n", + "for col_idx in range(perturbation_effects_list_boolean_logic.shape[1]):\n", + " col = perturbation_effects_list_boolean_logic[:, col_idx]\n", + " col_pvals = generate_pvalues(col)\n", + " perturbation_effects_list_boolean_logic_pvalues[:, col_idx] = col_pvals" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 4: Assemble\n", + "\n", + "The final step is to assemble the data into a single tensor. Here is one way.\n", + "The order of the matrix in the last dimension is:\n", + "\n", + "1. signal/noise label\n", + "1. binding effect\n", + "1. binding pvalue\n", + "1. perturbation effect\n", + "1. perturbation pvalue\n", + "\n", + "For simplicity's sake, we will use the perturbation effect data we generated with no mean adjustment. However you can assemble the data using perturbation effect data generated from any of the 4 methods we covered above." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of the final data tensor: torch.Size([1000, 10, 5])\n" + ] + } + ], + "source": [ + "# Convert lists to tensors if they are not already\n", + "perturbation_effects_tensor = torch.stack(perturbation_effects_list_no_mean_adjustment, dim=1)\n", + "perturbation_pvalues_tensor = torch.stack(perturbation_pvalue_list_no_mean_adjustment, dim=1)\n", + "\n", + "# Ensure perturbation data is reshaped to match [n_genes, n_tfs]\n", + "# This step might need adjustment based on the actual shapes of your tensors.\n", + "perturbation_effects_tensor = perturbation_effects_tensor.unsqueeze(-1) # Adds an extra dimension for concatenation\n", + "perturbation_pvalues_tensor = perturbation_pvalues_tensor.unsqueeze(-1) # Adds an extra dimension for concatenation\n", + "\n", + "# Concatenate along the last dimension to form a [n_genes, n_tfs, 5] tensor\n", + "final_data_tensor = torch.cat((binding_data_tensor, perturbation_effects_tensor, perturbation_pvalues_tensor), dim=2)\n", + "\n", + "# Verify the shape\n", + "print(\"Shape of the final data tensor:\", final_data_tensor.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As an aside, I choose to structure the data this way by looking at the\n", + "result of strides, which describes how the data is stored in memory:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(3000, 3, 1)\n", + "(300, 3, 1)\n" + ] + } + ], + "source": [ + "tensor_continuous = torch.empty(100, 1000, 3)\n", + "strides_continuous = tensor_continuous.stride()\n", + "print(strides_continuous)\n", + "\n", + "\n", + "tensor_continuous = torch.empty(1000, 100, 3)\n", + "strides_continuous = tensor_continuous.stride()\n", + "print(strides_continuous)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Sanity checks\n", + "\n", + "Ensure that the generated data matches expectations.\n", + "\n", + "### The signal/noise ratios should match exactly the initial signal ratio" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "signal/nosie ratio is correct: True\n" + ] + } + ], + "source": [ + "tolerance = 1e-5\n", + "are_equal = torch.isclose(\n", + " torch.sum(final_data_tensor[:, :, 0] == 1, axis=0),\n", + " torch.tensor([val * n_genes for val, count in zip(signal, n_sample) for _ in range(count)],\n", + " dtype=torch.long),\n", + " atol=tolerance)\n", + "\n", + "print(f\"signal/nosie ratio is correct: {are_equal.all()}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Binding effect distributions should match expectations\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The noise binding max is 13.157892227172852 and the min is 0.0\n", + "the noise min is 0.0\n", + "the noise mean is 0.3589712679386139 and the std is 1.1559306383132935\n", + "The signal binding max is 78.94734954833984 and the min is 0.1315789520740509\n", + "the signal min is 0.1315789520740509\n", + "the signal mean is 2.4840002059936523 and the std is 6.374814510345459\n" + ] + } + ], + "source": [ + "labels = final_data_tensor[:, :, 0].flatten()\n", + "noise_binding = final_data_tensor[:, :, 1].flatten()[labels == 0]\n", + "signal_binding = final_data_tensor[:, :, 1].flatten()[labels == 1]\n", + "\n", + "print(f\"The noise binding max is {noise_binding.max()} and the min is {noise_binding.min()}\")\n", + "print(f\"the noise min is {noise_binding.min()}\")\n", + "print(f\"the noise mean is {noise_binding.mean()} and the std is {noise_binding.std()}\")\n", + "print(f\"The signal binding max is {signal_binding.max()} and the min is {signal_binding.min()}\")\n", + "print(f\"the signal min is {signal_binding.min()}\")\n", + "print(f\"the signal mean is {signal_binding.mean()} and the std is {signal_binding.std()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, "outputs": [ { "data": { + "image/png": "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5X32TCaTZc2aNdb2EydO5Huf5L2vAwICbH5O48ePtzg6OlqSk5MLNF8Adx4uOwRw11myZImio6PzPZo1a/aP+3p5een48eM6efJkoY/72WefydHRUS+88IJN+4svviiLxaItW7ZIun4WR5Kef/55m35jx47927FHjx6dr6106dLWf1+7dk0///yz2rZtK0n6+uuv8/UfOXKk9d+Ojo5q1aqVLBaLgoODre1eXl6qX7++fvrpp7+tRbo+V0kKCwuzaX/xxRclSZs3b77p/n+nUaNGuv/++7VmzRprW3p6uj755BP16tVLZrNZUuHnXhTr169Xu3btVLZsWf3888/WR0BAgHJycrR3715J11+z9PR066WpxeGvP+927drpl19+UWpq6i2NO2LECJt779q1aydJ1p93XFycTp48qUGDBumXX36xzjk9PV2dO3fW3r17811yeTNXr17VE088odKlS2vmzJk37HOjuf75/ffZZ5/JycnJeiZMuv7+vdnn5c9SU1NVpkyZAvX9/PPPlZmZqdDQUJuFeJ555hmZzeYiv6//zp8/k3mfPXd3dz355JPW9vr168vLy+uGn8lRo0bZnOVt166dcnJydPbs2WKtE4BxuOwQwF2ndevWatWqVb72vF+ib+bVV19Vnz59VK9ePTVp0kTdunXT0KFDCxTczp49q8qVK+f7Ra9hw4bW7Xn/6+DgkO8Sojp16vzt2H/tK0m//vqrpk6dqjVr1ujy5cs221JSUvL1r1atms1zT09Pubq65rtHx9PT8x/vncqbw19r9vX1lZeX1y398jd48GBNmDBB+/fv14MPPqiNGzfq6tWr1ksOpcLPvShOnjypb7/9VhUqVLjh9rzjPv/881q3bp26d++u++67T127dtWTTz6pbt26FfnYf/1ZlS1bVpL022+/WQNocY8ryfpHh5td0peSkmLd72ZycnI0YMAAfffdd9qyZYsqV66cr4+rq2u+17ds2bLWeqTr77VKlSrlu4Syfv36/1iDJJnNZpt7zW4m733717GdnZ1Vq1atYg01N5q7p6enqlSpku+yWU9PT5vXJM8//TwB3H0IXwDuKe3bt9ePP/6ojz/+WNu3b9eKFSs0f/58LVu2zOav1Eb785mePE8++aT279+vl156SS1atJCHh4dyc3PVrVu3G56dcHR0LFCbpHwLhPyd4ri36q8GDhyoiRMnavXq1XrwwQe1evVqlS1bVj169LD2KezcC+Kvizzk5uaqS5cumjhx4g3716tXT5JUsWJFxcXFadu2bdqyZYu2bNmiyMhIDRs2TCtXrixSLbf6cynquHmv3Zw5c/72y7D/GoL+zjPPPKNNmzbp/fffV6dOnQpVT3Fq0KCB4uLilJmZ+Y8rbt6Kv/ss/PV9lefv5l6Yn/3tep8AsB/CF4B7jre3t0aMGKERI0YoLS1N7du3V0REhDV8/d0vWdWrV9fnn3+u33//3ebs14kTJ6zb8/43NzdXp0+fVt26da39brTq3N/57bfftGPHDk2dOlWTJ0+2thflcsmiyJvDyZMnrWf2pOtfZJucnHzD7+kqqMqVK6tjx45av369/vOf/yg6OlrDhw+3/uJ8q3MvW7ZsvpXrMjMzdenSJZu22rVrKy0tTQEBAf84prOzs3r37q3evXsrNzdXzz//vJYvX67//Oc/Nz2jWdxuNQzXrl1b0vWzRQWZ99956aWXFBkZqQULFmjgwIG3VFP16tW1Y8cOpaWl2QS/+Pj4Au3fu3dvxcTE6H//+98/1pL3vo2Pj1etWrWs7ZmZmTp9+vRNX5O8s05/fW9xCSCAwuCeLwD3lL9ebufh4aE6derYLJ+e9x1bf/0lq0ePHsrJydHixYtt2ufPny+TyaTu3btLkgIDAyVdX876z958880C15n3F++//oU7bwW+2y3vLNRfjzdv3jxJuunKjQUxePBgXb58Wc8++6yysrJsLjm81bnXrl3ber9WnrfffjvfGYonn3xSMTEx2rZtW74xkpOTlZ2dLSn/e8bBwcF6meqf3zdGcHd3v6XLLv38/FS7dm298cYbSktLy7e9IMuYz5kzR2+88Yb+9a9/FXm1xz/r0aOHsrOztXTpUmtbTk5OgT8vo0ePVqVKlfTiiy/qhx9+yLf98uXLmj59uiQpICBAzs7OWrRokc3765133lFKSspN39fVq1eXo6NjvvfWXz/nAHAznPkCcE9p1KiROnToID8/P3l7e+vw4cP68MMPNWbMGGsfPz8/SdILL7ygwMBAOTo6asCAAerdu7c6duyof//73zpz5oyaN2+u7du36+OPP1ZoaKj1rIKfn5/69++vBQsW6JdffrEuNZ/3i2FBzl6YzWbrEtVZWVm67777tH37dp0+ffo2vCr5NW/eXEFBQXr77beVnJysRx55RAcPHtTKlSvVt29fdezY8ZbG79+/v55//nl9/PHHqlq1qs2y97c695EjR2r06NHq37+/unTpoiNHjmjbtm357n176aWXrAt9DB8+XH5+fkpPT9fRo0f14Ycf6syZMypfvrxGjhypX3/9VZ06dVKVKlV09uxZvfnmm2rRooXNWUEj+Pn5ae3atQoLC9MDDzwgDw8P9e7du8D7Ozg4aMWKFerevbsaN26sESNG6L777tOFCxe0a9cumc1mffrpp3+7/4YNGzRx4kTVrVtXDRs21HvvvWezvUuXLvLx8SnUnHr37q2HHnpIkyZN0pkzZ9SoUSN99NFHBQ6ZZcuW1YYNG9SjRw+1aNFCQ4YMsX6Gv/76a33wwQfy9/eXJFWoUEHh4eGaOnWqunXrpkcffVTx8fF666239MADD2jIkCF/exxPT0898cQTevPNN2UymVS7dm1t2rQp3z2JAHAzhC8A95QXXnhBn3zyibZv366MjAxVr15d06dP10svvWTt069fP40dO1Zr1qzRe++9J4vFogEDBsjBwUGffPKJJk+erLVr1yoyMlI1atTQnDlzrKsA5lm1apV8fX31wQcfaMOGDQoICNDatWtVv359ubq6FqjW1atXa+zYsVqyZIksFou6du36twsb3A4rVqxQrVq1FBUVpQ0bNsjX11fh4eGaMmXKLY9tNpvVu3dvrV+/XgMHDswXSG9l7s8884xOnz6td955R1u3blW7du0UHR2tzp072/Rzc3PTnj179Prrr2v9+vVatWqVzGaz6tWrp6lTp8rT01OSNGTIEL399tt66623lJycLF9fXz311FOKiIiwWTHPCM8//7zi4uIUGRmp+fPnq3r16oUKX9L1L2uOiYnRtGnTtHjxYqWlpcnX11dt2rTRs88+e9N9jxw5Iun6JaBDhw7Nt33Xrl2FDl95n6vQ0FC99957MplMevTRRzV37lzdf//9BRqjTZs2OnbsmObMmaPNmzfrv//9rxwcHNSwYUNNmjTJ5o8rERERqlChghYvXqzx48fL29tbo0aN0uuvv/6P3/H15ptvKisrS8uWLZOLi4uefPJJzZkzR02aNCnUnAHcu0wW7toEAEPExcXp/vvv13vvvWdzmR0AALg3cM8XANwGf/zxR762BQsWyMHBweYSOwAAcO/gskMAuA1mz56t2NhYdezYUU5OTtYlykeNGqWqVavauzwAAGAHXHYIALdBdHS0pk6dqu+++05paWmqVq2ahg4dqn//+99ycuLvXgAA3IsIXwAAAABgAO75AgAAAAADEL4AAAAAwADceFAAubm5unjxosqUKVOgL0cFAAAAUDJZLBb9/vvvqly5cqG/75HwVQAXL15kdTIAAAAAVufOnVOVKlUKtQ/hqwDKlCkj6foLbDab7VwNAAAAAHtJTU1V1apVrRmhMAhfBZB3qaHZbCZ8AQAAACjS7UgsuAEAAAAABiB8AQAAAIABCF8AAAAAYADu+QIAAADuEDk5OcrKyrJ3Gfe8UqVKydHRsdjHJXwBAAAAd4C0tDSdP39eFovF3qXc80wmk6pUqSIPD49iHZfwBQAAANhZTk6Ozp8/Lzc3N1WoUKFIK+mheFgsFl25ckXnz59X3bp1i/UMGOELAAAAsLOsrCxZLBZVqFBBpUuXtnc597wKFSrozJkzysrKKtbwxYIbAAAAwB2CM153htv1cyB8AQAAAIABCF8AAAAAYADu+QIAAADuVN9GGHu8ZsYeLyoqSqGhoUpOTr6lcUwmkzZs2KC+ffsWS123C2e+AAAAABTJ8OHD7/jAk2fJkiWqUaOGXF1d1aZNGx08eNDwGghfAAAAAEq0tWvXKiwsTFOmTNHXX3+t5s2bKzAwUJcvXza0DsIXAAAAgNti3rx5atq0qdzd3VW1alU9//zzSktLy9dv48aNqlu3rlxdXRUYGKhz587ZbP/444/VsmVLubq6qlatWpo6daqys7MLVcczzzyjESNGqFGjRlq2bJnc3Nz07rvv3vIcC4PwBQAAAOC2cHBw0KJFi3T8+HGtXLlSO3fu1MSJE236XL16Va+99ppWrVqlffv2KTk5WQMGDLBu/+KLLzRs2DCNGzdO3333nZYvX66oqCi99tprBaohMzNTsbGxCggIsKkrICBAMTExxTPRAiJ8AQAAALgtQkND1bFjR9WoUUOdOnXS9OnTtW7dOps+WVlZWrx4sfz9/eXn56eVK1dq//791nuypk6dqkmTJikoKEi1atVSly5dNG3aNC1fvrxANfz888/KycmRj4+PTbuPj48SExOLZ6IFxGqHAAAAAG6Lzz//XDNmzNCJEyeUmpqq7OxsXbt2TVevXpWbm5skycnJSQ888IB1nwYNGsjLy0vff/+9WrdurSNHjmjfvn02Z7pycnLyjXM3IHwBAAAAKHZnzpxRr1699Nxzz+m1116Tt7e3vvzySwUHByszM7PAoSktLU1Tp05Vv3798m1zdXX9x/3Lly8vR0dHJSUl2bQnJSXJ19e3YJMpJlx2CAAAAKDYxcbGKjc3V3PnzlXbtm1Vr149Xbx4MV+/7OxsHT582Po8Pj5eycnJatiwoSSpZcuWio+PV506dfI9HBz+Oc44OzvLz89PO3bssLbl5uZqx44d8vf3L4aZFhxnvgrj2AzJw8XeVdwZDP4CPgAAANyZUlJSFBcXZ9NWrlw51alTR1lZWXrzzTfVu3dv7du3T8uWLcu3f6lSpTR27FgtWrRITk5OGjNmjNq2bavWrVtLkiZPnqxevXqpWrVqevzxx+Xg4KAjR47o2LFjmj59eoFqDAsLU1BQkFq1aqXWrVtrwYIFSk9P14gRI255/oVB+AIAAADuVHfBH7x3796t+++/36YtODhYK1as0Lx58zRr1iyFh4erffv2mjFjhoYNG2bT183NTS+//LIGDRqkCxcuqF27dnrnnXes2wMDA7Vp0ya9+uqrmjVrlkqVKqUGDRpo5MiRBa7xqaee0pUrVzR58mQlJiaqRYsW2rp1a75FOG43k8VisRh6xLtQamqqPD09lbJvksyc+bruLvgPAQAAwN3i2rVrOn36tGrWrFmg+5hwe93s52HNBikpMpvNhRqXe74AAAAAwACELwAAAAAwAOELAAAAAAxA+AIAAAAAAxC+AAAAAMAAhC8AAAAAMADhCwAAAAAMQPgCAAAAAAMQvgAAAADAAE72LgAAAADAjUVElOzjRUVFKTQ0VMnJybc0jslk0oYNG9S3b99iqet24cwXAAAAgCIZPnz4HR94JGnv3r3q3bu3KleuLJPJpI0bN9qlDsIXAAAAgBItPT1dzZs315IlS+xaB+ELAAAAwG0xb948NW3aVO7u7qpataqef/55paWl5eu3ceNG1a1bV66urgoMDNS5c+dstn/88cdq2bKlXF1dVatWLU2dOlXZ2dkFrqN79+6aPn26HnvssVue060gfAEAAAC4LRwcHLRo0SIdP35cK1eu1M6dOzVx4kSbPlevXtVrr72mVatWad++fUpOTtaAAQOs27/44gsNGzZM48aN03fffafly5crKipKr732mtHTuWWELwAAAAC3RWhoqDp27KgaNWqoU6dOmj59utatW2fTJysrS4sXL5a/v7/8/Py0cuVK7d+/XwcPHpQkTZ06VZMmTVJQUJBq1aqlLl26aNq0aVq+fLk9pnRLWO0QAAAAwG3x+eefa8aMGTpx4oRSU1OVnZ2ta9eu6erVq3Jzc5MkOTk56YEHHrDu06BBA3l5een7779X69atdeTIEe3bt8/mTFdOTk6+ce4GhC8AAAAAxe7MmTPq1auXnnvuOb322mvy9vbWl19+qeDgYGVmZhY4NKWlpWnq1Knq169fvm2urq7FXfZtRfgCAAAAUOxiY2OVm5uruXPnysHh+t1Of73kUJKys7N1+PBhtW7dWpIUHx+v5ORkNWzYUJLUsmVLxcfHq06dOsYVf5sQvgAAAAAUWUpKiuLi4mzaypUrpzp16igrK0tvvvmmevfurX379mnZsmX59i9VqpTGjh2rRYsWycnJSWPGjFHbtm2tYWzy5Mnq1auXqlWrpscff1wODg46cuSIjh07punTpxeoxrS0NJ06dcr6/PTp04qLi5O3t7eqVatW9MkXEuELAAAAuENFRNi7gn+2e/du3X///TZtwcHBWrFihebNm6dZs2YpPDxc7du314wZMzRs2DCbvm5ubnr55Zc1aNAgXbhwQe3atdM777xj3R4YGKhNmzbp1Vdf1axZs1SqVCk1aNBAI0eOLHCNhw8fVseOHa3Pw8LCJElBQUGKiooqwqyLxmSxWCyGHe0ulZqaKk9PT6XsmySzh4u9y7kzNIuwdwUAAAAlxrVr13T69GnVrFnzrruPqSS62c/Dmg1SUmQ2mws1LkvNAwAAAIABCF8AAAAAYADCFwAAAAAYwO7h68KFCxoyZIjKlSun0qVLq2nTpjp8+LB1u8Vi0eTJk1WpUiWVLl1aAQEBOnnypM0Yv/76qwYPHiyz2SwvLy8FBwcrLS3Nps+3336rdu3aydXVVVWrVtXs2bMNmR8AAAAASHYOX7/99pseeughlSpVSlu2bNF3332nuXPnqmzZstY+s2fP1qJFi7Rs2TIdOHBA7u7uCgwM1LVr16x9Bg8erOPHjys6OlqbNm3S3r17NWrUKOv21NRUde3aVdWrV1dsbKzmzJmjiIgIvf3224bOFwAAALgZ1sK7M9yun4Ndl5qfNWuWqlatqsjISGtbzZo1rf+2WCxasGCBXnnlFfXp00eStGrVKvn4+Gjjxo0aMGCAvv/+e23dulWHDh1Sq1atJElvvvmmevTooTfeeEOVK1fW+++/r8zMTL377rtydnZW48aNFRcXp3nz5tmENAAAAMAeHB0dJUmZmZkqXbq0natBZmampP/3cykudg1fn3zyiQIDA/XEE09oz549uu+++/T888/rmWeekXT9y88SExMVEBBg3cfT01Nt2rRRTEyMBgwYoJiYGHl5eVmDlyQFBATIwcFBBw4c0GOPPaaYmBi1b99ezs7O1j6BgYGaNWuWfvvtN5szbZKUkZGhjIwM6/PU1NTb9RIAAAAAcnJykpubm65cuaJSpUrJwcHudwfds3Jzc3XlyhW5ubnJyal445Jdw9dPP/2kpUuXKiwsTP/617906NAhvfDCC3J2dlZQUJASExMlST4+Pjb7+fj4WLclJiaqYsWKNtudnJzk7e1t0+fPZ9T+PGZiYmK+8DVjxgxNnTq1+CYKAAAA3ITJZFKlSpV0+vRpnT171t7l3PMcHBxUrVo1mUymYh3XruErNzdXrVq10uuvvy5Juv/++3Xs2DEtW7ZMQUFBdqsrPDzc+q3X0vUzX1WrVrVbPQAAACj5nJ2dVbduXeslb7AfZ2fn23L20a7hq1KlSmrUqJFNW8OGDfW///1PkuTr6ytJSkpKUqVKlax9kpKS1KJFC2ufy5cv24yRnZ2tX3/91bq/r6+vkpKSbPrkPc/r82cuLi5ycXG5hZkBAAAAhefg4CBXV1d7l4HbxK4Xkz700EOKj4+3afvhhx9UvXp1SdcX3/D19dWOHTus21NTU3XgwAH5+/tLkvz9/ZWcnKzY2Fhrn507dyo3N1dt2rSx9tm7d6+ysrKsfaKjo1W/fv18lxwCAAAAwO1g1/A1fvx4ffXVV3r99dd16tQprV69Wm+//bZCQkIkXb/2NTQ0VNOnT9cnn3yio0ePatiwYapcubL69u0r6fqZsm7duumZZ57RwYMHtW/fPo0ZM0YDBgxQ5cqVJUmDBg2Ss7OzgoODdfz4ca1du1YLFy60ubQQAAAAAG4nu152+MADD2jDhg0KDw/Xq6++qpo1a2rBggUaPHiwtc/EiROVnp6uUaNGKTk5WQ8//LC2bt1qczr2/fff15gxY9S5c2c5ODiof//+WrRokXW7p6entm/frpCQEPn5+al8+fKaPHkyy8wDAAAAMIzJwje5/aPU1FR5enoqZd8kmT24F0yS1CzC3hUAAAAAhrNmg5QUmc3mQu3LFwgAAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAewaviIiImQymWweDRo0sG6/du2aQkJCVK5cOXl4eKh///5KSkqyGSMhIUE9e/aUm5ubKlasqJdeeknZ2dk2fXbv3q2WLVvKxcVFderUUVRUlBHTAwAAAAAru5/5aty4sS5dumR9fPnll9Zt48eP16effqr169drz549unjxovr162fdnpOTo549eyozM1P79+/XypUrFRUVpcmTJ1v7nD59Wj179lTHjh0VFxen0NBQjRw5Utu2bTN0ngAAAADubU52L8DJSb6+vvnaU1JS9M4772j16tXq1KmTJCkyMlINGzbUV199pbZt22r79u367rvv9Pnnn8vHx0ctWrTQtGnT9PLLLysiIkLOzs5atmyZatasqblz50qSGjZsqC+//FLz589XYGCgoXMFAAAAcO+y+5mvkydPqnLlyqpVq5YGDx6shIQESVJsbKyysrIUEBBg7dugQQNVq1ZNMTExkqSYmBg1bdpUPj4+1j6BgYFKTU3V8ePHrX3+PEZen7wxbiQjI0Opqak2DwAAAAC4FXYNX23atFFUVJS2bt2qpUuX6vTp02rXrp1+//13JSYmytnZWV5eXjb7+Pj4KDExUZKUmJhoE7zytudtu1mf1NRU/fHHHzesa8aMGfL09LQ+qlatWhzTBQAAAHAPs+tlh927d7f+u1mzZmrTpo2qV6+udevWqXTp0narKzw8XGFhYdbnqampBDAAAAAAt8Tulx3+mZeXl+rVq6dTp07J19dXmZmZSk5OtumTlJRkvUfM19c33+qHec//qY/ZbP7bgOfi4iKz2WzzAAAAAIBbcUeFr7S0NP3444+qVKmS/Pz8VKpUKe3YscO6PT4+XgkJCfL395ck+fv76+jRo7p8+bK1T3R0tMxmsxo1amTt8+cx8vrkjQEAAAAARrBr+JowYYL27NmjM2fOaP/+/Xrsscfk6OiogQMHytPTU8HBwQoLC9OuXbsUGxurESNGyN/fX23btpUkde3aVY0aNdLQoUN15MgRbdu2Ta+88opCQkLk4uIiSRo9erR++uknTZw4USdOnNBbb72ldevWafz48facOgAAAIB7jF3v+Tp//rwGDhyoX375RRUqVNDDDz+sr776ShUqVJAkzZ8/Xw4ODurfv78yMjIUGBiot956y7q/o6OjNm3apOeee07+/v5yd3dXUFCQXn31VWufmjVravPmzRo/frwWLlyoKlWqaMWKFSwzDwAAAMBQJovFYrF3EXe61NRUeXp6KmXfJJk9XOxdzp2hWYS9KwAAAAAMZ80GKSmFXhvijrrnCwAAAABKKsIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGCAOyZ8zZw5UyaTSaGhoda2a9euKSQkROXKlZOHh4f69++vpKQkm/0SEhLUs2dPubm5qWLFinrppZeUnZ1t02f37t1q2bKlXFxcVKdOHUVFRRkwIwAAAAD4f+6I8HXo0CEtX75czZo1s2kfP368Pv30U61fv1579uzRxYsX1a9fP+v2nJwc9ezZU5mZmdq/f79WrlypqKgoTZ482drn9OnT6tmzpzp27Ki4uDiFhoZq5MiR2rZtm2HzAwAAAAC7h6+0tDQNHjxY//d//6eyZcta21NSUvTOO+9o3rx56tSpk/z8/BQZGan9+/frq6++kiRt375d3333nd577z21aNFC3bt317Rp07RkyRJlZmZKkpYtW6aaNWtq7ty5atiwocaMGaPHH39c8+fPt8t8AQAAANyb7B6+QkJC1LNnTwUEBNi0x8bGKisry6a9QYMGqlatmmJiYiRJMTExatq0qXx8fKx9AgMDlZqaquPHj1v7/HXswMBA6xg3kpGRodTUVJsHAAAAANwKJ3sefM2aNfr666916NChfNsSExPl7OwsLy8vm3YfHx8lJiZa+/w5eOVtz9t2sz6pqan6448/VLp06XzHnjFjhqZOnVrkeQEAAADAX9ntzNe5c+c0btw4vf/++3J1dbVXGTcUHh6ulJQU6+PcuXP2LgkAAADAXc5u4Ss2NlaXL19Wy5Yt5eTkJCcnJ+3Zs0eLFi2Sk5OTfHx8lJmZqeTkZJv9kpKS5OvrK0ny9fXNt/ph3vN/6mM2m2941kuSXFxcZDabbR4AAAAAcCvsFr46d+6so0ePKi4uzvpo1aqVBg8ebP13qVKltGPHDus+8fHxSkhIkL+/vyTJ399fR48e1eXLl619oqOjZTab1ahRI2ufP4+R1ydvDAAAAAAwgt3u+SpTpoyaNGli0+bu7q5y5cpZ24ODgxUWFiZvb2+ZzWaNHTtW/v7+atu2rSSpa9euatSokYYOHarZs2crMTFRr7zyikJCQuTi4iJJGj16tBYvXqyJEyfq6aef1s6dO7Vu3Tpt3rzZ2AkDAAAAuKfZdcGNfzJ//nw5ODiof//+ysjIUGBgoN566y3rdkdHR23atEnPPfec/P395e7urqCgIL366qvWPjVr1tTmzZs1fvx4LVy4UFWqVNGKFSsUGBhojykBAAAAuEeZLBaLxd5F3OlSU1Pl6emplH2TZPZwsXc5d4ZmEfauAAAAADCcNRukpBR6bQi7f88XAAAAANwLCF8AAAAAYIAiha+ffvqpuOsAAAAAgBKtSOGrTp066tixo9577z1du3atuGsCAAAAgBKnSOHr66+/VrNmzRQWFiZfX189++yzOnjwYHHXBgAAAAAlRpHCV4sWLbRw4UJdvHhR7777ri5duqSHH35YTZo00bx583TlypXirhMAAAAA7mq3tOCGk5OT+vXrp/Xr12vWrFk6deqUJkyYoKpVq2rYsGG6dOlScdUJAAAAAHe1Wwpfhw8f1vPPP69KlSpp3rx5mjBhgn788UdFR0fr4sWL6tOnT3HVCQAAAAB3Naei7DRv3jxFRkYqPj5ePXr00KpVq9SjRw85OFzPcjVr1lRUVJRq1KhRnLUCAAAAwF2rSOFr6dKlevrppzV8+HBVqlTphn0qVqyod95555aKAwAAAICSokjh6+TJk//Yx9nZWUFBQUUZHgAAAABKnCLd8xUZGan169fna1+/fr1Wrlx5y0UBAAAAQElTpPA1Y8YMlS9fPl97xYoV9frrr99yUQAAAABQ0hQpfCUkJKhmzZr52qtXr66EhIRbLgoAAAAASpoiha+KFSvq22+/zdd+5MgRlStX7paLAgAAAICSpkjha+DAgXrhhRe0a9cu5eTkKCcnRzt37tS4ceM0YMCA4q4RAAAAAO56RVrtcNq0aTpz5ow6d+4sJ6frQ+Tm5mrYsGHc8wUAAAAAN1Ck8OXs7Ky1a9dq2rRpOnLkiEqXLq2mTZuqevXqxV0fAAAAAJQIRQpfeerVq6d69eoVVy0AAAAAUGIVKXzl5OQoKipKO3bs0OXLl5Wbm2uzfefOncVSHAAAAACUFEUKX+PGjVNUVJR69uypJk2ayGQyFXddAAAAAFCiFCl8rVmzRuvWrVOPHj2Kux4AAAAAKJGKtNS8s7Oz6tSpU9y1AAAAAECJVaTw9eKLL2rhwoWyWCzFXQ8AAAAAlEhFuuzwyy+/1K5du7RlyxY1btxYpUqVstn+0UcfFUtxAAAAAFBSFCl8eXl56bHHHivuWgAAAACgxCpS+IqMjCzuOgAAAACgRCvSPV+SlJ2drc8//1zLly/X77//Lkm6ePGi0tLSiq04AAAAACgpinTm6+zZs+rWrZsSEhKUkZGhLl26qEyZMpo1a5YyMjK0bNmy4q4TAAAAAO5qRTrzNW7cOLVq1Uq//fabSpcubW1/7LHHtGPHjmIrDgAAAABKiiKd+friiy+0f/9+OTs727TXqFFDFy5cKJbCAAAAAKAkKdKZr9zcXOXk5ORrP3/+vMqUKXPLRQEAAABASVOk8NW1a1ctWLDA+txkMiktLU1TpkxRjx49iqs2AAAAACgxinTZ4dy5cxUYGKhGjRrp2rVrGjRokE6ePKny5cvrgw8+KO4aAQAAAOCuV6TwVaVKFR05ckRr1qzRt99+q7S0NAUHB2vw4ME2C3AAAAAAAK4rUviSJCcnJw0ZMqQ4awEAAACAEqtI4WvVqlU33T5s2LAiFQMAAAAAJVWRwte4ceNsnmdlZenq1atydnaWm5sb4QsAAAAA/qJIqx3+9ttvNo+0tDTFx8fr4YcfZsENAAAAALiBIoWvG6lbt65mzpyZ76wYAAAAAKAYw5d0fRGOixcvFueQAAAAAFAiFOmer08++cTmucVi0aVLl7R48WI99NBDxVIYAAAAAJQkRQpfffv2tXluMplUoUIFderUSXPnzi2OugAAAACgRClS+MrNzS3uOgAAAACgRCvWe74AAAAAADdWpDNfYWFhBe47b968ohwCAAAAAEqUIoWvb775Rt98842ysrJUv359SdIPP/wgR0dHtWzZ0trPZDIVT5UAAAAAcJcrUvjq3bu3ypQpo5UrV6ps2bKSrn/x8ogRI9SuXTu9+OKLxVokAAAAANztTBaLxVLYne677z5t375djRs3tmk/duyYunbtWuK+6ys1NVWenp5K2TdJZg8Xe5dzZ2gWYe8KAAAAAMNZs0FKisxmc6H2LdKCG6mpqbpy5Uq+9itXruj3338vypAAAAAAUKIVKXw99thjGjFihD766COdP39e58+f1//+9z8FBwerX79+xV0jAAAAANz1inTP17JlyzRhwgQNGjRIWVlZ1wdyclJwcLDmzJlTrAUCAAAAQElQpHu+8qSnp+vHH3+UJNWuXVvu7u7FVtidhHu+boB7vgAAAHAPMvyerzyXLl3SpUuXVLduXbm7u+sWchwAAAAAlGhFCl+//PKLOnfurHr16qlHjx66dOmSJCk4OJhl5gEAAADgBooUvsaPH69SpUopISFBbm5u1vannnpKW7duLbbiAAAAAKCkKNKCG9u3b9e2bdtUpUoVm/a6devq7NmzxVIYAAAAAJQkRTrzlZ6ebnPGK8+vv/4qF5eCL0ixdOlSNWvWTGazWWazWf7+/tqyZYt1+7Vr1xQSEqJy5crJw8ND/fv3V1JSks0YCQkJ6tmzp9zc3FSxYkW99NJLys7Otumze/dutWzZUi4uLqpTp46ioqIKN2EAAAAAuEVFCl/t2rXTqlWrrM9NJpNyc3M1e/ZsdezYscDjVKlSRTNnzlRsbKwOHz6sTp06qU+fPjp+/Lik65c3fvrpp1q/fr327Nmjixcv2nyPWE5Ojnr27KnMzEzt379fK1euVFRUlCZPnmztc/r0afXs2VMdO3ZUXFycQkNDNXLkSG3btq0oUwcAAACAIinSUvPHjh1T586d1bJlS+3cuVOPPvqojh8/rl9//VX79u1T7dq1i1yQt7e35syZo8cff1wVKlTQ6tWr9fjjj0uSTpw4oYYNGyomJkZt27bVli1b1KtXL128eFE+Pj6Srn8H2csvv6wrV67I2dlZL7/8sjZv3qxjx45ZjzFgwAAlJycX+P40lpq/AZaaBwAAwD3I8KXmmzRpoh9++EEPP/yw+vTpo/T0dPXr10/ffPNNkYNXTk6O1qxZo/T0dPn7+ys2NlZZWVkKCAiw9mnQoIGqVaummJgYSVJMTIyaNm1qDV6SFBgYqNTUVOvZs5iYGJsx8vrkjXEjGRkZSk1NtXkAAAAAwK0o9IIbWVlZ6tatm5YtW6Z///vft1zA0aNH5e/vr2vXrsnDw0MbNmxQo0aNFBcXJ2dnZ3l5edn09/HxUWJioiQpMTHRJnjlbc/bdrM+qamp+uOPP1S6dOl8Nc2YMUNTp0695bkBAAAAQJ5Cn/kqVaqUvv3222IroH79+oqLi9OBAwf03HPPKSgoSN99912xjV8U4eHhSklJsT7OnTtn13oAAAAA3P2KdNnhkCFD9M477xRLAc7OzqpTp478/Pw0Y8YMNW/eXAsXLpSvr68yMzOVnJxs0z8pKUm+vr6SJF9f33yrH+Y9/6c+ZrP5hme9JMnFxcW6AmPeAwAAAABuRZG+5ys7O1vvvvuuPv/8c/n5+cnd3d1m+7x584pcUG5urjIyMuTn56dSpUppx44d6t+/vyQpPj5eCQkJ8vf3lyT5+/vrtdde0+XLl1WxYkVJUnR0tMxmsxo1amTt89lnn9kcIzo62joGAAAAABihUOHrp59+Uo0aNXTs2DG1bNlSkvTDDz/Y9DGZTAUeLzw8XN27d1e1atX0+++/a/Xq1dq9e7e2bdsmT09PBQcHKywsTN7e3jKbzRo7dqz8/f3Vtm1bSVLXrl3VqFEjDR06VLNnz1ZiYqJeeeUVhYSEWL9vbPTo0Vq8eLEmTpyop59+Wjt37tS6deu0efPmwkwdAAAAAG5JocJX3bp1denSJe3atUuS9NRTT2nRokX5FrQoqMuXL2vYsGG6dOmSPD091axZM23btk1dunSRJM2fP18ODg7q37+/MjIyFBgYqLfeesu6v6OjozZt2qTnnntO/v7+cnd3V1BQkF599VVrn5o1a2rz5s0aP368Fi5cqCpVqmjFihUKDAwsUs0AAAAAUBSF+p4vBwcHJSYmWi/xM5vNiouLU61atW5bgXcCvufrBvieLwAAANyDDP+erzxF+H5mAAAAALgnFSp8mUymfPd0FeYeLwAAAAC4VxXqni+LxaLhw4dbF7O4du2aRo8enW+1w48++qj4KgQAAACAEqBQ4SsoKMjm+ZAhQ4q1GAAAAAAoqQoVviIjI29XHQAAAABQot3SghsAAAAAgIIhfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGsGv4mjFjhh544AGVKVNGFStWVN++fRUfH2/T59q1awoJCVG5cuXk4eGh/v37KykpyaZPQkKCevbsKTc3N1WsWFEvvfSSsrOzbfrs3r1bLVu2lIuLi+rUqaOoqKjbPT0AAAAAsLJr+NqzZ49CQkL01VdfKTo6WllZWeratavS09OtfcaPH69PP/1U69ev1549e3Tx4kX169fPuj0nJ0c9e/ZUZmam9u/fr5UrVyoqKkqTJ0+29jl9+rR69uypjh07Ki4uTqGhoRo5cqS2bdtm6HwBAAAA3LtMFovFYu8i8ly5ckUVK1bUnj171L59e6WkpKhChQpavXq1Hn/8cUnSiRMn1LBhQ8XExKht27basmWLevXqpYsXL8rHx0eStGzZMr388su6cuWKnJ2d9fLLL2vz5s06duyY9VgDBgxQcnKytm7dmq+OjIwMZWRkWJ+npqaqatWqStk3SWYPl9v8KtwlmkXYuwIAAADAcKmpqfL09FRKSorMZnOh9r2j7vlKSUmRJHl7e0uSYmNjlZWVpYCAAGufBg0aqFq1aoqJiZEkxcTEqGnTptbgJUmBgYFKTU3V8ePHrX3+PEZen7wx/mrGjBny9PS0PqpWrVp8kwQAAABwT7pjwldubq5CQ0P10EMPqUmTJpKkxMREOTs7y8vLy6avj4+PEhMTrX3+HLzytudtu1mf1NRU/fHHH/lqCQ8PV0pKivVx7ty5YpkjAAAAgHuXk70LyBMSEqJjx47pyy+/tHcpcnFxkYsLlxcCAAAAKD53RPgaM2aMNm3apL1796pKlSrWdl9fX2VmZio5Odnm7FdSUpJ8fX2tfQ4ePGgzXt5qiH/u89cVEpOSkmQ2m1W6dOnbMaUCiVjawW7HvmU+/9wFAGC8iAh7VwAA+Dt2vezQYrFozJgx2rBhg3bu3KmaNWvabPfz81OpUqW0Y8cOa1t8fLwSEhLk7+8vSfL399fRo0d1+fJla5/o6GiZzWY1atTI2ufPY+T1yRsDAAAAAG43u575CgkJ0erVq/Xxxx+rTJky1nu0PD09Vbp0aXl6eio4OFhhYWHy9vaW2WzW2LFj5e/vr7Zt20qSunbtqkaNGmno0KGaPXu2EhMT9corrygkJMR66eDo0aO1ePFiTZw4UU8//bR27typdevWafPmzXabOwAAAIB7i13PfC1dulQpKSnq0KGDKlWqZH2sXbvW2mf+/Pnq1auX+vfvr/bt28vX11cfffSRdbujo6M2bdokR0dH+fv7a8iQIRo2bJheffVVa5+aNWtq8+bNio6OVvPmzTV37lytWLFCgYGBhs4XAAAAwL3rjvqerzuVdS3/Yv6er7v7nq8O9q4AAHAD3PMFALdXifmeLwAAAAAoqQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIABCF8AAAAAYADCFwAAAAAYgPAFAAAAAAYgfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwBQAAAAAGIHwBAAAAgAEIXwAAAABgAMIXAAAAABiA8AUAAAAABiB8AQAAAIAB7Bq+9u7dq969e6ty5coymUzauHGjzXaLxaLJkyerUqVKKl26tAICAnTy5EmbPr/++qsGDx4ss9ksLy8vBQcHKy0tzabPt99+q3bt2snV1VVVq1bV7Nmzb/fUAAAAAMCGXcNXenq6mjdvriVLltxw++zZs7Vo0SItW7ZMBw4ckLu7uwIDA3Xt2jVrn8GDB+v48eOKjo7Wpk2btHfvXo0aNcq6PTU1VV27dlX16tUVGxurOXPmKCIiQm+//fZtnx8AAAAA5HGy58G7d++u7t2733CbxWLRggUL9Morr6hPnz6SpFWrVsnHx0cbN27UgAED9P3332vr1q06dOiQWrVqJUl688031aNHD73xxhuqXLmy3n//fWVmZurdd9+Vs7OzGjdurLi4OM2bN88mpAEAAADA7XTH3vN1+vRpJSYmKiAgwNrm6empNm3aKCYmRpIUExMjLy8va/CSpICAADk4OOjAgQPWPu3bt5ezs7O1T2BgoOLj4/Xbb7/d8NgZGRlKTU21eQAAAADArbhjw1diYqIkycfHx6bdx8fHui0xMVEVK1a02e7k5CRvb2+bPjca48/H+KsZM2bI09PT+qhateqtTwgAAADAPe2ODV/2FB4erpSUFOvj3Llz9i4JAAAAwF3ujg1fvr6+kqSkpCSb9qSkJOs2X19fXb582WZ7dna2fv31V5s+Nxrjz8f4KxcXF5nNZpsHAAAAANyKOzZ81axZU76+vtqxY4e1LTU1VQcOHJC/v78kyd/fX8nJyYqNjbX22blzp3Jzc9WmTRtrn7179yorK8vaJzo6WvXr11fZsmUNmg0AAACAe51dw1daWpri4uIUFxcn6foiG3FxcUpISJDJZFJoaKimT5+uTz75REePHtWwYcNUuXJl9e3bV5LUsGFDdevWTc8884wOHjyoffv2acyYMRowYIAqV64sSRo0aJCcnZ0VHBys48ePa+3atVq4cKHCwsLsNGsAAAAA9yK7LjV/+PBhdezY0fo8LxAFBQUpKipKEydOVHp6ukaNGqXk5GQ9/PDD2rp1q1xdXa37vP/++xozZow6d+4sBwcH9e/fX4sWLbJu9/T01Pbt2xUSEiI/Pz+VL19ekydPZpl5AAAAAIYyWSwWi72LuNOlpqbK09NTKfsmyezhUmzjRiztUGxjGc6ng70rAADcQESEvSsAgJLNmg1SUgq9NsQde88XAAAAAJQkhC8AAAAAMADhCwAAAAAMQPgCAAAAAAMQvgAAAADAAIQvAAAAADAA4QsAAAAADED4AgAAAAADEL4AAAAAwACELwAAAAAwAOELAAAAAAxA+AIAAAAAAxC+AAAAAMAAhC8AAAAAMADhCwAAAAAMQPgCAAAAAAMQvgAAAADAAIQvAAAAADAA4QsAAAAADED4AgAAAAADEL4AAAAAwABO9i4Ad6mk3fau4M7i08HeFQAAAOAOx5kvAAAAADAA4QsAAAAADED4AgAAAAADEL4AAAAAwACELwAAAAAwAOELAAAAAAxA+AIAAAAAAxC+AAAAAMAAhC8AAAAAMADhCwAAAAAMQPgCAAAAAAMQvgAAAADAAIQvAAAAADAA4QsAAAAADED4AgAAAAADEL4AAAAAwACELwAAAAAwAOELAAAAAAxA+AIAAAAAAxC+AAAAAMAAhC8AAAAAMADhCwAAAAAMQPgCAAAAAAMQvgAAAADAAIQvAAAAADAA4QsAAAAADOBk7wKAEiFpt70ruLP4dLB3BcA9KyLC3hUAQMmWkVH0fTnzBQAAAAAGIHwBAAAAgAG47BBA8eMyTFtchgkAAMSZLwAAAAAwBOELAAAAAAxA+AIAAAAAAxC+AAAAAMAAhC8AAAAAMMA9tdrhkiVLNGfOHCUmJqp58+Z688031bp1a3uXBQAAABQeqwvbugtWF75nwtfatWsVFhamZcuWqU2bNlqwYIECAwMVHx+vihUr2rs8ACUZ/+cIoKjugl8mARTcPXPZ4bx58/TMM89oxIgRatSokZYtWyY3Nze9++679i4NAAAAwD3gnjjzlZmZqdjYWIWHh1vbHBwcFBAQoJiYmHz9MzIylJGRYX2ekpIiSUpNz8jX91ZkZKYX63gAAKCEyUi1dwW4k/G7pC2DPi8Z//9xLBZLofe9J8LXzz//rJycHPn4+Ni0+/j46MSJE/n6z5gxQ1OnTs3XXrXr/NtWIwAAAIC7xy+//CJPT89C7XNPhK/CCg8PV1hYmPV5cnKyqlevroSEhEK/wEBhpKamqmrVqjp37pzMZrO9y0EJxnsNRuG9BqPwXoNRUlJSVK1aNXl7exd633sifJUvX16Ojo5KSkqyaU9KSpKvr2++/i4uLnJxccnX7unpyYcZhjCbzbzXYAjeazAK7zUYhfcajOLgUPjlM+6JBTecnZ3l5+enHTt2WNtyc3O1Y8cO+fv727EyAAAAAPeKe+LMlySFhYUpKChIrVq1UuvWrbVgwQKlp6drxIgR9i4NAAAAwD3gnglfTz31lK5cuaLJkycrMTFRLVq00NatW/MtwnEjLi4umjJlyg0vRQSKE+81GIX3GozCew1G4b0Go9zKe81kKcoaiQAAAACAQrkn7vkCAAAAAHsjfAEAAACAAQhfAAAAAGAAwhcAAAAAGIDwVQBLlixRjRo15OrqqjZt2ujgwYP2Lgkl0N69e9W7d29VrlxZJpNJGzdutHdJKIFmzJihBx54QGXKlFHFihXVt29fxcfH27sslEBLly5Vs2bNrF946+/vry1btti7LNwDZs6cKZPJpNDQUHuXghImIiJCJpPJ5tGgQYNCjUH4+gdr165VWFiYpkyZoq+//lrNmzdXYGCgLl++bO/SUMKkp6erefPmWrJkib1LQQm2Z88ehYSE6KuvvlJ0dLSysrLUtWtXpaen27s0lDBVqlTRzJkzFRsbq8OHD6tTp07q06ePjh8/bu/SUIIdOnRIy5cvV7NmzexdCkqoxo0b69KlS9bHl19+Waj9WWr+H7Rp00YPPPCAFi9eLEnKzc1V1apVNXbsWE2aNMnO1aGkMplM2rBhg/r27WvvUlDCXblyRRUrVtSePXvUvn17e5eDEs7b21tz5sxRcHCwvUtBCZSWlqaWLVvqrbfe0vTp09WiRQstWLDA3mWhBImIiNDGjRsVFxdX5DE483UTmZmZio2NVUBAgLXNwcFBAQEBiomJsWNlAFA8UlJSJF3/pRi4XXJycrRmzRqlp6fL39/f3uWghAoJCVHPnj1tfm8DitvJkydVuXJl1apVS4MHD1ZCQkKh9ne6TXWVCD///LNycnLk4+Nj0+7j46MTJ07YqSoAKB65ubkKDQ3VQw89pCZNmti7HJRAR48elb+/v65duyYPDw9t2LBBjRo1sndZKIHWrFmjr7/+WocOHbJ3KSjB2rRpo6ioKNWvX1+XLl3S1KlT1a5dOx07dkxlypQp0BiELwC4R4WEhOjYsWOFvl4dKKj69esrLi5OKSkp+vDDDxUUFKQ9e/YQwFCszp07p3Hjxik6Olqurq72LgclWPfu3a3/btasmdq0aaPq1atr3bp1Bb6cmvB1E+XLl5ejo6OSkpJs2pOSkuTr62unqgDg1o0ZM0abNm3S3r17VaVKFXuXgxLK2dlZderUkST5+fnp0KFDWrhwoZYvX27nylCSxMbG6vLly2rZsqW1LScnR3v37tXixYuVkZEhR0dHO1aIksrLy0v16tXTqVOnCrwP93zdhLOzs/z8/LRjxw5rW25urnbs2ME16wDuShaLRWPGjNGGDRu0c+dO1axZ094l4R6Sm5urjIwMe5eBEqZz5846evSo4uLirI9WrVpp8ODBiouLI3jhtklLS9OPP/6oSpUqFXgfznz9g7CwMAUFBalVq1Zq3bq1FixYoPT0dI0YMcLepaGESUtLs/nLyenTpxUXFydvb29Vq1bNjpWhJAkJCdHq1av18ccfq0yZMkpMTJQkeXp6qnTp0nauDiVJeHi4unfvrmrVqun333/X6tWrtXv3bm3bts3epaGEKVOmTL77Vt3d3VWuXDnuZ0WxmjBhgnr37q3q1avr4sWLmjJlihwdHTVw4MACj0H4+gdPPfWUrly5osmTJysxMVEtWrTQ1q1b8y3CAdyqw4cPq2PHjtbnYWFhkqSgoCBFRUXZqSqUNEuXLpUkdejQwaY9MjJSw4cPN74glFiXL1/WsGHDdOnSJXl6eqpZs2batm2bunTpYu/SAKBIzp8/r4EDB+qXX35RhQoV9PDDD+urr75ShQoVCjwG3/MFAAAAAAbgni8AAAAAMADhCwAAAAAMQPgCAAAAAAMQvgAAAADAAIQvAAAAADAA4QsAAAAADED4AgAAAAADEL4AAAAAwACELwAAbqBDhw4KDQ21dxkAgBKE8AUAKHF69+6tbt263XDbF198IZPJpG+//dbgqgAA9zrCFwCgxAkODlZ0dLTOnz+fb1tkZKRatWqlZs2a2aEyAMC9jPAFAChxevXqpQoVKigqKsqmPS0tTevXr1ffvn01cOBA3XfffXJzc1PTpk31wQcf3HRMk8mkjRs32rR5eXnZHOPcuXN68skn5eXlJW9vb/Xp00dnzpyxbt+9e7dat24td3d3eXl56aGHHtLZs2dvcbYAgLsF4QsAUOI4OTlp2LBhioqKksVisbavX79eOTk5GjJkiPz8/LR582YdO3ZMo0aN0tChQ3Xw4MEiHzMrK0uBgYEqU6aMvvjiC+3bt08eHh7q1q2bMjMzlZ2drb59++qRRx7Rt99+q5iYGI0aNUomk6k4pgwAuAs42bsAAABuh6efflpz5szRnj171KFDB0nXLzns37+/qlevrgkTJlj7jh07Vtu2bdO6devUunXrIh1v7dq1ys3N1YoVK6yBKjIyUl5eXtq9e7datWqllJQU9erVS7Vr15YkNWzY8NYmCQC4q3DmCwBQIjVo0EAPPvig3n33XUnSqVOn9MUXXyg4OFg5OTmaNm2amjZtKm9vb3l4eGjbtm1KSEgo8vGOHDmiU6dOqUyZMvLw8JCHh4e8vb117do1/fjjj/L29tbw4cMVGBio3r17a+HChbp06VJxTRcAcBcgfAEASqzg4GD973//0++//67IyEjVrl1bjzzyiObMmaOFCxfq5Zdf1q5duxQXF6fAwEBlZmb+7Vgmk8nmEkbp+qWGedLS0uTn56e4uDibxw8//KBBgwZJun4mLCYmRg8++KDWrl2revXq6auvvro9kwcA3HEIXwCAEuvJJ5+Ug4ODVq9erVWrVunpp5+WyWTSvn371KdPHw0ZMkTNmzdXrVq19MMPP9x0rAoVKticqTp58qSuXr1qfd6yZUudPHlSFStWVJ06dWwenp6e1n7333+/wsPDtX//fjVp0kSrV68u/okDAO5IhC8AQInl4eGhp556SuHh4bp06ZKGDx8uSapbt66io6O1f/9+ff/993r22WeVlJR007E6deqkxYsX65tvvtHhw4c1evRolSpVyrp98ODBKl++vPr06aMvvvhCp0+f1u7du/XCCy/o/PnzOn36tMLDwxUTE6OzZ89q+/btOnnyJPd9AcA9hPAFACjRgoOD9dtvvykwMFCVK1eWJL3yyitq2bKlAgMD1aFDB/n6+qpv3743HWfu3LmqWrWq2rVrp0GDBmnChAlyc3Ozbndzc9PevXtVrVo19evXTw0bNlRwcLCuXbsms9ksNzc3nThxQv3791e9evU0atQohYSE6Nlnn72d0wcA3EFMlr9ewA4AAAAAKHac+QIAAAAAAxC+AAAAAMAAhC8AAAAAMADhCwAAAAAMQPgCAAAAAAMQvgAAAADAAIQvAAAAADAA4QsAAAAADED4AgAAAAADEL4AAAAAwACELwAAAAAwwP8HtGQOPEQNBkIAAAAASUVORK5CYII=", "text/plain": [ - "tensor([[ 0, 0],\n", - " [ 1, 1],\n", - " [ 2, 0],\n", - " ...,\n", - " [997, 1],\n", - " [998, 0],\n", - " [999, 1]], dtype=torch.int32)" + "
" ] }, - "execution_count": 2, "metadata": {}, - "output_type": "execute_result" + "output_type": "display_data" } ], "source": [ - "# generate a population of 1000 genes with 30% of them labelled 1 (signal)\n", - "# and 0 (noise)\n", - "# note that 1000 and 0.3 are the default values for this function\n", - "population1 = generate_gene_population(1000, 0.3)\n", "\n", - "population1" + "# Plotting\n", + "plt.figure(figsize=(10, 6))\n", + "plt.hist(noise_binding, bins=30, alpha=0.5, label='Label 0', color='orange')\n", + "plt.hist(signal_binding, bins=30, alpha=0.5, label='Label 1', color='blue')\n", + "plt.xlim(0,5)\n", + "plt.title('Histogram of Values in the 2nd Column')\n", + "plt.xlabel('Values')\n", + "plt.ylabel('Frequency')\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Perturbation effect distribtuions should match expectations" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The noise binding max is 3.423511505126953 and the min is -3.506139039993286\n", + "the noise min is -3.506139039993286\n", + "the noise mean is 0.010617653839290142 and the std is 0.988001823425293\n", + "The signal binding max is 6.107701301574707 and the min is -6.406703948974609\n", + "the signal min is -6.406703948974609\n", + "the signal mean is -0.011303802020847797 and the std is 3.136451482772827\n" + ] + } + ], + "source": [ + "noise_perturbation = final_data_tensor[:, :, 3].flatten()[labels == 0]\n", + "signal_perturbation = final_data_tensor[:, :, 3].flatten()[labels == 1]\n", + "\n", + "print(f\"The noise binding max is {noise_perturbation.max()} and the min is {noise_perturbation.min()}\")\n", + "print(f\"the noise min is {noise_perturbation.min()}\")\n", + "print(f\"the noise mean is {noise_perturbation.mean()} and the std is {noise_perturbation.std()}\")\n", + "print(f\"The signal binding max is {signal_perturbation.max()} and the min is {signal_perturbation.min()}\")\n", + "print(f\"the signal min is {signal_perturbation.min()}\")\n", + "print(f\"the signal mean is {signal_perturbation.mean()} and the std is {signal_perturbation.std()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, "metadata": {}, "outputs": [ { "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
gene_idsignalexpression_effectexpression_pvaluebinding_effectbinding_pvalueregulator
00False0.5865050.4909230.00.617704TF1
11True-1.8041740.8128140.00.931650TF1
22False-1.7112460.7967020.00.092472TF1
33False0.2168890.1345060.00.586258TF1
44False1.4779740.5711520.00.566378TF1
........................
995995False-3.2089760.5866911.00.157508TF1
996996True-2.4313890.9393270.00.661516TF1
997997True-2.8185330.3190820.00.117773TF1
998998False-1.8598000.7332800.00.606050TF1
999999True-1.9968190.4209800.00.649173TF1
\n", - "

1000 rows × 7 columns

\n", - "
" - ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1IAAAIjCAYAAAAJLyrXAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/H5lhTAAAACXBIWXMAAA9hAAAPYQGoP6dpAABTp0lEQVR4nO3deVhV5f7+8XszCwqICmjirKk540RmOZA4Hk3LzAmNMg1NJTU5leKcmkPOdjLUU6Z5yupYDjibkqampuZUKk6gZYDgkXH//vDH/rbDgYXgBn2/rmtfx/2sZ631efbadLh51mAym81mAQAAAAByzM7WBQAAAABAYUOQAgAAAACDCFIAAAAAYBBBCgAAAAAMIkgBAAAAgEEEKQAAAAAwiCAFAAAAAAYRpAAAAADAIIIUAAAAABhEkAJQaFWoUEH9+vWzdRkPvenTp6tSpUqyt7dXvXr1Hth+ly5dKpPJpLNnzz6wfRphMpkUERGR7/vZtm2bTCaT/vOf/+T7vh6Us2fPymQyaenSpTbZf79+/VShQgWb7BvAw4MgBaBAyPqled++fbdd3qJFC9WqVeu+9/Pdd989kF9+HxYbN27UqFGj1KxZM0VGRmry5MnZ+qSlpalkyZJ66qmn7rgds9ksPz8/NWjQID/LLdRWrFih2bNn22z/58+f17hx49S4cWMVL15cJUuWVIsWLbRp0yab1ZQlLi5OI0aMUPXq1eXq6io3Nzf5+/tr4sSJio+Pt3V5AB5RDrYuAABy68SJE7KzM/b3oO+++07z588nTOXQli1bZGdnpyVLlsjJyem2fRwdHfXCCy9o8eLFOnfunMqXL5+tz44dO3ThwgUNHz48v0t+YP73v//JwSHv/m90xYoVOnLkiIYNG5Zn2zTi66+/1tSpU9WlSxcFBwcrPT1dy5cv17PPPquPP/5Y/fv3t0ldP/74o9q3b6+kpCT17t1b/v7+kqR9+/bpvffe044dO7Rx40ab1Abg0UaQAlBoOTs727oEw5KTk+Xm5mbrMnLsypUrKlKkyB1DVJZevXpp0aJF+uyzzzR69Ohsy1esWCE7Ozv16NEjv0p94FxcXGxdQp5q2bKlYmJiVLJkSUvbwIEDVa9ePY0ZM8YmQSo+Pl7PPfec7O3t9dNPP6l69epWyydNmqR//etfD7wuAJA4tQ9AIfb3a6TS0tI0btw4Va1aVS4uLipRooSeeuopRUVFSbp1XcT8+fMl3bq+JeuVJTk5WW+++ab8/Pzk7Oysxx9/XO+//77MZrPVfv/3v//pjTfeUMmSJVWsWDH94x//0MWLF7NdMxMRESGTyaRjx46pZ8+eKl68uOX0t8OHD6tfv36qVKmSXFxc5Ovrq5dffll//PGH1b6ytnHy5En17t1bHh4eKlWqlN59912ZzWadP39enTt3lru7u3x9fTVjxowcfXbp6emaMGGCKleuLGdnZ1WoUEH//Oc/lZKSYuljMpkUGRmp5ORky2d1p2tamjVrpgoVKmjFihXZlqWlpek///mPWrZsqTJlyuR47Ldzp+uSbne9XHx8vIYNG2Y5nlWqVNHUqVOVmZlp1W/lypXy9/dXsWLF5O7urtq1a+uDDz4wXEvWsTp9+rT69esnT09PeXh4qH///rpx48Zdt9WiRQt9++23OnfunOWz/vs1PJmZmZo0aZLKli0rFxcXtW7dWqdPn862rT179qht27by8PCQq6urnnnmGe3ateue43niiSesQpR0648V7du314ULF3T9+nVLe79+/VS0aFFdvHhRXbp0UdGiRVWqVCmNGDFCGRkZVtuIj49Xv3795OHhIU9PTwUHB+f4dLzFixfr4sWLmjlzZrYQJUk+Pj565513rNoWLFigJ554Qs7OzipTpoxCQ0Pvub+s69C2bdtm1X67a7myxh4TE6OOHTuqaNGieuyxxyz/bfn555/VqlUrubm5qXz58tl+JrJOY961a5fCwsJUqlQpubm56bnnntPVq1dz9LkAKBiYkQJQoCQkJOj333/P1p6WlnbPdSMiIjRlyhS98soraty4sRITE7Vv3z4dOHBAzz77rF577TVdunRJUVFR+ve//221rtls1j/+8Q9t3bpVISEhqlevnjZs2KCRI0fq4sWLmjVrlqVvv3799Pnnn6tPnz5q2rSptm/frg4dOtyxrhdeeEFVq1bV5MmTLaEsKipKv/32m/r37y9fX18dPXpUH374oY4ePaoffvjBKuBJ0osvvqgaNWrovffe07fffquJEyfKy8tLixcvVqtWrTR16lR9+umnGjFihBo1aqSnn376rp/VK6+8omXLlun555/Xm2++qT179mjKlCn65ZdftGbNGknSv//9b3344Yfau3evPvroI0nSk08+edvtmUwm9ezZU5MnT9bRo0f1xBNPWJatX79e165dU69evXI19ty4ceOGnnnmGV28eFGvvfaaypUrp927dys8PFyXL1+2XIsUFRWll156Sa1bt9bUqVMlSb/88ot27dqloUOH5mrf3bt3V8WKFTVlyhQdOHBAH330kby9vS3bv523335bCQkJunDhguW7VrRoUas+7733nuzs7DRixAglJCRo2rRp6tWrl/bs2WPps2XLFrVr107+/v4aO3as7OzsFBkZqVatWmnnzp1q3Lix4fHExsbK1dVVrq6uVu0ZGRkKCgpSkyZN9P7772vTpk2aMWOGKleurEGDBkm69XPVuXNnff/99xo4cKBq1KihNWvWKDg4OEf7/uabb1SkSBE9//zzOeofERGhcePGKTAwUIMGDdKJEye0cOFC/fjjj9q1a5ccHR2NDf4OMjIy1K5dOz399NOaNm2aPv30Uw0ePFhubm56++231atXL3Xt2lWLFi1S3759FRAQoIoVK1ptY8iQISpevLjGjh2rs2fPavbs2Ro8eLBWrVqVJzUCeADMAFAAREZGmiXd9fXEE09YrVO+fHlzcHCw5X3dunXNHTp0uOt+QkNDzbf7T99XX31llmSeOHGiVfvzzz9vNplM5tOnT5vNZrN5//79ZknmYcOGWfXr16+fWZJ57NixlraxY8eaJZlfeumlbPu7ceNGtrbPPvvMLMm8Y8eObNsYMGCApS09Pd1ctmxZs8lkMr/33nuW9j///NNcpEgRq8/kdg4ePGiWZH7llVes2keMGGGWZN6yZYulLTg42Ozm5nbX7WU5evSoWZI5PDzcqr1Hjx5mFxcXc0JCgtlszvnYs74TZ86csbT9/TPO8vfvwoQJE8xubm7mkydPWvUbPXq02d7e3hwTE2M2m83moUOHmt3d3c3p6ek5GuNf3el4v/zyy1b9nnvuOXOJEiXuub0OHTqYy5cvn61969atZknmGjVqmFNSUiztH3zwgVmS+eeffzabzWZzZmamuWrVquagoCBzZmampd+NGzfMFStWND/77LMGR2g2nzp1yuzi4mLu06ePVXtwcLBZknn8+PFW7fXr1zf7+/tb3mf9XE2bNs3Slp6ebm7evLlZkjkyMvKu+y9evLi5bt26Oar1ypUrZicnJ3ObNm3MGRkZlvZ58+aZJZk//vhjq/r/+llnfcZbt2612uaZM2ey1Zk19smTJ1va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", "text/plain": [ - " gene_id signal expression_effect expression_pvalue binding_effect \\\n", - "0 0 False 0.586505 0.490923 0.0 \n", - "1 1 True -1.804174 0.812814 0.0 \n", - "2 2 False -1.711246 0.796702 0.0 \n", - "3 3 False 0.216889 0.134506 0.0 \n", - "4 4 False 1.477974 0.571152 0.0 \n", - ".. ... ... ... ... ... \n", - "995 995 False -3.208976 0.586691 1.0 \n", - "996 996 True -2.431389 0.939327 0.0 \n", - "997 997 True -2.818533 0.319082 0.0 \n", - "998 998 False -1.859800 0.733280 0.0 \n", - "999 999 True -1.996819 0.420980 0.0 \n", - "\n", - " binding_pvalue regulator \n", - "0 0.617704 TF1 \n", - "1 0.931650 TF1 \n", - "2 0.092472 TF1 \n", - "3 0.586258 TF1 \n", - "4 0.566378 TF1 \n", - ".. ... ... \n", - "995 0.157508 TF1 \n", - "996 0.661516 TF1 \n", - "997 0.117773 TF1 \n", - "998 0.606050 TF1 \n", - "999 0.649173 TF1 \n", - "\n", - "[1000 rows x 7 columns]" + "
" ] }, - "execution_count": 6, "metadata": {}, - "output_type": "execute_result" + "output_type": "display_data" } ], "source": [ - "# generate perturbation and binding data based on this population\n", - "# note that these are the default values for this function\n", - "population1_tf1_data = generate_perturbation_binding_data(\n", - "\tpopulation1,\n", - "\t0.0, 1.0,\n", - "\t3.0, 1.0,\n", - "\t1e-3,\n", - "\t0.5)\n", + "# Plotting\n", + "plt.figure(figsize=(10, 6))\n", + "plt.hist(noise_perturbation, bins=30, alpha=0.5, label='Label 0', color='orange')\n", + "plt.hist(signal_perturbation, bins=30, alpha=0.5, label='Label 1', color='blue')\n", + "plt.title('Histogram of Values in the 2nd Column')\n", + "plt.xlabel('Values')\n", + "plt.ylabel('Frequency')\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The binding effects should be positively correlated with the perturbaiton effects" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA04AAAIjCAYAAAA0vUuxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/H5lhTAAAACXBIWXMAAA9hAAAPYQGoP6dpAADazUlEQVR4nOzdd3hT5RcH8O9N0hYKbdlDRtkgU/beIDKVKRtkKLKHCzcuQAUB8ceWrWxQkL0VRFA2yJS9ymyBQtsk9/fHIW3TJjdJm9Hx/TxPHtqbm3tP05bec9/3PUdRVVUFERERERER2aXzdQBEREREREQpHRMnIiIiIiIiB5g4EREREREROcDEiYiIiIiIyAEmTkRERERERA4wcSIiIiIiInKAiRMREREREZEDTJyIiIiIiIgcYOJERERERETkABMnIvIpRVHw6aefevSY8+bNg6IouHjxolvP4263bt1Chw4dkD17diiKgkmTJgEAzp49ixdffBEhISFQFAVr1qzxaZzkPjt37oSiKFixYoVXzueJ3zd34+8BEaVUTJyIyK0sSUr8R65cudCwYUNs2LDB1+F53aeffpro/Yj/uHnzZuy+I0aMwKZNmzB69GgsXLgQL730EgCgV69eOHbsGL788kssXLgQVapUcWuMkZGR+PTTT7Fz5063HteTevfubfU+BgcHo0KFCpgwYQKioqLcdp7U+N6sX78+xSVH/D0gorTA4OsAiCht+uyzz1C4cGGoqopbt25h3rx5aNGiBdauXYtWrVrF7vfkyRMYDJ79r6hHjx7o3LkzAgICPHoeLdOmTUPmzJkTbc+SJUvsx9u3b8fLL7+Mt956K3bbkydP8Oeff+KDDz7A4MGDPRJbZGQkxowZAwBo0KCBR87hCQEBAZg9ezYA4MGDB1i5ciXeeustHDhwAEuWLHHLOVLje7N+/Xr88MMPNpMnb/y+aeHvARGlZkyciMgjmjdvbnVHuG/fvsidOzd+/vlnq8QpQ4YMHo9Fr9dDr9d7/DxaOnTogBw5cmjuExYWZnUBCQC3b98GgETbCTAYDOjevXvs5wMHDkT16tWxdOlSTJw4Ec8991ySj202mxEdHe2OMG0yGo0wm80eO7493vh908LfAyJKzThVj4i8IkuWLMiYMWOiu90J11xYpvScO3cOvXv3RpYsWRASEoLXXnsNkZGRVq+NiorCiBEjkDNnTgQFBaFNmza4evVqonPbWuNUqFAhtGrVCn/88QeqVauGDBkyoEiRIliwYEGi1x89ehT169dHxowZkT9/fnzxxReYO3eu29ZNWeJTVRU//PBD7PSlTz/9FKGhoQCAt99+G4qioFChQrGvu3btGvr06YPcuXMjICAAZcqUwY8//pjo+E+fPsWnn36KEiVKIEOGDMibNy/atWuH8+fP4+LFi8iZMycAYMyYMVbnBoCbN2/itddeQ/78+REQEIC8efPi5Zdf1vy6v/32WyiKgkuXLiV6bvTo0fD398f9+/cByLqV9u3bI0+ePMiQIQPy58+Pzp07Izw83OX3UafTxY4UWOKLiorCJ598gmLFiiEgIAAFChTAO++8k2g6n6IoGDx4MBYvXowyZcogICAA06dP13xvGjRoYHNkonfv3lbfp4sXL0JRFHz77beYNGkSihYtioCAAJw8eTJ2H5PJhPfffx958uRBpkyZ0KZNG1y5csXquL///js6duyIggULxn4tI0aMwJMnT6zO/cMPP8R+TZZH/K8z4UjUoUOH0Lx5cwQHByNz5sxo3Lgx9u3bZ7WP5Wd0z549GDlyJHLmzIlMmTKhbdu2sUlNcqW13wMiSns44kREHhEeHo47d+5AVVWEhYXh+++/x6NHj6xGCLR06tQJhQsXxtixY3Hw4EHMnj0buXLlwvjx42P36devHxYtWoSuXbuiVq1a2L59O1q2bOl0jOfOnUOHDh3Qt29f9OrVCz/++CN69+6NypUro0yZMgDkoqxhw4ZQFAWjR49GpkyZMHv2bJen/d27dy/RNoPBgCxZsqBevXpYuHAhevTogaZNm6Jnz54AgPLlyyNLliwYMWIEunTpghYtWsROc7p16xZq1KgRe8GfM2dObNiwAX379kVERASGDx8OQC7IW7VqhW3btqFz584YNmwYHj58iC1btuD48eNo0qQJpk2bhjfffBNt27ZFu3btYs8NAO3bt8eJEycwZMgQFCpUCGFhYdiyZQsuX75sdfEaX6dOnfDOO+9g2bJlePvtt62eW7ZsGV588UVkzZoV0dHRaNasGaKiojBkyBDkyZMH165dw7p16/DgwQOEhIS49B4DwPnz5wEA2bNnh9lsRps2bfDHH3/g9ddfx/PPP49jx47hu+++w5kzZxIVF9i+fTuWLVuGwYMHI0eOHKhQoYLm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ppQkJka+HkWRpreGNNvumIiIiIjSO09X1fNp4lS6dGk0a9YMV69exa5du5AvXz4MHDgQ/fv3t7l/VFQUoqKiYj+PiIhAgQIFrN6cx4+BJUviejlVrw707SujL1FRwEsvAbt3W5cttyRZEydKYkVERERERKlLmk6cMmTIAAAYOXIkOnbsiAMHDmDYsGGYPn06evXqlWj/Tz/9FGPGjEm03ZU3JyoK+OYbYOpUaRQLSHL13nvAK68k+UshIiIiIiIfStOJk7+/P6pUqYK9e/fGbhs6dCgOHDiAP//8M9H+zow4OctkAm7flmp8SVhSRUREREREKYinEyefrnrJmzcvSieoxvD8889j5cqVNvcPCAhAQECAW86t1wN58rjlUERERERElMb5tKpe7dq1cfr0aattZ86cQWhoqI8iIiIiIiIiSsynidOIESOwb98+fPXVVzh37hx++uknzJw5E4MGDfJlWERERERERFZ8mjhVrVoVq1evxs8//4yyZcvi888/x6RJk9CtWzdfhkVERERERGTFp8UhksvTC8CIiIiIiCh18HRu4NMRJ3cym4HffgM6dACqVQPatgV++UWq5xERERERESWHT6vqucuTJ0DHjsDmzVItz2QCDh4E1qwB6tWThCpzZl9HSUREREREqVWaGHH64ANg61b52DLCZPl3zx6AtSaIiIiIiCg50sQaJz+/cMTE2J/HqNcDV6+ybxMRERERUVrFNU5OiInRft5kAnbt8k4sRERERESU9qSJxMkZRqOvIyAiIiIiotQq3SRO1av7OgIiIiIiIkqt0kTi1Lq1rGOyRa8HmjUDihXzbkxERERERJR2pInEadIkoHhxQFHkYaHTAaGhwNy5PguNiIiIiIjSgDSROOXIAezfD0yYAJQqBQQHAyVKAOPGST+nvHl9HSEREREREaVmaaIcuadKDhIRERERUerAcuQp2IULwFtvyTTBggWBTp2A3bt9HRUREREREbmbwdcBpFZbtgBt2kgPKZNJtt24ASxfDnz0EfDZZ76Nz5csY5jx15sREREREaVmHHFKgrt3gVdeAaKi4pImIK5X1OefA2vX+iQ0n/rjD+Dll4EMGQCDAahSBVi4MC6RIiIiIiJKrZg4JcG8ecDTp/YTAr1eClWkJz/+CNSrB6xfD0RHA2YzcOgQ0LMn0KcPkyciIiIiSt2YOCXB779r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evWCFWoROaCxj/Lbre713U3TUSUTTqBVauS7jVGu65CIiIiISClXqoYK7r//fg4ePJj7SkxMLH6nzsoQVsX/L/aGE+r+C6p3s38uwAGuitDqP977qdUL6l7u+XxkPLR5wL+YQq3LS77b1Lsi8HGIiIiIiARISBOnGjVq4HQ62bMn/0ave/bsIS4urkD7iIgIoqOj872KpVIzuDwJBq6ChteC43jBBsMJMW0Bw/7ZcJ1IrOIHQI+34bxvIbbPifY55yvUhT4/+d63yDCg50fQ4k4wIvKfi+8PA5ZBZI3i3V+wxF8A8QM9nw+vAWdND148IiIiIiIlzLAsrzWzA65bt2507dqVl16yRy1M06R+/frceuut3HfffV7fm5aWRkxMDAenQXQFLw0rNobImpB9FHBDtW7Q7hGoWCd/u+zDcGwfhFeFsEpweJe9R1H6dvtYgyFQ/cz870ldB7u/AXcmVOtsJz2OwkaivMhMhZRFYGZCtU6lc7NYy4K142Hzc+A+cvygA+pcAj3esz9PEREREZEAyc0NDh4s/gBLIUKeOH3yyScMGzaM119/na5duzJ58mRmzJjB5s2bC6x9OlnBxOn4yE/l5tDpWajZ3R7ZcZWSIgtlgemGQ1vsJLByM332IiIiIhIUgU6cQr5i/+qrr+aff/7hoYceIjk5mQ4dOvDdd9/5TJryueogBODDkVPgcEJM61BHISIiIiJSokI+4lQcgc4qRURERESkdAh0blCqquqJiIiIiIiEghInERERERERH5Q4iYiIiIiI+KDESURERERExAclTiIiIiIiIj4ocRIREREREfFBiZOIiIiIiIgPSpxERERERER8UOIkIiIiIiLigxInERERERERH5Q4iYiIiIiI+KDESURERERExAclTiIiIiIiIj64Qh1AcViWBUBaWlqIIxERERERkVDKyQlycoSSVqoTp3379gFQr169EEciIiIiIiKng3379hETE1Pi/ZbqxKlatWoAJCQkBOTDEcmRlpZGvXr1SExMJDo6OtThSBmmZ02CRc+aBIueNQmWgwcPUr9+/dwcoaSV6sTJ4bCXaMXExOj/iBIU0dHRetYkKPSsSbDoWZNg0bMmwZKTI5R4vwHpVUREREREpAxR4iQiIiIiIuJDqU6cIiIimDBhAhEREaEORco4PWsSLHrWJFj0rEmw6FmTYAn0s2ZYgarXJyIiIiIiUkaU6hEnERERERGRYFDiJCIiIiIi4oMSJxERERERER+UOImIiIiIiPhQqhOnKVOm0LBhQyIjI+nWrRvLli0LdUhSik2aNIkzzzyTypUrU6tWLQYPHsyWLVvytTl69ChjxoyhevXqVKpUiSuuuII9e/aEKGIpK5544gkMw2Ds2LG5x/SsSUnZtWsX1157LdWrVycqKoq2bduyYsWK3POWZfHQQw8RHx9PVFQUffv2Zdu2bSGMWEojt9vN+PHjadSoEVFRUTRp0oT//e9/5K1BpmdNTtWiRYsYNGgQtWvXxjAMZs2ale+8P8/W/v37GTp0KNHR0VSpUoURI0aQnp5epDhKbeL0ySefMG7cOCZMmMCqVato3749/fv3JyUlJdShSSm1cOFCxowZw2+//cbcuXPJysriggsuICMjI7fNnXfeyVdffcXMmTNZuHAhu3fv5vLLLw9h1FLaLV++nNdff5127drlO65nTUrCgQMH6NmzJ2FhYXz77bds3LiRZ599lqpVq+a2eeqpp3jxxRd57bXXWLp0KRUrVqR///4cPXo0hJFLafPkk0/y6quv8vLLL7Np0yaefPJJnnrqKV566aXcNnrW5FRlZGTQvn17pkyZUuh5f56toUOHsmHDBubOncucOXNYtGgRo0aNKlogVinVtWtXa8yYMbm/u91uq3bt2takSZNCGJWUJSkpKRZgLVy40LIsy0pNTbXCwsKsmTNn5rbZtGmTBVhLliwJVZhSih06dMhq1qyZNXfuXOvcc8+17rjjDsuy9KxJybn33nutXr16eTxvmqYVFxdnPf3007nHUlNTrYiICOujjz4KRohSRlx00UXWjTfemO/Y5Zdfbg0dOtSyLD1rUnIA64svvsj93Z9na+PGjRZgLV++PLfNt99+axmGYe3atcvva5fKEafMzExWrlxJ3759c485HA769u3LkiVLQhiZlCUHDx4EoFq1agCsXLmSrKysfM9dy5YtqV+/vp47OSVjxozhoosuyvdMgZ41KTmzZ8+mS5cuXHnlldSqVYuOHTsybdq03PM7duwgOTk537MWExNDt27d9KxJkfTo0YN58+axdetWANasWcPixYsZOHAgoGdNAsefZ2vJkiVUqVKFLl265Lbp27cvDoeDpUuX+n0tV8mFHTx79+7F7XYTGxub73hsbCybN28OUVRSlpimydixY+nZsydnnHEGAMnJyYSHh1OlSpV8bWNjY0lOTg5BlFKaffzxx6xatYrly5cXOKdnTUrKn3/+yauvvsq4ceN44IEHWL58Obfffjvh4eEMGzYs93kq7O9TPWtSFPfddx9paWm0bNkSp9OJ2+3mscceY+jQoQB61iRg/Hm2kpOTqVWrVr7zLpeLatWqFen5K5WJk0igjRkzhvXr17N48eJQhyJlUGJiInfccQdz584lMjIy1OFIGWaaJl26dOHxxx8HoGPHjqxfv57XXnuNYcOGhTg6KUtmzJjBBx98wIcffkibNm1YvXo1Y8eOpXbt2nrWpMwolVP1atSogdPpLFBhas+ePcTFxYUoKikrbr31VubMmcP8+fOpW7du7vG4uDgyMzNJTU3N117PnRTVypUrSUlJoVOnTrhcLlwuFwsXLuTFF1/E5XIRGxurZ01KRHx8PK1bt853rFWrViQkJADkPk/6+1SK6+677+a+++5jyJAhtG3bluuuu44777yTSZMmAXrWJHD8ebbi4uIKFJDLzs5m//79RXr+SmXiFB4eTufOnZk3b17uMdM0mTdvHt27dw9hZFKaWZbFrbfeyhdffMFPP/1Eo0aN8p3v3LkzYWFh+Z67LVu2kJCQoOdOiqRPnz6sW7eO1atX5766dOnC0KFDc3/WsyYloWfPngW2Vdi6dSsNGjQAoFGjRsTFxeV71tLS0li6dKmeNSmSw4cP43Dk/1rpdDoxTRPQsyaB48+z1b17d1JTU1m5cmVum59++gnTNOnWrZv/Fyt2aYsQ+fjjj62IiAjr7bfftjZu3GiNGjXKqlKlipWcnBzq0KSUuvnmm62YmBhrwYIFVlJSUu7r8OHDuW1Gjx5t1a9f3/rpp5+sFStWWN27d7e6d+8ewqilrMhbVc+y9KxJyVi2bJnlcrmsxx57zNq2bZv1wQcfWBUqVLDef//93DZPPPGEVaVKFevLL7+01q5da1166aVWo0aNrCNHjoQwcilthg0bZtWpU8eaM2eOtWPHDuvzzz+3atSoYd1zzz25bfSsyak6dOiQ9fvvv1u///67BVjPPfec9fvvv1s7d+60LMu/Z2vAgAFWx44draVLl1qLFy+2mjVrZl1zzTVFiqPUJk6WZVkvvfSSVb9+fSs8PNzq2rWr9dtvv4U6JCnFgEJf06dPz21z5MgR65ZbbrGqVq1qVahQwbrsssuspKSk0AUtZcbJiZOeNSkpX331lXXGGWdYERERVsuWLa2pU6fmO2+apjV+/HgrNjbWioiIsPr06WNt2bIlRNFKaZWWlmbdcccdVv369a3IyEircePG1oMPPmgdO3Yst42eNTlV8+fPL/Q72rBhwyzL8u/Z2rdvn3XNNddYlSpVsqKjo60bbrjBOnToUJHiMCwrz5bOIiIiIiIiUkCpXOMkIiIiIiISTEqcREREREREfFDiJCIiIiIi4oMSJxERERERER+UOImIiIiIiPigxElERERERMQHJU4iIiIiIiI+KHESERERERHxQYmTiIh49Ndff2EYBqtXry5WPw8//DAdOnTI/X348OEMHjy4WH2WlOTkZPr160fFihWpUqWKx2MiIlK+KXESESmnhg8fjmEYua/q1aszYMAA1q5dm9umXr16JCUlccYZZ5TotV944QXefvvtEu2zMCffY85rwIABuW2ef/55kpKSWL16NVu3bvV4rLgaNmzI5MmTS6QvEREJPiVOIiLl2IABA0hKSiIpKYl58+bhcrm4+OKLc887nU7i4uJwuVwlet2YmJigjeTkvcec10cffZR7fvv27XTu3JlmzZpRq1Ytj8dERKR8U+IkIlKORUREEBcXR1xcHB06dOC+++4jMTGRf/75Byg4VW/BggUYhsG8efPo0qULFSpUoEePHmzZsiVfv0888QSxsbFUrlyZESNGcPTo0XznT56q17t3b26//XbuueceqlWrRlxcHA8//HC+92zevJlevXoRGRlJ69at+fHHHzEMg1mzZvl9jzmvqlWrAvYo0Geffca7776LYRgMHz680GMAqampjBw5kpo1axIdHc3555/PmjVr8l3rq6++4swzzyQyMpIaNWpw2WWX5d7fzp07ufPOO3NHvQB27tzJoEGDqFq1KhUrVqRNmzZ88803Xu9HRERCQ4mTiIgAkJ6ezvvvv0/Tpk2pXr2617YPPvggzz77LCtWrMDlcnHjjTfmnpsxYwYPP/wwjz/+OCtWrCA+Pp5XXnnF5/XfeecdKlasyNKlS3nqqad49NFHmTt3LgBut5vBgwdToUIFli5dytSpU3nwwQeLd8PA8uXLGTBgAFdddRVJSUm88MILhR4DuPLKK0lJSeHbb79l5cqVdOrUiT59+rB//34Avv76ay677DIuvPBCfv/9d+bNm0fXrl0B+Pzzz6lbty6PPvpo7qgXwJgxYzh27BiLFi1i3bp1PPnkk1SqVKnY9yUiIiWvZOdeiIhIqTJnzpzcL+oZGRnEx8czZ84cHA7v/6722GOPce655wJw3333cdFFF3H06FEiIyOZPHkyI0aMYMSIEQBMnDiRH3/8scCo08natWvHhAkTAGjWrBkvv/wy8+bNo1+/fsydO5ft27ezYMEC4uLicmPo169fke4xxwMPPMADDzxAzZo1iYiIICoqKrdfoMCxxYsXs2zZMlJSUoiIiADgmWeeYdasWXz66aeMGjWKxx57jCFDhvDII4/k9tO+fXsAqlWrhtPppHLlyvmuk5CQwBVXXEHbtm0BaNy4sc/7ERGR0NCIk4hIOXbeeeexevVqVq9ezbJly+jfvz8DBw5k586dXt/Xrl273J/j4+MBSElJAWDTpk1069YtX/vu3bv7jCVvnzn95vS5ZcsW6tWrly/pyBnN8SXvPea8Ro8e7dd7c6xZs4b09HSqV69OpUqVcl87duxg+/btAKxevZo+ffoUqd/bb7+diRMn0rNnTyZMmJCvMIeIiJxeNOIkIlKOVaxYkaZNm+b+/sYbbxATE8O0adOYOHGix/eFhYXl/pyzXsc0zWLFkrfPnH6L2ycUvMdTkZ6eTnx8PAsWLChwLqfIRVRUVJH7HTlyJP379+frr7/mhx9+YNKkSTz77LPcdtttxYpXRERKnkacREQkl2EYOBwOjhw5csp9tGrViqVLl+Y79ttvvxUrrhYtWpCYmMiePXtyjy1fvrxYfRZFp06dSE5OxuVy0bRp03yvGjVqAPaI2bx58zz2ER4ejtvtLnC8Xr16jB49ms8//5z//Oc/TJs2LWD3ISIip04jTiIi5dixY8dITk4G4MCBA7z88sukp6czaNCgU+7zjjvuYPjw4XTp0oWePXvywQcfsGHDhmKt3+nXrx9NmjRh2LBhPPXUUxw6dIj//ve/wIkRL0/y3mMOl8uVm/D4o2/fvnTv3p3Bgwfz1FNP0bx5c3bv3p1bEKJLly5MmDCBPn360KRJE4YMGUJ2djbffPMN9957L2BX8Fu0aBFDhgwhIiKCGjVqMHbsWAYOHEjz5s05cOAA8+fPp1WrVkX8dEREJBg04iQiUo599913xMfHEx8fT7du3Vi+fDkzZ86kd+/ep9zn1Vdfzfjx47nnnnvo3LkzO3fu5Oabby5WnE6nk1mzZpGens6ZZ57JyJEjc6vqRUZGen1v3nvMefXq1atI1zcMg2+++YZzzjmHG264gebNmzNkyBB27txJbGwsYJccnzlzJrNnz6ZDhw6cf/75LFu2LLePRx99lL/++osmTZpQs2ZNwK4WOGbMGFq1asWAAQNo3ry5XxUIRUQk+AzLsqxQByEiIlJUv/zyC7169eKPP/6gSZMmoQ5HRETKOCVOIiJSKnzxxRdUqlSJZs2a8ccff3DHHXdQtWpVFi9eHOrQRESkHNAaJxERKRUOHTrEvffeS0JCAjVq1KBv3748++yzoQ5LRETKCY04iYiIiIiI+KDiECIiIiIiIj4ocRIREREREfFBiZOIiIiIiIgPSpxERERERER8UOIkIiIiIiLigxInERERERERH5Q4iYiIiIiI+KDESURERERExIf/B0IdHYpIQ0ubAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# plot the binding effects vs the perturbation effects\n", + "# color the points by the label\n", + "# make sure the labels are categorical\n", + "# label 0 should be blue while label 1 should be orange\n", + "\n", + "# Plotting\n", + "plt.figure(figsize=(10, 6))\n", + "plt.scatter(final_data_tensor[:, :, 1].flatten(), final_data_tensor[:, :, 3].flatten().abs(), c=['orange' if x == 0 else 'blue' for x in labels])\n", + "plt.title('Binding Effects vs Perturbation Effects')\n", + "plt.xlabel('Binding Effects')\n", + "plt.ylabel('Perturbation Effects')\n", + "plt.xlim(0,100)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Re-generate data with an explicit relationship between a give TF's binding and perturbation effects" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of the final data tensor: torch.Size([1000, 10, 5])\n" + ] + } + ], + "source": [ + "# in this case, select the TF binding data that corresponds with the effect data\n", + "# which we wish to produce. use the .unsqueeze(1) method to add the TF dimension\n", + "# after selecting the TF\n", + "perturbation_effects_tf_influenced = generate_perturbation_effects(\n", + " binding_data_tensor, \n", + " max_mean_adjustment=3.0, # try 0.1, 3.0, and 10.0\n", + " signal_mean=5.0, # try 3.0, 5.0, or 10.0\n", + " noise_mean=0.0, # try adjusting this\n", + ")\n", + "perturbation_pvalue_tf_influenced = torch.zeros_like(perturbation_effects_tf_influenced)\n", + "for col_idx in range(perturbation_effects_tf_influenced.shape[1]):\n", + " col = perturbation_effects_tf_influenced[:, col_idx]\n", + " col_pvals = generate_pvalues(col)\n", + " perturbation_pvalue_tf_influenced[:, col_idx] = col_pvals\n", + "\n", + "perturbation_effects_tensor_tf_influened = perturbation_effects_tf_influenced.unsqueeze(-1)\n", + "perturbation_pvalues_tensor_tf_influenced = perturbation_pvalue_tf_influenced.unsqueeze(-1)\n", + "\n", + "final_data_tensor_tf_influenced = torch.cat(\n", + " (binding_data_tensor,\n", + " perturbation_effects_tensor_tf_influened,\n", + " perturbation_pvalues_tensor_tf_influenced), \n", + " dim=2)\n", + "\n", + "# Verify the shape\n", + "print(\"Shape of the final data tensor:\", final_data_tensor.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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VapUcfi9cdfMmTNx//599O7dG2FhYciRIwdu3boFABg9ejQURcE777yDK1euYOLEiWjatCn27NmDiIgIAMDixYtx9+5d9OnTBzlz5sSOHTvw9ddf4/z581i8eDEA4LXXXsPFixexevVqzJ0712lMBw8eRP369ZE1a1YMHToUISEh+Pbbb9GoUSNs3LgRtWrVstp+wIAByJ49O0aMGIEzZ85g4sSJ6N+/PxYuXOjS9+BReh8QUQBTiYj8bObMmSqANLewsDB11qxZabYHoI4YMSLp/ogRI1QAavfu3a22e+6559ScOXMm3d+zZ48KQO3bt6/Vdi+99FKafVpiOn36dNJjRYoUUQGof//9d9JjV65cUcPCwtTBgwcnPTZgwABVURR19+7dSY9dv35dzZEjR5p92mL5emzdypQpk+Z70a9fP6vHTp8+rQJQP//8c6vHe/TooebPn1+9du2a1eMdO3ZUo6Ki1Lt376qqqqozZsxQAagTJkxIE5vZbFZVVVWvXr2a5numqqp68+ZNm8d2Re3atdXq1atbPbZjxw4VgDpnzhxVVVV19+7dKgB18eLFbu+/S5cuaubMmdWrV6+qV69eVU+cOKF++umnqqIoauXKlVVVVdW5c+eqOp1O3bRpk9Vrp02bpgJQt2zZkvQYAFWn06kHDx602tbe90ZVVbVhw4Zqw4YNbcZWpEiRpPuWn2HWrFnVK1euWG27fv16FYBasGBBNT4+PunxRYsWqQDUr776Kukxy880pTFjxqiKoqhnz55Neqxfv36qvVOA1F9L27Zt1dDQUPXkyZNJj128eFGNjIxUGzRokPSY5T3UtGnTpN8bVVXVN998U9Xr9eqtW7dsHs/iUX0fEFFg4nQ7ItLMlClTsHr1aqxevRrz5s1D48aN0bNnTyxdutSl17/++utW9+vXr4/r168jPj4egCxOB4CBAwdabWe5cuyK8uXLo379+kn3c+fOjTJlyuDUqVNJj61YsQK1a9dG1apVkx7LkSMHXn75ZZePAwA///xz0vfDcps5c6Zb+7BQVRU///wzWrVqBVVVce3ataRbs2bNEBcXh127diUdN1euXBgwYECa/aSchmVLREQEQkNDsWHDhqTpca7q0KED/vvvv6SRHQBYuHAhwsLC0KZNGwBIGilauXJlmqmUrrhz5w5y586N3Llzo2TJknj33XdRu3Zt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GHXSpEkqAPXy5csOvx+OSh8DUNevX2/1vXC19LGqqmpsbKzar18/NTo6Wg0JCVHz5cunPvnkk+r06dOttrt796763nvvqcWKFUvarn379lZln7du3apWr15dDQ0NTfr+Xbt2Te3Xr59atmxZNXPmzGpUVJRaq1YtddGiRQ6/5pS+++47FYAaGRmp3rt3z+q5U6dOqd27d1dLlCihhoeHqzly5FAbN26srlmzxul+LSXAnXn48KH62WefqRUqVFDDwsLU7Nmzq9WrV1c/+ugjNS4uLmk7W997C1vfG4t58+apxYsXV0NDQ9WqVauqK1eutFsC3NbP0FICfMGCBerw4cPVPHnyqBEREWqLFi2synqrqqoeOnRIbdq0qZolSxY1V65caq9evdS9e/eqANSZM2cmbWc0GtUBAwaouXPnVhVFsSoHnjp+VVXVXbt2qc2aNVOzZMmiZsqUSW3cuLG6detWq20s76GdO3fajD/l77Etj/r7gIgCi6KqAb76mIgoSA0aNAjffvstEhIS7C5mJyIiosDDNUlERF5w7949q/vXr1/H3LlzUa9ePSZIREREQYZrkoiIvKB27dpo1KgRypUrh9jYWPzwww+Ij4/HBx98oHVoRERE5CYmSUREXvDss89iyZIlmD59OhRFwWOPPYYffvgBDRo00Do0IiIichPXJBEREREREaXANUlEREREREQpMEkiIiIiIiJKIcOvSTKbzbh48SIiIyNdbuhIREREREQZj6qquH37NgoUKACdzv54UYZPki5evIjo6GitwyAiIiIiogARExODQoUK2X0+wydJkZGRAOQbkTVrVo2jISIiIiIircTHxyM6OjopR7AnwydJlil2WbNmZZJEREREREROl+GwcAMREREREVEKTJKIiIiIiIhSYJJERERERESUQoZfk0RERERE5C5VVWE0GmEymbQOhdyg1+thMBjS3fqHSRIRERERUQoPHz7EpUuXcPfuXa1DIQ9kypQJ+fPnR2hoqMf7YJJERERERPR/ZrMZp0+fhl6vR4ECBRAaGpruUQnyD1VV8fDhQ1y9ehWnT59GqVKlHDaMdYRJEhERERHR/z18+BBmsxnR0dHIlCmT1uGQmyIiIhASEoKzZ8/i4cOHCA8P92g/LNxARERERJSKpyMQpD1v/Oz40yciIiIi8oHERMf3KXBxuh0RERERkRcZ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O896hMsshMOrweGCxhdZv4yHRgffL+UFWZC9FZxhEN3N/wWaiIiIiIgNdmuDoC+SAJ5//nmeeOIJ0tPT6dmzJ88++yz9+/e39d6gKJJERERERCTgalWRVBUqkkREREREBOzXBkG9JklERERERKS6qUgSEREREREpRUWSiIiIiIhIKSqSRERERERESlGRJCIiIiIiUoqKJBERERERkVJUJImIiIiIiJSiIklERERERKQUFUkiIiIiIiKlqEgSEREREREpRUWSiIiIiIhIKSqSRERERERESlGRJCIiIiIiUoor0An4m2maAGRnZwc4ExERERERCaTimqC4RqhIrS+Sjh07BkDz5s0DnImIiIiIiASDY8eOER0dXeF5w/RWRtVwHo+HAwcOEBUVhWEYAc0lOzub5s2bs2/fPho0aBDQXGor3ePqofvsf7rH/qd77H+6x9VD99n/dI/9r7rusWmaHDt2jKSkJByOilce1fqZJIfDQbNmzQKdRhkNGjTQv2B+pntcPXSf/U/32P90j/1P97h66D77n+6x/1XHPa5sBqmYGjeIiIiIiIiUoiJJRERERESkFBVJ1SgsLIypU6cSFhYW6FRqLd3j6qH77H+6x/6ne+x/usfVQ/fZ/3SP/S/Y7nGtb9wgIiIiIiJyJjSTJCIiIiIiUoqKJBERERERkVJUJImIiIiIiJSiIklERERERKQUFUnV6IUXXqBVq1aEh4fTv39/Vq1aFeiUaqylS5dy5ZVXkpSUhGEYzJ8/v8x50zT529/+RmJiIhEREQwdOpTt27cHJtkaatq0afTt25eoqCiaNGnC1VdfzdatW8vE5OXlkZycTGxsLPXr1+faa6/l4MGDAcq45pk1axbdu3cv2ThvwIABfPbZZyXndX99b/r06RiGwaRJk0qO6T5X3UMPPYRhGGVenTp1Kjmve+wbP//8MzfccAOxsbFERERwzjnnsGbNmpLz+ruvalq1anXa99gwDJKTkwF9j33B7Xbz4IMP0rp1ayIiImjbti1///vfKd1HLli+xyqSqsm7777LXXfdxdSpU1m3bh09evRg+PDhZGRkBDq1Gik3N5cePXrwwgsvlHt+xowZPPvss7z44ousXLmSevXqMXz4cPLy8qo505pryZIlJCcns2LFChYtWkRhYSGXXnopubm5JTGTJ09mwYIFvPfeeyxZsoQDBw4w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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plotting. Note that the 'noise' group effects are still range from 0 to 3\n", + "\n", + "plt.figure(figsize=(10, 6))\n", + "plt.scatter(final_data_tensor_tf_influenced[:, :, 1].flatten(), final_data_tensor_tf_influenced[:, :, 3].flatten().abs(), c=['orange' if x == 0 else 'blue' for x in labels])\n", + "plt.title('Binding Effects vs Perturbation Effects')\n", + "plt.xlabel('Binding Effects')\n", + "plt.ylabel('Perturbation Effects')\n", "\n", - "# label this with an arbitrary TF name\n", - "population1_tf1_data['regulator'] = 'TF1'\n", + "legend_labels = ['Bound', 'Unbound']\n", + "colors = ['blue', 'orange']\n", + "legend_handles = [plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=color, markersize=10) for color in colors]\n", + "plt.legend(legend_handles, legend_labels)\n", "\n", - "population1_tf1_data" + "plt.show()" ] } ], @@ -260,7 +640,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.11.5" } }, "nbformat": 4, diff --git a/docs/tutorials/hyperparameter_sweep.ipynb b/docs/tutorials/hyperparameter_sweep.ipynb new file mode 100644 index 0000000..36220c1 --- /dev/null +++ b/docs/tutorials/hyperparameter_sweep.ipynb @@ -0,0 +1,278 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook introduces how to perform a hyperparameter sweep to find the best hyperparameters for our model using the Optuna library. Feel free to modify the objective function if you would like to test other hyperparameters or values." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Seed set to 42\n" + ] + }, + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# imports \n", + "import argparse\n", + "from argparse import Namespace\n", + "\n", + "from pytorch_lightning import Trainer, LightningModule, seed_everything\n", + "from pytorch_lightning.callbacks import ModelCheckpoint\n", + "from pytorch_lightning.loggers import CSVLogger, TensorBoardLogger\n", + "from torchsummary import summary\n", + "\n", + "from yeastdnnexplorer.data_loaders.synthetic_data_loader import SyntheticDataLoader\n", + "from yeastdnnexplorer.ml_models.simple_model import SimpleModel\n", + "from yeastdnnexplorer.ml_models.customizable_model import CustomizableModel\n", + "\n", + "import optuna\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "\n", + "# set random seed for reproducability\n", + "seed_everything(42)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we define loggers and checkpoints for our model. Checkpoints tell pytorch when to save instances of the model (that can be loaded and inspected later) and loggers tell pytorch how to format the metrics that the model logs during its training. " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Checkpoint to save the best version of model (during the entire training process) based on the metric passed into \"monitor\"\n", + "best_model_checkpoint = ModelCheckpoint(\n", + " monitor=\"val_mse\", # You can modify this to save the best model based on any other metric that the model you're testing tracks and reports\n", + " mode=\"min\",\n", + " filename=\"best-model-{epoch:02d}-{val_loss:.2f}.ckpt\",\n", + " save_top_k=1, # Can modify this to save the top k models\n", + ")\n", + "\n", + "# Callback to save checkpoints every 2 epochs, regardless of performance\n", + "periodic_checkpoint = ModelCheckpoint(\n", + " filename=\"periodic-{epoch:02d}.ckpt\",\n", + " every_n_epochs=2,\n", + " save_top_k=-1, # Setting -1 saves all checkpoints\n", + ")\n", + "\n", + "# define loggers for the model\n", + "tb_logger = TensorBoardLogger(\"logs/tensorboard_logs\")\n", + "csv_logger = CSVLogger(\"logs/csv_logs\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we perform our hyperparameter sweep using the Optuna library. To do this, we need to define an objective function that returns a scalar value. This scalar value will be the value that our sweep is attempting to minimize. We train one instance of our model inside each call to the objective function (each model on each iteration will use a different selection of hyperparameters). In our objective function, we return the validation mse associated with the instance of the model. This is because we would like to find the combination of hyperparameters that leads to the lowest validation mse. We use validation mse instead of test mse since we do not want to risk fitting to the test data at all while tuning hyperparameters.\n", + "\n", + "If you'd like to try different hyperparameters, you just need to modify the list of possible values corresponding to the hyperparameter in question.\n", + "\n", + "If you'd like to run the hyperparamter sweep on real data instead of synthetic data, simply swap out the synthetic data loader for the real data loader." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# on each call to the objective function, it will choose a hyperparameter value from each of the suggest_categorical arrays and pass them into the model\n", + " # this allows us to test many different hyperparameter configurations during our sweep\n", + "\n", + "def objective(trial):\n", + " # model hyperparameters\n", + " lr = trial.suggest_categorical(\"lr\", [0.01])\n", + " hidden_layer_num = trial.suggest_categorical(\"hidden_layer_num\", [1, 2, 3, 5])\n", + " activation = trial.suggest_categorical(\n", + " \"activation\", [\"ReLU\", \"Sigmoid\", \"Tanh\", \"LeakyReLU\"]\n", + " )\n", + " optimizer = trial.suggest_categorical(\"optimizer\", [\"Adam\", \"SGD\", \"RMSprop\"])\n", + " L2_regularization_term = trial.suggest_categorical(\n", + " \"L2_regularization_term\", [0.0, 0.1]\n", + " )\n", + " dropout_rate = trial.suggest_categorical(\n", + " \"dropout_rate\", [0.0, 0.5]\n", + " )\n", + "\n", + " # data module hyperparameters\n", + " batch_size = trial.suggest_categorical(\"batch_size\", [32])\n", + "\n", + " # training hyperparameters\n", + " max_epochs = trial.suggest_categorical(\n", + " \"max_epochs\", [1]\n", + " ) # default is 10\n", + "\n", + " # defining what to pass in for the hidden layer sizes list based on the number of hidden layers\n", + " hidden_layer_sizes_configurations = {\n", + " 1: [[64], [256]],\n", + " 2: [[64, 32], [256, 64]],\n", + " 3: [[256, 128, 32], [512, 256, 64]],\n", + " 5: [[512, 256, 128, 64, 32]],\n", + " }\n", + " hidden_layer_sizes = trial.suggest_categorical(\n", + " f\"hidden_layer_sizes_{hidden_layer_num}_layers\",\n", + " hidden_layer_sizes_configurations[hidden_layer_num],\n", + " )\n", + "\n", + " print(\"=\" * 70)\n", + " print(\"About to create model with the following hyperparameters:\")\n", + " print(f\"lr: {lr}\")\n", + " print(f\"hidden_layer_num: {hidden_layer_num}\")\n", + " print(f\"hidden_layer_sizes: {hidden_layer_sizes}\")\n", + " print(f\"activation: {activation}\")\n", + " print(f\"optimizer: {optimizer}\")\n", + " print(f\"L2_regularization_term: {L2_regularization_term}\")\n", + " print(f\"dropout_rate: {dropout_rate}\")\n", + " print(f\"batch_size: {batch_size}\")\n", + " print(f\"max_epochs: {max_epochs}\")\n", + " print(\"\")\n", + "\n", + " # create data module\n", + " data_module = SyntheticDataLoader(\n", + " batch_size=batch_size,\n", + " num_genes=4000,\n", + " signal_mean=3.0,\n", + " signal=[0.5] * 10,\n", + " n_sample=[1, 2, 2, 4, 4],\n", + " val_size=0.1,\n", + " test_size=0.1,\n", + " random_state=42,\n", + " max_mean_adjustment=3.0,\n", + " )\n", + "\n", + " num_tfs = sum(data_module.n_sample) # sum of all n_sample is the number of TFs\n", + "\n", + " # create model\n", + " model = CustomizableModel(\n", + " input_dim=num_tfs,\n", + " output_dim=num_tfs,\n", + " lr=lr,\n", + " hidden_layer_num=hidden_layer_num,\n", + " hidden_layer_sizes=hidden_layer_sizes,\n", + " activation=activation,\n", + " optimizer=optimizer,\n", + " L2_regularization_term=L2_regularization_term,\n", + " dropout_rate=dropout_rate,\n", + " )\n", + "\n", + " # create trainer\n", + " trainer = Trainer(\n", + " max_epochs=max_epochs,\n", + " deterministic=True,\n", + " accelerator=\"cpu\",\n", + " # callbacks and loggers are commented out for now since running a large sweep would generate an unnecessarily huge amount of checkpoints and logs\n", + " # callbacks=[best_model_checkpoint, periodic_checkpoint],\n", + " # logger=[tb_logger, csv_logger],\n", + " )\n", + "\n", + " # train model\n", + " trainer.fit(model, data_module)\n", + "\n", + " # get best validation loss from the model\n", + " return trainer.callback_metrics[\"val_mse\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we define an optuna study, which represents our hyperparameter sweep. It will run the objective function n_trials times and choose the model that gave the best val_mse across all of those trials with different hyperparameters. Note that this will create a very large amount of output as it will show training stats for every model. This is why we print out the best params and loss in a separate cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "STUDY_NAME = \"CustomizableModelHyperparameterSweep3\"\n", + "NUM_TRIALS = 5 # you will need a lot more than 5 trials if you have many possible combinations of hyperparams\n", + "\n", + "# Perform hyperparameter optimization using Optuna\n", + "study = optuna.create_study(\n", + " direction=\"minimize\", # we want to minimize the val_mse\n", + " study_name=STUDY_NAME,\n", + " # storage=\"sqlite:///db.sqlite3\", # you can save the study results in a database if you'd like, this is needed if you want to try and use the optuna dashboard library to dispaly results\n", + ")\n", + "study.optimize(objective, n_trials=NUM_TRIALS)\n", + "\n", + "# Get the best hyperparameters and their corresponding values\n", + "best_params = study.best_params\n", + "best_loss = study.best_value" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Print out the best hyperparameters and the val_mse assocaited with the model with the best hyperparameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"RESULTS\" + (\"=\" * 70))\n", + "print(f\"Best hyperparameters: {best_params}\")\n", + "print(f\"Best loss: {best_loss}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And that's it! Now you could take what you found to be the best hyperparameters and train a model with them for many more epochs. The [Optuna Documentation](https://optuna.readthedocs.io/en/stable/) will be a helpful resource if you'd like to add more to this notebook or the hyperparam sweep functions" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/tutorials/lightning_crash_course.ipynb b/docs/tutorials/lightning_crash_course.ipynb new file mode 100644 index 0000000..f51e3a2 --- /dev/null +++ b/docs/tutorials/lightning_crash_course.ipynb @@ -0,0 +1,232 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Lightning Crash Course\n", + "This project uses the PyTorch Lightning Library to define and train the machine learning models. PyTorch Lightning is built on top of pytorch, and it abstracts away some of the setup and biolerplate for models (such as writing out training loops). In this notebook, we provide a brief introduction to how to use the models and dataModules we've defined to train models." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# imports\n", + "from pytorch_lightning import Trainer\n", + "from pytorch_lightning.callbacks import ModelCheckpoint\n", + "from pytorch_lightning.loggers import CSVLogger, TensorBoardLogger\n", + "\n", + "from yeastdnnexplorer.data_loaders.synthetic_data_loader import SyntheticDataLoader\n", + "from yeastdnnexplorer.data_loaders.real_data_loader import RealDataLoader\n", + "from yeastdnnexplorer.ml_models.simple_model import SimpleModel\n", + "from yeastdnnexplorer.ml_models.customizable_model import CustomizableModel" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In Pytorch Lightning, the data is kept completely separate from the models. This allows for you to easy train a model using different datasets or train different models on the same dataset. `DataModules` encapsulate all the logic of loading in a specific dataset and splitting into training, testing, and validation sets. In this project, we have two data loaders defined: `SyntheticDataLoader` for the in silico data (which takes in many parameters that allow you to specify how the data is generated) and `RealDataLoader` which contains all of the logic for loading in the real experiment data and putting it into a form that the models expect.\n", + "\n", + "Once you decide what model you want to train and what dataModule you want to use, you can bundle these with a `Trainer` object to train the model on the dataset.\n", + "\n", + "If you'd like to learn more about the models and dataModules we've defined, there is extensive documentation in each of the files that explains each method's purpose." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# define an instance of our simple linear baseline model\n", + "model = SimpleModel(\n", + " input_dim=10,\n", + " output_dim=10,\n", + " lr=1e-2,\n", + ")\n", + "\n", + "# define an instance of the synthetic data loader\n", + "# see the constructor for the full list of params and their explanations\n", + "data_module = SyntheticDataLoader(\n", + " batch_size=32,\n", + " num_genes=3000,\n", + " signal=[0.5] * 5,\n", + " n_sample=[1, 1, 2, 2, 4],\n", + " val_size=0.1,\n", + " test_size=0.1,\n", + " signal_mean=3.0,\n", + ")\n", + "\n", + "# define a trainer instance\n", + "trainer = Trainer(\n", + " max_epochs=10,\n", + " deterministic=True,\n", + " accelerator=\"cpu\", # change to \"gpu\" if you have access to one\n", + ")\n", + "\n", + "# train the model\n", + "trainer.fit(model, data_module)\n", + "\n", + "# test the model (recall that data_module specifies the train / test split, we don't need to do it explicitly here)\n", + "test_results = trainer.test(model, data_module)\n", + "print(test_results)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It's very easy to train the same model on a different dataset, for example if we want to use real world data we can just swap to the data module that we've defined for the real world data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# we need to redefine a new instance with the same params unless we want it to pick up where it left off\n", + "new_model = SimpleModel(\n", + " input_dim=30, # note that the input and output dims are equal to the num TFs in the dataset\n", + " output_dim=30,\n", + " lr=1e-2,\n", + ")\n", + "\n", + "real_data_module = RealDataLoader(\n", + " batch_size=32,\n", + " val_size=0.1,\n", + " test_size=0.1,\n", + " data_dir_path=\"../../data/init_analysis_data_20240409/\", # note that this is relative to where real_data_loader.py is\n", + " perturbation_dataset_title=\"hu_reimann_tfko\",\n", + ")\n", + "\n", + "# we also have to define a new trainer instance, not really sure why but it seems to be necessary\n", + "trainer = Trainer(\n", + " max_epochs=10,\n", + " deterministic=True,\n", + " accelerator=\"cpu\", # change to \"gpu\" if you have access to one\n", + ")\n", + "\n", + "trainer.fit(new_model, real_data_module)\n", + "test_results = trainer.test(new_model, real_data_module)\n", + "print(test_results)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we wanted to do the same thing with our more complex and customizable `CustomizableModel` (which allows you to pass in many params like the number of hidden layers, dropout rate, choice of optimizer, etc) the code would look identical to above except that we would be initializing a `CustomizableModel` instead of a `SimpleModel`. See the documentation in `customizable_model.py` for more" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Checkpointing & Logging\n", + "PyTorch lightning gives us the power to define checkpoints and loggers that will be used during training. Checkpoints will save checkpoints of your model during training. In the following code, we define a checkpoint that saves the model's state when it produced the lowest validation mean squared error on the validation set during training. We also define another checkpoint to periodically save a checkpoint of the model after every 2 training epochs. These checkpoints are powerful because they can be reloaded later. You can continue training a model after loading its checkpoint or you can test the model checkpoint on new data.\n", + "\n", + "Loggers are responsible for saving metrics about the model as it is training for us to look at later. We define several loggers to track this data. See the comments above the Tensorboard logger to see how to use Tensorboard to visualize the metrics as the model trains\n", + "\n", + "To use checkpoints and loggers, we have to pass them into the Trainer object that we use to train the model with a dataModule. \n", + "\n", + "There are many more types of checkpoints and loggers you can create and use, PyTorch Lightning's documentation is very helpful here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# this will be used to save the model checkpoint that performs the best on the validation set\n", + "best_model_checkpoint = ModelCheckpoint(\n", + " monitor=\"val_mse\", # we can depend on any metric we want\n", + " mode=\"min\",\n", + " filename=\"best-model-{epoch:02d}-{val_loss:.2f}\",\n", + " save_top_k=1, # we can save more than just the top model if we want\n", + ")\n", + "\n", + "# Callback to save checkpoints every 2 epochs, regardless of model performance\n", + "periodic_checkpoint = ModelCheckpoint(\n", + " filename=\"periodic-{epoch:02d}\",\n", + " every_n_epochs=2,\n", + " save_top_k=-1, # Setting -1 saves all checkpoints \n", + ")\n", + "\n", + "# csv logger is a very basic logger that will create a csv file with our metrics as we train\n", + "csv_logger = CSVLogger(\"logs/csv_logs\") # we define the directory we want the logs to be saved in\n", + "\n", + "# tensorboard logger is a more advanced logger that will create a directory with a bunch of files that can be visualized with tensorboard\n", + "# tensorboard is a library that can be ran via the command line, and will create a local server that can be accessed via a web browser\n", + "# that displays the training metrics in a more interactive way (on a dashboard)\n", + "# you can run tensorboard by running the command `tensorboard --logdir=path/to/log/dir` in the terminal\n", + "tb_logger = TensorBoardLogger(\"logs/tensorboard_logs\", name=\"test-run-2\")\n", + "\n", + "# If we wanted to use these checkpoints and loggers, we would pass them to the trainer like so:\n", + "trainer_with_checkpoints_and_loggers = Trainer(\n", + " max_epochs=10,\n", + " deterministic=True,\n", + " accelerator=\"cpu\",\n", + " callbacks=[best_model_checkpoint, periodic_checkpoint],\n", + " logger=[csv_logger, tb_logger],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Loading in and using a Checkpoint" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load a model from a checkpoint\n", + "# We can load a model from a checkpoint like so:\n", + "path_to_checkpoint = \"example/path/not/real.ckpt\"\n", + "\n", + "# note that we need to use the same model class that was used to save the checkpoint\n", + "model = SimpleModel.load_from_checkpoint(path_to_checkpoint)\n", + "\n", + "# we can load the model and continue training from where it left off\n", + "trainer.fit(model, data_module)\n", + "\n", + "# we could also load the model and test it\n", + "test_results = trainer.test(model, data_module)\n", + "\n", + "# we could also load the model and make predictions\n", + "predictions = model(data_module.test_dataloader())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/tutorials/testing_model_metrics.ipynb b/docs/tutorials/testing_model_metrics.ipynb new file mode 100644 index 0000000..493715e --- /dev/null +++ b/docs/tutorials/testing_model_metrics.ipynb @@ -0,0 +1,353 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this notebook, we run several simple experiments to gain a deeper understanding of the metrics that we use to evaluate our models and how they respond to changes in the parameters we use for generating our in silico data." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# imports\n", + "import torch\n", + "\n", + "from pytorch_lightning import Trainer, LightningModule\n", + "from pytorch_lightning.callbacks import ModelCheckpoint\n", + "from pytorch_lightning.loggers import CSVLogger, TensorBoardLogger\n", + "\n", + "from yeastdnnexplorer.data_loaders.synthetic_data_loader import SyntheticDataLoader\n", + "from yeastdnnexplorer.ml_models.simple_model import SimpleModel\n", + "\n", + "import matplotlib.pyplot as plt\n", + "\n", + "torch.manual_seed(42) # For CPU\n", + "torch.cuda.manual_seed_all(42) # For all CUDA devices" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Define checkpoints and loggers for the models" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# define checkpoints for the model\n", + "# tells it when to save snapshots of the model during training\n", + "# Callback to save the best model based on validation loss\n", + "best_model_checkpoint = ModelCheckpoint(\n", + " monitor=\"val_mse\",\n", + " mode=\"min\",\n", + " filename=\"best-model-{epoch:02d}-{val_loss:.2f}\",\n", + " save_top_k=1,\n", + ")\n", + "\n", + "# Callback to save checkpoints every 5 epochs, regardless of performance\n", + "periodic_checkpoint = ModelCheckpoint(\n", + " filename=\"periodic-{epoch:02d}\",\n", + " every_n_epochs=2,\n", + " save_top_k=-1, # Setting -1 saves all checkpoints\n", + ")\n", + "\n", + "# configure loggers\n", + "tb_logger = TensorBoardLogger(\"logs/tensorboard_logs\")\n", + "csv_logger = CSVLogger(\"logs/csv_logs\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we define a helper function that will generate data and train a simple linear model with all of the given parameters, print the test results of the model, and return the trained model and its test results. This will allow us to easily run experiments where we compare model performance while tweaking data or model parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def train_simple_model_with_params(\n", + " batch_size: int,\n", + " lr: float,\n", + " max_epochs: int,\n", + " using_random_seed: bool,\n", + " accelerator: str,\n", + " num_genes: int,\n", + " signal_mean: float,\n", + " val_size: float,\n", + " test_size: float,\n", + " signal: list[float],\n", + " n_sample: list[int],\n", + " max_mean_adjustment: float,\n", + ") -> LightningModule:\n", + " data_module = SyntheticDataLoader(\n", + " batch_size=batch_size,\n", + " num_genes=num_genes,\n", + " signal_mean=signal_mean,\n", + " signal=signal, # old: [0.1, 0.15, 0.2, 0.25, 0.3],\n", + " n_sample=n_sample, # sum of this is num of tfs\n", + " val_size=val_size,\n", + " test_size=test_size,\n", + " random_state=42,\n", + " max_mean_adjustment=max_mean_adjustment,\n", + " )\n", + "\n", + " num_tfs = sum(data_module.n_sample) # sum of all n_sample is the number of TFs\n", + "\n", + " model = SimpleModel(input_dim=num_tfs, output_dim=num_tfs, lr=lr)\n", + " trainer = Trainer(\n", + " max_epochs=max_epochs,\n", + " deterministic=using_random_seed,\n", + " accelerator=accelerator,\n", + " # callbacks=[best_model_checkpoint, periodic_checkpoint],\n", + " # logger=[tb_logger, csv_logger],\n", + " )\n", + " trainer.fit(model, data_module)\n", + " test_results = trainer.test(model, datamodule=data_module)\n", + " print(\"Printing test results...\")\n", + " print(\n", + " test_results\n", + " ) # this prints all metrics that were logged during the test phase\n", + "\n", + " return model, test_results" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Experiment 1\n", + "\n", + "Now we can use this function to run simple experiments, like testing how the model's test mse changes when we tweak the mean of the bound genes while holding all other parameters the same. For simplicity, we will not be performing any mean adjustments while generating the data, but we could modify this in the future by incresing the max_mean_adjustment (to use a normal mean adjustment) or adding onto our experiment function to take in our special mean adjustment functions (to use either of the special dependent mean adjustment logic that we've defined, see `generate_in_silico_data.ipynb` for more about this).\n", + "\n", + "Note that this will create a lot of output since we are training several models, so we create the plot in a separate cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "signal_means = [0.5, 1.0, 2.0, 3.0, 5.0]\n", + "test_mses = []\n", + "for signal_mean in signal_means:\n", + " model, test_results = train_simple_model_with_params(\n", + " batch_size=32,\n", + " lr=0.01,\n", + " max_epochs=10,\n", + " using_random_seed=True,\n", + " accelerator=\"cpu\",\n", + " num_genes=1000,\n", + " val_size=0.1,\n", + " test_size=0.1,\n", + " signal=[0.5] * 5,\n", + " n_sample=[1, 1, 2, 2, 4], # sum of this is num of tfs\n", + " signal_mean=signal_mean,\n", + " max_mean_adjustment=0.0\n", + " )\n", + " test_mses.append(test_results[0][\"test_mse\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot Results" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(signal_means, test_mses, marker=\"o\")\n", + "plt.xlabel(\"Signal Mean\")\n", + "plt.xticks(signal_means, rotation=45)\n", + "plt.yticks(test_mses)\n", + "plt.ylabel(\"Test MSE\")\n", + "plt.title(\"Test MSE as a function of Signal Mean\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Experiment 2\n", + "\n", + "We can run a similar experiment where we test the effect of the bound / unbound ratio (aka signal / noise ratio) on the model's MSE" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "signal_noise_ratios = [0.05, 0.1, 0.25, 0.5, 0.75, 0.9]\n", + "test_mses = []\n", + "\n", + "for signal_noise_ratio in signal_noise_ratios:\n", + " model, test_results = train_simple_model_with_params(\n", + " batch_size=32,\n", + " lr=0.01,\n", + " max_epochs=10,\n", + " using_random_seed=True,\n", + " accelerator=\"cpu\",\n", + " num_genes=1000,\n", + " val_size=0.1,\n", + " test_size=0.1,\n", + " signal=[signal_noise_ratio] * 5,\n", + " n_sample=[1, 1, 2, 2, 4],\n", + " signal_mean=3.0,\n", + " max_mean_adjustment=0.0\n", + " )\n", + " print(test_results)\n", + " test_mses.append(test_results[0][\"test_mse\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(signal_noise_ratios, test_mses, marker=\"o\")\n", + "plt.xlabel(\"Percentage of Data in Signal Group\")\n", + "plt.ylabel(\"Test MSE\")\n", + "plt.xticks(signal_noise_ratios, rotation=45)\n", + "plt.yticks(test_mses)\n", + "plt.title(\"Test MSE as a function of signal/noise ratio (signal mean = 3.0)\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Experiment 3\n", + "\n", + "Here we run a little experiment to verify that our smse (standardized mean squared error) metric is actually scale and mean invariant (ie doesn't depend on the scale or mean of the data so long as the variance is roughly the same). Note that this isn't a perfect experiment, as increasing the max mean adjustment (and therefore the scale) will increase the variance by a factor as a result of how our in silico data generation functions work, so there will definitely be a little difference in smse values, but the difference in mse and mae should be a much larger percentage.\n", + "\n", + "We will train and test two models that are exactly the same except that one is trained on a dataset with a small bound mean and mean adjustment and one is trained on a dataset with a large bound mean adn mean adjustment. This will give the two datasets drastically different scales and means. Unfortunately, it will also give them slightly different variances which should cause a slight difference in smse. But again it should be a much smaller percentage difference than the difference between mses and maes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# these params will be consistent for both datasets\n", + "num_genes = 3000\n", + "val_size = 0.1\n", + "test_size = 0.1\n", + "signal = [0.5] * 5\n", + "n_sample = [1, 1, 2, 2, 4]\n", + "random_state = 42\n", + "\n", + "# the first data loader will load a dataset with a small scale and a small bound mean\n", + "small_scale_and_mean_dataloader = SyntheticDataLoader(\n", + " num_genes=num_genes,\n", + " signal=signal, \n", + " n_sample=n_sample,\n", + " val_size=val_size,\n", + " test_size=test_size,\n", + " random_state=random_state,\n", + " signal_mean=1.0,\n", + " max_mean_adjustment=1.0\n", + ")\n", + "\n", + "# the second data loader will generate a dataset with a large scale and a large bound mean\n", + "large_scale_and_mean_dataloader = SyntheticDataLoader(\n", + " num_genes=num_genes,\n", + " signal=signal, \n", + " n_sample=n_sample,\n", + " val_size=val_size,\n", + " test_size=test_size,\n", + " random_state=random_state,\n", + " signal_mean=10.0,\n", + " max_mean_adjustment=10.0\n", + ")\n", + "\n", + "num_tfs = sum(n_sample) # sum of all n_sample is the number of TFs\n", + "\n", + "model = SimpleModel(input_dim=num_tfs, output_dim=num_tfs, lr=0.01)\n", + "trainer = Trainer(\n", + " max_epochs=10,\n", + " deterministic=True,\n", + " accelerator='cpu',\n", + " # callbacks=[best_model_checkpoint, periodic_checkpoint],\n", + " # logger=[tb_logger, csv_logger],\n", + ")\n", + "\n", + "trainer.fit(model, small_scale_and_mean_dataloader)\n", + "small_test_results = trainer.test(model, datamodule=small_scale_and_mean_dataloader)\n", + "print(\"Printing small test results...\")\n", + "print(small_test_results)\n", + "\n", + "\n", + "trainer.fit(model, large_scale_and_mean_dataloader)\n", + "large_test_results = trainer.test(model, datamodule=large_scale_and_mean_dataloader)\n", + "print(\"Printing large test results...\")\n", + "print(large_test_results)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/tutorials/visualizing_and_testing_data_generation_methods.ipynb b/docs/tutorials/visualizing_and_testing_data_generation_methods.ipynb new file mode 100644 index 0000000..6167edc --- /dev/null +++ b/docs/tutorials/visualizing_and_testing_data_generation_methods.ipynb @@ -0,0 +1,631 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this notebook, we will run an experiment to display the average perturbation effect values that we generate with the 4 different methods we have for perturbation effect generation (other than the method for generating the perturbation effect values, we will be holding everything else the same). \n", + "\n", + "Recall that we have 4 methods for generating perturbation effect data (see `generate_in_silico_data.ipynb` for more information on these):\n", + "1. No Mean Adjustment\n", + "2. Standard Mean Adjustment\n", + "3. Mean adjustment dependent on all TFs bound to gene in question\n", + "4. Mean adjustment dependent on binary relationships between bound and unbound TFs to gene in question.\n", + "\n", + "After understanding what the generated data looks like for each of these methods, we will perform another experiment where we train the same model on data generated with each of these methods and compare the model's performance to a simple linear model." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# imports\n", + "from yeastdnnexplorer.probability_models.generate_data import (generate_gene_population, \n", + " generate_binding_effects,\n", + " generate_pvalues,\n", + " generate_perturbation_effects)\n", + "\n", + "import torch\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from yeastdnnexplorer.probability_models.relation_classes import Relation, And, Or\n", + "from yeastdnnexplorer.probability_models.generate_data import (\n", + " default_perturbation_effect_adjustment_function,\n", + " perturbation_effect_adjustment_function_with_tf_relationships,\n", + " perturbation_effect_adjustment_function_with_tf_relationships_boolean_logic\n", + ")\n", + "\n", + "from pytorch_lightning import Trainer, LightningModule, seed_everything\n", + "from pytorch_lightning.callbacks import ModelCheckpoint\n", + "from pytorch_lightning.loggers import CSVLogger, TensorBoardLogger\n", + "from torchsummary import summary\n", + "\n", + "from yeastdnnexplorer.data_loaders.synthetic_data_loader import SyntheticDataLoader\n", + "from yeastdnnexplorer.ml_models.simple_model import SimpleModel\n", + "from yeastdnnexplorer.ml_models.customizable_model import CustomizableModel\n", + "\n", + "torch.manual_seed(42) # For CPU\n", + "torch.cuda.manual_seed_all(42) # For all CUDA devices" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Generating the binding data will be the same as always, see `generate_in_silico_data.ipynb`" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "n_genes = 3000\n", + "\n", + "signal = [0.5, 0.5, 0.5, 0.5, 0.5]\n", + "n_sample = [1, 1, 2, 2, 4]\n", + "\n", + "# this will be a list of length 10 with a GenePopulation object in each element\n", + "gene_populations_list = []\n", + "for signal_proportion, n_draws in zip(signal, n_sample):\n", + " for _ in range(n_draws):\n", + " gene_populations_list.append(generate_gene_population(n_genes, signal_proportion))\n", + " \n", + "# Generate binding data for each gene population\n", + "binding_effect_list = [generate_binding_effects(gene_population)\n", + " for gene_population in gene_populations_list]\n", + "\n", + "# Calculate p-values for binding data\n", + "binding_pvalue_list = [generate_pvalues(binding_data) for binding_data in binding_effect_list]\n", + "\n", + "binding_data_combined = [torch.stack((gene_population.labels, binding_effect, binding_pval), dim=1)\n", + " for gene_population, binding_effect, binding_pval\n", + " in zip (gene_populations_list, binding_effect_list, binding_pvalue_list)]\n", + "\n", + "# Stack along a new dimension (dim=1) to create a tensor of shape [num_genes, num_TFs, 3]\n", + "binding_data_tensor = torch.stack(binding_data_combined, dim=1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we define our experiment, this function will return the average perturbation effects (across n_iterations iterations) for each TF for a specific gene for each of the 4 data generation method we have at our disposal. Due to the randomness in the generated data, we need to find the averages over a number of iterations to get the true common values.\n", + "\n", + "We also need to define dictionaries of TF relationships for our third and fourth methods of generating perturbation data, see `generate_in_silico_data.ipynb` for an explanation of what these represent and how they are used / structured. The documentation in `generate_data.py` may be helpful as well." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "tf_relationships = {\n", + " 0: [1],\n", + " 1: [8],\n", + " 2: [5, 6],\n", + " 3: [4],\n", + " 4: [5],\n", + " 5: [9],\n", + " 6: [4],\n", + " 7: [1, 4],\n", + " 8: [6],\n", + " 9: [4],\n", + "}\n", + "\n", + "tf_relationships_dict_boolean_logic = {\n", + " 0: [And(3, 4, 8), Or(3, 7), Or(1, 1)],\n", + " 1: [And(5, Or(7, 8))],\n", + " 2: [],\n", + " 3: [Or(7, 9), And(6, 7)],\n", + " 4: [And(1, 2)],\n", + " 5: [Or(0, 1, 2, 8, 9)],\n", + " 6: [And(0, Or(1, 2))],\n", + " 7: [Or(2, And(5, 6, 9))],\n", + " 8: [],\n", + " 9: [And(6, And(3, Or(0, 9)))],\n", + "}\n", + "\n", + "def experiment(n_iterations = 10, GENE_IDX = 0):\n", + " print(\"Bound (1) and Unbound (0) Labels for gene \" + str(GENE_IDX) + \":\")\n", + " print(binding_data_tensor[GENE_IDX, :, 0])\n", + "\n", + " num_tfs = sum(n_sample)\n", + " \n", + " no_mean_adjustment_scores = torch.zeros(num_tfs)\n", + " normal_mean_adjustment_scores = torch.zeros(num_tfs)\n", + " dep_mean_adjustment_scores = torch.zeros(num_tfs)\n", + " boolean_logic_scores = torch.zeros(num_tfs)\n", + "\n", + " # we generate perturbation effects for each TF on each iteration and then add them to the running totals\n", + " for i in range(n_iterations):\n", + " # Method 1: Generate perturbation effects without mean adjustment\n", + " perturbation_effects_list_no_mean_adjustment = [generate_perturbation_effects(binding_data_tensor[:, tf_index, :].unsqueeze(1), tf_index=0) \n", + " for tf_index in range(sum(n_sample))]\n", + " perturbation_effects_list_no_mean_adjustment = torch.stack(perturbation_effects_list_no_mean_adjustment, dim=1)\n", + "\n", + " # Method 2: Generate perturbation effects with normal mean adjustment\n", + " perturbation_effects_list_normal_mean_adjustment = generate_perturbation_effects(\n", + " binding_data_tensor, \n", + " max_mean_adjustment=10.0\n", + " )\n", + "\n", + " # Method 3: Generate perturbation effects with dependent mean adjustment\n", + " perturbation_effects_list_dep_mean_adjustment = generate_perturbation_effects(\n", + " binding_data_tensor, \n", + " tf_relationships=tf_relationships,\n", + " adjustment_function=perturbation_effect_adjustment_function_with_tf_relationships,\n", + " max_mean_adjustment=10.0,\n", + " )\n", + " \n", + " # Method 4: Generate perturbation effects with binary relations between the TFs\n", + " perturbation_effects_list_boolean_logic = generate_perturbation_effects(\n", + " binding_data_tensor, \n", + " adjustment_function=perturbation_effect_adjustment_function_with_tf_relationships_boolean_logic,\n", + " tf_relationships=tf_relationships_dict_boolean_logic,\n", + " max_mean_adjustment=10.0,\n", + " )\n", + "\n", + " # take absolute values since we only care about the magnitude of the effects\n", + " no_mean_adjustment_scores += abs(perturbation_effects_list_no_mean_adjustment[GENE_IDX, :])\n", + " normal_mean_adjustment_scores += abs(perturbation_effects_list_normal_mean_adjustment[GENE_IDX, :])\n", + " dep_mean_adjustment_scores += abs(perturbation_effects_list_dep_mean_adjustment[GENE_IDX, :])\n", + " boolean_logic_scores += abs(perturbation_effects_list_boolean_logic[GENE_IDX, :])\n", + "\n", + " if (i + 1) % 5 == 0:\n", + " print(f\"iteration {i+1} completed\")\n", + " \n", + " # divide by the number of iterations to get the averages\n", + " no_mean_adjustment_scores /= n_iterations\n", + " normal_mean_adjustment_scores /= n_iterations\n", + " dep_mean_adjustment_scores /= n_iterations\n", + " boolean_logic_scores /= n_iterations\n", + " \n", + " return no_mean_adjustment_scores, normal_mean_adjustment_scores, dep_mean_adjustment_scores, boolean_logic_scores" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can run the experiment for n_iterations, I find that you should iterate at least 30 times, but closer to 100 is most ideal. This could take 1-5 minutes depending on your computer." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Bound (1) and Unbound (0) Labels for gene 0:\n", + "tensor([0., 0., 0., 1., 1., 1., 1., 1., 0., 1.])\n", + "iteration 5 completed\n", + "iteration 10 completed\n", + "iteration 15 completed\n", + "iteration 20 completed\n", + "iteration 25 completed\n", + "iteration 30 completed\n", + "iteration 35 completed\n", + "iteration 40 completed\n", + "iteration 45 completed\n", + "iteration 50 completed\n" + ] + } + ], + "source": [ + "GENE_IDX = 0\n", + "experiment_results = experiment(n_iterations=50, GENE_IDX=GENE_IDX)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We now plot our results." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Bound (signal) TFs for gene 0 are: [3, 4, 5, 6, 7, 9]\n", + "Unbound (noise) TFs for gene 0 are: [0, 1, 2, 8]\n", + "tensor([0., 0., 0., 1., 1., 1., 1., 1., 0., 1.])\n" + ] + }, + { + "data": { + "image/png": "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2rVrIdaKiIiIiIj+6wo1UHJ0dFR5PXfuXJQpUwaNGzcupBoREREREREVcqCUVVpaGn7++Wd8+eWXkMlkGvOkpqYiNTVVeh0fHw8ASE9PR3p6+jupZ06U6y/seryP2Ha6Ybvphu2mO7adbthuumG76Ybtpju2nW6KUrvpuw4yIYTQa4k62r59O3r16oUHDx7AxcVFY57g4GBMnz5dLX3z5s2wsLAo6CoSEREREVERlZSUhF69eiEuLg42Njb5Lq/IBEr+/v4wMTHBr7/+mmMeTT1Kbm5uePHihV4aIz/S09Nx5MgRtGzZEsbGxoVal/cN2043bDfdsN10x7bTDdtNN2w33bDddMe2001Rarf4+HgUL15cb4FSkRh6d//+fRw9ehShoaG55jM1NYWpqalaurGxcaFvGKWiVJf3DdtON2w33bDddMe20w3bTTdsN92w3XTHttNNUWg3fa+/SDxHaf369ShRogTatWtX2FUhIiIiIiIq/EBJoVBg/fr1CAoKgpFRkejgIiIiIiKi/7hCD5SOHj2KBw8eoH///oVdFSIiIiIiIgBF4B6lVq1aoYjMJ0FERERERASgCPQoERERERERFTUMlIiIiIiIiLJhoERERERERJQNAyUiIiIiIqJsGCgRERERERFlw0CJiIiIiIgoGwZKRERERERE2TBQIiIiIiIiyoaBEhERERERUTYMlIiIiIiIiLJhoERERERERJQNAyUiIiIiIqJsGCgRERERERFlw0CJiIiIiIgoGwZKRERERERE2TBQIiIiIiIiyoaBEhERERERUTYMlIiIiIiIiLJhoERERERERJQNAyUiIiIiIqJsGCgRERERERFlw0CJiIiIiIgoGwZKRERERERE2TBQIiIiIiIiyoaBEhERERERUTYMlIiIiIiIiLJhoERERERERJQNAyUiIiIiIqJsGCgRERERERFlw0CJiIiIiIgoGwZKRERERERE2TBQIiIiIiIiyoaBEhERERERUTYMlIiIiIiIiLJhoERERERERJQNAyUiIiIiIqJsGCgRERERERFlw0CJiIiIiIgoGyNtMtnb20Mmk2lV4KtXr/JVISIiIiIiosKmVaC0dOnSAq4GERERERFR0aFVoBQUFFTQ9SAiIiIiIioy8nWPUkpKCuLj41X+8urRo0f49NNP4eDgAHNzc1SpUgXnz5/PT7WIiIiI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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "x_vals = list(range(sum(n_sample)))\n", + "print(\"Bound (signal) TFs for gene \" + str(GENE_IDX) + \" are: \" + str(binding_data_tensor[GENE_IDX, :, 0].nonzero().flatten().tolist()))\n", + "print(\"Unbound (noise) TFs for gene \" + str(GENE_IDX) + \" are: \" + str((1 - binding_data_tensor[GENE_IDX, :, 0]).nonzero().flatten().tolist()))\n", + "print(binding_data_tensor[GENE_IDX, :, 0])\n", + "plt.figure(figsize=(10, 6))\n", + "\n", + "# Plot each set of experiment results with a different color\n", + "colors = ['red', 'green', 'blue', 'orange']\n", + "for index, results in enumerate(experiment_results):\n", + " plt.scatter(x_vals, results, color=colors[index])\n", + "\n", + "plt.title('Pertubation Effects for Gene ' + str(GENE_IDX) + ' with Different Adjustment Functions (averaged across 100 trials)')\n", + "plt.xlabel('TF Index')\n", + "plt.ylabel('Perturbation Effect Val')\n", + "plt.xticks(x_vals)\n", + "plt.grid(True)\n", + "plt.legend(['No Mean Adjustment', 'Normal (non-dependent) Mean Adjust', 'Dependent Mean Adjustment', 'Boolean Logic Adjustment'])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Recall that for the dependent mean adjustment, the TF in question must be bound and all of the TFs in its dependency array (in the tf_relationships dictionary) must be bound as well. This is why we do not adjust the mean for TF 7 despite it being bound, it depends on TF 1 and TF 4 both being bound, and TF1 is not bound.\n", + "\n", + "Similarly, for the boolean logic adjustment, we do not adjust the mean for 6 despite it being bound because it depends on (TF0 && (TF1 || TF2)) being bound, and none of those 3 TFs are bound to the gene we are studying.\n", + "\n", + "Note that if you change GENE_IDX, the random seed, or any of the relationship dictionaris that this explanation will no longer apply to the data you are seeing in the plot." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Training models on data generated from the 4 different methods\n", + "In the next experiment, we will be training the exact same model on data generated from each of these 4 methods. We will also train a simple linear model on all four methods to use as a baseline to compare to. Other than the method used to generate the data, everything else will be held the same." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# define checkpoints and loggers\n", + "best_model_checkpoint = ModelCheckpoint(\n", + " monitor=\"val_mse\",\n", + " mode=\"min\",\n", + " filename=\"best-model-{epoch:02d}-{val_loss:.2f}\",\n", + " save_top_k=1,\n", + ")\n", + "\n", + "# Callback to save checkpoints every 5 epochs, regardless of performance\n", + "periodic_checkpoint = ModelCheckpoint(\n", + " filename=\"periodic-{epoch:02d}\",\n", + " every_n_epochs=2,\n", + " save_top_k=-1, # Setting -1 saves all checkpoints\n", + ")\n", + "\n", + "# define loggers for the model\n", + "tb_logger = TensorBoardLogger(\"logs/tensorboard_logs\")\n", + "csv_logger = CSVLogger(\"logs/csv_logs\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We define a few helper functions to run our experiment. We make helper functions for things that will mostly be the same across each training loop so that we don't have to keep redefining them." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def get_data_module(max_mean_adjustment, adjustment_function = default_perturbation_effect_adjustment_function, tf_relationships_dict = {}):\n", + " return SyntheticDataLoader(\n", + " batch_size=32,\n", + " num_genes=4000,\n", + " signal_mean=3.0,\n", + " signal=[0.5] * 5,\n", + " n_sample=[1, 1, 2, 2, 4], # sum of this is num of tfs\n", + " val_size=0.1,\n", + " test_size=0.1,\n", + " random_state=42,\n", + " max_mean_adjustment=max_mean_adjustment,\n", + " adjustment_function=adjustment_function,\n", + " tf_relationships=tf_relationships_dict,\n", + " )\n", + "\n", + "def get_model(num_tfs):\n", + " return CustomizableModel(\n", + " input_dim=num_tfs,\n", + " output_dim=num_tfs,\n", + " lr=0.01,\n", + " hidden_layer_num=2,\n", + " hidden_layer_sizes=[64, 32],\n", + " activation=\"LeakyReLU\",\n", + " optimizer=\"RMSprop\",\n", + " L2_regularization_term=0.0,\n", + " dropout_rate=0.0,\n", + " )\n", + "\n", + "def get_linear_model(num_tfs):\n", + " return SimpleModel(\n", + " input_dim=num_tfs,\n", + " output_dim=num_tfs,\n", + " lr=0.01\n", + " )\n", + "\n", + "def get_trainer():\n", + " # uncomment callbacks or logggers if you would like checkpoints / logs\n", + " return Trainer(\n", + " max_epochs=10,\n", + " deterministic=True,\n", + " accelerator=\"cpu\",\n", + " # callbacks=[best_model_checkpoint, periodic_checkpoint],\n", + " # logger=[tb_logger, csv_logger],\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# These lists will store the test results for different models and data generation methods\n", + "model_mses = []\n", + "linear_model_test_mses = []" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Train models on data generated with no mean adjustment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data_module = get_data_module(0.0)\n", + "num_tfs = sum(data_module.n_sample)\n", + "\n", + "# nonlinear model\n", + "model = get_model(num_tfs)\n", + "trainer = get_trainer()\n", + "trainer.fit(model, data_module)\n", + "test_results = trainer.test(model, datamodule=data_module)\n", + "print(\"Printing test results...\")\n", + "print(test_results)\n", + "model_mses.append(test_results[0][\"test_mse\"])\n", + "\n", + "# linear model\n", + "linear_model = get_linear_model(num_tfs)\n", + "trainer = get_trainer()\n", + "trainer.fit(linear_model, data_module)\n", + "test_results = trainer.test(linear_model, datamodule=data_module)\n", + "print(\"Printing linear model test results\")\n", + "print(test_results)\n", + "linear_model_test_mses.append(test_results[0][\"test_mse\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Train models on data generated with normal mean adjustments" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data_module = get_data_module(3.0)\n", + "num_tfs = sum(data_module.n_sample)\n", + "\n", + "# nonlinear model\n", + "model = get_model(num_tfs)\n", + "trainer = get_trainer()\n", + "trainer.fit(model, data_module)\n", + "test_results = trainer.test(model, datamodule=data_module)\n", + "print(\"Printing test results...\")\n", + "print(test_results)\n", + "model_mses.append(test_results[0][\"test_mse\"])\n", + "\n", + "# linear model\n", + "linear_model = get_linear_model(num_tfs)\n", + "trainer = get_trainer()\n", + "trainer.fit(linear_model, data_module)\n", + "test_results = trainer.test(linear_model, datamodule=data_module)\n", + "print(\"Printing linear model test results\")\n", + "print(test_results)\n", + "linear_model_test_mses.append(test_results[0][\"test_mse\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Train model on data generated with dependent mean adjustments (method 3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# define dictionary of relations between TFs (see generate_in_silico_data.ipynb for an explanation of how this dict is defined / used)\n", + "tf_relationships_dict = {\n", + " 0: [1],\n", + " 1: [8],\n", + " 2: [5, 6],\n", + " 3: [4],\n", + " 4: [5],\n", + " 5: [9],\n", + " 6: [4],\n", + " 7: [1, 4],\n", + " 8: [6],\n", + " 9: [4],\n", + "}\n", + "\n", + "data_module = get_data_module(\n", + " 3.0, \n", + " adjustment_function=perturbation_effect_adjustment_function_with_tf_relationships, \n", + " tf_relationships_dict=tf_relationships_dict\n", + ")\n", + "num_tfs = sum(data_module.n_sample)\n", + "\n", + "print(\"Number of TFs: \", num_tfs)\n", + "\n", + "# nonlinear model\n", + "model = get_model(num_tfs)\n", + "trainer = get_trainer()\n", + "trainer.fit(model, data_module)\n", + "test_results = trainer.test(model, datamodule=data_module)\n", + "print(\"Printing test results...\")\n", + "print(test_results)\n", + "model_mses.append(test_results[0][\"test_mse\"])\n", + "\n", + "# linear model\n", + "linear_model = get_linear_model(num_tfs)\n", + "trainer = get_trainer()\n", + "trainer.fit(linear_model, data_module)\n", + "test_results = trainer.test(linear_model, datamodule=data_module)\n", + "print(\"Printing linear model test results\")\n", + "print(test_results)\n", + "linear_model_test_mses.append(test_results[0][\"test_mse\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Train models on data generated using the binary relations between TFs (method 4)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tf_relationships_dict_boolean_logic = {\n", + " 0: [And(3, 4, 8), Or(3, 7), Or(1, 1)],\n", + " 1: [And(5, Or(7, 8))],\n", + " 2: [],\n", + " 3: [Or(7, 9), And(6, 7)],\n", + " 4: [And(1, 2)],\n", + " 5: [Or(0, 1, 2, 8, 9)],\n", + " 6: [And(0, Or(1, 2))],\n", + " 7: [Or(2, And(5, 6, 9))],\n", + " 8: [],\n", + " 9: [And(6, And(3, Or(0, 9)))],\n", + "}\n", + "\n", + "data_module = get_data_module(\n", + " 3.0, \n", + " adjustment_function=perturbation_effect_adjustment_function_with_tf_relationships_boolean_logic, \n", + " tf_relationships_dict=tf_relationships_dict_boolean_logic\n", + ")\n", + "\n", + "# nonlinear model\n", + "model = get_model(num_tfs)\n", + "trainer = get_trainer()\n", + "trainer.fit(model, data_module)\n", + "test_results = trainer.test(model, datamodule=data_module)\n", + "print(\"Printing test results...\")\n", + "print(test_results)\n", + "model_mses.append(test_results[0][\"test_mse\"])\n", + "\n", + "# linear model\n", + "linear_model = get_linear_model(num_tfs)\n", + "trainer = get_trainer()\n", + "trainer.fit(linear_model, data_module)\n", + "test_results = trainer.test(linear_model, datamodule=data_module)\n", + "print(\"Printing linear model test results\")\n", + "print(test_results)\n", + "linear_model_test_mses.append(test_results[0][\"test_mse\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can plot the results of our experiment. TODO add explantion for plot here? Probably not the right place to put it (I feel like that belongs in the presentation or something, because this notebook could be modified and the explanation wouldn't make sense)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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UL16dd555x0KFChA3759WbFihfn1nu6BPnToECaTiQsXLgBw8eJFatasSbJkyUiUKBG5c+dm7dq1XLhwgffffx+AZMmSYTKZaNGiBQDBwcF06dKFVKlS4ebmRpkyZdi3b5/5NaJed8OGDRQsWBB3d3cqVKjArVu3WLduHTlz5sTLy4smTZoQFBRk3i76EPGofTz9LyqGs2fPUrt2bXx8fPD09KRo0aJs3rw5xvv34MEDGjduTKJEiUiXLh0TJ0587u/v8uXLNGzYkKRJk5I8eXJq165tfq+i1KxZk/nz5z93PwA1atTgzp077Ny501z2yy+/ULlyZVKlSmVR9/79+zRv3pxkyZLh4eFB1apVOX36tEUdPz8/MmTIgIeHBx999BH//PNPjNdcsWIFhQoVws3NjSxZstC/f3/CwsL+M1ZrUYItIiL/51s38lFcHuksyz3S6xFdImLXli6F+vXhylNzQV69Gln+OpLsu3fvsn79ejp16kSiRIlirI+69zQiIoLatWtz9+5dtm/fzqZNmzh37hwff/yxRf2zZ8+ybt061q9fz7x58/j555+pXr06V65cYfv27QwbNoxvv/2WP/74w2K77777jnr16nH48GE++eQTGjVqxIkTJ2KN+f79+1SoUIGCBQuyf/9+1q9fz82bN2nYsCHw/wSyadOmBAQE8Oeff/Ldd9/x008/4ePjE+s+jxw5QkBAAEWKFDGXHTp0iGPHjvHVV1/F+tzkF7kvt1OnTgQHB/P7779z9OhRhg0bhqenJ76+vixZsgSAv//+m+vXrzN2bOSF6B49erBkyRJ++eUXDh48SNasWalSpQp379612He/fv2YMGECu3btMieuY8aMYe7cuaxZs4aNGzcyfvz4WOMqVaoU169fN//bsmULbm5uvPfeewA8fPiQatWq8dtvv/Hnn3/y4YcfUrNmTS5dumSxnxEjRpA/f37+/PNPevXqRdeuXdm0aVOsrxkaGkqVKlVInDgxO3bsYOfOnXh6evLhhx8SEhJirlesWDGuXLkSI/F+mouLC5988gkzZ840l/n5+dGqVasYdTt27MiBAwdYuXIlu3fvxjAMqlWrZn7M3h9//EHr1q3p3Lkzhw4d4v3332fgwIEW+9ixYwfNmjWja9euHD9+nKlTp+Ln5xfjwtFrZcgrCwgIMAAjICDA1qE8U0hIiLF8+XIjJCTE1qGISEIQHmaEXNkS2W5c2WIY4WG2jkhEEoj4eM7x+PFj4/jx48bjx49favuwMMNIn94wIPZ/JpNh+PpG1rOmP/74wwCMpUuXPrfexo0bDUdHR+PSpUvmsmPHjhmAsXfvXsMwDKNv376Gh4eHERgYaK5TpUoVI1OmTEZ4eLi5LHv27MaQIUPMy4Dx2WefWbxe8eLFjQ4dOhiGYRjnz583AOPPP/80DMMwBgwYYFSuXNmi/uXLlw3A+Pvvvw3DMIzg4GCjQIECRsOGDY1cuXIZbdu2fe7xLVu2zHB0dDQiIiLMZQsWLDAA4+DBg8/dduvWrQZg3Lt3z1z2559/GoBx/vx5wzAMI2/evEa/fv3ivP3Dhw8NZ2dnY86cOeaykJAQI23atMbw4cMtttu8ebO5zpAhQwzAOHv2rLmsffv2RpUqVczL5cqVM7p27Rojjjt37hhZsmQxOnbs+NzjzZ07tzF+/HjzcsaMGY0PP/zQos7HH39sVK1a1bwMGMuWLTMMwzBmz55tZM+e3eK9Dg4ONtzd3Y0NGzaYy6Lyn23btj0zlqhjOXTokJE4cWLj4cOHxvbt241UqVIZoaGhRv78+Y2+ffsahmEYJ0+eNABjx44dFsfs7u5uLFy40DAMw2jcuLFRrVq1GMeSJEkS83LFihWNwYMHW9SZPXu2kSZNmliP92nPayvimvOpB1tERGJycIRUZSJ/TlVGw8JFxK7t2BGz5zo6w4DLlyPrWVNkLvDfTpw4ga+vL76+vuayXLlykTRpUoue5kyZMlnc3+vj40OuXLkseoB9fHy4deuWxf5LliwZY/lZPdiHDx9m69ateHp6mv/lyJEDiOxBh8hezTlz5rBkyRKePHnC6NGjn3t8jx8/xtXV1eIZxXF9b+KiS5cuDBw4kNKlS9O3b1+OHDny3Ppnz54lNDSU0qVLm8ucnZ0pVqxYjPclX7585p99fHzw8PAgS5YsFmVPv99PCw0NpV69emTMmNHcgw6RPdjdu3cnZ86cJE2aFE9PT06cOBGjB/tFf39nzpwhceLE5t9f8uTJefLkifn3B+Du7g5gMbz9WfLnz0+2bNlYvHgxM2bMoGnTpjg5OVnUOXHiBE5OThQvXtxcliJFCrJnz26O9cSJExbrYzu2w4cP88MPP1h8/tq2bcv169fjFKs1OP13FRERERER+3U9jk8njGu9uMqWLRsmk4mTJ09aZX/Ozs4WyyaTKdayiIiIl36Nhw8fUrNmTYYNGxZjXfRJrXbt2gVEDoO/e/durEPgo3h7exMUFERISAguLi4AvPvuuwCcPHmSggULPnPbqIsH0RPyqCHHUdq0aUOVKlXMQ7aHDBnCyJEjrfIoqujv78u+3x06dODy5cvs3bvXIjHt3r07mzZt4scffyRr1qy4u7tTv359i6HcL+rhw4cULlyYOXPmxFiXMmVK889RQ+Gjlz1Pq1atmDhxIsePH2fv3r0vHd9/efjwIf3796du3Zi3tLm5ub22141OPdgiIiIiIs8R18mOrT0pcvLkyalSpQoTJ07k0aNHMdZHTdyVM2dOLl++zOXLl83rjh8/zv3798mVK9crxxF9MrWo5Zw5c8Zat1ChQhw7doxMmTKRNWtWi39RSfTZs2f54osvmD59OsWLF6d58+bPTTILFChgPqboZbly5WLkyJGxbhv13kQlgNejXf2I7Zndvr6+fPbZZyxdupSvvvqK6dOnA5gT+vBo08W/8847uLi4WEzcFRoayr59+6zyfkc3atQoFi5cyIoVK0iRIoXFup07d9KiRQs++ugj8ubNS+rUqWO9J/pFf3+nT58mVapUMX5/SZIkMdf766+/cHZ2Jnfu3HE6jiZNmnD06FHy5MkT63uUM2dOwsLCLO7//+eff/j777/N9XPmzBljfoCnj61QoUL8/fffMWLPmjVrrPfqvw5KsEVEREREnqNsWUifHkzPeIqhyQS+vpH1rG3ixImEh4dTrFgxlixZwunTpzlx4gTjxo0zD4/94IMPyJs3L5988gkHDx5k7969NGvWjHLlyllMDPayFi1axIwZMzh16hR9+/Zl7969dO7cOda6nTp14u7duzRu3Jh9+/Zx9uxZNmzYQMuWLQkPDyc8PJxPP/2UKlWq0LJlS2bOnMmRI0cYOXLkM18/ZcqUFCpUCH9/f3OZyWRi5syZnDp1irJly7J27VrOnTvHkSNHGDRoELVr1wYga9as+Pr60q9fP06fPs2aNWtivFa3bt3YsGED58+f5+DBg2zdutWcgGbMmBGTycTq1au5ffs2Dx8+JFGiRHTo0IGvv/6a9evXc/z4cdq2bUtQUBCtW7d+1bfbbPPmzfTo0YMRI0bg7e3NjRs3uHHjBgEBAUDkCIelS5dy6NAhDh8+TJMmTWK92LBz506GDx/OqVOnmDhxIosWLaJr166xvuYnn3yCt7c3tWvXZseOHZw/f55t27bRpUsXrkS7T2LHjh2ULVvWPFT8vyRLlozr168/89nt2bJlo1q1arRv3x5/f38OHz7Mp59+Srp06cy/yy5durB+/Xp+/PFHTp8+zYQJE1i/fr3Ffr7//ntmzZpF//79OXbsGCdOnGD+/Pl8++23cYrTGpRgi4iIiIg8h6MjRN36+nSSHbU8ZkxkPWvLkiULBw8e5P333+err74iT548VKpUid9++838HGKTycSKFStIliwZ7733Hh988AFZsmRhwYIFVomhf//+zJ8/n3z58jFr1izmzZv3zJ7atGnTsnPnTsLDw6lcuTJ58+alW7duJE2aFAcHBwYNGsTFixeZOnUqEDlsfNq0aXz77bccPnz4mTG0adMmxrDlYsWKsX//frJmzUrbtm3JmTMntWrV4tixY4wZMwaIHKI9b948Tp48Sb58+Rg2bFiMmafDw8Pp1KkTOXPm5MMPP+Tdd99l0qRJAKRLl47+/fvTq1cvfHx8zBcWhg4dSr169WjatCmFChXizJkzbNiwgWTJkr3Uexwbf39/wsPD+eyzz0iTJo35X1RyPGrUKJIlS0apUqWoWbMmVapUoVChQjH289VXX7F//34KFizIwIEDGTVqFFWqVIn1NT08PPj999/JkCEDdevWJWfOnLRu3ZonT57g5eVlrjd//nzatm37QseTNGnS594KMHHiRAoVKkSNGjUoWbIkhmGwdu1a87D6EiVKMH36dMaOHUv+/PnZuHFjjMS5SpUqrF69mo0bN1K0aFFKlCjB6NGjyZgx4wvF+ipMhjVnCLBTgYGBJEmShICAAIsPXnwSGhrK2rVrqVatWox7P0REYqN2Q0ReRnxsO548ecL58+fJnDnzK92HuXQpdO1qOeGZr29kch3LLZ9vBZPJxLJly6hTp45N43j8+DHZs2dnwYIFMSa2kjdr3bp1fPXVVxw5ciTGZGUvKyIigsDAQLy8vN7YUO7YPK+tiGvOp0nORERERETioG5dqF07crbw69cj77kuW/b19FyLJXd3d2bNmsWdO3dsHYrde/ToETNnzrRacv220bsiIiIiIhJHjo5Qvryto7BP5fXGxwv169e3dQjxmhJsERERERGJle4mFXkxmuRMRERERERExAqUYIuIiIiI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YgVKsEVERERERESsQAm2iIiIiIiIiBUowRYRERERERGxAiXYIiIiIiIiIlagBBu4evUqn376KSlSpMDd3Z28efOyf/9+W4clIiIiIiIiCYiTrQOwtXv37lG6dGnef/991q1bR8qUKTl9+jTJkiWzdWgiIiIiIiKSgNh9gj1s2DB8fX2ZOXOmuSxz5sw2jEhEREREREQSIrtPsFeuXEmVKlVo0KAB27dvJ126dHTs2JG2bds+c5vg4GCCg4PNy4GBgQCEhoYSGhr62mN+GVFxxdf4RCT+UbshIi9DbYeIvKiE0G7ENTaTYRjGa44lXnNzcwPgyy+/pEGDBuzbt4+uXbsyZcoUmjdvHus2/fr1o3///jHK586di4eHx2uNV0RERERERN6soKAgmjRpQkBAAF5eXs+sZ/cJtouLC0WKFGHXrl3msi5durBv3z52794d6zax9WD7+vpy586d577ZthQaGsqmTZuoVKkSzs7Otg5HRBIAtRsi8jLUdojIi0oI7UZgYCDe3t7/mWDb/RDxNGnSkCtXLouynDlzsmTJkmdu4+rqiqura4xyZ2fnePuBiJIQYhSR+EXthoi8DLUdIvKi4nO7Ede47P4xXaVLl+bvv/+2KDt16hQZM2a0UUQiIiIiIiKSENl9gv3FF1+wZ88eBg8ezJkzZ5g7dy7Tpk2jU6dOtg5NREREREREEhC7T7CLFi3KsmXLmDdvHnny5GHAgAGMGTOGTz75xNahiYiIiIiISAJi9/dgA9SoUYMaNWrYOgwRERERERFJwOy+B1tERERERETEGpRgi4iIiIiIiFiBEmwRERERERERK1CCLSIiIiIiImIFSrBFRERERERErEAJtoiIiIiIiIgVKMEWERERERERsQIl2CIiIiIiIiJWoARbRERERERExAqUYIuIiIiIiIhYgRJsEREREREREStQgi0iIiIiIiJiBUqwRURERERERKxACbaIiIiIiIiIFSjBFhEREREREbECJdgiIiIiIiIiVqAEW0RERERERMQKlGCLiIiIiIiIWIESbBERERERERErUIItIiIiIiIiYgVKsEVERERERESsQAm2iIiIiIiIiBUowRYRERERERGxAiXYIiIiIiIiIlagBFtERERERETECpRgi4iIiIiIiFiBEmwRERERERERK1CCLSIiIiIiImIFSrBFRERERERErEAJ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+fPnc/DgQfbt2xen+kOGDKF///4xyjdu3IiHh4e1w7OqTZs22ToEEUlg1G6IyH9zhERzLUo2JZrx/4XDwOG1bzYkEUlw4vM5R1BQUJzqmQzDMF5zLPHa5cuXKVKkCJs2bTLfe12+fHkKFCjAmDFjYt0mth5sX19f7ty5g5eX15sI+4WFhoayadMmKlWqhLOzs63DEZEEQO2GiMTJLf//Dwsnsud6U6IZVHrUCmce/79euTWQqowNAhSR+C4hnHMEBgbi7e1NQEDAc3M+u+/BPnDgALdu3aJQoULmsvDwcH7//XcmTJhAcHAwjo6OFtu4urri6uoaY1/Ozs7x9gMRJSHEKCLxi9oNEXmu0BsQPZH+lzOPLRPs0BugtkREniM+n3PENS67T7ArVqzI0aNHLcpatmxJjhw56NmzZ4zkWkRERESicU9j3XoiIgmY3SfYiRMnJk+ePBZliRIlIkWKFDHKRUREROQpKcuCR3oIugrEduehKXJ9yrJvOjIRkTdOj+kSERERkZfn4AiFx/67YHpq5b/LhcdE1hMRecvZfQ92bLZt22brEEREREQSDt+6UHYxHOgKQf/8v9wjfWRy7VvXZqGJiLxJSrBFRERE5NX51oV0teH677A/MHLW8DTvqedaROyKhoiLiIiIiHU4OP7/UVypyii5FhG7owRbRERERERExAqUYIuIiIiIiIhYgRJsEREREREREStQgi0iIiIiIiJiBUqwRURERERERKxACbaIiIiIiIiIFSjBFhEREREREbECJdgiIiIiIiIiVqAEW0RERERERMQKlGCLiIiIiIiIWIESbBERERERERErUIItIiIiIiIiYgVKsEVERERERESsQAm2PYgIh1v+kT/f8o9cFhEREREREatSgv22u7wUVmaC7dUjl7dXj1y+vNSWUYmIiIiIiLx1lGC/zS4vhR31IeiKZXnQ1chyJdkiIiIiIiJWowT7bRURDge6AkYsK/8tO9BNw8VFRERERESsRAn22+r2jpg91xYMCLocWU9ERERERERemRLst9Xj69atJyIiIiIiIs+lBPtt5Z7GuvVExK6Eh4P/vw8f8PePXBYRERGR51OC/bZKWRY80gOmZ1QwgYdvZD0RkWiWLoVMmaD6vw8fqF49cnmp5kUUEREReS4l2G8rB0coPPbfhaeT7H+XC4+JrCci8q+lS6F+fbjy1BQOV69GlivJFhEREXk2JdhvM9+6UHYxeKSzLPdIH1nuW9c2cYlIvBQeDl27ghHLwweiyrp103BxERERkWdRgv22860LtS5AuTWRy+XWQK3zSq5FJIYdO2L2XEdnGHD5cmQ9EREREYlJCbY9cHCEVGUif05VRsPCRSRW1+P4UIG41hMRERGxN0qwRUQEgDRxfKhAXOuJiIiI2Bsl2CIiAkDZspA+PZie8fABkwl8fSPriYiIiEhMSrCByZMnky9fPry8vPDy8qJkyZKsW7fO1mGJiLxRjo4w9t+HDzydZEctjxkTWU9EREREYlKCDaRPn56hQ4dy4MAB9u/fT4UKFahduzbHjh2zdWgiIm9U3bqweDGke+rhA+nTR5bX1fyIIiIiIs/kZOsA4oOaNWtaLA8aNIjJkyezZ88ecufObaOoRERso25dqF0bfv8dAgNhzRp47z31XIuIiIj8FyXYTwkPD2fRokU8evSIkiVLxlonODiY4OBg83JgYCAAoaGhhIaGvpE4X1RUXPE1PhGJf4oXD2XTpsj/IyIgIsLWEYlIQqBzDhF5UQmh3YhrbCbDMIzXHEuCcPToUUqWLMmTJ0/w9PRk7ty5VKtWLda6/fr1o3///jHK586di4eHx+sOVURERERERN6goKAgmjRpQkBAAF5eXs+spwT7XyEhIVy6dImAgAAWL17MTz/9xPbt28mVK1eMurH1YPv6+nLnzp3nvtm2FBoayqZNm6hUqRLOzs62DkdEEgC1GyLyMtR2iMiLSgjtRmBgIN7e3v+ZYGuI+L9cXFzImjUrAIULF2bfvn2MHTuWqVOnxqjr6uqKq6trjHJnZ+d4+4GIkhBiFJH4Re2GiLwMtR0i8qLic7sR17g0i/gzREREWPRSi4iIiIiIiDyPerCBb775hqpVq5IhQwYePHjA3Llz2bZtGxs2bLB1aCIiIiIiIpJAKMEGbt26RbNmzbh+/TpJkiQhX758bNiwgUqVKtk6NBEREREREUkglGADP//8s61DEBERERERkQRO92CLiIiIiIiIWIESbBERERERERErUIItIiIiIiIiYgVKsEVERERERESsQAm2iIiIiIiIiBUowRYRERERERGxAiXYIiIiIiIiIlagBFtERERERETECpRgi4iIiIiIiFiBEmwRERERERERK1CCLSIiIiIiImIFSrBFRERERERErEAJtoiIiIiIiIgVKMEWERERERERsQIl2CIiIiJiFeHh4O8f+bO/f+SyiIg9UYItIiIiIq9s6VLIlAmqV49crl49cnnpUltGJSLx3dt2YU4JtoiIiIi8kqVLoX59uHLFsvzq1chyJdkiEpu38cKcEmwREREReWnh4dC1KxhGzHVRZd26JfxeKRGxrrf1wpwSbBERERF5aTt2xDxBjs4w4PLlyHoiIvB2X5hTgi0iIiIiL+36devWE5G339t8YU4JtoiIiIi8tDRprFtPRN5+b/OFOSXYIiIiIvLSypaF9OnBZIp9vckEvr6R9URE4O2+MKcEW0RERERemqMjjB0b+fPTSXbU8pgxkfVERODtvjCnBFtEREREXkndurB4MaRLZ1mePn1ked26tolLROKnt/nCnBJsEREREXlldevChQuwZk3k8po1cP68kmsRid3bemFOCbaIiIiIWIWjI5QpE/lzmTIJs/dJRN6ct/HCnBJsERERERERsYm37cKcEmwRERERERERK1CCLSIiIiIiImIFSrCBIUOGULRoURInTkyqVKmoU6cOf//9t63DEhERERERkQRECTawfft2OnXqxJ49e9i0aROhoaFUrlyZR48e2To0ERERERERSSCcbB1AfLB+/XqLZT8/P1KlSsWBAwd47733bBSViIiIiIiIJCTqwY5FQEAAAMmTJ7dxJCIiIiIiIpJQqAf7KREREXTr1o3SpUuTJ0+eWOsEBwcTHBxsXg4MDAQgNDSU0NDQNxLni4qKK77GJyLxj9oNEXkZajtE5EUlhHYjrrGZDMMwXnMsCUqHDh1Yt24d/v7+pE+fPtY6/fr1o3///jHK586di4eHx+sOUURERERERN6goKAgmjRpQkBAAF5eXs+spwQ7ms6dO7NixQp+//13MmfO/Mx6sfVg+/r6cufOnee+2bYUGhrKpk2bqFSpEs7OzrYOR0QSALUbIvIy1HaIyItKCO1GYGAg3t7e/5lga4g4YBgGn3/+OcuWLWPbtm3PTa4BXF1dcXV1jVHu7Owcbz8QURJCjCISv6jdEJGXobZDRF5UfG434hqXEmygU6dOzJ07lxUrVpA4cWJu3LgBQJIkSXB3d7dxdCIiIiIiIpIQaBZxYPLkyQQEBFC+fHnSpElj/rdgwQJbhyYiIiIiIiIJhHqwiRwiLiIiIiIiIvIq1IMtIiIiIiIiYgVKsEVERERERESsQAm2iIiIiIiIiBUowRYRERERERGxAiXYIiIiIiIiIlagBFtERERERETECpRgi4iIiIiIiFiBEmwRERERERERK1CCLSIiIiIiImIFSrBFRERERERErEAJth0IDwd//8if/f0jl0VERERERMS6lGC/5ZYuhUyZoHr1yOXq1SOXly61ZVQiIiIiIiJvHyXYb7GlS6F+fbhyxbL86tXIciXZIiIiIiIi1qME+y0VHg5du4JhxFwXVdatm4aLi4iIiIiIWIsS7LfUjh0xe66jMwy4fDmynoiIiIiIiLw6JdhvqevXrVtPREREREREnk8J9lsqTRrr1hMREREREZHnU4L9lipbFtKnB5Mp9vUmE/j6RtYTERERERGRV6cE+y3l6Ahjx0b+/HSSHbU8ZkxkPREREREREXl1SrDfYnXrwuLFkC6dZXn69JHldevaJi4REREREZG3kZOtA5DXq25dqF0bfv8dAgNhzRp47z31XIuIiIiIiFiberDtgKMjlCkT+XOZMkquRUREREREXgcl2CIiIiIiIiJWoARbRERERERExAqUYIuIiIiIiIhYgRJsEREREREREStQgi0iIiIiIiJiBXpMlxUYhgFAYGCgjSN5ttDQUIKCgggMDMTZ2dnW4YhIAqB2Q0RehtoOEXlRCaHdiMr1onK/Z1GCbQUPHjwAwNfX18aRiIiIiIiIyOvy4MEDkiRJ8sz1JuO/UnD5TxEREVy7do3EiRNjMplsHU6sAgMD8fX15fLly3h5edk6HBFJANRuiMjLUNshIi8qIbQbhmHw4MED0qZNi4PDs++0Vg+2FTg4OJA+fXpbhxEnXl5e8fZDKyLxk9oNEXkZajtE5EXF93bjeT3XUTTJmYiIiIiIiIgVKMEWERERERERsQIl2HbC1dWVvn374urqautQRCSBULshIi9DbYeIvKi3qd3QJGciIiIiIiIiVqAebBERERERERErUIItIiIiIiIiYgVKsEVERERERESsQAl2Aqdb6EVEREREROIHJdgJWEREBCaTydZhiEgCowtzIvKi1G6IyIuy13ZDCXYCtXv3bq5fvw5A9+7dGTt2rI0jEpGEQBfmRORFnThxgqCgIAC+//57tm3bZtuARCTei36+cerUKe7du2fjiN4cJ1sHIC8mIiKCu3fvUrp0aRo1akSiRIlYtGgR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EydSoEABPvjgAw4dOkSFChXw8vLi/fffJyQkxNJNFnkl6Am2iIjIK2jp0qX069eP3r17s3XrVry8vJg7d6654FBGobLp06djMBjo3bs38fHx9O7d28ItFxFLq1u3LjY2NqSlpfHNN98QGRnJvn372LFjB/7+/jx69Ii0tDSaNm3KW2+9xYwZMzQLRuRPMpgy9uYQERGRV8KcOXMYOnQoW7ZsoXnz5ixevJixY8fy6aefMnfuXIAsIRugf//+nDlzhj179lio1SLyMshYRgJw8OBBvL29uX//Prt27aJQoULs27ePDRs20L17d955550s56anp2tauMh/oYAtIiLyitm7dy83btzgk08+AeDhw4esXbsWHx+f/xiyMw+sRcR6Ze4LoqKiGDlyJNevX+fbb7/FycmJxMREcubMqT5D5H+ggC0iIvKKyjz4ffToEWvWrHkmZD+9hY4GzCLW5Y/6gKdD9qhRo7h69Sq7d++mRIkSz9ygE5E/R3M8REREXlGZg7KDgwOffPIJvr6+BAcHM2TIEIBnttBRuBaxHikpKeY+YO/evTx69MjcB2SEbIBatWoxbdo0SpcuTeXKlblz547Ctcj/SAFbREQkm8gcsufOnWt+ii0i1mf79u3UqVMHgGHDhjFkyBCSk5OzvOfpkD1hwgR69OhBgQIFXnh7RbILVREXERHJRhwcHOjQoQOFCxemVatWlm6OiFiAyWTCxsaGxMREypUrR0xMDMeOHaNgwYLPvDfzrJZ69epRr1494NkaDiLy52gNtoiISDb29PpLEbEePXv2JCgoiGrVqnH8+HFAfYLI301TxEVERLIxDaRFrEfGczOTyUR6ejqtWrViyZIl2NjY8MEHH5CcnIydnd0zU8VF5PnRE2wRERERkVdc5j2q4+PjMRgM5MqVC4B///vfDB8+nNy5c/P999+bp36HhobSqFEj8ubNa7F2i2Q3eoItIiIiIvKKywjXkyZNolWrVtSpU4eVK1cC0KhRI/z9/YmPj6dWrVocO3aMxo0b88UXX5AnTx5LNlsk21HAFhERERF5RaWnp5v/29/fnwULFvDhhx/y7rvv0rNnT8aNG0daWhofffQRCxYswGg04uLiQnJyMtu2bctSSVxE/jotzBIREREReUVlPLk+c+YMCQkJBAYG0qxZMwAaNGiAm5sbJpOJ8ePHU7duXSIjI/nxxx+pWrUqNjY2Knom8pzpt0lERERE5BW2b98+GjRoQN68ec3TwgG6d+8OQK9evbCxsWH48OE4ODhQrVo14MnTb4VrkedLU8RFRERERF5h9erVY9asWcTGxnLq1Kks08a7d+9OYGAgkydPJiQkJMt5GU+/ReT50S0rEREREZFXROZq4Zmndw8ZMoS4uDgmTJhA4cKF6du3r/mcrl27UrBgQf75z39apM0i1kQBW0RERETkFZA5XH/55ZdERUXx6NEjatasydChQxk3bhwA/fv3x2Aw4O7ubj43Y1221lyL/L302yUiIiIi8grICNfe3t4EBAQwbNgwHj9+zKJFi4iMjCQ8PJxx48Zha2uLp6cncXFxDBkyJMs1FK5F/l5aeCEiIiIi8hLLvKY6KiqKTZs2ERYWhre3NzVr1uTBgwe4uLiYA/iYMWPw8vJiw4YN2oJL5AVTwBYREREReQl5e3tz9OhRbGxszCH7zp07GAwG6tSpw8aNG+nWrRt+fn64u7sTFxfHhg0bSEtLY8aMGXz33Xfa51rkBVPAFhERERF5yZw4cYLvv/+egQMHcvLkSfPTaUdHRypUqEBgYCDdu3fHz8+Pfv36AXDkyBG2bt3KxYsXAczh2mAwWOxziFgbg0m3tEREREREXjrffvstc+bM4fbt2yxdupRq1apx8+ZNateuzZUrV/D392fw4MEAJCYm4uLiQr58+QgODlaoFrEQBWwRERERkZdISkoKRqMRgHXr1rF8+XIePnzI0qVLefvttzl58iQNGjTg448/pmXLluTJk4elS5dy69Ytjh8/jp2dnZ5ci1iIAraIiIiIyEsiczCeMmUKR44c4cKFC5w8eZL33nuPRYsWUb16daKiohgyZAh37tyhcOHClC5dmsDAQIxGI2lpadja2lr4k4hYJwVsEREREZGXzPz58xkzZgyhoaGUK1eOb7/9ltWrV/P48WPzdPG4uDgSExMxGo3ky5cP0D7XIpamgC0iIiIi8hJJTU3Fzc2NvHnzsnDhQvPx8PBwxo4dS65cuVi+fDmVK1fOcp6mhYtYnqqIi4iIiIi8ROzs7MidOzfnz58nOTnZfLxVq1Y0a9aMQ4cO0apVK86fP5/lPIVrEctTwBYRERERsZCM/a2fVq1aNa5evcqOHTtITEw0H69UqRLNmzenV69elCtX7kU1U0T+JE0RFxERERGxgPT0dPP+1mFhYSQnJ5M7d25atGgBQIsWLYiOjmbChAnUr1+fvHnz4ubmRrVq1fj8888xGAwqaCbyklHAFhERERF5wTKvlx4xYgRLliyhePHiREdH4+npib+/PwCurq6cO3eOq1evUrRoUUwmE6dOndJWXCIvKZUYFBERERF5wTKC8bVr19i7dy/fffcd+fPn5+DBg/Ts2ZPHjx+zePFivvnmGw4ePEh0dDQGg4FOnTpha2urJ9ciLykFbBERERERC5gyZQonTpygWrVqVK5cGaPRSOnSpcmZMyedO3fGxsaGhQsXUrt2bWrXrm0+T+Fa5OWlImciIiIiIi9YWloa9vb2hIWFceLECYxGo/k1Z2dngoOD+eqrr+jatesz5ypci7y8FLBFRERERP5mT1cLt7W1ZeDAgcybN49jx44xefLkLK87OzuzbNkybty48YeVxkXk5aMiZyIiIiIif6PM1cKPHz/OzZs3KVu2LMWKFSNfvnzMmzePIUOGMHnyZEaPHv1fryEiLy+twRYRERER+ZuYTCZzMB41ahShoaEkJCRQqlQp8uTJw+LFixk0aBC2trYMHjwYGxsbvL29n7mOwrXIq0G/qSIiIiIif5OMauHz588nICCAZcuWcfnyZd5//3327t3LuXPnAOjbty9z5sxh9OjRrFq1ypJNFpG/QE+wRURERET+JiaTidTUVA4cOMCIESOoV68e4eHhLFmyhHnz5tG4cWMSEhJIS0tj4MCBFC1aFGdnZ0s3W0T+R3qCLSIiIiLyNzEYDBiNRpKSkqhYsSLbtm2jc+fO+Pn54e7uTmpqKqtXr2bHjh0AtG/fHjs7O1JTUy3cchH5X+gJtoiIiIjIc/JHxchy5cqFl5cXMTEx+Pv74+7uDsDdu3cJDg5+5qm1nZ2G6SKvIlURFxERERF5DjKH65MnT5I7d24Aypcvz8OHD/nnP//JgwcPOHLkCABJSUm4ubnx4MEDvv/+e+1vLZINKGCLiIiIiPxFJpPJXNBsxIgRrF27lsTERPLmzYubmxvjxo3jwIEDdOrUCaPRiNFopECBAiQnJ3PgwAGMRiNpaWkK2SKvOM09ERERERH5CzKH623bthEcHExgYCApKSn8/PPPeHt78/DhQ2bOnMn58+cJCAggLS2NokWL0rZtW2xtbUlNTdW0cJFsQE+wRURERESeg7CwMMLCwihZsiQTJ040H9+4cSPt27dn4cKFeHh4PHOenlyLZB+qIi4iIiIi8hedO3cOPz8/NmzYQGxsrPl4WloaLi4u9OnTh4iICJKSkkhLS8tyrsK1SPahgC0iIiIi8n/09CTQihUr4u3tTdWqVQkJCWHv3r3Ab+G5QIEC3Lt3jxw5cihQi2RjCtgiIiIiIv8H6enp5jXXMTExXL9+HYDWrVszceJEKlWqxMSJE80h+9GjRxw8eJASJUqYzxOR7EmVFERERERE/iSTyWTeisvX15ctW7Zw8+ZNSpUqhY+PD02aNCExMZHp06fTrFkz3nnnHUqXLs3jx48JDAw0X0NBWyR70hNsEREREZE/KSMYf/7558yfP5/PPvuM77//nqtXr+Lt7c2VK1do3rw5Pj4+1KpVi+TkZBo2bMjhw4ext7cnOTlZ4VokG1PAFhERERH5P7h+/Tpbt25l8eLFdO7cmXPnznH79m369++Pk5MTAI0bN2b48OEUL16cTZs2ceTIEQBy5MhhyaaLyN9MAVtERERE5A+kp6eb/zujsFliYiIPHz6kTZs2bN26lTZt2uDn50ffvn2Ji4tj2bJlxMfH07p1azw8PDAYDHh5eXH48GFLfQwReUG0BltERERE5A9krLeG36aHOzk5kStXLrp168bmzZvx9/fH3d0dgGvXrhEUFESpUqVo2rQprVq1IikpieDgYIoWLWqRzyAiL47B9PQeAyIiIiIiwoEDB4iMjCQ8PBx7e3vat29P3bp1qVKlCn5+fsyYMYNGjRqxZs0a4MmTbVdXV1JTU9m6dWuWcB4XF0eePHks9VFE5AVRwBYRERERecrKlSvx9fWlatWqwJOAvHPnTho0aMD06dOpWLEiQ4YMYffu3bz33nsUKVKEEydOEBMTw9GjRzEajebtvFTUTMR6KGCLiIiIiGSyePFiBg8ezJIlS2jVqhWOjo4ALF26lFmzZlG4cGECAwPJkycP27dvJyAggBIlSlCqVCkmTpyInZ0dqamp2NlpNaaItVHAFhERERH5/77++mu6detGREQETZo0IT09PctU74CAALy8vPD09GTKlCm/e420tDRsbW1fVJNF5CWigC0iIiIiAty6dYsGDRrg6OhIQEAAb731FvBb9fCMqd59+/YlIiKCH3/8EQcHB4u1V0RePtqmS0REREQEKFKkCNOmTcPW1pZJkyZx6NAh4LdgnZqaCkDDhg2Jj4/n7t27FmuriLycFLBFRERExOplPKV2cXFh5MiR/PLLL8ydOzdLyM6Y9h0dHU2NGjVwcnKyWHtF5OWkgC0iIiIiVs9gMJhDtrOzMz4+PuaQffDgQfN77t69S2RkJO+//76KmInIM7QGW0RERESsUnx8PK+99lqWYyaTyTwlPCwsDF9fX8qXL8/w4cOpWbMmrVu35vbt20RGRmJnZ5fl/SIiCtgiIiIiYnUCAwM5ePAgc+fOxd7ePstrmUPzpk2bmDJlChUqVODMmTPExcXx448/YjQaVS1cRJ6heS0iIiIiYlWWLFlCv379CA8PfyZcw2/TxQ0GA23atMFgMNC/f39KlChhDtfa51pEfo+eYIuIiIiI1Vi6dCkDBgxg3bp1tG3b9j++N/OT7KioKGrWrImtra3CtYj8IRU5ExERERGr8PXXX+Ph4cGGDRuyhOvx48dz4cKFZ96fufBZrVq1sLW1JS0tTeFaRP6QAraIiIiIZHvx8fHcunULgLS0NPPxDh06sGLFCnLnzv275z1dwExrrkXkP9HtNxERERHJ1latWkV0dDRjx47lzp07dOrUibVr1xIcHMy5c+eIjIykSJEilm6miGQDCtgiIiIikm1lFDTbsmULdnZ2TJ06lfT0dNq1a0ehQoU4cuQIpUqVsnQzRSSb0BRxEREREcmWVq1ahaenJ+Hh4TRv3ty8nnr69OlMmjSJe/fucfDgQQu3UkSyEz3BFhEREZFsJzAwkF69evHPf/6TFi1aAGQpUObj48OjR4/o0qULqampdO7c2ZLNFZFsQk+wRURERCRbWbp0Kb1796Z379789NNPeHl5AWBnZ5elwNn06dMZOnQovXv3Zvny5ZZqrohkI9oHW0RERESyjTlz5jB06FC2bNlC8+bNWbx4MWPHjuXTTz9l7ty5wJMn2Zmrgffv358zZ86wZ88eC7VaRLILBWwRERERyTb27t3LjRs3+OSTTwB4+PAha9euxcfH5z+GbJPJ9MyWXCIi/1dagy0iIiIi2caHH34I/BaY8+XLZw7bPj4+AMydOxdbW1tSU1PNa7INBoNCtoj8ZQrYIiIiIpLtZA7KDg4O5pA9duxYbGxsmD17tjlc/945IiL/CwVsEREREcn2MkK2wWDAw8ODMmXKmIufiYg8L1qDLSIiIiJW48GDB+zdu5dWrVplWYMtIvI8KGCLiIiIiFXKvAZbROR5UMAWEREREREReQ5sLN0AERERERERkexAAVtERERERETkOVDAFhEREREREXkOFLBFREREREREngMFbBEREREREZHnQAFbRERERERE5DlQwBYRERERERF5DhSwRURERERERJ4DBWwRERF5Lvbs2YPBYODBgwd/+pwyZcowZ86cv61NIiIiL5ICtoiIiJVwc3PDYDDQr1+/Z14bOHAgBoMBNze3F98wERGRbEIBW0RExIqUKlWKNWvWkJCQYD6WmJjI6tWrcXJysmDLREREXn0K2CIiIlakRo0alCpVig0bNpiPbdiwAScnJ6pXr24+lpSUxKBBgyhcuDA5c+akXr16HD58OMu1tm7dSsWKFcmVKxcfffQRly5deubf27dvH/Xr1ydXrlyUKlWKQYMG8fjx47/t84mIiFiSAraIiIiV6dWrFwEBAea/r1ixgp49e2Z5z8iRI1m/fj1BQUEcO3aMChUq0LRpU+7fvw/Ar7/+Srt27WjdujU//PADffr0YdSoUVmuER0dTbNmzWjfvj0nT55k7dq17Nu3D09Pz7//Q4qIiFiAAraIiIiV6dq1K/v27ePy5ctcvnyZyMhIunbtan798ePHLFy4ED8/P5o3b07lypVZunQpuXLlYvny5QAsXLiQ8uXLM2vWLN588026dOnyzPrtqVOn0qVLFwYPHswbb7zBBx98wLx581i5ciWJiYkv8iOLiIi8EHaWboCIiIi8WIUKFaJly5YEBgZiMplo2bIlBQsWNL8eHR1NSkoKdevWNR8zGo3UqlWLM2fOAHDmzBnef//9LNetU6dOlr+fOHGCkydP8vXXX5uPmUwm0tPTuXjxIm+99dbf8fFEREQsRgFbRETECvXq1cs8VXvBggV/y78RFxeHh4cHgwYNeuY1FVQTEZHsSAFbRETECjVr1ozk5GQMBgNNmzbN8lr58uXJkSMHkZGRlC5dGoCUlBQOHz7M4MGDAXjrrbfYtGlTlvMOHjyY5e81atTg9OnTVKhQ4e/7ICIiIi8RrcEWERGxQra2tpw5c4bTp09ja2ub5bXcuXPTv39/RowYQUREBKdPn8bd3Z34+Hh69+4NQL9+/Th//jwjRozg559/ZvXq1QQGBma5jre3N/v378fT05MffviB8+fPExYWpiJnIiKSbSlgi4iIWCkHBwccHBx+97Vp06bRvn17unXrRo0aNfjll1/Yvn07jo6OwJMp3uvXryc0NJRq1aqxaNEipkyZkuUa//jHP9i7dy/nzp2jfv36VK9enfHjx1O8ePG//bOJiIhYgsFkMpks3QgRERERERGRV52eYIuIiIiIiIg8BwrYIiIiIiIiIs+BAraIiIiIiIjIc6CALSIiIiIiIvIcKGCLiIiIiIiIPAcK2CIiIiIiIiLPgQK2iIiIiIiIyHOggC0iIiIiIiLyHChgi4iIiIiIiDwHCtgiIiIiIiIiz4ECtoiIiIiIiMhzoIAtIiIiIiIi8hz8P1gP7XB1+aaGAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "data_gen_methods = [\"No Mean Adjustment\", \"Dependent Mean Adjustment\", \"TF Dependent Mean Adjustment\", \"TF Dependent Mean Adjust with Boolean Logic\"]\n", + "plt.figure(figsize=(10, 6))\n", + "plt.scatter(data_gen_methods, model_mses, color='blue')\n", + "plt.scatter(data_gen_methods, linear_model_test_mses, color='orange')\n", + "plt.title('Model MSE Comparison (bound mean = 3.0)')\n", + "plt.xlabel('Model')\n", + "plt.ylabel('MSE')\n", + "plt.grid(True)\n", + "plt.xticks(rotation=45, ha=\"right\")\n", + "plt.legend(['Complex (Customizable) Model', 'Linear Model'])\n", + "plt.tight_layout() # Adjust layout to make room for the rotated x-axis labels\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/experiments/inspect_simple_model.py b/experiments/inspect_simple_model.py new file mode 100644 index 0000000..f310287 --- /dev/null +++ b/experiments/inspect_simple_model.py @@ -0,0 +1,60 @@ +"""Script to inspect the parameters of a trained model (passed in via a checkpoint +file)""" + +import argparse + +from yeastdnnexplorer.ml_models.simple_model import SimpleModel + + +def inspect_model_experiment(checkpoint_file_path: str) -> None: + """ + Runs the simple experiement to inspect the parameters of a trained model. + + :param checkpoint_file_path: The path to the model checkpoint file that we want to + inspect + :type checkpoint_file_path: str + + """ + + # load the model from the checkpoint + model = SimpleModel.load_from_checkpoint(checkpoint_path=checkpoint_file_path) + + print("Model Hyperparameters===========================================") + print(model.hparams) + + print("Model Parameters================================================") + for name, param in model.named_parameters(): + print(f"{name}: {param.size()}") + print(f"\t{param.data}") + + +def parse_args_for_inspect_model_experiment() -> argparse.Namespace: + """ + Parses the command line arguments for the inspect_model_experiment function Fails + with error message if the required argument (checkpoint_file) is not provided. + + :return: The parsed command line arguments + :rtype: argparse.Namespace + + """ + parser = argparse.ArgumentParser(description="Inspcting Model Parameters") + parser.add_argument( + "--checkpoint_file", type=str, action="store", required=True + ) # this will be the checkpoint file that we want to inspect + args = parser.parse_args() + return args + + +def main() -> None: + """Main method to run he experiment for inspecting the parameters of a trained + model.""" + args = parse_args_for_inspect_model_experiment() + + # use default values if flag not present in command line arguments + checkpoint_file_path = args.checkpoint_file + + inspect_model_experiment(checkpoint_file_path) + + +if __name__ == "__main__": + main() diff --git a/experiments/simple_model_synthetic_data.py b/experiments/simple_model_synthetic_data.py new file mode 100644 index 0000000..021841d --- /dev/null +++ b/experiments/simple_model_synthetic_data.py @@ -0,0 +1,161 @@ +"""Script to train our simple model on synthetic data and save the best model based on +validation loss.""" + +import argparse +from argparse import Namespace + +from pytorch_lightning import Trainer, seed_everything +from pytorch_lightning.callbacks import ModelCheckpoint +from pytorch_lightning.loggers import CSVLogger, TensorBoardLogger + +from yeastdnnexplorer.data_loaders.synthetic_data_loader import SyntheticDataLoader +from yeastdnnexplorer.ml_models.simple_model import SimpleModel + +# Callback to save the best model based on validation loss +best_model_checkpoint = ModelCheckpoint( + monitor="val_loss", + mode="min", + filename="best-model-{epoch:02d}-{val_loss:.2f}", + save_top_k=1, +) + +# Callback to save checkpoints every 5 epochs, regardless of performance +periodic_checkpoint = ModelCheckpoint( + filename="periodic-{epoch:02d}", + every_n_epochs=2, + save_top_k=-1, # Setting -1 saves all checkpoints +) +# Need to configure the loggers we're going to use +tb_logger = TensorBoardLogger("logs/tensorboard_logs") +csv_logger = CSVLogger("logs/csv_logs") + + +def simple_model_synthetic_data_experiment( + batch_size: int, + lr: float, + max_epochs: int, + using_random_seed: bool, + accelerator: str, +) -> None: + """ + Trains a SimpleModel on synthetic data and saves the best model based on validation + loss. Defines an instance of Trainer, which is used to train the model with the + given dataModule. While much of the training process is captured via logging, we + also print the test results at the end of training. We don't need to do assrtions + for type checking, as this was done in the parse_args_for_synthetic_data_experiment + function. + + :param batch_size: The batch size to use for training + :type batch_size: int + :param lr: The learning rate to use for training + :type lr: float + :param max_epochs: The maximum number of epochs to train for + :type max_epochs: int + :param using_random_seed: Whether or not to use a random seed for reproducibility + :type using_random_seed: bool + :param accelerator: The accelerator to use for training (e.g. 'gpu', 'cpu') + :type accelerator: str + + """ + + data_module = SyntheticDataLoader( + batch_size=batch_size, + num_genes=1000, + signal=[0.1, 0.15, 0.2, 0.25, 0.3], + n_sample=[1, 1, 2, 2, 4], + val_size=0.1, + test_size=0.1, + random_state=42, + ) + + num_tfs = sum(data_module.n_sample) # sum of all n_sample is the number of TFs + + model = SimpleModel(input_dim=num_tfs, output_dim=num_tfs, lr=lr) + trainer = Trainer( + max_epochs=max_epochs, + deterministic=using_random_seed, + accelerator=accelerator, + callbacks=[best_model_checkpoint, periodic_checkpoint], + logger=[tb_logger, csv_logger], + ) + trainer.fit(model, data_module) + + test_results = trainer.test(model, datamodule=data_module) + print( + test_results + ) # this prints all metrics that were logged during the test phase + + +def parse_args_for_synthetic_data_experiment() -> Namespace: + """ + Parses command line arguments for the synthetic data experiment. + + :return: The command line arguments + :rtype: Namespace + :raises ValueError: If batch_size is not an integer greater than 0 + :raises ValueError: If lr is not a float greater than 0 + :raises ValueError: If max_epochs is not an integer greater than 0 + :raises ValueError: If random_seed is not an integer greater than or equal to 0 + :raises ValueError: If gpus is not an integer greater than or equal to 0 + + """ + parser = argparse.ArgumentParser( + description="Simple Model Synthetic Data Experiment" + ) + parser.add_argument("--batch_size", action="store", type=int) + parser.add_argument("--lr", action="store", type=float) + parser.add_argument("--max_epochs", action="store", type=int) + parser.add_argument("--random_seed", action="store", type=int) + parser.add_argument("--gpus", action="store", type=int) + + # note that this performs the type checking needed + # so we don't need assertion checks for that + args = parser.parse_args() + + # assert correct values + if args.batch_size and args.batch_size < 1: + raise ValueError("batch_size must be an integer greater than 0") + if args.lr and args.lr <= 0: + raise ValueError("lr must be a float greater than 0") + if args.max_epochs and args.max_epochs < 1: + raise ValueError("max_epochs must be an integer greater than 0") + if args.random_seed and args.random_seed < 0: + raise ValueError("random_seed must be an integer greater than or equal to 0") + if args.gpus and args.gpus < 0: + raise ValueError("gpus must be an integer greater than or equal to 0") + + return args + + +def main() -> None: + """ + Main method to run the experiment for training the simple model using the syntheetic + data loader. + + Saves the best model based on validation loss. + + """ + args = parse_args_for_synthetic_data_experiment() + + # use default values if flag not present in command line arguments + batch_size = args.batch_size or 32 + lr = args.lr or 0.01 + max_epochs = args.max_epochs or 10 + random_seed = args.random_seed or 42 + gpus = args.gpus or 0 + + # set random seed for reproducibility + seed_everything(random_seed) + + # run the experiment + simple_model_synthetic_data_experiment( + batch_size=batch_size, + lr=lr, + max_epochs=max_epochs, + using_random_seed=True, + accelerator="gpu" if (gpus > 0) else "cpu", + ) + + +if __name__ == "__main__": + main() diff --git a/mkdocs.yml b/mkdocs.yml index df4776b..6a81c40 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,9 +1,7 @@ site_name: yeastdnnexplorer site_description: "A collection of objects and functions to work with calling cards sequencing tools" site_author: "ben mueller , chase mateusiak , michael brent " -# TODO: update this when moved to brentlab site_url: "https://brentlab.github.io/yeastdnnexplorer/" -# TODO: update this when moved to brentlab repo_url: "https://github.com/brentlab/yeastdnnexplorer" repo_name: "yeastdnnexplorer" edit_uri: "edit/master/docs/" @@ -26,17 +24,31 @@ plugins: docstring_style: 'sphinx' nav: -- Home: - - index.md +- Home: index.md - Tutorials: - - Generate in silico data: tutorials/generate_in_silico_data.ipynb + - Generate In-silico Data: tutorials/generate_in_silico_data.ipynb + - Hyperparameter Sweep: tutorials/hyperparameter_sweep.ipynb + - Lightning Crash Course: tutorials/lightning_crash_course.ipynb + - Testing Model Metrics: tutorials/testing_model_metrics.ipynb + - Visualizing and Testing Data Generation Methods: tutorials/visualizing_and_testing_data_generation_methods.ipynb - API: - - Probability Models: - - Generate Gene Population: probability_models/generate_gene_population.md - - probability_models/generate_perturbation_binding_data.md - - probability_models/generate_perturbation_effects.md - - probability_models/generate_binding_effects.md - - probability_models/generate_pvalues.md + - Data Loaders: + - Synthetic Data Loader: data_loaders/synthetic_data_loader.md + - Real Data Loader: data_loaders/real_data_loader.md + - Models: + - ml_models/customizable_model.md + - ml_models/metrics_compute_nrmse.md + - ml_models/metrics_smse.md + - ml_models/simple_model.md + - Probability Models: + - probability_models/default_perturbation_effect_adjustment_function.md + - probability_models/GenePopulation.md + - probability_models/generate_binding_effects.md + - Generate Gene Population: probability_models/generate_gene_population.md + - probability_models/generate_perturbation_effects.md + - probability_models/generate_pvalues.md + - probability_models/perturbation_effect_adjustment_function_with_tf_relationships_boolean_logic.md + - probability_models/perturbation_effect_adjustment_function_with_tf_relationships.md markdown_extensions: - smarty diff --git a/pyproject.toml b/pyproject.toml index edb713c..2a7d309 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,19 +1,29 @@ [tool.poetry] name = "yeastdnnexplorer" -version = "0.0.0dev" +version = "0.0.1" description = "A development environment to explore implementations of deep neural networks for predicting the relationship between transcription factor and target genes using binding and perturbation data" -authors = ["ben mueller ", "chase mateusiak ", "michael brent "] +authors = ["ben mueller ", "chase mateusiak ", "michael brent "] license = "GPL-3.0" readme = "README.md" +exclude = ["experiments/"] [tool.poetry.dependencies] python = "^3.11" -torch = "^2.2.0" +torch = "^2.0, <2.3.0" numpy = "^1.26.3" pandas = "^2.2.0" +pytorch-lightning = "^2.1.4" +scikit-learn = "1.4.0" +tensorboard = "^2.16.1" +torchsummary = "^1.5.1" +optuna = "^3.6.0" +optuna-dashboard = "^0.15.1" [tool.poetry.group.dev.dependencies] +jupyter = "^1.0.0" +matplotlib = "^3.8.3" +seaborn = "^0.13.2" black = "^24.1.1" flake8 = "^7.0.0" isort = "^5.13.2" @@ -46,11 +56,12 @@ pythonpath = ['.'] [tool.coverage.run] include = ["yeastdnnexplorer/**"] -omit = ["*/tests/*"] +omit = ["*/tests/*", "experiments/"] [tool.black] line-length = 88 target-version = ['py311'] +include = '\.py$' [tool.isort] profile = "black" diff --git a/yeastdnnexplorer/data_loaders/__init__.py b/yeastdnnexplorer/data_loaders/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yeastdnnexplorer/data_loaders/real_data_loader.py b/yeastdnnexplorer/data_loaders/real_data_loader.py new file mode 100644 index 0000000..4c914a6 --- /dev/null +++ b/yeastdnnexplorer/data_loaders/real_data_loader.py @@ -0,0 +1,329 @@ +import os + +import pandas as pd +import torch +from pytorch_lightning import LightningDataModule +from sklearn.model_selection import train_test_split +from torch.utils.data import DataLoader, TensorDataset + + +class RealDataLoader(LightningDataModule): + """ + A class to load in data from the CSV data for various binding and perturbation + experiments. + + After loading in the data, the data loader will parse the data into the form + expected by our models. It will also split the data into training, testing, and + validation sets for the model to use. + + NOTE: Right now the only binding dataset this works with is the brent_nf_cc dataset + because it has the same set of genes in each CSV file. This is the case for all of + the perturbation datasets, but not for the other 2 binding datasets. In the future + we would like to write a dataModule that handles the other 2 binding datasets. For + now, you can only pass in a parameter for the title of the perturb response + dataset that you want to use, and brent_nf_cc is hardcoded as the binding dataset. + + """ + + def __init__( + self, + batch_size: int = 32, + val_size: float = 0.1, + test_size: float = 0.1, + random_state: int = 42, + data_dir_path: str | None = None, + perturbation_dataset_title: str = "hu_reimann_tfko", + ) -> None: + """ + Constructor of RealDataLoader. + + :param batch_size: The number of samples in each mini-batch + :type batch_size: int + :param val_size: The proportion of the dataset to include in the validation + split + :type val_size: float + :param test_size: The proportion of the dataset to include in the test split + :type test_size: float + :param random_state: The random seed to use for splitting the data (keep this + consistent to ensure reproduceability) + :type random_state: int + :param data_dir_path: The path to the directory containing the CSV files for the + binding and perturbation data + :type data_dir_path: str + :param perturbation_dataset_title: The title of the perturbation dataset to use + (one of 'hu_reimann_tfko', 'kemmeren_tfko', or 'mcisaac_oe') + :type perturbation_dataset_title: str + :raises TypeError: If batch_size is not an positive integer + :raises TypeError: If val_size is not a float between 0 and 1 (inclusive) + :raises TypeError: If test_size is not a float between 0 and 1 (inclusive) + :raises TypeError: If random_state is not an integer + :raises ValueError: If val_size + test_size is greater than 1 (i.e. the splits + are too large) + :raises ValueError: if no data_dir is provided + :raises AssertinoError: if the dataset sizes do not match up after reading in + the data from the CSV files + + """ + if not isinstance(batch_size, int) or batch_size < 1: + raise TypeError("batch_size must be a positive integer") + if not isinstance(val_size, (int, float)) or val_size <= 0 or val_size >= 1: + raise TypeError("val_size must be a float between 0 and 1 (inclusive)") + if not isinstance(test_size, (int, float)) or test_size <= 0 or test_size >= 1: + raise TypeError("test_size must be a float between 0 and 1 (inclusive)") + if not isinstance(random_state, int): + raise TypeError("random_state must be an integer") + if data_dir_path is None: + raise ValueError("data_dir_path must be provided") + if test_size + val_size > 1: + raise ValueError("val_size + test_size must be less than or equal to 1") + if not isinstance( + perturbation_dataset_title, str + ) and perturbation_dataset_title in [ + "hu_reimann_tfko", + "kemmeren_tfko", + "mcisaac_oe", + ]: + raise TypeError( + "perturbation_dataset_title must be a string and must be one" + " of 'hu_reimann_tfko', 'kemmeren_tfko', or 'mcisaac_oe'" + ) + + super().__init__() + self.batch_size = batch_size + self.val_size = val_size + self.test_size = test_size + self.random_state = random_state + self.data_dir_path = data_dir_path + self.perturbation_dataset_title = perturbation_dataset_title + + self.final_data_tensor: torch.Tensor = None + self.binding_effect_matrix: torch.Tensor | None = None + self.perturbation_effect_matrix: torch.Tensor | None = None + self.val_dataset: TensorDataset | None = None + self.test_dataset: TensorDataset | None = None + + def prepare_data(self) -> None: + """ + This function reads in the binding data and perturbation data from the CSV files + that we have for these datasets. + + It throws out any genes that are not present in both the binding and + perturbation sets, and then structures the data in a way that the model expects + and can use + + """ + + brent_cc_path = os.path.join(self.data_dir_path, "binding/brent_nf_cc") + brent_nf_csv_files = [ + f for f in os.listdir(brent_cc_path) if f.endswith(".csv") + ] + perturb_dataset_path = os.path.join( + self.data_dir_path, f"perturbation/{self.perturbation_dataset_title}" + ) + perturb_dataset_csv_files = [ + f for f in os.listdir(perturb_dataset_path) if f.endswith(".csv") + ] + + # get a list of the genes in the binding data csvs + # for brent_cc (and the 3 perturb response datasets) the genes are + # in the same order in each csv, so it suffices to grab the target_locus_tag + # column from the first one + brent_cc_genes_ids = pd.read_csv( + os.path.join(brent_cc_path, brent_nf_csv_files[0]) + )["target_locus_tag"] + perturb_dataset_genes_ids = pd.read_csv( + os.path.join(perturb_dataset_path, perturb_dataset_csv_files[0]) + )["target_locus_tag"] + + # Get the intersection of the genes in the binding and perturbation data + common_genes = set(brent_cc_genes_ids).intersection(perturb_dataset_genes_ids) + + # Read in binding data from csv files + binding_data_effects = pd.DataFrame() + binding_data_pvalues = pd.DataFrame() + for i, file in enumerate(brent_nf_csv_files): + file_path = os.path.join(brent_cc_path, file) + df = pd.read_csv(file_path) + + # only keep the genes that are in the intersection + # of the genes in the binding and perturbation data + df = df[df["target_locus_tag"].isin(common_genes)] + + # we need to handle duplicates now + # (some datasets have multiple occurrences of the same gene) + # we will keep the occurrence with the highest value in the 'effect' column + # we can do this by sorting the dataframe by the 'effect' column + # in descending order and keeping the fist occurrence of each gene + # this does require us to do some additional work later (see how we + # are consistently setting the index to 'target_locus_tag', + # this ensures all of our datasets are in the same order) + df = df.sort_values("effect", ascending=False).drop_duplicates( + subset="target_locus_tag", keep="first" + ) + + # on the first iteration, add target_locus_tag column to the binding data + if i == 0: + binding_data_effects["target_locus_tag"] = df["target_locus_tag"] + binding_data_pvalues["target_locus_tag"] = df["target_locus_tag"] + binding_data_effects.set_index("target_locus_tag", inplace=True) + binding_data_pvalues.set_index("target_locus_tag", inplace=True) + + binding_data_effects[file] = df.set_index("target_locus_tag")["effect"] + binding_data_pvalues[file] = df.set_index("target_locus_tag")["pvalue"] + + # Read in perturbation data from csv files + perturbation_effects = pd.DataFrame() + perturbation_pvalues = pd.DataFrame() + for i, file in enumerate(perturb_dataset_csv_files): + file_path = os.path.join(perturb_dataset_path, file) + df = pd.read_csv(file_path) + + # only keep the genes that are in the + # intersection of the genes in the binding and perturbation data + df = df[df["target_locus_tag"].isin(common_genes)] + + # handle duplicates + df = df.sort_values("effect", ascending=False).drop_duplicates( + subset="target_locus_tag", keep="first" + ) + + # on the first iteration, add the target_locus_tag + # column to the perturbation data + if i == 0: + perturbation_effects["target_locus_tag"] = df["target_locus_tag"] + perturbation_pvalues["target_locus_tag"] = df["target_locus_tag"] + perturbation_effects.set_index("target_locus_tag", inplace=True) + perturbation_pvalues.set_index("target_locus_tag", inplace=True) + + perturbation_effects[file] = df.set_index("target_locus_tag")["effect"] + perturbation_pvalues[file] = df.set_index("target_locus_tag")["pvalue"] + + # shapes should be equal at this point + assert binding_data_effects.shape == perturbation_effects.shape + assert binding_data_pvalues.shape == perturbation_pvalues.shape + + # reindex so that the rows in binding and perturb data match up + # (we need genes to be in the same order) + perturbation_effects = perturbation_effects.reindex(binding_data_effects.index) + perturbation_pvalues = perturbation_pvalues.reindex(binding_data_pvalues.index) + + # concat the data into the shape expected by the model + # we need to first convert the data to tensors + binding_data_effects_tensor = torch.tensor( + binding_data_effects.values, dtype=torch.float64 + ) + binding_data_pvalues_tensor = torch.tensor( + binding_data_pvalues.values, dtype=torch.float64 + ) + perturbation_effects_tensor = torch.tensor( + perturbation_effects.values, dtype=torch.float64 + ) + perturbation_pvalues_tensor = torch.tensor( + perturbation_pvalues.values, dtype=torch.float64 + ) + + # note that we no longer have a signal / noise tensor + # (like for the synthetic data) + self.final_data_tensor = torch.stack( + [ + binding_data_effects_tensor, + binding_data_pvalues_tensor, + perturbation_effects_tensor, + perturbation_pvalues_tensor, + ], + dim=-1, + ) + + def setup(self, stage: str | None = None) -> None: + """ + This function runs after prepare_data finishes and is used to split the data + into train, validation, and test sets It ensures that these datasets are of the + correct dimensionality and size to be used by the model. + + :param stage: The stage of the data setup (either 'fit' for training, 'validate' + for validation, or 'test' for testing), unused for now as the model is not + complicated enough to necessitate this + :type stage: Optional[str] + + """ + self.binding_effect_matrix = self.final_data_tensor[:, :, 0] + self.perturbation_effect_matrix = self.final_data_tensor[:, :, 2] + + # split into train, val, and test + X_train, X_temp, Y_train, Y_temp = train_test_split( + self.binding_effect_matrix, + self.perturbation_effect_matrix, + test_size=(self.val_size + self.test_size), + random_state=self.random_state, + ) + + # normalize test_size so that it is a percentage of the remaining data + self.test_size = self.test_size / (self.val_size + self.test_size) + X_val, X_test, Y_val, Y_test = train_test_split( + X_temp, Y_temp, test_size=self.test_size, random_state=self.random_state + ) + + # Convert to tensors + X_train, Y_train = torch.tensor(X_train, dtype=torch.float32), torch.tensor( + Y_train, dtype=torch.float32 + ) + X_val, Y_val = torch.tensor(X_val, dtype=torch.float32), torch.tensor( + Y_val, dtype=torch.float32 + ) + X_test, Y_test = torch.tensor(X_test, dtype=torch.float32), torch.tensor( + Y_test, dtype=torch.float32 + ) + + # Set our datasets + self.train_dataset = TensorDataset(X_train, Y_train) + self.val_dataset = TensorDataset(X_val, Y_val) + self.test_dataset = TensorDataset(X_test, Y_test) + + def train_dataloader(self) -> DataLoader: + """ + Function to return the training dataloader, we shuffle to avoid learning based + on the order of the data. + + :return: The training dataloader + :rtype: DataLoader + + """ + return DataLoader( + self.train_dataset, + batch_size=self.batch_size, + num_workers=15, + shuffle=True, + persistent_workers=True, + ) + + def val_dataloader(self) -> DataLoader: + """ + Function to return the validation dataloader. + + :return: The validation dataloader + :rtype: DataLoader + + """ + return DataLoader( + self.val_dataset, + batch_size=self.batch_size, + num_workers=15, + shuffle=False, + persistent_workers=True, + ) + + def test_dataloader(self) -> DataLoader: + """ + Function to return the testing dataloader. + + :return: The testing dataloader + :rtype: DataLoader + + """ + return DataLoader( + self.test_dataset, + batch_size=self.batch_size, + num_workers=15, + shuffle=False, + persistent_workers=True, + ) diff --git a/yeastdnnexplorer/data_loaders/synthetic_data_loader.py b/yeastdnnexplorer/data_loaders/synthetic_data_loader.py new file mode 100644 index 0000000..8c53670 --- /dev/null +++ b/yeastdnnexplorer/data_loaders/synthetic_data_loader.py @@ -0,0 +1,322 @@ +from collections.abc import Callable + +import torch +from pytorch_lightning import LightningDataModule +from sklearn.model_selection import train_test_split +from torch.utils.data import DataLoader, TensorDataset + +from yeastdnnexplorer.probability_models.generate_data import ( + default_perturbation_effect_adjustment_function, + generate_binding_effects, + generate_gene_population, + generate_perturbation_effects, + generate_pvalues, +) +from yeastdnnexplorer.probability_models.relation_classes import Relation + + +class SyntheticDataLoader(LightningDataModule): + """A class for a synthetic data loader that generates synthetic bindiing & + perturbation effect data for training, validation, and testing a model This class + contains all of the logic for generating and parsing the synthetic data, as well as + splitting it into train, validation, and test sets It is a subclass of + pytorch_lightning.LightningDataModule, which is similar to a regular PyTorch + DataLoader but with added functionality for data loading.""" + + def __init__( + self, + batch_size: int = 32, + num_genes: int = 1000, + signal: list[float] = [0.1, 0.2, 0.2, 0.4, 0.5], + signal_mean: float = 3.0, + n_sample: list[int] = [1, 2, 2, 4, 4], + val_size: float = 0.1, + test_size: float = 0.1, + random_state: int = 42, + max_mean_adjustment: float = 0.0, + adjustment_function: Callable[ + [torch.Tensor, float, float, float], torch.Tensor + ] = default_perturbation_effect_adjustment_function, + tf_relationships: dict[int, list[int] | list[Relation]] = {}, + ) -> None: + """ + Constructor of SyntheticDataLoader. + + :param batch_size: The number of samples in each mini-batch + :type batch_size: int + :param num_genes: The number of genes in the synthetic data (this is the number + of datapoints in our dataset) + :type num_genes: int + :param signal: The proportion of genes in each sample group that are put in the + signal grop (i.e. have a non-zero binding effect and expression response) + :type signal: List[int] + :param n_sample: The number of samples to draw from each signal group + :type n_sample: List[int] + :param val_size: The proportion of the dataset to include in the validation + split + :type val_size: float + :param test_size: The proportion of the dataset to include in the test split + :type test_size: float + :param random_state: The random seed to use for splitting the data (keep this + consistent to ensure reproduceability) + :type random_state: int + :param signal_mean: The mean of the signal distribution + :type signal_mean: float + :param max_mean_adjustment: The maximum mean adjustment to apply to the mean + of the signal (bound) perturbation effects + :type max_mean_adjustment: float + :param adjustment_function: A function that adjusts the mean of the signal + (bound) perturbation effects + :type adjustment_function: Callable[[torch.Tensor, float, float, + float, dict[int, list[int]]], torch.Tensor] + :raises TypeError: If batch_size is not an positive integer + :raises TypeError: If num_genes is not an positive integer + :raises TypeError: If signal is not a list of integers or floats + :raises TypeError: If n_sample is not a list of integers + :raises TypeError: If val_size is not a float between 0 and 1 (inclusive) + :raises TypeError: If test_size is not a float between 0 and 1 (inclusive) + :raises TypeError: If random_state is not an integer + :raises TypeError: If signal_mean is not a float + :raises ValueError: If val_size + test_size is greater than 1 (i.e. the splits + are too large) + + """ + if not isinstance(batch_size, int) or batch_size < 1: + raise TypeError("batch_size must be a positive integer") + if not isinstance(num_genes, int) or num_genes < 1: + raise TypeError("num_genes must be a positive integer") + if not isinstance(signal, list) or not all( + isinstance(x, (int, float)) for x in signal + ): + raise TypeError("signal must be a list of integers or floats") + if not isinstance(n_sample, list) or not all( + isinstance(x, int) for x in n_sample + ): + raise TypeError("n_sample must be a list of integers") + if not isinstance(val_size, (int, float)) or val_size <= 0 or val_size >= 1: + raise TypeError("val_size must be a float between 0 and 1 (inclusive)") + if not isinstance(test_size, (int, float)) or test_size <= 0 or test_size >= 1: + raise TypeError("test_size must be a float between 0 and 1 (inclusive)") + if not isinstance(random_state, int): + raise TypeError("random_state must be an integer") + if not isinstance(signal_mean, float): + raise TypeError("signal_mean must be a float") + if test_size + val_size > 1: + raise ValueError("val_size + test_size must be less than or equal to 1") + + super().__init__() + self.batch_size = batch_size + self.num_genes = num_genes + self.signal_mean = signal_mean + self.signal = signal or [0.1, 0.15, 0.2, 0.25, 0.3] + self.n_sample = n_sample or [1 for _ in range(len(self.signal))] + self.num_tfs = sum(self.n_sample) # sum of all n_sample is the number of TFs + self.val_size = val_size + self.test_size = test_size + self.random_state = random_state + + self.max_mean_adjustment = max_mean_adjustment + self.adjustment_function = adjustment_function + self.tf_relationships = tf_relationships + + self.final_data_tensor: torch.Tensor = None + self.binding_effect_matrix: torch.Tensor | None = None + self.perturbation_effect_matrix: torch.Tensor | None = None + self.val_dataset: TensorDataset | None = None + self.test_dataset: TensorDataset | None = None + + def prepare_data(self) -> None: + """Function to generate the raw synthetic data and save it in a tensor For + explanations of the functions used to generate the data, see the + generate_in_silico_data tutorial notebook in the docs No assertion checks are + performed as that is handled in the functions in generate_data.py.""" + # this will be a list of length 10 with a GenePopulation object in each element + gene_populations_list = [] + for signal_proportion, n_draws in zip(self.signal, self.n_sample): + for _ in range(n_draws): + gene_populations_list.append( + generate_gene_population(self.num_genes, signal_proportion) + ) + + # Generate binding data for each gene population + binding_effect_list = [ + generate_binding_effects(gene_population) + for gene_population in gene_populations_list + ] + + # Calculate p-values for binding data + binding_pvalue_list = [ + generate_pvalues(binding_data) for binding_data in binding_effect_list + ] + + binding_data_combined = [ + torch.stack((gene_population.labels, binding_effect, binding_pval), dim=1) + for gene_population, binding_effect, binding_pval in zip( + gene_populations_list, binding_effect_list, binding_pvalue_list + ) + ] + + # Stack along a new dimension (dim=1) to create a tensor of shape + # [num_genes, num_TFs, 3] + binding_data_tensor = torch.stack(binding_data_combined, dim=1) + + # if we are using a mean adjustment, we need to generate perturbation + # effects in a slightly different way than if we are not using + # a mean adjustment + if self.max_mean_adjustment > 0: + perturbation_effects_list = generate_perturbation_effects( + binding_data_tensor, + signal_mean=self.signal_mean, + tf_index=0, # unused + max_mean_adjustment=self.max_mean_adjustment, + adjustment_function=self.adjustment_function, + tf_relationships=self.tf_relationships, + ) + + perturbation_pvalue_list = torch.zeros_like(perturbation_effects_list) + for col_index in range(perturbation_effects_list.shape[1]): + perturbation_pvalue_list[:, col_index] = generate_pvalues( + perturbation_effects_list[:, col_index] + ) + + # take absolute values + perturbation_effects_list = torch.abs(perturbation_effects_list) + + perturbation_effects_tensor = perturbation_effects_list + perturbation_pvalues_tensor = perturbation_pvalue_list + else: + perturbation_effects_list = [ + generate_perturbation_effects( + binding_data_tensor[:, tf_index, :].unsqueeze(1), + signal_mean=self.signal_mean, + tf_index=0, # unused + ) + for tf_index in range(sum(self.n_sample)) + ] + perturbation_pvalue_list = [ + generate_pvalues(perturbation_effects) + for perturbation_effects in perturbation_effects_list + ] + + # take absolute values + perturbation_effects_list = [ + torch.abs(perturbation_effects) + for perturbation_effects in perturbation_effects_list + ] + + # Convert lists to tensors + perturbation_effects_tensor = torch.stack(perturbation_effects_list, dim=1) + perturbation_pvalues_tensor = torch.stack(perturbation_pvalue_list, dim=1) + + # Ensure perturbation data is reshaped to match [n_genes, n_tfs] + # This step might need adjustment based on the actual shapes of your tensors. + perturbation_effects_tensor = perturbation_effects_tensor.unsqueeze( + -1 + ) # Adds an extra dimension for concatenation + perturbation_pvalues_tensor = perturbation_pvalues_tensor.unsqueeze( + -1 + ) # Adds an extra dimension for concatenation + + # Concatenate along the last dimension to form a [n_genes, n_tfs, 5] tensor + self.final_data_tensor = torch.cat( + ( + binding_data_tensor, + perturbation_effects_tensor, + perturbation_pvalues_tensor, + ), + dim=2, + ) + + def setup(self, stage: str | None = None) -> None: + """ + This function runs after prepare_data finishes and is used to split the data + into train, validation, and test sets It ensures that these datasets are of the + correct dimensionality and size to be used by the model. + + :param stage: The stage of the data setup (either 'fit' for training, 'validate' + for validation, or 'test' for testing), unused for now as the model is not + complicated enough to necessitate this + :type stage: Optional[str] + + """ + self.binding_effect_matrix = self.final_data_tensor[:, :, 1] + self.perturbation_effect_matrix = self.final_data_tensor[:, :, 3] + + # split into train, val, and test + X_train, X_temp, Y_train, Y_temp = train_test_split( + self.binding_effect_matrix, + self.perturbation_effect_matrix, + test_size=(self.val_size + self.test_size), + random_state=self.random_state, + ) + + # normalize test_size so that it is a percentage of the remaining data + self.test_size = self.test_size / (self.val_size + self.test_size) + X_val, X_test, Y_val, Y_test = train_test_split( + X_temp, Y_temp, test_size=self.test_size, random_state=self.random_state + ) + + # Convert to tensors + X_train, Y_train = torch.tensor(X_train, dtype=torch.float32), torch.tensor( + Y_train, dtype=torch.float32 + ) + X_val, Y_val = torch.tensor(X_val, dtype=torch.float32), torch.tensor( + Y_val, dtype=torch.float32 + ) + X_test, Y_test = torch.tensor(X_test, dtype=torch.float32), torch.tensor( + Y_test, dtype=torch.float32 + ) + + # Set our datasets + self.train_dataset = TensorDataset(X_train, Y_train) + self.val_dataset = TensorDataset(X_val, Y_val) + self.test_dataset = TensorDataset(X_test, Y_test) + + def train_dataloader(self) -> DataLoader: + """ + Function to return the training dataloader, we shuffle to avoid learning based + on the order of the data. + + :return: The training dataloader + :rtype: DataLoader + + """ + return DataLoader( + self.train_dataset, + batch_size=self.batch_size, + num_workers=15, + shuffle=True, + persistent_workers=True, + ) + + def val_dataloader(self) -> DataLoader: + """ + Function to return the validation dataloader. + + :return: The validation dataloader + :rtype: DataLoader + + """ + return DataLoader( + self.val_dataset, + batch_size=self.batch_size, + num_workers=15, + shuffle=False, + persistent_workers=True, + ) + + def test_dataloader(self) -> DataLoader: + """ + Function to return the testing dataloader. + + :return: The testing dataloader + :rtype: DataLoader + + """ + return DataLoader( + self.test_dataset, + batch_size=self.batch_size, + num_workers=15, + shuffle=False, + persistent_workers=True, + ) diff --git a/yeastdnnexplorer/ml_models/__init__.py b/yeastdnnexplorer/ml_models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yeastdnnexplorer/ml_models/customizable_model.py b/yeastdnnexplorer/ml_models/customizable_model.py new file mode 100644 index 0000000..02de097 --- /dev/null +++ b/yeastdnnexplorer/ml_models/customizable_model.py @@ -0,0 +1,246 @@ +from typing import Any + +import pytorch_lightning as pl +import torch +import torch.nn as nn +from torch.optim import Optimizer +from torchmetrics import MeanAbsoluteError + +from yeastdnnexplorer.ml_models.metrics import SMSE + + +class CustomizableModel(pl.LightningModule): + """ + A class for a customizable model that takes in binding effects for each + transcription factor and predicts gene expression values This class contains all of + the logic for setup, training, validation, and testing of the model, as well as + defining how data is passed through the model It is a subclass of + pytorch_lightning.LightningModule, which is similar to a regular PyTorch nn.module + but with added functionality for training and validation. + + This model takes in many more parameters that SimpleModel, allowing us to + experiement with many hyperparameter and architecture choices in order to decide + what is best for our task & data + + """ + + def __init__( + self, + input_dim: int, + output_dim: int, + lr: float = 0.001, + hidden_layer_num: int = 1, + hidden_layer_sizes: list = [128], + activation: str = "ReLU", # can be "ReLU", "Sigmoid", "Tanh", "LeakyReLU" + optimizer: str = "Adam", # can be "Adam", "SGD", "RMSprop" + L2_regularization_term: float = 0.0, + dropout_rate: float = 0.0, + ) -> None: + """ + Constructor of CustomizableModel. + + :param input_dim: The number of input features to our model, these are the + binding effects for each transcription factor for a specific gene + :type input_dim: int + :param output_dim: The number of output features of our model, this is the + predicted gene expression value for each TF + :type output_dim: int + :param lr: The learning rate for the optimizer + :type lr: float + :raises TypeError: If input_dim is not an integer + :raises TypeError: If output_dim is not an integer + :raises TypeError: If lr is not a positive float + :raises ValueError: If input_dim or output_dim are not positive + :param hidden_layer_num: The number of hidden layers in the model + :type hidden_layer_num: int + :param hidden_layer_sizes: The size of each hidden layer in the model + :type hidden_layer_sizes: list + + """ + if not isinstance(input_dim, int): + raise TypeError("input_dim must be an integer") + if not isinstance(output_dim, int): + raise TypeError("output_dim must be an integer") + if not isinstance(lr, float) or lr <= 0: + raise TypeError("lr must be a positive float") + if input_dim < 1 or output_dim < 1: + raise ValueError("input_dim and output_dim must be positive integers") + if not isinstance(hidden_layer_num, int): + raise TypeError("hidden_layer_num must be an integer") + if not isinstance(hidden_layer_sizes, list) or not all( + isinstance(i, int) for i in hidden_layer_sizes + ): + raise TypeError("hidden_layer_sizes must be a list of integers") + if len(hidden_layer_sizes) != hidden_layer_num: + raise ValueError( + "hidden_layer_sizes must have length equal to hidden_layer_num" + ) + if not isinstance(activation, str) or activation not in [ + "ReLU", + "Sigmoid", + "Tanh", + "LeakyReLU", + ]: + raise ValueError( + "activation must be one of 'ReLU', 'Sigmoid', 'Tanh', 'LeakyReLU'" + ) + if not isinstance(optimizer, str) or optimizer not in [ + "Adam", + "SGD", + "RMSprop", + ]: + raise ValueError("optimizer must be one of 'Adam', 'SGD', 'RMSprop'") + if not isinstance(L2_regularization_term, float) or L2_regularization_term < 0: + raise TypeError("L2_regularization_term must be a non-negative float") + if not isinstance(dropout_rate, float) or dropout_rate < 0 or dropout_rate > 1: + raise TypeError("dropout_rate must be a float between 0 and 1") + + super().__init__() + self.input_dim = input_dim + self.output_dim = output_dim + self.lr = lr + self.hidden_layer_num = hidden_layer_num + self.hidden_layer_sizes = hidden_layer_sizes + self.optimizer = optimizer + self.L2_regularization_term = L2_regularization_term + self.save_hyperparameters() + + match activation: + case "ReLU": + self.activation = nn.ReLU() + case "Sigmoid": + self.activation = nn.Sigmoid() + case "Tanh": + self.activation = nn.Tanh() + case "LeakyReLU": + self.activation = nn.LeakyReLU() + case _: + raise ValueError( + "activation must be one of 'ReLU', 'Sigmoid', 'Tanh', 'LeakyReLU'" + ) + + self.input_layer = nn.Linear(input_dim, hidden_layer_sizes[0]) + self.hidden_layers = nn.ModuleList([]) + for i in range(hidden_layer_num - 1): + self.hidden_layers.append( + nn.Linear(hidden_layer_sizes[i], hidden_layer_sizes[i + 1]) + ) + self.output_layer = nn.Linear(hidden_layer_sizes[-1], output_dim) + + self.dropout = nn.Dropout(p=dropout_rate) + + self.mae = MeanAbsoluteError() + self.SMSE = SMSE() + + def forward(self, x: torch.Tensor) -> torch.Tensor: + """ + Forward pass of the model (i.e. how predictions are made for a given input) + + :param x: The input data to the model (minus the target y values) + :type x: torch.Tensor + :return: The predicted y values for the input x, this is a tensor of shape + (batch_size, output_dim) + :rtype: torch.Tensor + + """ + x = self.dropout(self.activation(self.input_layer(x))) + for hidden_layer in self.hidden_layers: + x = self.dropout(self.activation(hidden_layer(x))) + x = self.output_layer(x) + return x + + def training_step(self, batch: Any, batch_idx: int) -> torch.Tensor: + """ + Training step for the model, this is called for each batch of data during + training. + + :param batch: The batch of data to train on + :type batch: Any + :param batch_idx: The index of the batch + :type batch_idx: int + :return: The loss for the training batch + :rtype: torch.Tensor + + """ + x, y = batch + y_pred = self(x) + mse_loss = nn.functional.mse_loss(y_pred, y) + self.log("train_mse", mse_loss) + self.log("train_mae", self.mae(y_pred, y)) + self.log("train_smse", self.SMSE(y_pred, y)) + return mse_loss + + def validation_step(self, batch: Any, batch_idx: int) -> torch.Tensor: + """ + Validation step for the model, this is called for each batch of data during + validation. + + :param batch: The batch of data to validate on + :type batch: Any + :param batch_idx: The index of the batch + :type batch_idx: int + :return: The loss for the validation batch + :rtype: torch.Tensor + + """ + x, y = batch + y_pred = self(x) + mse_loss = nn.functional.mse_loss(y_pred, y) + self.log("val_mse", mse_loss) + self.log("val_mae", self.mae(y_pred, y)) + self.log("val_smse", self.SMSE(y_pred, y)) + return mse_loss + + def test_step(self, batch: Any, batch_idx: int) -> torch.Tensor: + """ + Test step for the model, this is called for each batch of data during testing + Testing is only performed after training and validation when we have chosen a + final model We want to test our final model on unseen data (which is why we use + validation sets to "test" during training) + + :param batch: The batch of data to test on (this will have size (batch_size, + input_dim) + :type batch: Any + :param batch_idx: The index of the batch + :type batch_idx: int + :return: The loss for the test batch + :rtype: torch.Tensor + + """ + x, y = batch + y_pred = self(x) + mse_loss = nn.functional.mse_loss(y_pred, y) + self.log("test_mse", mse_loss) + self.log("test_mae", self.mae(y_pred, y)) + self.log("test_smse", self.SMSE(y_pred, y)) + return mse_loss + + def configure_optimizers(self) -> Optimizer: + """ + Configure the optimizer for the model. + + :return: The optimizer for the model + :rtype: Optimizer + + """ + match self.optimizer: + case "Adam": + return torch.optim.Adam( + self.parameters(), + lr=self.lr, + weight_decay=self.L2_regularization_term, + ) + case "SGD": + return torch.optim.SGD( + self.parameters(), + lr=self.lr, + weight_decay=self.L2_regularization_term, + ) + case "RMSprop": + return torch.optim.RMSprop( + self.parameters(), + lr=self.lr, + weight_decay=self.L2_regularization_term, + ) + case _: + raise ValueError("optimizer must be one of 'Adam', 'SGD', 'RMSprop'") diff --git a/yeastdnnexplorer/ml_models/metrics.py b/yeastdnnexplorer/ml_models/metrics.py new file mode 100644 index 0000000..3280b2c --- /dev/null +++ b/yeastdnnexplorer/ml_models/metrics.py @@ -0,0 +1,75 @@ +import torch +import torch.nn.functional as F +from torchmetrics import Metric + + +class SMSE(Metric): + """ + A class for computing the standardized mean squared error (SMSE) metric. + + This metric is defined as the mean squared error divided by the variance of the true + values (the target data). Because we are dividing by the variance of the true + values, this metric is scale-independent and does not depend on the mean of the true + values. It allows us to effectively compare models drawn from different datasets + with differring scales or means (as long as their variances are at least relatively + similar) + + """ + + def __init__(self): + """Initialize the SMSE metric.""" + super().__init__() + self.add_state("mse", default=torch.tensor(0.0), dist_reduce_fx="sum") + self.add_state("variance", default=torch.tensor(0.0), dist_reduce_fx="sum") + self.add_state("num_samples", default=torch.tensor(0), dist_reduce_fx="sum") + + def update(self, y_pred: torch.Tensor, y_true: torch.Tensor): + """ + Update the metric with new predictions and true values. + + :param y_pred: The predicted y values + :type y_pred: torch.Tensor + :param y_true: The true y values + :type y_true: torch.Tensor + + """ + self.mse += F.mse_loss(y_pred, y_true, reduction="sum") + self.variance += torch.var(y_true, unbiased=False) * y_true.size( + 0 + ) # Total variance (TODO should we have unbiased=False here?) + self.num_samples += y_true.numel() + + def compute(self): + """ + Compute the SMSE metric. + + :return: The SMSE metric + :rtype: torch.Tensor + + """ + mean_mse = self.mse / self.num_samples + mean_variance = self.variance / self.num_samples + return mean_mse / mean_variance + + +# TODO move this to be a metric class +def compute_nrmse(self, y_pred, y_true): + """ + Compute the Normalized Root Mean Squared Error. This can be used to better compare + models trained on different datasets with differnet scales, although it is not + perfectly scale invariant. + + :param y_pred: The predicted y values + :type y_pred: torch.Tensor + :param y_true: The true y values + :type y_true: torch.Tensor + :return: The normalized root mean squared error + :rtype: torch.Tensor + + """ + rmse = torch.sqrt(F.mse_loss(y_pred, y_true)) + + # normalize with the range of true y values + y_range = y_true.max() - y_true.min() + nrmse = rmse / y_range + return nrmse diff --git a/yeastdnnexplorer/ml_models/simple_model.py b/yeastdnnexplorer/ml_models/simple_model.py new file mode 100644 index 0000000..4f2e3bd --- /dev/null +++ b/yeastdnnexplorer/ml_models/simple_model.py @@ -0,0 +1,147 @@ +from typing import Any + +import pytorch_lightning as pl +import torch +import torch.nn as nn +from torch.optim import Optimizer +from torchmetrics import MeanAbsoluteError + +from yeastdnnexplorer.ml_models.metrics import SMSE + + +class SimpleModel(pl.LightningModule): + """A class for a simple linear model that takes in binding effects for each + transcription factor and predicts gene expression values This class contains all of + the logic for setup, training, validation, and testing of the model, as well as + defining how data is passed through the model It is a subclass of + pytorch_lightning.LightningModule, which is similar to a regular PyTorch nn.module + but with added functionality for training and validation.""" + + def __init__(self, input_dim: int, output_dim: int, lr: float = 0.001) -> None: + """ + Constructor of SimpleModel. + + :param input_dim: The number of input features to our model, these are the + binding effects for each transcription factor for a specific gene + :type input_dim: int + :param output_dim: The number of output features of our model, this is the + predicted gene expression value for each TF + :type output_dim: int + :param lr: The learning rate for the optimizer + :type lr: float + :raises TypeError: If input_dim is not an integer + :raises TypeError: If output_dim is not an integer + :raises TypeError: If lr is not a positive float + :raises ValueError: If input_dim or output_dim are not positive + + """ + if not isinstance(input_dim, int): + raise TypeError("input_dim must be an integer") + if not isinstance(output_dim, int): + raise TypeError("output_dim must be an integer") + if not isinstance(lr, float) or lr <= 0: + raise TypeError("lr must be a positive float") + if input_dim < 1 or output_dim < 1: + raise ValueError("input_dim and output_dim must be positive integers") + + super().__init__() + self.input_dim = input_dim + self.output_dim = output_dim + self.lr = lr + self.save_hyperparameters() + + self.mae = MeanAbsoluteError() + self.SMSE = SMSE() + + # define layers for the model here + self.linear1 = nn.Linear(input_dim, output_dim) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + """ + Forward pass of the model (i.e. how predictions are made for a given input) + + :param x: The input data to the model (minus the target y values) + :type x: torch.Tensor + :return: The predicted y values for the input x, this is a tensor of shape + (batch_size, output_dim) + :rtype: torch.Tensor + + """ + return self.linear1(x) + + def training_step(self, batch: Any, batch_idx: int) -> torch.Tensor: + """ + Training step for the model, this is called for each batch of data during + training. + + :param batch: The batch of data to train on + :type batch: Any + :param batch_idx: The index of the batch + :type batch_idx: int + :return: The loss for the training batch + :rtype: torch.Tensor + + """ + x, y = batch + y_pred = self(x) + loss = nn.functional.mse_loss(y_pred, y) + self.log("train_mse", loss) + self.log("train_mae", self.mae(y_pred, y)) + self.log("train_smse", self.SMSE(y_pred, y)) + return loss + + def validation_step(self, batch: Any, batch_idx: int) -> torch.Tensor: + """ + Validation step for the model, this is called for each batch of data during + validation. + + :param batch: The batch of data to validate on + :type batch: Any + :param batch_idx: The index of the batch + :type batch_idx: int + :return: The loss for the validation batch + :rtype: torch.Tensor + + """ + x, y = batch + y_pred = self(x) + loss = nn.functional.mse_loss(y_pred, y) + + self.log("val_mse", loss) + self.log("val_mae", self.mae(y_pred, y)) + self.log("val_smse", self.SMSE(y_pred, y)) + return loss + + def test_step(self, batch: Any, batch_idx: int) -> torch.Tensor: + """ + Test step for the model, this is called for each batch of data during testing + Testing is only performed after training and validation when we have chosen a + final model We want to test our final model on unseen data (which is why we use + validation sets to "test" during training) + + :param batch: The batch of data to test on (this will have size (batch_size, + input_dim) + :type batch: Any + :param batch_idx: The index of the batch + :type batch_idx: int + :return: The loss for the test batch + :rtype: torch.Tensor + + """ + x, y = batch + y_pred = self(x) + loss = nn.functional.mse_loss(y_pred, y) + self.log("test_mse", loss) + self.log("test_mae", self.mae(y_pred, y)) + self.log("test_smse", self.SMSE(y_pred, y)) + return loss + + def configure_optimizers(self) -> Optimizer: + """ + Configure the optimizer for the model. + + :return: The optimizer for the model + :rtype: Optimizer + + """ + return torch.optim.Adam(self.parameters(), lr=self.lr) diff --git a/yeastdnnexplorer/probability_models/generate_data.py b/yeastdnnexplorer/probability_models/generate_data.py index 1b30402..f6d8d4b 100644 --- a/yeastdnnexplorer/probability_models/generate_data.py +++ b/yeastdnnexplorer/probability_models/generate_data.py @@ -1,135 +1,183 @@ +import inspect import logging +from collections.abc import Callable -import pandas as pd import torch +from yeastdnnexplorer.probability_models.relation_classes import Relation + logger = logging.getLogger(__name__) +class GenePopulation: + """A simple class to hold a tensor boolean 1D vector where 0 is meant to identify + genes which are unaffected by a given TF and 1 is meant to identify genes which are + affected by a given TF.""" + + def __init__(self, labels: torch.Tensor) -> None: + """ + Constructor of GenePopulation. + + :param labels: This can be any 1D tensor of boolean values. But it is meant to + be the output of `generate_gene_population()` + :type labels: torch.Tensor + :raises TypeError: If labels is not a tensor + :raises ValueError: If labels is not a 1D tensor + :raises TypeError: If labels is not a boolean tensor + + """ + if not isinstance(labels, torch.Tensor): + raise TypeError("labels must be a tensor") + if not labels.ndim == 1: + raise ValueError("labels must be a 1D tensor") + if not labels.dtype == torch.bool: + raise TypeError("labels must be a boolean tensor") + self.labels = labels + + def __repr__(self): + return f"" + + def generate_gene_population( total: int = 1000, signal_group: float = 0.3 -) -> torch.Tensor: +) -> GenePopulation: """ Generate two sets of genes, one of which will be considered genes which show a - signal to both TF binding and response, and the other which does not. The return is - a tensor where the first column is the gene/feature identifier (0 to total-1) and - the second column is binary indicating whether the gene is in the signal group or - not. + signal, and the other which does not. The return is a one dimensional boolean tensor + where a value of '0' means that the gene at that index is part of the noise group + and a '1' means the gene at that index is part of the signal group. The length of + the tensor is the number of genes in this simulated organism. :param total: The total number of genes. defaults to 1000 :type total: int, optional :param signal_group: The proportion of genes in the signal group. defaults to 0.3 :type signal_group: float, optional - :return: A tensor where the first column is the gene/feature identifier and the - second column is binary indicating whether the gene is in the signal group or - not. - :rtype: torch.Tensor - :raises ValueError: if total is not an integer + :return: A one dimensional tensor of boolean values where the set of indices with a + value of '1' are the signal group and the set of indices with a value of '0' are + the noise group. + :rtype: GenePopulation + :raises TypeError: if total is not an integer :raises ValueError: If signal_group is not between 0 and 1 """ if not isinstance(total, int): - raise ValueError("total must be an integer") + raise TypeError("total must be an integer") if not 0 <= signal_group <= 1: raise ValueError("signal_group must be between 0 and 1") signal_group_size = int(total * signal_group) logger.info("Generating %s genes with signal", signal_group_size) - # Generating gene identifiers - gene_ids = torch.arange(total, dtype=torch.int32) - - # Generating binary labels for signal group labels = torch.cat( ( - torch.ones(signal_group_size, dtype=torch.int32), - torch.zeros(total - signal_group_size, dtype=torch.int32), + torch.ones(signal_group_size, dtype=torch.bool), + torch.zeros(total - signal_group_size, dtype=torch.bool), ) - ) - - # Randomly shuffling labels - shuffled_indices = torch.randperm(total) - shuffled_labels = labels[shuffled_indices] + )[torch.randperm(total)] - # Combining gene IDs and their labels - gene_populations = torch.stack((gene_ids, shuffled_labels), dim=1) + return GenePopulation(labels) - return gene_populations - -def generate_perturbation_effects( - total: int, - signal_group_size: int, - unaffected_mean: float, - unaffected_std: float, - affected_mean: float, - affected_std: float, +def generate_binding_effects( + gene_population: GenePopulation, + background_hops_range: tuple[int, int] = (1, 100), + noise_experiment_hops_range: tuple[int, int] = (0, 1), + signal_experiment_hops_range: tuple[int, int] = (1, 6), + total_background_hops: int = 1000, + total_experiment_hops: int = 76, + pseudocount: float = 1e-10, ) -> torch.Tensor: """ - Generate perturbation effects for genes. - - See generate_perturbation_binding_data() for more details. - - :raises ValueError: If signal_group_size is not less than total + Generate enrichment effects for genes using vectorized operations, based on their + signal designation, with separate experiment hops ranges for noise and signal genes. + + Note that the default values are a scaled down version of actual data. See also + https://github.com/cmatKhan/callingCardsTools/blob/main/callingcardstools/PeakCalling/yeast/enrichment.py + + :param gene_population: A GenePopulation object. See `generate_gene_population()` + :type gene_population: GenePopulation + :param background_hops_range: The range of hops for background genes. Defaults to + (1, 100) + :type background_hops_range: Tuple[int, int], optional + :param noise_experiment_hops_range: The range of hops for noise genes. Defaults to + (0, 1) + :type noise_experiment_hops_range: Tuple[int, int], optional + :param signal_experiment_hops_range: The range of hops for signal genes. Defaults to + (1, 6) + :type signal_experiment_hops_range: Tuple[int, int], optional + :param total_background_hops: The total number of background hops. Defaults to 1000 + :type total_background_hops: int, optional + :param total_experiment_hops: The total number of experiment hops. Defaults to 76 + :type total_experiment_hops: int, optional + :param pseudocount: A pseudocount to avoid division by zero. Defaults to 1e-10 + :type pseudocount: float, optional + :return: A tensor of enrichment values for each gene. + :rtype: torch.Tensor + :raises TypeError: If gene_population is not a GenePopulation object + :raises TypeError: If total_background_hops is not an integer + :raises TypeError: If total_experiment_hops is not an integer + :raises TypeError: If pseudocount is not a float + :raises TypeError: If background_hops_range is not a tuple + :raises TypeError: If noise_experiment_hops_range is not a tuple + :raises TypeError: If signal_experiment_hops_range is not a tuple + :raises ValueError: If background_hops_range is not a tuple of length 2 + :raises ValueError: If noise_experiment_hops_range is not a tuple of length 2 + :raises ValueError: If signal_experiment_hops_range is not a tuple of length 2 """ - if signal_group_size > total: - raise ValueError("Signal group size must not exceed total") - - unaffected_group_size = total - signal_group_size - - unaffected_perturbation_effect = torch.cat( - ( - torch.normal( - unaffected_mean, unaffected_std, size=(unaffected_group_size // 2,) - ), - torch.normal( - -unaffected_mean, unaffected_std, size=(unaffected_group_size // 2,) - ), - ) + # NOTE: torch intervals are half open on the right, so we add 1 to the + # high end of the range to make it inclusive + + # check input + if not isinstance(gene_population, GenePopulation): + raise TypeError("gene_population must be a GenePopulation object") + if not isinstance(total_background_hops, int): + raise TypeError("total_background_hops must be an integer") + if not isinstance(total_experiment_hops, int): + raise TypeError("total_experiment_hops must be an integer") + if not isinstance(pseudocount, float): + raise TypeError("pseudocount must be a float") + for arg, tup in { + "background_hops_range": background_hops_range, + "noise_experiment_hops_range": noise_experiment_hops_range, + "signal_experiment_hops_range": signal_experiment_hops_range, + }.items(): + if not isinstance(tup, tuple): + raise TypeError(f"{arg} must be a tuple") + if not len(tup) == 2: + raise ValueError(f"{arg} must be a tuple of length 2") + if not all(isinstance(i, int) for i in tup): + raise TypeError(f"{arg} must be a tuple of integers") + + # Generate background hops for all genes + background_hops = torch.randint( + low=background_hops_range[0], + high=background_hops_range[1] + 1, + size=(gene_population.labels.shape[0],), ) - affected_perturbation_effect = torch.cat( - ( - torch.normal(affected_mean, affected_std, size=(signal_group_size // 2,)), - torch.normal(-affected_mean, affected_std, size=(signal_group_size // 2,)), - ) + # Generate experiment hops noise genes + noise_experiment_hops = torch.randint( + low=noise_experiment_hops_range[0], + high=noise_experiment_hops_range[1] + 1, + size=(gene_population.labels.shape[0],), ) - - perturbation_effect = torch.cat( - (unaffected_perturbation_effect, affected_perturbation_effect) + # Generate experiment hops signal genes + signal_experiment_hops = torch.randint( + low=signal_experiment_hops_range[0], + high=signal_experiment_hops_range[1] + 1, + size=(gene_population.labels.shape[0],), ) - return perturbation_effect - - -def generate_binding_effects( - total: int, signal_group_size: int, unaffected_lambda: float, affected_lambda: float -) -> torch.Tensor: - """ - Generate binding effects for genes. - - see generate_perturbation_binding_data() for more details. - - :raises ValueError: If unaffected_lambda or affected_lambda is not non-negative - :raises ValueError: If signal_group_size is not less than total - """ - if unaffected_lambda < 0 or affected_lambda < 0: - raise ValueError("Lambda values must be non-negative") - if signal_group_size > total or signal_group_size < 0: - raise ValueError("Signal group size must be less than total") - - unaffected_group_size = total - signal_group_size - - unaffected_binding_effect = torch.poisson( - torch.full((unaffected_group_size,), unaffected_lambda) - ) - affected_binding_effect = torch.poisson( - torch.full((signal_group_size,), affected_lambda) + # Use signal designation to select appropriate experiment hops + experiment_hops = torch.where( + gene_population.labels == 1, signal_experiment_hops, noise_experiment_hops ) - binding_effect = torch.cat((unaffected_binding_effect, affected_binding_effect)) - return binding_effect + # Calculate enrichment for all genes + return (experiment_hops.float() / (total_experiment_hops + pseudocount)) / ( + (background_hops.float() / (total_background_hops + pseudocount)) + pseudocount + ) def generate_pvalues( @@ -180,125 +228,394 @@ def generate_pvalues( return pvalues -def generate_perturbation_binding_data( - gene_populations: torch.Tensor, - unaffected_perturbation_abs_mean: float = 0.0, - unaffected_perturbation_std: float = 1.0, - affected_perturbation_abs_mean: float = 3.0, - affected_perturbation_std: float = 1.0, - unaffected_binding_lambda: float = 1e-3, - affected_binding_lambda: float = 3.0, -) -> pd.DataFrame: +def default_perturbation_effect_adjustment_function( + binding_enrichment_data: torch.Tensor, + signal_mean: float, + noise_mean: float, + max_adjustment: float, + **kwargs, +) -> torch.Tensor: """ - Using a normal distribution for the perturbation effect, a poisson distribution for - the binding effect, simulate the perturbation and binding data. Note that for the - perturbation data, the affected and unaffected genes are divided into half where one - half has a positive perturbation_mean and the other has a negative perturbation_mean - in order to simulate both up and down regulation. Pvalues are calculated from a - random distribution based on their effect size, with the assumption that larger - effects are less likely to be false positives. - - :param gene_populations: A tensor where the first column is the gene/feature - identifier and the second column is binary indicating whether the gene - is in the signal group or not. See generate_gene_population() for - more details. - :type gene_populations: torch.Tensor - :param unaffected_perturbation_abs_mean: The absolute mean of the - perturbation effect for the unaffected genes. defaults to 0.0 - :type unaffected_perturbation_abs_mean: float, optional - :param unaffected_perturbation_std: The standard deviation of the - perturbation effect for the unaffected genes. defaults to 1.0 - :type unaffected_perturbation_std: float, optional - :param affected_perturbation_abs_mean: The absolute mean of the - perturbation effect for the affected genes. defaults to 3.0 - :type affected_perturbation_abs_mean: float, optional - :param affected_perturbation_std: The standard deviation of the - perturbation effect for the affected genes. defaults to 1.0 - :type affected_perturbation_std: float, optional - :param unaffected_binding_lambda: The lambda parameter for the poisson - distribution for the unaffected genes. defaults to 1e-3 - :type unaffected_binding_lambda: float, optional - :param affected_binding_lambda: The lambda parameter for the poisson - distribution for the affected genes. defaults to 3.0 - :type affected_binding_lambda: float, optional - - :return: A dataframe containing the following columns: - gene_id: (str) The gene identifier - signal: (boolean) Whether the gene is in the signal group or not - expression_effect: (float) The perturbation effect - expression_pvalue: (float) The pvalue of the perturbation effect - binding_effect: (float) The binding effect - binding_pvalue: (float) The pvalue of the binding effect - :rtype: pd.DataFrame - - :raises ValueError: If gene_populations is not a tensor with two columns - where the second column is binary - :raises ValueError: If unaffected_perturbation_abs_mean is not a float - :raises ValueError: If unaffected_perturbation_std is not a float - :raises ValueError: If affected_perturbation_abs_mean is not a float - :raises ValueError: If affected_perturbation_std is not a float - :raises ValueError: If unaffected_binding_lambda is not a float or <= 0 - :raises ValueError: If affected_binding_lambda is not a float or <= 0 + Default function to adjust the mean of the perturbation effect based on the + enrichment score. + + All functions that are passed to generate_perturbation_effects() in the argument + adjustment_function must have the same signature as this function. + + :param binding_enrichment_data: A tensor of enrichment scores for each gene with + dimensions [n_genes, n_tfs, 3] where the entries in the third dimension are a + matrix with columns [label, enrichment, pvalue]. + :type binding_enrichment_data: torch.Tensor + :param signal_mean: The mean for signal genes. + :type signal_mean: float + :param noise_mean: The mean for noise genes. + :type noise_mean: float + :param max_adjustment: The maximum adjustment to the base mean based on enrichment. + :type max_adjustment: float + :param tf_relationships: Unused in this function. It is only here to match the + signature of the other adjustment functions. + :type tf_relationships: dict[int, list[int]], optional + :return: Adjusted mean as a tensor. + :rtype: torch.Tensor """ - # check inputs - if not isinstance(gene_populations, torch.Tensor): - raise ValueError("gene_populations must be a tensor") - if gene_populations.shape[1] != 2: - raise ValueError("gene_populations must have two columns") - if gene_populations.dtype != torch.int32 and gene_populations.dtype != torch.int64: - raise ValueError("gene_populations must have torch.int32 or torch.int64 dtype") - if gene_populations.shape[0] == 0: - raise ValueError("gene_populations must have at least one row") - if not torch.all((gene_populations[:, 1] == 0) | (gene_populations[:, 1] == 1)): - raise ValueError("gene_populations second column must be binary") - if not isinstance(unaffected_perturbation_abs_mean, float): - raise ValueError("unaffected_perturbation_abs_mean must be a float") - if not isinstance(unaffected_perturbation_std, float): - raise ValueError("unaffected_perturbation_std must be a float") - if not isinstance(affected_perturbation_abs_mean, float): - raise ValueError("affected_perturbation_abs_mean must be a float") - if not isinstance(affected_perturbation_std, float): - raise ValueError("affected_perturbation_std must be a float") - if not isinstance(unaffected_binding_lambda, float): - raise ValueError("unaffected_binding_lambda must be a float") - if unaffected_binding_lambda <= 0: - raise ValueError("unaffected_binding_lambda must be > 0") - if not isinstance(affected_binding_lambda, float): - raise ValueError("affected_binding_lambda must be a float") - if affected_binding_lambda <= 0: - raise ValueError("affected_binding_lambda must be > 0") - - total = gene_populations.shape[0] - signal_group_size = torch.sum(gene_populations[:, 1]).item() - - # Generate effects - perturbation_effect = generate_perturbation_effects( - total, - signal_group_size, - unaffected_perturbation_abs_mean, - unaffected_perturbation_std, - affected_perturbation_abs_mean, - affected_perturbation_std, - ) - binding_effect = generate_binding_effects( - total, signal_group_size, unaffected_binding_lambda, affected_binding_lambda + # Extract signal/noise labels and enrichment scores + signal_labels = binding_enrichment_data[:, :, 0] + enrichment_scores = binding_enrichment_data[:, :, 1] + + adjusted_mean_matrix = torch.where( + signal_labels == 1, enrichment_scores, torch.zeros_like(enrichment_scores) ) - # Generate p-values - perturbation_pvalues = generate_pvalues(perturbation_effect) - binding_pvalues = generate_pvalues(binding_effect) - - # Combine into DataFrame and return - df = pd.DataFrame( - { - "gene_id": gene_populations[:, 0].numpy(), - "signal": gene_populations[:, 1].numpy().astype(bool), - "expression_effect": perturbation_effect.numpy(), - "expression_pvalue": perturbation_pvalues.numpy(), - "binding_effect": binding_effect.numpy(), - "binding_pvalue": binding_pvalues.numpy(), - } + for gene_idx in range(signal_labels.shape[0]): + for tf_index in range(signal_labels.shape[1]): + if signal_labels[gene_idx, tf_index] == 1: + # draw a random value between 0 and 1 to use to control + # magnitude of adjustment + adjustment_multiplier = torch.rand(1) + + # randomly adjust the gene by some portion of the max adjustment + adjusted_mean_matrix[gene_idx, tf_index] = signal_mean + ( + adjustment_multiplier * max_adjustment + ) + else: + # related tfs are not all bound, so set the enrichment + # score to noise mean + adjusted_mean_matrix[gene_idx, tf_index] = noise_mean + + return adjusted_mean_matrix + + +def perturbation_effect_adjustment_function_with_tf_relationships_boolean_logic( + binding_enrichment_data: torch.Tensor, + signal_mean: float, + noise_mean: float, + max_adjustment: float, + tf_relationships: dict[int, list[Relation]], +) -> torch.Tensor: + """ + Adjust the mean of the perturbation effect based on the enrichment score and the + provided binary / boolean or unary relationships between TFs. For each gene, the + mean of the TF-gene pair's perturbation effect will be adjusted if the TF is bound + to the gene and all of the Relations associated with the TF are satisfied (ie they + evaluate to True). These relations could be unary conditions or Ands or Ors between + TFs. A TF being bound corresponds to a true value, which means And(4, 5) would be + satisfied is both TF 4 and TF 5 are bound to the gene in question. The adjustment + will be a random value not exceeding the maximum adjustment. + + :param binding_enrichment_data: A tensor of enrichment scores for each gene with + dimensions [n_genes, n_tfs, 3] where the entries in the third dimension are a + matrix with columns [label, enrichment, pvalue]. + :type binding_enrichment_data: torch.Tensor + :param signal_mean: The mean for signal genes. + :type signal_mean: float + :param noise_mean: The mean for noise genes. + :type noise_mean: float + :param max_adjustment: The maximum adjustment to the base mean based on enrichment. + :type max_adjustment: float + :param tf_relationships: A dictionary where the keys are TF indices and the values + are lists of Relation objects that represent the conditions that must be met for + the mean of the perturbation effect associated with the TF-gene pair to be + adjusted. + :type tf_relationships: dict[int, list[Relation]] + :return: Adjusted mean as a tensor. + :rtype: torch.Tensor + :raises ValueError: If tf_relationships is not a dictionary between ints and lists + of Relations + :raises ValueError: If the tf_relationships dict does not have the same number of + TFs as the binding_data tensor passed into the function + :raises ValueError: If the tf_relationships dict has any TFs in the values that are + not also in the keys or any key or value TFs that are out of bounds for the + binding_data tensor + + """ + if ( + not isinstance(tf_relationships, dict) + or not all(isinstance(v, list) for v in tf_relationships.values()) + or not all(isinstance(k, int) for k in tf_relationships.keys()) + or not all( + isinstance(i, Relation) for v in tf_relationships.values() for i in v + ) + ): + raise ValueError( + "tf_relationships must be a dictionary between \ + ints and lists of Relation objects" + ) + if not all( + k in range(binding_enrichment_data.shape[1]) for k in tf_relationships.keys() + ): + raise ValueError( + "all TFs mentioned in tf_relationships must be within \ + the bounds of the binding_data tensor's number of TFs" + ) + if not len(tf_relationships) == binding_enrichment_data.shape[1]: + raise ValueError( + "tf_relationships must have the same number of TFs as \ + the binding_data tensor passed into the function" + ) + + # Extract signal/noise labels and enrichment scores + signal_labels = binding_enrichment_data[:, :, 0] # shape: (num_genes, num_tfs) + enrichment_scores = binding_enrichment_data[:, :, 1] # shape: (num_genes, num_tfs) + + # we set all unbound scores to 0, then we will go through and also set any + # bound scores to noise_mean if the related boolean statements are not satisfied + adjusted_mean_matrix = torch.where( + signal_labels == 1, enrichment_scores, torch.zeros_like(enrichment_scores) + ) # shape: (num_genes, num_tfs) + + for gene_idx in range(signal_labels.shape[0]): + for tf_index, relations in tf_relationships.items(): + # check if all relations (boolean relationships) + # associated with TFs are satisfied + if signal_labels[gene_idx, tf_index] == 1 and all( + relation.evaluate(signal_labels[gene_idx].tolist()) + for relation in relations + ): + # draw a random value between 0 and 1 to use to + # control magnitude of adjustment + adjustment_multiplier = torch.rand(1) + + # randomly adjust the gene by some portion of the max adjustment + adjusted_mean_matrix[gene_idx, tf_index] = signal_mean + ( + adjustment_multiplier * max_adjustment + ) + else: + # related tfs are not all bound, set the enrichment score to noise mean + adjusted_mean_matrix[gene_idx, tf_index] = noise_mean + + return adjusted_mean_matrix # shape (num_genes, num_tfs) + + +def perturbation_effect_adjustment_function_with_tf_relationships( + binding_enrichment_data: torch.Tensor, + signal_mean: float, + noise_mean: float, + max_adjustment: float, + tf_relationships: dict[int, list[int]], +) -> torch.Tensor: + """ + Adjust the mean of the perturbation effect based on the enrichment score and the + provided relationships between TFs. For each gene, the mean of the TF-gene pair's + perturbation effect will be adjusted if the TF is bound to the gene and all related + TFs are also bound to the gene. The adjustment will be a random value not exceeding + the maximum adjustment. + + :param binding_enrichment_data: A tensor of enrichment scores for each gene with + dimensions [n_genes, n_tfs, 3] where the entries in the third dimension are a + matrix with columns [label, enrichment, pvalue]. + :type binding_enrichment_data: torch.Tensor + :param signal_mean: The mean for signal genes. + :type signal_mean: float + :param noise_mean: The mean for noise genes. + :type noise_mean: float + :param max_adjustment: The maximum adjustment to the base mean based on enrichment. + :type max_adjustment: float + :param tf_relationships: A dictionary where the keys are the indices of the TFs and + the values are lists of indices of other TFs that are related to the key TF. + :type tf_relationships: dict[int, list[int]] + :return: Adjusted mean as a tensor. + :rtype: torch.Tensor + :raises ValueError: If tf_relationships is not a dictionary between ints and lists + of ints + :raises ValueError: If the tf_relationships dict does not have the same number of + TFs as the binding_data tensor passed into the function + :raises ValueError: If the tf_relationships dict has any TFs in the values that are + not also in the keys or any key or value TFs that are out of bounds for the + binding_data tensor + + """ + if ( + not isinstance(tf_relationships, dict) + or not all(isinstance(v, list) for v in tf_relationships.values()) + or not all(isinstance(k, int) for k in tf_relationships.keys()) + or not all(isinstance(i, int) for v in tf_relationships.values() for i in v) + ): + raise ValueError( + "tf_relationships must be a dictionary between ints and lists of ints" + ) + if not all( + k in range(binding_enrichment_data.shape[1]) for k in tf_relationships.keys() + ) or not all( + i in range(binding_enrichment_data.shape[1]) + for v in tf_relationships.values() + for i in v + ): + raise ValueError( + "all keys and values in tf_relationships must be within the \ + bounds of the binding_data tensor's number of TFs" + ) + if not len(tf_relationships) == binding_enrichment_data.shape[1]: + raise ValueError( + "tf_relationships must have the same number of TFs as the \ + binding_data tensor passed into the function" + ) + + # Extract signal/noise labels and enrichment scores + signal_labels = binding_enrichment_data[:, :, 0] # shape: (num_genes, num_tfs) + enrichment_scores = binding_enrichment_data[:, :, 1] # shape: (num_genes, num_tfs) + + # we set all unbound scores to 0, then we will go through and also + # set any bound scores to noise_mean if the related tfs are not also bound + adjusted_mean_matrix = torch.where( + signal_labels == 1, enrichment_scores, torch.zeros_like(enrichment_scores) + ) # shape: (num_genes, num_tfs) + + for gene_idx in range(signal_labels.shape[0]): + for tf_index, related_tfs in tf_relationships.items(): + if signal_labels[gene_idx, tf_index] == 1 and torch.all( + signal_labels[gene_idx, related_tfs] == 1 + ): + # draw a random value between 0 and 1 to use to + # control magnitude of adjustment + adjustment_multiplier = torch.rand(1) + + # randomly adjust the gene by some portion of the max adjustment + adjusted_mean_matrix[gene_idx, tf_index] = signal_mean + ( + adjustment_multiplier * max_adjustment + ) + else: + # related tfs are not all bound, set the enrichment score to noise mean + adjusted_mean_matrix[gene_idx, tf_index] = noise_mean + + return adjusted_mean_matrix # shape (num_genes, num_tfs) + + +def generate_perturbation_effects( + binding_data: torch.Tensor, + tf_index: int | None = None, + noise_mean: float = 0.0, + noise_std: float = 1.0, + signal_mean: float = 3.0, + signal_std: float = 1.0, + max_mean_adjustment: float = 0.0, + adjustment_function: Callable[ + [torch.Tensor, float, float, float], torch.Tensor + ] = default_perturbation_effect_adjustment_function, + **kwargs, +) -> torch.Tensor: + """ + Generate perturbation effects for genes. + + If `max_mean_adjustment` is greater than 0, then the mean of the + effects are adjusted based on the binding_data and the function passed + in `adjustment_function`. See `default_perturbation_effect_adjustment_function()` + for the default option. If `max_mean_adjustment` is 0, then the mean + is not adjusted. Additional keyword arguments may be passed in that will be + passed along to the adjustment function. + + :param binding_data: A tensor of binding data with dimensions [n_genes, n_tfs, 3] + where the entries in the third dimension are a matrix with columns + [label, enrichment, pvalue]. + :type binding_data: torch.Tensor + :param tf_index: The index of the TF in the binding_data tensor. Not used if we + are adjusting the means (ie only used if max_mean_adjustment == 0). + Defaults to None + :type tf_index: int + :param noise_mean: The mean for noise genes. Defaults to 0.0 + :type noise_mean: float, optional + :param noise_std: The standard deviation for noise genes. Defaults to 1.0 + :type noise_std: float, optional + :param signal_mean: The mean for signal genes. Defaults to 3.0 + :type signal_mean: float, optional + :param signal_std: The standard deviation for signal genes. Defaults to 1.0 + :type signal_std: float, optional + :param max_mean_adjustment: The maximum adjustment to the base mean based + on enrichment. Defaults to 0.0 + :type max_mean_adjustment: float, optional + + :return: A tensor of perturbation effects for each gene. + :rtype: torch.Tensor + + :raises ValueError: If binding_data is not a 3D tensor with the third + dimension having a length of 3 + :raises ValueError: If noise_mean, noise_std, signal_mean, signal_std, + or max_mean_adjustment are not floats + + """ + # check that a valid combination of inputs has been passed in + if max_mean_adjustment == 0.0 and tf_index is None: + raise ValueError("If max_mean_adjustment is 0, then tf_index must be specified") + + if binding_data.ndim != 3 or binding_data.shape[2] != 3: + raise ValueError( + "enrichment_tensor must have dimensions [num_genes, num_TFs, " + "[label, enrichment, pvalue]]" + ) + # check the rest of the inputs + if not all( + isinstance(i, float) + for i in (noise_mean, noise_std, signal_mean, signal_std, max_mean_adjustment) + ): + raise ValueError( + "noise_mean, noise_std, signal_mean, signal_std, " + "and max_mean_adjustment must be floats" + ) + # check the Callable signature + if not all( + i in inspect.signature(adjustment_function).parameters + for i in ( + "binding_enrichment_data", + "signal_mean", + "noise_mean", + "max_adjustment", + ) + ): + raise ValueError( + "adjustment_function must have the signature " + "(binding_enrichment_data, signal_mean, noise_mean, max_adjustment)" + ) + + # Initialize an effects tensor for all genes + effects = torch.empty( + binding_data.size(0), dtype=torch.float32, device=binding_data.device ) - return df + # Randomly assign signs for each gene + # fmt: off + signs = torch.randint(0, 2, (effects.size(0),), + dtype=torch.float32, + device=binding_data.device) * 2 - 1 + # fmt: on + + # Apply adjustments to the base mean for the signal genes, if necessary + if max_mean_adjustment > 0 and adjustment_function is not None: + # Assuming adjustment_function returns a vector of means for each gene. + # Signal genes that meet the criteria for adjustment will be affected by + # the status of the TFs. What TFs affect a given gene must be specified by + # the adjustment_function() + adjusted_means = adjustment_function( + binding_data, + signal_mean, + noise_mean, + max_mean_adjustment, + **kwargs, + ) + + # add adjustments, ensuring they respect the original sign + if adjusted_means.ndim == 1: + effects = signs * torch.abs( + torch.normal(mean=adjusted_means, std=signal_std) + ) + else: + effects = torch.zeros_like(adjusted_means) + for col_idx in range(effects.size(1)): + effects[:, col_idx] = signs * torch.abs( + torch.normal(mean=adjusted_means[:, col_idx], std=signal_std) + ) + else: + signal_mask = binding_data[:, tf_index, 0] == 1 + + # Generate effects based on the noise and signal means, applying the sign + effects[~signal_mask] = signs[~signal_mask] * torch.abs( + torch.normal( + mean=noise_mean, std=noise_std, size=(torch.sum(~signal_mask),) + ) + ) + effects[signal_mask] = signs[signal_mask] * torch.abs( + torch.normal( + mean=signal_mean, std=signal_std, size=(torch.sum(signal_mask),) + ) + ) + + return effects diff --git a/yeastdnnexplorer/probability_models/relation_classes.py b/yeastdnnexplorer/probability_models/relation_classes.py new file mode 100644 index 0000000..7903c58 --- /dev/null +++ b/yeastdnnexplorer/probability_models/relation_classes.py @@ -0,0 +1,93 @@ +class Relation: + """Base class for relations between TF indices.""" + + def evaluate(self, bound_vec: list[int]): + raise NotImplementedError + + +class And(Relation): + """Class for representing the logical AND of multiple conditions Allows nesed + conditions, i.e. And(1, Or(2, 3))""" + + def __init__(self, *conditions): + """ + :param conditions: List of conditions to be evaluated + :type conditions: List[float | Relation] + """ + self.conditions = conditions + + def evaluate(self, bound_vec): + """ + Returns true if the And() condition evaluates to true Evaluates nested + conditions as needed. + + :param bound_vec: Vector of TF indices (0 or 1) indicating which TFs are bound + for the gene in question + :type bound_vec: List[float] + + """ + if type(bound_vec) is not list or not all( + isinstance(x, float) for x in bound_vec + ): + raise ValueError("bound_vec must be a list of floats") + + if not self.conditions: + return True + + # Each condition can be an index or another Relation (And/Or) + return all( + c.evaluate(bound_vec) if isinstance(c, Relation) else bound_vec[c] + for c in self.conditions + ) + + def __str__(self): + return f"AND({', '.join(str(c) for c in self.conditions)})" + + +class Or(Relation): + def __init__(self, *conditions): + """ + :param conditions: List of conditions to be evaluated + :type conditions: List[int | Relation] + """ + self.conditions = conditions + + def evaluate(self, bound_vec): + """ + Returns true if the Or() condition evaluates to true Evaluates nested conditions + as needed. + + :param bound_vec: Vector of TF indices (0 or 1) indicating which TFs are bound + for the gene in question + :type bound_vec: List[int] + + """ + if type(bound_vec) is not list or not all( + isinstance(x, float) for x in bound_vec + ): + raise ValueError("bound_vec must be a list of floats") + + if not self.conditions: + return True + + # Each condition can be an index or another Relation (And/Or) + return any( + c.evaluate(bound_vec) if isinstance(c, Relation) else bound_vec[c] + for c in self.conditions + ) + + def __str__(self): + return f"OR({', '.join(str(c) for c in self.conditions)})" + + +# EXAMPLE USAGE: +# Defining a complex condition: +# "index 2 should only have its score adjusted if it is activated and if +# (index 5 and 7) or 3 is activated" +condition = And( + 2, # Index 2 must be activated + Or( + And(5, 7), # Both indices 5 and 7 must be activated + 3, # Or index 3 must be activated + ), +) diff --git a/yeastdnnexplorer/tests/data_loaders/__init__.py b/yeastdnnexplorer/tests/data_loaders/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yeastdnnexplorer/tests/data_loaders/test_synthetic_data_loader.py b/yeastdnnexplorer/tests/data_loaders/test_synthetic_data_loader.py new file mode 100644 index 0000000..a3bbe66 --- /dev/null +++ b/yeastdnnexplorer/tests/data_loaders/test_synthetic_data_loader.py @@ -0,0 +1,17 @@ +import pytest +from torch.utils.data import DataLoader + +from yeastdnnexplorer.data_loaders.synthetic_data_loader import SyntheticDataLoader + + +@pytest.fixture +def data_module(): + return SyntheticDataLoader() + + +def test_data_loading(data_module): + data_module.prepare_data() + data_module.setup() + assert isinstance(data_module.train_dataloader(), DataLoader) + assert isinstance(data_module.val_dataloader(), DataLoader) + assert isinstance(data_module.test_dataloader(), DataLoader) diff --git a/yeastdnnexplorer/tests/ml_models/__init__.py b/yeastdnnexplorer/tests/ml_models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yeastdnnexplorer/tests/ml_models/test_simple_model.py b/yeastdnnexplorer/tests/ml_models/test_simple_model.py new file mode 100644 index 0000000..16cf833 --- /dev/null +++ b/yeastdnnexplorer/tests/ml_models/test_simple_model.py @@ -0,0 +1,36 @@ +import pytest +import torch + +from yeastdnnexplorer.ml_models.simple_model import SimpleModel + + +@pytest.fixture +def model(): + return SimpleModel(input_dim=4, output_dim=4) + + +def test_model_forward_pass(model): + x = torch.randn(32, 4) # 32 is batch size, 4 is input dim + output = model(x) + assert output.shape == (32, 4) # 32 is batch size, 4 is output dim + + +def test_model_training_step(model): + batch = (torch.randn(32, 4), torch.randn(32, 4)) # 32 is batch size, 4 is input dim + batch_idx = 0 + loss = model.training_step(batch, batch_idx) + assert loss.ndim == 0 # loss should be a scalar (0 dimensional tensor) + + +def test_model_validation_step(model): + batch = (torch.randn(32, 4), torch.randn(32, 4)) # 32 is batch size, 4 is input dim + batch_idx = 0 + loss = model.validation_step(batch, batch_idx) + assert loss.ndim == 0 # loss should be a scalar (0 dimensional tensor) + + +def test_model_test_step(model): + batch = (torch.randn(32, 4), torch.randn(32, 4)) # 32 is batch size, 4 is input dim + batch_idx = 0 + loss = model.test_step(batch, batch_idx) + assert loss.ndim == 0 # loss should be a scalar (0 dimensional tensor) diff --git a/yeastdnnexplorer/tests/probability_models/test_generate_data.py b/yeastdnnexplorer/tests/probability_models/test_generate_data.py index cb16d9c..00f4d4f 100644 --- a/yeastdnnexplorer/tests/probability_models/test_generate_data.py +++ b/yeastdnnexplorer/tests/probability_models/test_generate_data.py @@ -1,12 +1,11 @@ # mypy: disable-error-code=arg-type -import pandas as pd import pytest import torch from yeastdnnexplorer.probability_models.generate_data import ( + GenePopulation, generate_binding_effects, generate_gene_population, - generate_perturbation_binding_data, generate_perturbation_effects, generate_pvalues, ) @@ -17,108 +16,34 @@ def test_generate_gene_population(): signal_ratio = 0.3 signal_group_size = int(total_genes * signal_ratio) - gene_populations = generate_gene_population(total_genes, signal_ratio) + gene_population = generate_gene_population(total_genes, signal_ratio) - # Check if the output is a 2D tensor - assert gene_populations.ndim == 2 + # Check if the output is a 1D tensor + assert gene_population.labels.ndim == 1 # Check if the output has the correct shape - assert gene_populations.shape == (total_genes, 2) - - # Check if the first column contains identifiers 0 to total-1 - assert all(gene_populations[:, 0] == torch.arange(total_genes)) + assert gene_population.labels.shape == torch.Size([total_genes]) # Check if the second column contains the correct number of signal # and non-signal genes - assert torch.sum(gene_populations[:, 1]) == signal_group_size - assert torch.sum(gene_populations[:, 1] == 0) == total_genes - signal_group_size + assert torch.sum(gene_population.labels) == signal_group_size + assert torch.sum(gene_population.labels == 0) == total_genes - signal_group_size # Additional tests could include checking the datatype of the tensor elements - assert gene_populations.dtype == torch.int32 - - -@pytest.mark.parametrize("total, ratio", [(1000, 0.3), (500, 0.5), (2000, 0.1)]) -def test_gene_populations(total, ratio): - gene_populations = generate_gene_population(total, ratio) - signal_group_size = int(total * ratio) - - assert gene_populations.shape == (total, 2) - assert torch.sum(gene_populations[:, 1]) == signal_group_size - assert torch.sum(gene_populations[:, 1] == 0) == total - signal_group_size - - -def test_gene_populations_invalid_input(): - with pytest.raises(ValueError): - # invalid string input - generate_gene_population(total="1000", signal_group=0.3) - - with pytest.raises(ValueError): - generate_gene_population(total=1000, signal_group=1.2) - - with pytest.raises(ValueError): - generate_perturbation_binding_data(torch.rand((100, 1))) # Invalid shape - - with pytest.raises(ValueError): - generate_perturbation_binding_data(torch.rand((0, 2))) # Empty tensor - - with pytest.raises(ValueError): - generate_perturbation_binding_data(torch.rand((100, 2))) # Non-integer tensor - - with pytest.raises(ValueError): - generate_perturbation_binding_data( - torch.tensor([[1, -1], [2, 2]], dtype=torch.int32) - ) - - -def test_generate_perturbation_effects_valid_inputs(): - total = 100 - signal_group_size = 50 - unaffected_mean = 1.0 - unaffected_std = 0.5 - affected_mean = 2.0 - affected_std = 0.7 - - effects = generate_perturbation_effects( - total, - signal_group_size, - unaffected_mean, - unaffected_std, - affected_mean, - affected_std, - ) - - # Check if the returned tensor has the correct shape - assert effects.shape[0] == total, ( - "The number of effects generated " "does not match the total" - ) - - # Check if the returned object is a tensor - assert isinstance(effects, torch.Tensor), "Returned object is not a tensor" - - -def test_generate_perturbation_effects_invalid_inputs(): - # Test with negative total - with pytest.raises(ValueError): - generate_perturbation_effects(-100, 50, 1.0, 0.5, 2.0, 0.7) - - # Test with signal group size greater than total - with pytest.raises(ValueError): - generate_perturbation_effects(50, 100, 1.0, 0.5, 2.0, 0.7) + assert gene_population.labels.dtype == torch.bool - # Test with non-numeric mean or standard deviation - with pytest.raises(TypeError): - generate_perturbation_effects( - 100, 50, "invalid", 0.5, 2.0, 0.7 - ) # mypy: ignore arg-type # noqa - with pytest.raises(TypeError): - generate_perturbation_effects(100, 50, 1.0, "invalid", 2.0, 0.7) - - with pytest.raises(TypeError): - generate_perturbation_effects(100, 50, 1.0, 0.5, "invalid", 0.7) - - with pytest.raises(TypeError): - generate_perturbation_effects(100, 50, 1.0, 0.5, 2.0, "invalid") +def test_generate_binding_effects_success(): + # set torch seed + torch.manual_seed(42) + # Create a mock GenePopulation with some genes + # labeled as signal and others as noise + gene_population = GenePopulation(torch.tensor([1, 0, 1, 0], dtype=torch.bool)) + # Call generate_binding_effects with valid arguments + enrichment = generate_binding_effects(gene_population) + # Check that the result is a tensor of the correct shape + assert isinstance(enrichment, torch.Tensor) + assert enrichment.shape == (4,) def test_generate_pvalues_valid_input(): @@ -156,88 +81,95 @@ def test_generate_pvalues_invalid_input(): ) # Invalid input as non-numeric tensor -def test_generate_binding_effects_valid_input(): - total = 100 - signal_group_size = 30 - unaffected_lambda = 2.0 - affected_lambda = 5.0 - - # Call the function - binding_effect = generate_binding_effects( - total, signal_group_size, unaffected_lambda, affected_lambda +def test_generate_perturbation_effects_with_and_without_adjustment(): + torch.manual_seed(42) + # Create mock binding data with the first + # column indicating signal (1) or noise (0), + # the second column indicates the enrichment, and the third the p-value. + # Add an extra dimension for TFs -- the function requires a 3D tensor. + binding_data = torch.tensor( + [ + [1.0000, 0.5000, 0.0700], + [0.0000, 0.2000, 0.0500], + [1.0000, 0.8000, 0.0100], + [0.0000, 0.1000, 0.9000], + ] + ).unsqueeze( + 1 + ) # Add TF dimension + + # Specify means and standard deviations + noise_mean = 0.0 + noise_std = 1.0 + signal_mean = 4.0 + signal_std = 1.0 + + # First, test without mean adjustment + effects_without_adjustment = generate_perturbation_effects( + binding_data=binding_data, + tf_index=0, + noise_mean=noise_mean, + noise_std=noise_std, + signal_mean=signal_mean, + signal_std=signal_std, + max_mean_adjustment=0.0, # No adjustment ) - # Check if the output is a tensor - assert isinstance(binding_effect, torch.Tensor) - - # Check if the output size is correct - assert binding_effect.shape[0] == total - - # Check if the first part corresponds to unaffected group - assert torch.all(binding_effect[: total - signal_group_size] >= 0) + # Extract masks for signal and noise genes based on labels + signal_mask = binding_data[:, :, 0].squeeze() == 1 + noise_mask = binding_data[:, :, 0].squeeze() == 0 - # Check if the second part corresponds to affected group - assert torch.all(binding_effect[total - signal_group_size :] >= 0) + # Assert the effects tensor is of the correct shape + assert effects_without_adjustment.shape[0] == binding_data.shape[0] + assert torch.isclose( + torch.abs(effects_without_adjustment[signal_mask]).mean(), + torch.tensor(signal_mean), + atol=signal_std, + ) + assert torch.isclose( + torch.abs(effects_without_adjustment[~signal_mask]).mean(), + torch.tensor(noise_mean), + atol=noise_std, + ) + assert torch.isclose( + torch.abs(effects_without_adjustment[signal_mask]).std(), + torch.tensor(signal_std), + atol=signal_std, + ) + assert torch.isclose( + torch.abs(effects_without_adjustment[~signal_mask]).std(), + torch.tensor(noise_std), + atol=noise_std, + ) -@pytest.mark.parametrize( - "total, signal_group_size, unaffected_lambda, affected_lambda", - [ - (-10, 5, 2.0, 5.0), # Negative total - (100, -5, 2.0, 5.0), # Negative signal group size - (100, 150, 2.0, 5.0), # Signal group size larger than total - (100, 50, -2.0, 5.0), # Negative unaffected lambda - (100, 50, 2.0, -5.0), # Negative affected lambda - ], -) -def test_generate_binding_effects_invalid_input( - total, signal_group_size, unaffected_lambda, affected_lambda -): - with pytest.raises(ValueError): - generate_binding_effects( - total, signal_group_size, unaffected_lambda, affected_lambda - ) - - -def test_generate_perturbation_binding_data(): - # Setup - gene_count = 100 - signal_group_size = 50 - gene_populations = torch.randint(0, 2, (gene_count, 2), dtype=torch.int32) - gene_populations[:, 1] = (torch.arange(gene_count) < signal_group_size).int() - - # Call the function - result = generate_perturbation_binding_data(gene_populations) + # Test with mean adjustment + effects_with_adjustment = generate_perturbation_effects( + binding_data=binding_data, + tf_index=0, + noise_mean=noise_mean, + noise_std=noise_std, + signal_mean=signal_mean, + signal_std=signal_std, + max_mean_adjustment=4.0, # Applying adjustment + ) - # Validate the result - assert isinstance(result, pd.DataFrame), "Output should be a DataFrame" - assert "gene_id" in result.columns, "DataFrame should have gene_id column" - assert "signal" in result.columns, "DataFrame should have signal column" - assert ( - "expression_effect" in result.columns - ), "DataFrame should have expression_effect column" + # Assert that signal genes with adjustments have a mean effect greater than + # the base mean assert ( - "expression_pvalue" in result.columns - ), "DataFrame should have expression_pvalue column" - assert ( - "binding_effect" in result.columns - ), "DataFrame should have binding_effect column" - assert ( - "binding_pvalue" in result.columns - ), "DataFrame should have binding_pvalue column" - assert len(result) == gene_count, "DataFrame should have one row per gene" - - # Check data types - assert pd.api.types.is_numeric_dtype( - result["expression_effect"] - ), "expression_effect should be numeric" - assert pd.api.types.is_numeric_dtype( - result["binding_effect"] - ), "binding_effect should be numeric" - assert pd.api.types.is_numeric_dtype( - result["expression_pvalue"] - ), "expression_pvalue should be numeric" - assert pd.api.types.is_numeric_dtype( - result["binding_pvalue"] - ), "binding_pvalue should be numeric" - assert pd.api.types.is_bool_dtype(result["signal"]), "signal should be boolean" + torch.abs(effects_with_adjustment[signal_mask]).mean() + > torch.abs(effects_without_adjustment[signal_mask]).mean() + ) + + # Assert that the mean effect for noise genes remains close to the noise mean + assert torch.isclose( + torch.abs(effects_with_adjustment[noise_mask]).mean(), + torch.tensor(noise_mean), + atol=noise_std, + ) + # and that the noise standard deviation remains close to the noise std + assert torch.isclose( + torch.abs(effects_with_adjustment[noise_mask]).std(), + torch.tensor(noise_std), + atol=noise_std, + )