diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 308c1f82..d5f89431 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -5,9 +5,9 @@ Anybody is welcome to contribute to the development of the RAVEN Toolbox, but pl Each function should start with a commented section describing the function and explaining the parameters. Existing functions can clarify what style should be used. ### Bugfixes, New Features and Functions -* For any development, whether bug fixes, introducing new functions or new/updated features for existing functions: make a separate branch from `devel` and name the branch for instance after the function/feature you are fixing/developing. If you work on a fix, start the branch name with `fix/`, if you work on a feature, start the branch name with `feat/`. Examples: `fix/format_reactions` or `feat/new_algorithms`. +* For any development, whether bug fixes, introducing new functions or new/updated features for existing functions: make a separate branch from `develop` and name the branch for instance after the function/feature you are fixing/developing. If you work on a fix, start the branch name with `fix/`, if you work on a feature, start the branch name with `feat/`. Examples: `fix/format_reactions` or `feat/new_algorithms`. * Make commits to this branch while developing. Aim for backward compatibility, and try to avoid very new MATLAB functions when possible, to accommodate users with older MATLAB versions. -* When you are happy with your new function/feature, make a pull request to the `devel` branch. Also, see [Pull request](#pull-request) below. +* When you are happy with your new function/feature, make a pull request to the `develop` branch. Also, see [Pull request](#pull-request) below. ### Semantic Commits Use semantic commit messages to make it easier to show what you are aiming to do: @@ -28,26 +28,26 @@ A more detailed explanation or comments can be left in the commit description. ### External Software Update * Once the newer version for any external software (BLAST+, DIAMOND, HMMER) is available, identify the newest version which is simultaneously available for macOS, Unix/Linux and Windows -* Create a separate branch from the `devel` branch and name it e.g. `chore/updateBinaries` +* Create a separate branch from the `develop` branch and name it e.g. `chore/updateBinaries` * Commit the changes for each program and operating system separately, e.g. `chore: update HMMER (Win) to 3.2.1` -* As soon as all binaries for particular program are updated through commits, update the corresponding license file, if available and place it in e.g. `software/blast+` directory. -* Update the version number in `RAVENdir/software/version.txt` file. -* Do some testing to ensure that the new binaries are working correctly. Upon successful tests, create a Pull Request to the `devel` branch. +* As soon as all binaries for particular program are updated through commits, update the corresponding license file, if available and place it in e.g. `src/external/blast+` directory. +* Update the version number in `RAVENdir/src/external/version.txt` file. +* Do some testing to ensure that the new binaries are working correctly. Upon successful tests, create a Pull Request to the `develop` branch. ### Pull Requests -* No changes should be directly committed to the `main` or `devel` branches. Commits are made to side branches, after which pull requests are made for merging with `main` or `devel`. +* No changes should be directly committed to the `main` or `develop` branches. Commits are made to side branches, after which pull requests are made for merging with `main` or `develop`. * The person making the pull request and the one accepting the merge _cannot_ be the same person. * Typically, wait a few days before merging, to allow for other developers to inspect the pull request. * A merge with the mainbranch invokes a new release (see [versioning](#versioning)). ### Versioning -RAVEN Toolbox follows [semantic versioning](https://semver.org/). After each successful Pull Request (PR) to `main`, the `version.txt` file should be updated in a separate PR. When this PR is merged, a new [release](https://github.com/SysBioChalmers/RAVEN/releases) should be specified, with a list detailing the most significant changes since the previous release. +RAVEN Toolbox follows [semantic versioning](https://semver.org/). After each successful Pull Request (PR) to `main`, the `version.txt` file should be updated in a separate PR. When this PR is merged, a new [release](https://github.com/CompMetEng/RAVEN/releases) should be specified, with a list detailing the most significant changes since the previous release. -Ensure that the documentation is also updated (in `devel`) before creating a PR to `main`. It can be updated by running `updateDocumentation()`. +Ensure that the documentation is also updated (in `develop`) before creating a PR to `main`. It can be updated by running `updateDocumentation()`. ### Categorize Releases * PRs pushed into `main` should be patch releases by default. -* Developers who push PRs to `devel` may propose target release categories (e.g. minor or patch) that can be commented on and discussed by reviewers and other developers. +* Developers who push PRs to `develop` may propose target release categories (e.g. minor or patch) that can be commented on and discussed by reviewers and other developers. * As a result of the above step, minor releases hopefully can be determined. * To help the process of categorizing releases, some conditions are suggested qualification for minor releases: * with database updates (e.g. KEGG) diff --git a/.github/issue_template.md b/.github/issue_template.md index d31135e1..48b4ee61 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -26,4 +26,4 @@ etc. etc. **I hereby confirm that I have:** -- [ ] Checked that a similar issue does not [already exist](https://github.com/SysBioChalmers/RAVEN/issues?q=is%3Aissue) or is already [discussed](https://github.com/SysBioChalmers/RAVEN/discussions). +- [ ] Checked that a similar issue does not [already exist](https://github.com/CompMetEng/RAVEN/issues?q=is%3Aissue) or is already [discussed](https://github.com/CompMetEng/RAVEN/discussions). diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 35742302..c04f8144 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -11,4 +11,4 @@ - [ ] This PR has `develop` as target branch, and will be resolved with a *squash-merge*. -- [ ] This PR has `main` as target branch, and will be resolved as descriped [here](https://github.com/SysBioChalmers/RAVEN/wiki/Development-policy#make-a-new-release). +- [ ] This PR has `main` as target branch, and will be resolved as described [here](https://github.com/CompMetEng/RAVEN/blob/develop/.github/CONTRIBUTING.md#versioning). diff --git a/.github/scripts/assemble_offline_software.py b/.github/scripts/assemble_offline_software.py index 0d0384d1..52be9a27 100644 --- a/.github/scripts/assemble_offline_software.py +++ b/.github/scripts/assemble_offline_software.py @@ -1,14 +1,14 @@ #!/usr/bin/env python -"""Populate RAVEN's software/ with every platform's external binaries. +"""Populate RAVEN's src/external/ with every platform's external binaries. -Downloads the per-platform binary ZIPs from raven-data and lays them out under -``/software//`` using RAVEN's suffix scheme — bare name = Linux, +Downloads the per-platform binary ZIPs from toolbox-assets and lays them out under +``/src/external//`` using RAVEN's suffix scheme — bare name = Linux, ``.mac`` = macOS, ``.exe`` = Windows. With all platforms present in one tree, the whole RAVEN checkout can be zipped into a single **OS-independent** offline distribution (see the release-bundle workflow). Run from the RAVEN root, or pass ``--raven ``. Requires network access to -https://github.com/SysBioChalmers/raven-data . +https://github.com/CompMetEng/toolbox-assets . """ from __future__ import annotations @@ -18,11 +18,11 @@ import zipfile from pathlib import Path -RAVEN_DATA = "https://github.com/SysBioChalmers/raven-data/releases/download" -# raven-data platform key -> RAVEN binary suffix. +TOOLBOX_ASSETS = "https://github.com/CompMetEng/toolbox-assets/releases/download" +# toolbox-assets platform key -> RAVEN binary suffix. PLATFORMS = {"linux-x86_64": "", "macos-arm64": ".mac", "windows-x86_64": ".exe"} -# (software subdir, raven-data bundle, {platform|"_": version}, {bare executable names}) +# (software subdir, toolbox-assets bundle, {platform|"_": version}, {bare executable names}) CLI_TOOLS = [ ("blast+", "blast", {"_": "2.17.0"}, {"blastp", "makeblastdb"}), ("diamond", "diamond", {"_": "2.1.17"}, {"diamond"}), @@ -31,7 +31,7 @@ def _fetch(url: str) -> bytes: - with urllib.request.urlopen(url, timeout=120) as resp: # noqa: S310 (pinned raven-data URLs) + with urllib.request.urlopen(url, timeout=120) as resp: # noqa: S310 (pinned toolbox-assets URLs) return resp.read() @@ -50,7 +50,7 @@ def _extract(zip_bytes: bytes, dest: Path, rename: dict[str, str]) -> None: def assemble(raven: Path) -> None: - soft = raven / "software" + soft = raven / "src" / "external" for subdir, bundle, vmap, exes in CLI_TOOLS: dest = soft / subdir for plat, suffix in PLATFORMS.items(): @@ -60,7 +60,7 @@ def assemble(raven: Path) -> None: # macOS ships bare names; rename them to .mac. Linux keeps bare names, # Windows already carries .exe — so only the macOS members need renaming. rename = {e: e + ".mac" for e in exes} if suffix == ".mac" else {} - _extract(_fetch(f"{RAVEN_DATA}/{bundle}-{version}/{asset}"), dest, rename) + _extract(_fetch(f"{TOOLBOX_ASSETS}/{bundle}-{version}/{asset}"), dest, rename) def main(argv: list[str] | None = None) -> None: diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 3c7183aa..7b639ab1 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -1,8 +1,8 @@ name: Documentation -# Regenerate doc/ from the function help texts and commit the result. +# Regenerate docs/html/ from the function help texts and commit the result. # -# doc/ is generated output that is nevertheless tracked, so it drifts as soon +# docs/html/ is generated output that is nevertheless tracked, so it drifts as soon # as anyone edits a docstring or adds/removes a function without remembering to # run updateDocumentation. Running it here instead means nobody has to. # @@ -18,7 +18,7 @@ name: Documentation on: push: - branches: [develop3, develop, main] + branches: [develop, main] paths: - '**.m' - '.github/workflows/documentation.yml' @@ -34,7 +34,7 @@ concurrency: jobs: update-documentation: - name: Regenerate doc/ + name: Regenerate docs/html/ runs-on: ubuntu-latest timeout-minutes: 20 @@ -71,11 +71,11 @@ jobs: - name: Commit regenerated documentation run: | - # updateDocumentation deletes and rebuilds doc/, so stage first: + # updateDocumentation deletes and rebuilds docs/html/, so stage first: # a plain "git diff" would not see added or removed pages. - git add -A doc - if git diff --cached --quiet -- doc; then - echo "doc/ is already up to date." + git add -A docs/html + if git diff --cached --quiet -- docs/html; then + echo "docs/html/ is already up to date." exit 0 fi git config user.name "github-actions[bot]" diff --git a/.github/workflows/release-bundle.yml b/.github/workflows/release-bundle.yml index fdcf3a5c..418a379f 100644 --- a/.github/workflows/release-bundle.yml +++ b/.github/workflows/release-bundle.yml @@ -1,7 +1,7 @@ name: Offline binary bundle # Build the OS-independent offline RAVEN distribution: the full repo with -# software/ populated with every platform's external binaries, so an air-gapped +# src/external/ populated with every platform's external binaries, so an air-gapped # user downloads one ZIP and runs RAVEN on any OS without fetching anything. # Attached automatically to each published release; also runnable on demand # against an existing tag. @@ -37,7 +37,7 @@ jobs: with: python-version: '3.12' - - name: Populate software/ with every platform's binaries + - name: Populate src/external/ with every platform's binaries run: python .github/scripts/assemble_offline_software.py - name: Build RAVEN--full.zip diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bef69b5b..e895a36a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -46,7 +46,7 @@ jobs: if ~isfolder(resultsDir); mkdir(resultsDir); end import matlab.unittest.TestRunner import matlab.unittest.plugins.XMLPlugin - cd(fullfile('testing', 'function_tests')); + cd(fullfile('tests', 'function_tests')); files = string({dir('*.m').name}); files(files == "RavenTestCase.m") = []; files(files == "tBinaries.m") = []; % run separately by the binary-tests job (Linux + macOS) @@ -72,7 +72,7 @@ jobs: binary-tests: name: Binary download (${{ matrix.os }}) - # Validate the on-demand fetch of the external binaries from raven-data on + # Validate the on-demand fetch of the external binaries from toolbox-assets on # Linux and macOS. Running the downloaded binaries also confirms the macOS # Gatekeeper quarantine is cleared so they can execute. strategy: diff --git a/.gitignore b/.gitignore index 956c5b58..d102dd98 100644 --- a/.gitignore +++ b/.gitignore @@ -38,15 +38,15 @@ Temporary Items # first use (see buildGlobalKEGGModel); none of them belong in the repo. The # glob (rather than a version-pinned name) keeps this working across a # KEGG_VERSION.md bump without needing its own update. -reconstruction/kegg/keggModel.mat -reconstruction/kegg/kegg*_core.tar.gz -reconstruction/kegg/kegg*_core/ +src/reconstruction/kegg/keggModel.mat +src/reconstruction/kegg/kegg*_core.tar.gz +src/reconstruction/kegg/kegg*_core/ #software -software/scip/ -software/blast+/ -software/diamond/ -software/hmmer/ -software/WoLFPSORT/ +src/external/scip/ +src/external/blast+/ +src/external/diamond/ +src/external/hmmer/ +src/external/WoLFPSORT/ !.keep #doc diff --git a/CHANGELOG.md b/CHANGELOG.md index 41cdf996..423aec56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,153 @@ # Changelog +## 3.0.0b2 — 2026-11-01 + +Second beta toward 3.0. Changes below are relative to 3.0.0b1. + +### Breaking changes + +* The folder layout changed (see Repository layout and downloads); code that adds RAVEN folders to the path or addresses binaries by path needs the new paths. +* `fetchRavenDataAsset` is replaced by `fetchToolboxAsset`. +* `gapFillMILP` returns `[addedRxns, newModel, exitFlag]` and has no `weights` option. +* `sortModel` keeps the directions of the reactions unless `sortReversible` is requested (was: it sorted them by default). +* `parseTaskList` refuses a binary `.xls` file. +* `gapReport`, `scaleBiomassFraction`, `getModelFromHomology` and `getIndexes` raise errors in cases they accepted before (see below). + +### Repository layout and downloads + +* The MATLAB code is in `src/` (was: folders in the repository root), the tests in `tests/` (was: `testing/`), the generated help pages in `docs/html/` (was: `doc/`), the readme files in `docs/readme/`, the logo in `img/` (was: `RAVEN.png`), and the tutorials in `tutorials/` with data under `tutorials/data` (was: `tutorial/`). The external software is in `src/external` (was: `software/`). `checkRaven`, `downloadRavenBinaries`, `getBlast`, `getDiamond`, `getKEGGModelForOrganism`, `optimizeProb`, `setRavenSolver` and `updateDocumentation` use the new paths. +* `parseHPA`, `parseHPArna` and `scoreModel` are in `INIT/` (was: `omics/`, which is removed). `tOmics.m` is merged into `tINIT.m`. +* `fetchToolboxAsset` replaces `fetchRavenDataAsset`, and assets are downloaded from `CompMetEng/toolbox-assets` (was: raven-data). `downloadRavenBinaries`, `buildGlobalKEGGModel`, `getKEGGModelForOrganism`, `getModelFromKEGG`, `getPhylDist`, `keggDataVersion` and `checkRaven` use it. +* `fetchToolboxAsset` downloads to a `.part` file and checks the SHA-256 sum listed in the `manifest-v3` of toolbox-assets. A mismatch deletes the file and raises an error; an asset that the manifest does not list, or a manifest that cannot be read, gives a warning. +* The repository, documentation and version-check links point to `CompMetEng`. + +### I/O + +* `writeYAMLmodel` writes an ec reaction without enzymes (a row of zeros in `model.ec.rxnEnzMat`) with no `enzymes` entry (was: `- enzymes: !!omap` with nothing under it, which is not valid YAML). `readYAMLmodel` reads a missing `enzymes` entry as a reaction without enzymes, and reads `- enzymes: !!omap` with no entries and `- enzymes: !!omap []` from files written by earlier versions. +* `readYAMLmodel` reads notes maps (as written by cobrapy and raven-toolbox) into `metNotes` and `rxnNotes` (was: the error "Unknown entry"), reads folded scalars whose continuation starts with `-`, and keeps one `model.subSystems` entry per reaction (was: empty entries were dropped, so later subsystems sat on the wrong reaction). +* `exportForGit` raises `RAVEN:notMainBranch` when a RAVEN checkout is on a branch other than `main` or `master`. +* `writeYAMLmodel` writes the enzymes of each `ec-rxns` entry indented under `- enzymes` (was: at the indentation of the key, which ruamel.yaml could not read), quotes the YAML 1.1 booleans (`y`, `n`, `yes`, `no`, `on`, `off` in any case), and writes non-integers with the shortest digits that read back exactly. `printOrange` prints `\n` as a line break. +* `exportForGit`: the txt table is headed `Rxn id`, writes bounds and objective with the digits needed to read them back and pads no field. The output folder is created with its parents also when `subDirs` is false, and the logical flags are validated. + +### Manipulation + +* `mergeCompartments`: a merged metabolite is the copy already in compartment `s`, if any, + otherwise the copy whose id sorts first, with every field of that copy (was: the first copy + in model order), so `ACCOA_c` and `ACCOA_m` become `ACCOA_c` whatever their order. +* `simplifyModel` with `constrainReversible`: on a model without a reversible reaction this step + changes nothing (was: an error "logical indices ... outside of the array bounds", as + `getAllowedBounds` took the empty reaction list for all reactions). `prepINITModel` therefore + runs on a model whose reactions are all irreversible after the first simplification, with the + result of raven-toolbox's `prep_init_model`. +* `removeGenes`: the help states that, when at least one of the requested genes is in the model, + genes no remaining reaction uses are removed as well; when none is, no gene, reaction or bound + changes. + +### Gap-filling + +* `gapFillTopological`: without `targets` and without an objective reaction it raises + `RAVEN:badInput` (was: an error without identifier). Help states the default seeds and + targets. +* `checkProduction` with `checkNeededForProduction`: the returned `model` holds excretion + reactions only (was: the excretion reaction of the last metabolite in `notProduced` was + returned as an uptake reaction). This matters for code that uses the fifth output. + +### Curation + +* `renameModelGenes` with a file path as `geneTable`: the table is read and the genes are + renamed (was: an error `MATLAB:invalidConversion` when the message that names the file + was printed). + +### Queries + +* `getIndexes` with type `'metnames'` and `returnLogical` true: raises `RAVEN:badInput` (was: + the error "Unable to use a value of type cell as an index"). A name can match several + metabolites, so no single logical vector is defined. + +### Analysis + +* `findGeneDeletions` with `'dgd'`: `grRatioMuts` has one element per gene pair, as the help + states (was: twice as many, the second half zero). This matters for code that indexes + `grRatioMuts` by the number of its elements. + +### Context-specific models (ftINIT / tINIT) + +* `ftINIT` with `resolveTies`: each main-extraction step takes its fluxes from the + least-total-flux solution for the tie-broken on/off state, so the direction and magnitude + at which the next step forces a reaction do not depend on the solver's vertex. + That phase stops at a relative or absolute gap of 1e-6. +* `ftINIT` with `resolveTies`: among the least-total-flux solutions, each main-extraction + step takes its fluxes from the one with the least id-rank-weighted backward flux, so the + direction of a flux that carries the same total either way no longer depends on the + solver. The id-rank phases rank a merged reaction by the smallest original reaction id in + its group, so they do not depend on which member names it. +* `ftINITInternalAlg` with `resolveTies`: when the primary solve stops at the time limit, the + tie-break phases are skipped and the primary solution is kept, as in raven-toolbox (was: the + phases ran on the unproven incumbent). The count and id-rank phases get half of + `params.TimeLimit` each, rounded down and at least 1 s (was: the full limit). +* `ftINIT` with `resolveTies`: the last step skips the minimal-flux and direction phases, as + no step reads its fluxes (new `ftINITInternalAlg` argument `pinFluxes`). The reaction set + does not change; the fluxes and `metProduction` of the last step are those of the id-rank + phase. +* `ftINITInternalAlg`: new optional positional arguments `rankKeys` (14th, the key each + reaction is sorted by in the id-rank phases, default `model.rxns`) and `pinFluxes` (15th, + default `true`). The "tie resolution did not converge" warning of `resolveTies` also names + `phase2c-minflux` and `phase2d-direction` when the minimal-flux or direction phase does not + prove optimal. +* `mergeLinear`: groups are numbered 1, 2, ... in the order of the first reaction of each + group in `origRxnIds` (was: the order in which the reactions were merged). +* `mergeLinear`: a reaction flipped after the first merge pass is the one marked in + `reversedRxns` (was: the reaction at the same index of the original model). + +### Utilities + +* `grRuleToDNF`: help states the grammar it reads, that of `parseGrRule` (`and`/`or` in any + case, `&`, `&&`, `|`, `||`, operators as whole words, so `G1&G2` is one gene id), and that a + malformed rule raises `RAVEN:badGrRule`. +* `checkRaven`: when the binaries cannot be downloaded, the message names the `RAVEN--full.zip` bundle attached to each release for an offline install (was: "the `*-binaries` RAVEN release"). + +### Homology and curation + +* `getModelFromHomology` has the options `complexPolicy` (`flag`, `keep` or `drop`: how a reaction is treated when an `and`-linked subunit has no ortholog), `keepGeneFree` (reactions of the template without a grRule are transferred) and `preserveNotes` (`rxnNotes` and `rxnConfidenceScores` of the template are kept; `rxnReferences` are always kept). A gene that several template genes reach appears once per grRule. The draft has no version, date or description, and the descriptive annotation of the template is not copied (was: those of the templates). A preferred order that removes no gene is accepted (was: an error). +* `getModelFromHomology` raises an error that names the gene ids of the new organism that split in a grRule (whitespace, parentheses, `|` or `&`, for example the UniProt id `sp|P33284|HXK_KLULA`) (was: the ids were accepted). +* New: `hitsFromSuppOrth` reads a `suppOrth.tsv` into a blast structure for `getModelFromHomology`, in both directions, with filters on support, predictors, identity and E-value and the option `proteinToGene` for a protein to gene mapping. `runSuppOrth` runs the `suppOrth` command and reads its output. +* `curateModelFromTables` reads the tables by header name in any order, with optional columns, and writes every supplied field to existing entities (was: `metNotes` and `rxnReferences` were not written). The MIRIAM columns follow the core columns, and grRules go to the right reaction when existing and new reactions are mixed. An empty grRules, lb, ub or rev cell leaves the existing value; the new option `overwriteWithEmpty` (default `true`) decides whether other empty cells clear a field. Cells are read without evaluating text: numeric cells accept plain decimals, `Inf` and `NaN`, and lb, ub and rev accept `true` and `false`; commas, hexadecimal numbers and expressions raise `RAVEN:badInput`. A file, column, gene-rule or repeated-id error raises `RAVEN:badInput`. +* `scaleBiomassFraction` raises an error when the balancing component would get a fraction of 0 or less (was: the balancing pseudoreaction was inverted), when it has no current fraction (was: balancing was skipped) or when it is not a component of the biomass. The new argument `targetTotal` sets the balanced total of the biomass. +* `setGAM` and `scaleBiomassPseudoreaction` raise an error before the model changes: `setGAM` when the NGAM reaction is missing and `scaleBiomassPseudoreaction` when the proton metabolite is missing. `setGAM` refuses `ngamValue` without `ngamRxn`, and an empty `ngamRxn` counts as not given. +* `sortModel` keeps the directions of the reactions unless `sortReversible` is requested (was: the default sorted them). `generateNewIds` takes the numerically largest id and counts only finite integer suffixes. +* `replaceMets`: the final `contractModel` call is restricted to the surviving metabolite (was: unrelated duplicate reactions were merged as well). +* `addIdentifierPrefix` prefixes whole gene ids in grRules (was: an id with a dash, dot or colon, for example `5-HT1A`, got a prefix at every word start, so `exportModel` failed). +* `getIndexes` with an ambiguous `name[comp]` raises an error that names the matching metabolites. +* `gapFillMILP` adds database reactions only and does not reverse draft reactions, which keep their bounds. The second output `reversedRxns` and the option `weights` are removed. `bigM` replaces infinite bounds of universal reactions; a finite bound is used as it is (was: bounds capped at `bigM`). +* `prepINITModel` raises `RAVEN:badInput` when the task structure has no `id` field or a non-text id. +* `getKEGGModelForOrganism` works with the kegg118 reference model, which has no `rxnNotes` (was: an error when adding the provenance note). +* `gapReport` reports the metabolites that are produced or consumed with the boundary closed for every model (was: only for models with an `unconstrained` field). It closes the reactions of `getExchangeRxns` when the model has no `unconstrained` field, and stops with `RAVEN:infeasibleModel` when the model is infeasible under its own bounds or with its boundary closed, for example with a forced ATP maintenance flux. + +### Solvers + +* `optimizeProb` returns the logical `res.hitTimeLimit` for every solver. `runINIT` and `ftINITInternalAlg` raise "Time limit reached without finding a solution" (was: "The problem is infeasible" with every solver except Gurobi). `getMinNrFluxes` returns exit code -2 for a solution that is not proven optimal at the time limit (was: 1 with Gurobi), and -1 with a warning when no solution came back. +* `optimizeProb` solves MILPs, including those of `gapFillMILP`, `fillGaps`, `getMinimalMedium`, `getMinNrFluxes`, `ftINIT` and `runINIT`, with the bundled GLPK and on the GLPK of the COBRA Toolbox, with a `RAVEN:glpkMILP` warning (was: refused). + +### Sampling and reporting + +* `reporterMetabolites` uses the population standard deviation for the background, which gives the scores of raven-toolbox (was: the sample standard deviation). The score is 0 when the spread of the background is 0 or at rounding level, P-values outside [0, 1] are refused, and metabolites without a score are dropped from all result fields. +* `sampleChebyshevCenter` passes a sparse constraint matrix, so `randomSampling` with the method `'chrr'` works with Gurobi (was: Gurobi rejected the dense matrix). +* `sampleACHR` corrects the flux vectors towards `S*v = 0` every few steps and before each sample, and keeps them within the bounds. It raises the warning `sampleACHR:steadyStateNotRestored` when a sample keeps a residual above 1e-9 relative (was: the residual of `S*v` grew exponentially, up to 9 after 100 steps). +* `haveFlux` returns all reactions as blocked when the simplification removes every reaction (was: a GLPK error on an empty matrix). + +### Tasks + +* `parseTaskList` reads `false` and `0` as false, trims `;` lists, ignores the case of headers, keeps empty CHANGED bounds as `NaN`, and reads `no` as false. `fitTasks` leaves a bound unchanged when the CHANGED LB or UB is `NaN`. An absent IN LB, IN UB, OUT LB or OUT UB column gives the default bounds 0 and 1000. Comment rows and orphan rows are skipped with a warning, a non-numeric bound raises `RAVEN:badInput`, and a binary `.xls` file is refused with `RAVEN:badInput` (save it as `.xlsx` or `.txt`). +* `checkTasks` warns when a task metabolite matches several model metabolites. `removeLowScoreGenes` keeps the first gene of the rule on a tie. +* `ftINIT` removes the genes of removed reactions from the output. + +### Documentation + +* Help blocks of 176 files are rewritten in impersonal, present-tense wording with British spelling; the code is unchanged. Statements that differed from the code are corrected, for example `simplifyModel` `deleteDuplicates` (first occurrence kept), `removeBadRxns` `refModel`, `replaceMets` outputs, `getMinNrFluxes` `params`, `checkRaven` `developMode` and the `downloadRavenBinaries` offline bundle. `reverseRxns` has the RAVEN 3 help structure. + ## 3.0.0b1 — 2026-09-07 -First beta toward 3.0. Changes below are relative to RAVEN 2.9.3, the last 2.x release: +First beta toward 3.0. Changes below are relative to RAVEN 2.9.3. The last 2.x release is 2.11.4: a reorganized codebase, fewer toolbox dependencies, hardened I/O and MILP algorithms, plus capabilities backported from the `raven-toolbox` (Python) port. diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index 4ffedce5..00000000 --- a/LICENSE.md +++ /dev/null @@ -1,541 +0,0 @@ -GNU GENERAL PUBLIC LICENSE -========================== - -Version 3, 29 June 2007 - -Copyright © 2007 Free Software Foundation, Inc. <> - -Everyone is permitted to copy and distribute verbatim copies of this license -document, but changing it is not allowed. - -## Preamble - -The GNU General Public License is a free, copyleft license for software and other -kinds of works. - -The licenses for most software and other practical works are designed to take away -your freedom to share and change the works. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change all versions of a -program--to make sure it remains free software for all its users. We, the Free -Software Foundation, use the GNU General Public License for most of our software; it -applies also to any other work released this way by its authors. You can apply it to -your programs, too. - -When we speak of free software, we are referring to freedom, not price. Our General -Public Licenses are designed to make sure that you have the freedom to distribute -copies of free software (and charge for them if you wish), that you receive source -code or can get it if you want it, that you can change the software or use pieces of -it in new free programs, and that you know you can do these things. - -To protect your rights, we need to prevent others from denying you these rights or -asking you to surrender the rights. Therefore, you have certain responsibilities if -you distribute copies of the software, or if you modify it: responsibilities to -respect the freedom of others. - -For example, if you distribute copies of such a program, whether gratis or for a fee, -you must pass on to the recipients the same freedoms that you received. You must make -sure that they, too, receive or can get the source code. And you must show them these -terms so they know their rights. - -Developers that use the GNU GPL protect your rights with two steps: (1) assert -copyright on the software, and (2) offer you this License giving you legal permission -to copy, distribute and/or modify it. - -For the developers' and authors' protection, the GPL clearly explains that there is -no warranty for this free software. For both users' and authors' sake, the GPL -requires that modified versions be marked as changed, so that their problems will not -be attributed erroneously to authors of previous versions. - -Some devices are designed to deny users access to install or run modified versions of -the software inside them, although the manufacturer can do so. This is fundamentally -incompatible with the aim of protecting users' freedom to change the software. The -systematic pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we have designed -this version of the GPL to prohibit the practice for those products. If such problems -arise substantially in other domains, we stand ready to extend this provision to -those domains in future versions of the GPL, as needed to protect the freedom of -users. - -Finally, every program is threatened constantly by software patents. States should -not allow patents to restrict development and use of software on general-purpose -computers, but in those that do, we wish to avoid the special danger that patents -applied to a free program could make it effectively proprietary. To prevent this, the -GPL assures that patents cannot be used to render the program non-free. - -The precise terms and conditions for copying, distribution and modification follow. - -## TERMS AND CONDITIONS - -### 0. Definitions. - -“This License” refers to version 3 of the GNU General Public License. - -“Copyright” also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - -“The Program” refers to any copyrightable work licensed under this -License. Each licensee is addressed as “you”. “Licensees” and -“recipients” may be individuals or organizations. - -To “modify” a work means to copy from or adapt all or part of the work in -a fashion requiring copyright permission, other than the making of an exact copy. The -resulting work is called a “modified version” of the earlier work or a -work “based on” the earlier work. - -A “covered work” means either the unmodified Program or a work based on -the Program. - -To “propagate” a work means to do anything with it that, without -permission, would make you directly or secondarily liable for infringement under -applicable copyright law, except executing it on a computer or modifying a private -copy. Propagation includes copying, distribution (with or without modification), -making available to the public, and in some countries other activities as well. - -To “convey” a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through a computer -network, with no transfer of a copy, is not conveying. - -An interactive user interface displays “Appropriate Legal Notices” to the -extent that it includes a convenient and prominently visible feature that (1) -displays an appropriate copyright notice, and (2) tells the user that there is no -warranty for the work (except to the extent that warranties are provided), that -licensees may convey the work under this License, and how to view a copy of this -License. If the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - -### 1. Source Code. - -The “source code” for a work means the preferred form of the work for -making modifications to it. “Object code” means any non-source form of a -work. - -A “Standard Interface” means an interface that either is an official -standard defined by a recognized standards body, or, in the case of interfaces -specified for a particular programming language, one that is widely used among -developers working in that language. - -The “System Libraries” of an executable work include anything, other than -the work as a whole, that (a) is included in the normal form of packaging a Major -Component, but which is not part of that Major Component, and (b) serves only to -enable use of the work with that Major Component, or to implement a Standard -Interface for which an implementation is available to the public in source code form. -A “Major Component”, in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system (if any) on which -the executable work runs, or a compiler used to produce the work, or an object code -interpreter used to run it. - -The “Corresponding Source” for a work in object code form means all the -source code needed to generate, install, and (for an executable work) run the object -code and to modify the work, including scripts to control those activities. However, -it does not include the work's System Libraries, or general-purpose tools or -generally available free programs which are used unmodified in performing those -activities but which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for the work, and -the source code for shared libraries and dynamically linked subprograms that the work -is specifically designed to require, such as by intimate data communication or -control flow between those subprograms and other parts of the work. - -The Corresponding Source need not include anything that users can regenerate -automatically from other parts of the Corresponding Source. - -The Corresponding Source for a work in source code form is that same work. - -### 2. Basic Permissions. - -All rights granted under this License are granted for the term of copyright on the -Program, and are irrevocable provided the stated conditions are met. This License -explicitly affirms your unlimited permission to run the unmodified Program. The -output from running a covered work is covered by this License only if the output, -given its content, constitutes a covered work. This License acknowledges your rights -of fair use or other equivalent, as provided by copyright law. - -You may make, run and propagate covered works that you do not convey, without -conditions so long as your license otherwise remains in force. You may convey covered -works to others for the sole purpose of having them make modifications exclusively -for you, or provide you with facilities for running those works, provided that you -comply with the terms of this License in conveying all material for which you do not -control copyright. Those thus making or running the covered works for you must do so -exclusively on your behalf, under your direction and control, on terms that prohibit -them from making any copies of your copyrighted material outside their relationship -with you. - -Conveying under any other circumstances is permitted solely under the conditions -stated below. Sublicensing is not allowed; section 10 makes it unnecessary. - -### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - -No covered work shall be deemed part of an effective technological measure under any -applicable law fulfilling obligations under article 11 of the WIPO copyright treaty -adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention -of such measures. - -When you convey a covered work, you waive any legal power to forbid circumvention of -technological measures to the extent such circumvention is effected by exercising -rights under this License with respect to the covered work, and you disclaim any -intention to limit operation or modification of the work as a means of enforcing, -against the work's users, your or third parties' legal rights to forbid circumvention -of technological measures. - -### 4. Conveying Verbatim Copies. - -You may convey verbatim copies of the Program's source code as you receive it, in any -medium, provided that you conspicuously and appropriately publish on each copy an -appropriate copyright notice; keep intact all notices stating that this License and -any non-permissive terms added in accord with section 7 apply to the code; keep -intact all notices of the absence of any warranty; and give all recipients a copy of -this License along with the Program. - -You may charge any price or no price for each copy that you convey, and you may offer -support or warranty protection for a fee. - -### 5. Conveying Modified Source Versions. - -You may convey a work based on the Program, or the modifications to produce it from -the Program, in the form of source code under the terms of section 4, provided that -you also meet all of these conditions: - -* **a)** The work must carry prominent notices stating that you modified it, and giving a -relevant date. -* **b)** The work must carry prominent notices stating that it is released under this -License and any conditions added under section 7. This requirement modifies the -requirement in section 4 to “keep intact all notices”. -* **c)** You must license the entire work, as a whole, under this License to anyone who -comes into possession of a copy. This License will therefore apply, along with any -applicable section 7 additional terms, to the whole of the work, and all its parts, -regardless of how they are packaged. This License gives no permission to license the -work in any other way, but it does not invalidate such permission if you have -separately received it. -* **d)** If the work has interactive user interfaces, each must display Appropriate Legal -Notices; however, if the Program has interactive interfaces that do not display -Appropriate Legal Notices, your work need not make them do so. - -A compilation of a covered work with other separate and independent works, which are -not by their nature extensions of the covered work, and which are not combined with -it such as to form a larger program, in or on a volume of a storage or distribution -medium, is called an “aggregate” if the compilation and its resulting -copyright are not used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work in an aggregate -does not cause this License to apply to the other parts of the aggregate. - -### 6. Conveying Non-Source Forms. - -You may convey a covered work in object code form under the terms of sections 4 and -5, provided that you also convey the machine-readable Corresponding Source under the -terms of this License, in one of these ways: - -* **a)** Convey the object code in, or embodied in, a physical product (including a -physical distribution medium), accompanied by the Corresponding Source fixed on a -durable physical medium customarily used for software interchange. -* **b)** Convey the object code in, or embodied in, a physical product (including a -physical distribution medium), accompanied by a written offer, valid for at least -three years and valid for as long as you offer spare parts or customer support for -that product model, to give anyone who possesses the object code either (1) a copy of -the Corresponding Source for all the software in the product that is covered by this -License, on a durable physical medium customarily used for software interchange, for -a price no more than your reasonable cost of physically performing this conveying of -source, or (2) access to copy the Corresponding Source from a network server at no -charge. -* **c)** Convey individual copies of the object code with a copy of the written offer to -provide the Corresponding Source. This alternative is allowed only occasionally and -noncommercially, and only if you received the object code with such an offer, in -accord with subsection 6b. -* **d)** Convey the object code by offering access from a designated place (gratis or for -a charge), and offer equivalent access to the Corresponding Source in the same way -through the same place at no further charge. You need not require recipients to copy -the Corresponding Source along with the object code. If the place to copy the object -code is a network server, the Corresponding Source may be on a different server -(operated by you or a third party) that supports equivalent copying facilities, -provided you maintain clear directions next to the object code saying where to find -the Corresponding Source. Regardless of what server hosts the Corresponding Source, -you remain obligated to ensure that it is available for as long as needed to satisfy -these requirements. -* **e)** Convey the object code using peer-to-peer transmission, provided you inform -other peers where the object code and Corresponding Source of the work are being -offered to the general public at no charge under subsection 6d. - -A separable portion of the object code, whose source code is excluded from the -Corresponding Source as a System Library, need not be included in conveying the -object code work. - -A “User Product” is either (1) a “consumer product”, which -means any tangible personal property which is normally used for personal, family, or -household purposes, or (2) anything designed or sold for incorporation into a -dwelling. In determining whether a product is a consumer product, doubtful cases -shall be resolved in favor of coverage. For a particular product received by a -particular user, “normally used” refers to a typical or common use of -that class of product, regardless of the status of the particular user or of the way -in which the particular user actually uses, or expects or is expected to use, the -product. A product is a consumer product regardless of whether the product has -substantial commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - -“Installation Information” for a User Product means any methods, -procedures, authorization keys, or other information required to install and execute -modified versions of a covered work in that User Product from a modified version of -its Corresponding Source. The information must suffice to ensure that the continued -functioning of the modified object code is in no case prevented or interfered with -solely because modification has been made. - -If you convey an object code work under this section in, or with, or specifically for -use in, a User Product, and the conveying occurs as part of a transaction in which -the right of possession and use of the User Product is transferred to the recipient -in perpetuity or for a fixed term (regardless of how the transaction is -characterized), the Corresponding Source conveyed under this section must be -accompanied by the Installation Information. But this requirement does not apply if -neither you nor any third party retains the ability to install modified object code -on the User Product (for example, the work has been installed in ROM). - -The requirement to provide Installation Information does not include a requirement to -continue to provide support service, warranty, or updates for a work that has been -modified or installed by the recipient, or for the User Product in which it has been -modified or installed. Access to a network may be denied when the modification itself -materially and adversely affects the operation of the network or violates the rules -and protocols for communication across the network. - -Corresponding Source conveyed, and Installation Information provided, in accord with -this section must be in a format that is publicly documented (and with an -implementation available to the public in source code form), and must require no -special password or key for unpacking, reading or copying. - -### 7. Additional Terms. - -“Additional permissions” are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. Additional -permissions that are applicable to the entire Program shall be treated as though they -were included in this License, to the extent that they are valid under applicable -law. If additional permissions apply only to part of the Program, that part may be -used separately under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - -When you convey a copy of a covered work, you may at your option remove any -additional permissions from that copy, or from any part of it. (Additional -permissions may be written to require their own removal in certain cases when you -modify the work.) You may place additional permissions on material, added by you to a -covered work, for which you have or can give appropriate copyright permission. - -Notwithstanding any other provision of this License, for material you add to a -covered work, you may (if authorized by the copyright holders of that material) -supplement the terms of this License with terms: - -* **a)** Disclaiming warranty or limiting liability differently from the terms of -sections 15 and 16 of this License; or -* **b)** Requiring preservation of specified reasonable legal notices or author -attributions in that material or in the Appropriate Legal Notices displayed by works -containing it; or -* **c)** Prohibiting misrepresentation of the origin of that material, or requiring that -modified versions of such material be marked in reasonable ways as different from the -original version; or -* **d)** Limiting the use for publicity purposes of names of licensors or authors of the -material; or -* **e)** Declining to grant rights under trademark law for use of some trade names, -trademarks, or service marks; or -* **f)** Requiring indemnification of licensors and authors of that material by anyone -who conveys the material (or modified versions of it) with contractual assumptions of -liability to the recipient, for any liability that these contractual assumptions -directly impose on those licensors and authors. - -All other non-permissive additional terms are considered “further -restrictions” within the meaning of section 10. If the Program as you received -it, or any part of it, contains a notice stating that it is governed by this License -along with a term that is a further restriction, you may remove that term. If a -license document contains a further restriction but permits relicensing or conveying -under this License, you may add to a covered work material governed by the terms of -that license document, provided that the further restriction does not survive such -relicensing or conveying. - -If you add terms to a covered work in accord with this section, you must place, in -the relevant source files, a statement of the additional terms that apply to those -files, or a notice indicating where to find the applicable terms. - -Additional terms, permissive or non-permissive, may be stated in the form of a -separately written license, or stated as exceptions; the above requirements apply -either way. - -### 8. Termination. - -You may not propagate or modify a covered work except as expressly provided under -this License. Any attempt otherwise to propagate or modify it is void, and will -automatically terminate your rights under this License (including any patent licenses -granted under the third paragraph of section 11). - -However, if you cease all violation of this License, then your license from a -particular copyright holder is reinstated (a) provisionally, unless and until the -copyright holder explicitly and finally terminates your license, and (b) permanently, -if the copyright holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - -Moreover, your license from a particular copyright holder is reinstated permanently -if the copyright holder notifies you of the violation by some reasonable means, this -is the first time you have received notice of violation of this License (for any -work) from that copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - -Termination of your rights under this section does not terminate the licenses of -parties who have received copies or rights from you under this License. If your -rights have been terminated and not permanently reinstated, you do not qualify to -receive new licenses for the same material under section 10. - -### 9. Acceptance Not Required for Having Copies. - -You are not required to accept this License in order to receive or run a copy of the -Program. Ancillary propagation of a covered work occurring solely as a consequence of -using peer-to-peer transmission to receive a copy likewise does not require -acceptance. However, nothing other than this License grants you permission to -propagate or modify any covered work. These actions infringe copyright if you do not -accept this License. Therefore, by modifying or propagating a covered work, you -indicate your acceptance of this License to do so. - -### 10. Automatic Licensing of Downstream Recipients. - -Each time you convey a covered work, the recipient automatically receives a license -from the original licensors, to run, modify and propagate that work, subject to this -License. You are not responsible for enforcing compliance by third parties with this -License. - -An “entity transaction” is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an organization, or -merging organizations. If propagation of a covered work results from an entity -transaction, each party to that transaction who receives a copy of the work also -receives whatever licenses to the work the party's predecessor in interest had or -could give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if the predecessor -has it or can get it with reasonable efforts. - -You may not impose any further restrictions on the exercise of the rights granted or -affirmed under this License. For example, you may not impose a license fee, royalty, -or other charge for exercise of rights granted under this License, and you may not -initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging -that any patent claim is infringed by making, using, selling, offering for sale, or -importing the Program or any portion of it. - -### 11. Patents. - -A “contributor” is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The work thus -licensed is called the contributor's “contributor version”. - -A contributor's “essential patent claims” are all patent claims owned or -controlled by the contributor, whether already acquired or hereafter acquired, that -would be infringed by some manner, permitted by this License, of making, using, or -selling its contributor version, but do not include claims that would be infringed -only as a consequence of further modification of the contributor version. For -purposes of this definition, “control” includes the right to grant patent -sublicenses in a manner consistent with the requirements of this License. - -Each contributor grants you a non-exclusive, worldwide, royalty-free patent license -under the contributor's essential patent claims, to make, use, sell, offer for sale, -import and otherwise run, modify and propagate the contents of its contributor -version. - -In the following three paragraphs, a “patent license” is any express -agreement or commitment, however denominated, not to enforce a patent (such as an -express permission to practice a patent or covenant not to sue for patent -infringement). To “grant” such a patent license to a party means to make -such an agreement or commitment not to enforce a patent against the party. - -If you convey a covered work, knowingly relying on a patent license, and the -Corresponding Source of the work is not available for anyone to copy, free of charge -and under the terms of this License, through a publicly available network server or -other readily accessible means, then you must either (1) cause the Corresponding -Source to be so available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner consistent with -the requirements of this License, to extend the patent license to downstream -recipients. “Knowingly relying” means you have actual knowledge that, but -for the patent license, your conveying the covered work in a country, or your -recipient's use of the covered work in a country, would infringe one or more -identifiable patents in that country that you have reason to believe are valid. - -If, pursuant to or in connection with a single transaction or arrangement, you -convey, or propagate by procuring conveyance of, a covered work, and grant a patent -license to some of the parties receiving the covered work authorizing them to use, -propagate, modify or convey a specific copy of the covered work, then the patent -license you grant is automatically extended to all recipients of the covered work and -works based on it. - -A patent license is “discriminatory” if it does not include within the -scope of its coverage, prohibits the exercise of, or is conditioned on the -non-exercise of one or more of the rights that are specifically granted under this -License. You may not convey a covered work if you are a party to an arrangement with -a third party that is in the business of distributing software, under which you make -payment to the third party based on the extent of your activity of conveying the -work, and under which the third party grants, to any of the parties who would receive -the covered work from you, a discriminatory patent license (a) in connection with -copies of the covered work conveyed by you (or copies made from those copies), or (b) -primarily for and in connection with specific products or compilations that contain -the covered work, unless you entered into that arrangement, or that patent license -was granted, prior to 28 March 2007. - -Nothing in this License shall be construed as excluding or limiting any implied -license or other defenses to infringement that may otherwise be available to you -under applicable patent law. - -### 12. No Surrender of Others' Freedom. - -If conditions are imposed on you (whether by court order, agreement or otherwise) -that contradict the conditions of this License, they do not excuse you from the -conditions of this License. If you cannot convey a covered work so as to satisfy -simultaneously your obligations under this License and any other pertinent -obligations, then as a consequence you may not convey it at all. For example, if you -agree to terms that obligate you to collect a royalty for further conveying from -those to whom you convey the Program, the only way you could satisfy both those terms -and this License would be to refrain entirely from conveying the Program. - -### 13. Use with the GNU Affero General Public License. - -Notwithstanding any other provision of this License, you have permission to link or -combine any covered work with a work licensed under version 3 of the GNU Affero -General Public License into a single combined work, and to convey the resulting work. -The terms of this License will continue to apply to the part which is the covered -work, but the special requirements of the GNU Affero General Public License, section -13, concerning interaction through a network will apply to the combination as such. - -### 14. Revised Versions of this License. - -The Free Software Foundation may publish revised and/or new versions of the GNU -General Public License from time to time. Such new versions will be similar in spirit -to the present version, but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Program specifies that -a certain numbered version of the GNU General Public License “or any later -version” applies to it, you have the option of following the terms and -conditions either of that numbered version or of any later version published by the -Free Software Foundation. If the Program does not specify a version number of the GNU -General Public License, you may choose any version ever published by the Free -Software Foundation. - -If the Program specifies that a proxy can decide which future versions of the GNU -General Public License can be used, that proxy's public statement of acceptance of a -version permanently authorizes you to choose that version for the Program. - -Later license versions may give you additional or different permissions. However, no -additional obligations are imposed on any author or copyright holder as a result of -your choosing to follow a later version. - -### 15. Disclaimer of Warranty. - -THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER -EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE -QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE -DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -### 16. Limitation of Liability. - -IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY -COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS -PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, -INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE -OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE -WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - -### 17. Interpretation of Sections 15 and 16. - -If the disclaimer of warranty and limitation of liability provided above cannot be -given local legal effect according to their terms, reviewing courts shall apply local -law that most closely approximates an absolute waiver of all civil liability in -connection with the Program, unless a warranty or assumption of liability accompanies -a copy of the Program in return for a fee. \ No newline at end of file diff --git a/RAVEN.png b/RAVEN.png deleted file mode 100644 index a411f80b..00000000 Binary files a/RAVEN.png and /dev/null differ diff --git a/README.md b/README.md index 26e597fd..9f07297b 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,45 @@ - - -[![Current release](https://img.shields.io/github/release/SysBioChalmers/RAVEN/all.svg)](https://GitHub.com/SysBioChalmers/RAVEN/releases/) -[![GitHub Discussions](https://img.shields.io/github/discussions-search?query=repo%3Asysbiochalmers%2raven&label=GitHub%20Discussions)](https://github.com/SysBioChalmers/RAVEN/discussions) + + + RAVEN + + +[![Release](https://img.shields.io/github/v/release/CompMetEng/RAVEN?include_prereleases)](https://github.com/CompMetEng/RAVEN/releases/) +[![Documentation](https://readthedocs.org/projects/raven-docs/badge/?version=latest)](https://raven-docs.readthedocs.io/en/latest/) +[![GitHub Discussions](https://img.shields.io/github/discussions-search?query=repo%3ACompMetEng%2FRAVEN&label=GitHub%20Discussions)](https://github.com/CompMetEng/RAVEN/discussions) [![Zenodo](https://zenodo.org/badge/DOI/10.5281/zenodo.3689518.svg)](https://doi.org/10.5281/zenodo.3689518) -[![MATLAB File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://se.mathworks.com/matlabcentral/fileexchange/112330-raven-toolbox) + +**[Documentation](https://raven-docs.readthedocs.io/en/latest/)** · [Installation](https://raven-docs.readthedocs.io/en/latest/installation/index.html) · [Guide](https://raven-docs.readthedocs.io/en/latest/guide/index.html) · [API reference](https://raven-docs.readthedocs.io/en/latest/api/index.html) · [Migrate from RAVEN 2](https://raven-docs.readthedocs.io/en/latest/migrate.html) · [Citations](https://raven-docs.readthedocs.io/en/latest/references.html) ## About RAVEN -The **RAVEN** (Reconstruction, Analysis and Visualization of Metabolic Networks) Toolbox is a software suite for MATLAB that allows for semi-automated reconstruction of genome-scale models (GEMs). It makes use of published models and/or the KEGG database, coupled with extensive gap-filling and quality control features. The software suite also contains methods for visualizing simulation results and omics data, as well as a range of methods for performing simulations and analyzing the results. The software is a useful tool for system-wide data analysis in a metabolic context and for streamlined reconstruction of metabolic networks based on protein homology. +The **RAVEN** (Reconstruction, Analysis and Visualization of Metabolic Networks) Toolbox is a software suite for MATLAB for the semi-automated reconstruction of genome-scale metabolic models (GEMs). It reconstructs from published models or from the KEGG database, with gap-filling and quality control, and it simulates and analyses models and visualises simulation results and omics data. For context-specific model extraction, `ftINIT` is the method to use; `getINITModel` and `runINIT` are the original tINIT implementation, kept to reproduce models built with it. -The latest stable release is **RAVEN 2.11.3**, on the `main` branch. +RAVEN has a Python port, [raven-toolbox](https://github.com/CompMetEng/raven-toolbox), built on [cobrapy](https://github.com/opencobra/cobrapy). The documentation covers both toolboxes, and [RAVEN 3 and raven-toolbox](https://raven-docs.readthedocs.io/en/latest/raven3-vs-raven-toolbox.html) lists the functions that exist in one of them and the functions that return a different result in each. -> **This `develop3` branch is where RAVEN 3 is under active development**, with the upcoming **3.0.0b1** beta as its first release, targeting a final **3.0.0**. RAVEN 3 is a deliberate breaking release: the codebase has been reorganized into functional folders, optional function arguments can now be passed positionally, by name, or both, external binaries and KEGG reference data are fetched on demand instead of bundled in the repository, several MATLAB toolbox dependencies were dropped, and a number of low-value or duplicate functions were removed alongside new capabilities. Until 3.0.0 is tagged, this branch can still change. See the [migration guide](#documentation) for the full list of changes for users upgrading from RAVEN 2. +This repository is where RAVEN 3 is developed. RAVEN 3 is a breaking release: the code is reorganised into functional folders, optional function arguments are passed by position, by name or both, external binaries and KEGG reference data are fetched on demand, several MATLAB toolbox dependencies are dropped, and low-value and duplicate functions are removed. The code can change until 3.0.0 is tagged. The last RAVEN 2 release is 2.11.4, maintained in [SysBioChalmers/RAVEN](https://github.com/SysBioChalmers/RAVEN). [Migrating from RAVEN 2](https://raven-docs.readthedocs.io/en/latest/migrate.html) lists every change for users who upgrade. -For context-specific model extraction, use `ftINIT`; `getINITModel` and `runINIT` are the original tINIT implementation, kept for reproducing models built with it. +## Get started +1. Install RAVEN as described in the [installation instructions](https://raven-docs.readthedocs.io/en/latest/installation/raven.html). The Add-Ons manager of MATLAB installs the latest stable release, which is RAVEN 2; a RAVEN 3 pre-release is installed from this repository, and the instructions describe how. `checkRaven` verifies the installation and the solver. +2. Follow [Getting started](https://raven-docs.readthedocs.io/en/latest/guide/getting-started.html) in the guide, which has MATLAB and Python examples side by side, or open a script in the [`tutorials`](https://github.com/CompMetEng/RAVEN/tree/develop/tutorials) folder. The tutorials come from the original RAVEN paper and are described in [Legacy tutorials](https://raven-docs.readthedocs.io/en/latest/tutorials/index.html). +3. The [GEM reconstruction protocol](https://raven-docs.readthedocs.io/en/latest/protocol/index.html) reconstructs a model for *Hansenula polymorpha* from start to finish. -## Documentation -The information about downloading, installing and developing RAVEN is included in the [Wiki](https://github.com/SysBioChalmers/RAVEN/wiki). The source code documentation is also available [online](http://sysbiochalmers.github.io/RAVEN/doc/). +## Documentation and support -Full user documentation, including a migration guide for users upgrading from RAVEN 2 to RAVEN 3, is published at [raven-docs](https://raven-docs.readthedocs.io/en/latest/). +The installation instructions, the guide, the protocol, the tuned parameter defaults and the API reference for MATLAB and Python are at [raven-docs](https://raven-docs.readthedocs.io/en/latest/). The function help texts in this repository generate the MATLAB part of the API reference. Use [GitHub Discussions](https://github.com/CompMetEng/RAVEN/discussions) for questions and comments, and [issues](https://github.com/CompMetEng/RAVEN/issues) for bug reports. +## Cite us + +If you use RAVEN in your work, please cite: -## Cite Us -If you use RAVEN in your scientific work, please cite: > Wang H, Marcišauskas S, Sánchez BJ, Domenzain I, Hermansson D, Agren R, Nielsen J, Kerkhoven EJ. (2018) RAVEN 2.0: A versatile toolbox for metabolic network reconstruction and a case study on _Streptomyces coelicolor_. PLoS Comput Biol 14(10): e1006541. doi:[10.1371/journal.pcbi.1006541](https://doi.org/10.1371/journal.pcbi.1006541). -All releases are also archived in [Zenodo](https://doi.org/10.5281/zenodo.3689518), so you can cite the specific version of RAVEN used in your study +All releases are archived on [Zenodo](https://doi.org/10.5281/zenodo.3689518), which allows citing the version used. If you use ftINIT, please also cite: -If you use ftINIT in your scientific work, please cite: > Gustafsson J, Anton M, Roshanzamir F, Jörnsten R, Kerkhoven EJ, Robinson JL, Nielsen J. (2023) Generation and analysis of context-specific genome-scale metabolic models derived from single-cell RNA-Seq data. Proc Natl Acad Sci 120(6): e2217868120. doi:[10.1073/pnas.2217868120](https://doi.org/10.1073/pnas.2217868120) -For crediting supporting work, please cite doi:[10.1002/msb.145122](http://msb.embopress.org/content/10/3/721) (`tInit`); doi:[10.1371/journal.pcbi.1000859](http://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1000859) (`randomsampling`). For crediting RAVEN 1, cite doi:[10.1371/journal.pcbi.1002980](http://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1002980). For more details, see [wiki#cite-us](https://github.com/SysBioChalmers/RAVEN/wiki#cite-us). - -## Contact Us -Use [GitHub Discussions](https://github.com/SysBioChalmers/RAVEN/discussions) for support, to ask questions or leave comments. +[Citations](https://raven-docs.readthedocs.io/en/latest/references.html) lists the papers behind individual methods, among them the first RAVEN paper (2013), random sampling and the INIT method. ## Contributing -Contributions are always welcome! Please read the [Contributor guidelines](https://github.com/SysBioChalmers/RAVEN/blob/main/.github/CONTRIBUTING.md) to get started. RAVEN 2.x maintenance happens on the `develop` branch; RAVEN 3 development happens on `develop3`. +Contributions are welcome. Read the [contributing guidelines](https://github.com/CompMetEng/RAVEN/blob/develop/.github/CONTRIBUTING.md) first. RAVEN 3 is developed on the `develop` branch, and RAVEN 2 is maintained in [SysBioChalmers/RAVEN](https://github.com/SysBioChalmers/RAVEN). diff --git a/_config.yml b/_config.yml index 0add0be6..9e2e16f8 100644 --- a/_config.yml +++ b/_config.yml @@ -1,4 +1,4 @@ theme: jekyll-theme-minimal -logo: RAVEN.png +logo: img/raven-logo.png show_downloads: true -keep_files: ["doc/"] +keep_files: ["docs/html/"] diff --git a/analysis/sampleACHR.m b/analysis/sampleACHR.m deleted file mode 100644 index c2432d78..00000000 --- a/analysis/sampleACHR.m +++ /dev/null @@ -1,81 +0,0 @@ -function solutions = sampleACHR(model, nSamples, thinning, warmup, seed) -% sampleACHR Artificially Centered Hit-and-Run flux sampling. -% -% Draws nSamples flux vectors approximately uniformly from the flux polytope -% {v : S*v = 0, lb <= v <= ub}, using the ACHR algorithm (Kaufman & Smith 1998). -% No rounding is performed, so on strongly elongated polytopes (e.g. -% enzyme-constrained models) mixing is slower than sampleCHRR; prefer CHRR there. -% -% Parameters -% ---------- -% model : struct -% a RAVEN model structure. -% nSamples : double -% number of flux vectors to return (default 1000). -% thinning : double -% Markov-chain steps between recorded samples (default 100). -% warmup : double -% nPoints-by-nRxns warmup vertices (default: generated by sampleWarmupPoints). -% seed : double -% random seed for reproducibility (default: current RNG state). -% -% Returns -% ------- -% solutions : double -% nRxns-by-nSamples matrix of sampled flux distributions. -% -% See also -% -------- -% randomSampling, sampleCHRR, sampleWarmupPoints - -if nargin < 2 || isempty(nSamples); nSamples = 1000; end -if nargin < 3 || isempty(thinning); thinning = 100; end -if nargin < 4 || isempty(warmup); warmup = sampleWarmupPoints(model); end -if nargin < 5; seed = []; end -if ~isempty(seed) - rng(seed); -end - -nRxns = numel(model.rxns); -lb = model.lb(:); -ub = model.ub(:); -nW = size(warmup, 1); - -center = mean(warmup, 1)'; % nRxns-by-1 -prev = center; -nTotal = 0; -dirTol = 1e-12; - -solutions = zeros(nRxns, nSamples); -rec = 0; -nSteps = thinning*nSamples; - -for stepno = 1:nSteps - pidx = randi(nW); - delta = warmup(pidx, :)' - center; - - posD = delta > dirTol; - negD = delta < -dirTol; - ubp = ub - prev; - lbp = lb - prev; - % alpha range keeping lb <= prev + alpha*delta <= ub - upperLims = [ubp(posD)./delta(posD); lbp(negD)./delta(negD)]; - lowerLims = [lbp(posD)./delta(posD); ubp(negD)./delta(negD)]; - if isempty(upperLims); tHi = 0; else; tHi = min(upperLims); end - if isempty(lowerLims); tLo = 0; else; tLo = max(lowerLims); end - - if tHi > tLo - alpha = tLo + rand*(tHi - tLo); - prev = prev + alpha*delta; - end - - % Running centre (quasi-Markovian ACHR: centre converges and steadies). - center = (nTotal*center + prev) / (nTotal + 1); - nTotal = nTotal + 1; - - if mod(stepno, thinning) == 0 - rec = rec + 1; - solutions(:, rec) = prev; - end -end -end diff --git a/biomass/scaleBiomassFraction.m b/biomass/scaleBiomassFraction.m deleted file mode 100644 index ba360243..00000000 --- a/biomass/scaleBiomassFraction.m +++ /dev/null @@ -1,65 +0,0 @@ -function model = scaleBiomassFraction(model, biomassConfig, componentName, newValue, varargin) -% scaleBiomassFraction Rescale a biomass component to a target value. -% -% Rescale a biomass component to a target g/gDW value, optionally -% balancing a second component so the total biomass mass stays at 1 g/gDW. -% The MATLAB counterpart of yeast-GEM's legacy scaleBioMass. -% -% Parameters -% ---------- -% model : struct -% RAVEN model struct. -% biomassConfig : struct -% Struct (see getBiomassFractions). -% componentName : char -% Component to rescale. -% newValue : double -% Target fraction in g/gDW. -% -% Name-Value Arguments -% -------------------- -% balanceOut : char -% Second component name to adjust so the biomass total remains 1 -% g/gDW. Empty / omit to skip balancing. -% -% Returns -% ------- -% model : struct -% Modified model. -% -% Examples -% -------- -% model = scaleBiomassFraction(model, biomassConfig, 'protein', 0.5, 'carbohydrate'); -% -% See also -% -------- -% getBiomassFractions - -p=parseRAVENargs(varargin, {'balanceOut',''}); -balanceOut=p.balanceOut; - -fractions = getBiomassFractions(model, biomassConfig); -if ~isfield(fractions, componentName) - error('scaleBiomassFraction:unknownComponent', ... - 'biomassConfig has no component named %s', componentName); -end -current = fractions.(componentName); -if current == 0 - error('scaleBiomassFraction:zeroCurrent', ... - ['Cannot scale %s to %g: current fraction is 0 ' ... - '(pseudoreaction missing or empty).'], componentName, newValue); -end -factor = newValue / current; -model = scaleBiomassPseudoreaction(model, biomassConfig, componentName, factor); - -if ~isempty(balanceOut) - fractions = getBiomassFractions(model, biomassConfig); - total = fractions.total; - balanceCurrent = fractions.(balanceOut); - if balanceCurrent == 0 - return; - end - balanceFactor = (balanceCurrent + (1 - total)) / balanceCurrent; - model = scaleBiomassPseudoreaction(model, biomassConfig, balanceOut, balanceFactor); -end -end diff --git a/checkInstallation.m b/checkInstallation.m index 298453cc..8bfe180b 100644 --- a/checkInstallation.m +++ b/checkInstallation.m @@ -3,10 +3,10 @@ % % Forwards all arguments and outputs to checkRaven. % -% NOTE: this function is run before RAVEN has been added to the MATLAB -% path, so it must not call any other RAVEN functions until checkRaven has -% added RAVEN to the path itself. It cannot use the shared -% utils/deprecationWarning for the same reason. +% This function runs before RAVEN is on the MATLAB path. It therefore +% calls no other RAVEN function until checkRaven has added RAVEN to the +% path, and it does not use the shared deprecationWarning function in +% the utils folder. % % Usage: [currVer, installType] = checkInstallation(developMode, checkBinaries) diff --git a/checkRaven.m b/checkRaven.m index aa462a86..d207bec2 100755 --- a/checkRaven.m +++ b/checkRaven.m @@ -1,35 +1,34 @@ function [currVer, installType] = checkRaven(developMode, checkBinaries) -% checkRaven -% Checks that all necessary functions are installed and working. It also -% checks whether there are any functions -% with overlapping names between RAVEN and other toolboxes or -% user-defined functions, which are accessible from MATLAB pathlist +% checkRaven Check the RAVEN installation and set up the MATLAB path. % -% NOTE: this function is run before RAVEN has been added to the MATLAB -% path, so it must not call any other RAVEN functions until it has added -% RAVEN to the path itself. Its arguments are therefore parsed directly -% rather than via parseRAVENargs. +% Reports the installation type and RAVEN version, adds RAVEN to the +% MATLAB path, tests model import and export, the LP solvers and the +% binary executables, and checks for functions with overlapping names +% between RAVEN and other toolboxes or user-defined functions on the +% MATLAB path. +% +% This function runs before RAVEN is on the MATLAB path. It therefore +% calls no other RAVEN function until it has added RAVEN to the path, and +% it parses its arguments without parseRAVENargs. % % Parameters % ---------- -% developMode logical indicating development mode, which includes -% testing of binaries that are required to update KEGG -% HMMs (default false). If 'versionOnly' is -% specified, only the version is reported as currVer, no -% further installation or tests are performed. -% checkBinaries logical whether non-developMode binaries should be -% checked for functionality. If false, it overwrites -% developMode. Default true. +% developMode 'versionOnly' to return the version in currVer without +% any further installation check or test. Any other +% value runs all checks. Default false. +% checkBinaries logical, true to test that the BLAST+, DIAMOND and +% HMMER binaries execute. If false, the binary check is +% skipped. Default true. % % Output: % currVer current RAVEN version % installType how RAVEN is installed -% 0: via git (as .git folder is found) +% 0: via git (a .git folder is found) % 1: as MATLAB Add-On -% 2: neither of the above, direct download of ZIP file -% This matches the installations mentioned in the wiki: -% https://github.com/SysBioChalmers/RAVEN/wiki/Installation -% 0 = advanced / 1 = easy / 2 = medium +% 2: direct download of a ZIP file +% The values correspond to the installation types +% Advanced (0), Easy (1) and Medium (2) described at +% https://raven-docs.readthedocs.io/en/latest/installation/raven.html % % Usage: [currVer, installType] = checkRaven(developMode) @@ -82,7 +81,7 @@ if ~versionOnly fprintf([currVer '\n']); try - newVer=strtrim(webread('https://raw.githubusercontent.com/SysBioChalmers/RAVEN/main/version.txt')); + newVer=strtrim(webread('https://raw.githubusercontent.com/CompMetEng/RAVEN/main/version.txt')); newVerNum=str2double(strsplit(newVer,'.')); currVerNum=str2double(strsplit(currVer,'.')); for i=1:3 @@ -95,10 +94,10 @@ printOrange(' to get the latest RAVEN release\n'); case 1 printOrange(myStr(' Instructions on how to upgrade',40)) - fprintf('here\n'); + fprintf('here\n'); case 2 printOrange(myStr(' Instructions on how to upgrade',40)) - fprintf('here\n'); + fprintf('here\n'); end break elseif i==3 @@ -166,7 +165,7 @@ fprintf(myStr(' > Checking libSBML version',40)) model = []; try - evalc('model = importModel(fullfile(ravenDir,''tutorial'',''empty.xml''));'); + evalc('model = importModel(fullfile(ravenDir,''tutorials'',''data'',''empty.xml''));'); try libSBMLver=OutputSBML_RAVEN; % Only works in libSBML 5.17.0+ fprintf([libSBMLver.libSBML_version_string '\n']); @@ -193,7 +192,7 @@ end reportCheck(' > Export SBML format', @() exportModel(model, fullfile(tmpDir,'model.xml'))); -reportCheck(' > Import YAML format', @() readYAMLmodel(fullfile(ravenDir,'tutorial','empty.yml'))); +reportCheck(' > Import YAML format', @() readYAMLmodel(fullfile(ravenDir,'tutorials','data','empty.yml'))); reportCheck(' > Export YAML format', @() writeYAMLmodel(model, fullfile(tmpDir,'model.yml'))); reportCheck(' > Export Excel format', @() exportToExcelFormat(model, fullfile(tmpDir,'model.xlsx'))); @@ -251,14 +250,14 @@ printOrange(' Skipping check of binary executables\n') else % The BLAST+/DIAMOND/HMMER binaries are downloaded on demand from - % raven-data; fetch any that are missing for this platform before + % toolbox-assets; fetch any that are missing for this platform before % checking. This doubles as the up-front "prefetch" for users who want % everything ready after installation. try downloadRavenBinaries({'blast+','diamond','hmmer'}); catch ME - printOrange([' Could not download binaries from raven-data: ' ME.message '\n' ... - ' (offline? fetch the "*-binaries" RAVEN release for an offline install)\n']); + printOrange([' Could not download binaries from toolbox-assets: ' ME.message '\n' ... + ' (offline? install from the RAVEN--full.zip bundle attached to each RAVEN release)\n']); end % Run each bundled binary with a version/help flag to confirm it % executes on this system, rather than running the full test cases. @@ -294,13 +293,13 @@ function testSolver(solver, model) function testBinary(ravenDir, tool, binName, versionArg) % Run a bundled binary with versionArg to confirm it executes; error if not. if ispc - binEnd = '.exe'; % native Windows builds (raven-data windows-x86_64 ZIPs) + binEnd = '.exe'; % native Windows builds (toolbox-assets windows-x86_64 ZIPs) elseif ismac binEnd = '.mac'; else binEnd = ''; end -cmd = ['"' fullfile(ravenDir,'software',tool,[binName binEnd]) '" ' versionArg]; +cmd = ['"' fullfile(ravenDir,'src','external',tool,[binName binEnd]) '" ' versionArg]; [status,~] = system(cmd); if status ~= 0 error('%s did not execute (exit status %d)', binName, status); @@ -339,7 +338,7 @@ function testBinary(ravenDir, tool, binName, versionArg) status = 0; % No need to run on Windows return; end -binDir = fullfile(ravenDir,'software'); +binDir = fullfile(ravenDir,'src','external'); % blast+/diamond/hmmer may be present from an on-demand download % (downloadRavenBinaries already chmods those) or from the offline bundle; diff --git a/curation/curateModelFromTables.m b/curation/curateModelFromTables.m deleted file mode 100644 index 2cda31fe..00000000 --- a/curation/curateModelFromTables.m +++ /dev/null @@ -1,363 +0,0 @@ -function newModel=curateModelFromTables(model,metsInfo,varargin) -% curateModelFromTables Curate or add mets, rxns and genes from tables. -% -% Curate existing and/or add new metabolites, reactions and genes from -% tabular data files. -% -% If the *.tsv files contain metabolites, reactions and/or genes that are -% already present in the model, then information in the model will be -% overwritten. Note that this includes empty annotations in the *.tsv -% files! Metabolites are matched by metaboliteName[comp]; reactions by the -% stoichiometry of its reactants and products; genes by their gene name. -% This function can therefore be used to add new entities in the model, or -% curate those already existing in the model. -% -% Parameters -% ---------- -% model : struct -% RAVEN model structure to be curated. -% metsInfo : char -% Path to a *.tsv file with metabolite information, or 'none' to skip -% metabolite curation. Columns: metNames, comps, formula, charge, -% inchi, metNotes, then any number of MIRIAM-namespace columns. -% -% Name-Value Arguments -% -------------------- -% genesInfo : char -% Path to a *.tsv file with gene information, or 'none'. Columns: -% genes, geneShortNames, then MIRIAM. -% rxnsCoeffs : char -% Path to a *.tsv file with reaction stoichiometric coefficients, or -% 'none'. Columns: rxnIdx, rxnNames, metNames, comps, coefficient. One -% row per (reaction, metabolite) pair. -% rxnsInfo : char -% Path to a *.tsv file with reaction information, or 'none'. Columns: -% rxnIdx, rxnNames, grRules, lb, ub, rev, subSystems, eccodes, -% rxnNotes, rxnReferences, rxnConfidenceScores, then MIRIAM. -% metPrefix : char -% Prefix used to mint fresh metabolite ids (e.g. 's_' for yeast-GEM, -% 'M_' for the BiGG default) (default 'M_'). -% rxnPrefix : char -% Prefix used to mint fresh reaction ids (default 'R_'). -% -% Returns -% ------- -% newModel : struct -% Curated RAVEN model structure. -% -% Examples -% -------- -% newModel = curateModelFromTables(model, metsInfo, genesInfo, ... -% rxnsCoeffs, rxnsInfo, metPrefix, rxnPrefix); -% -% Notes -% ----- -% The 'everything after the core columns is MIRIAM' convention applies to -% all three info tables: any column whose header is not one of the listed -% core fields is treated as a MIRIAM annotation namespace and stored on -% the matching entity. - -p=parseRAVENargs(varargin, {'genesInfo','none'; 'rxnsCoeffs','none'; 'rxnsInfo','none'; 'metPrefix',[]; 'rxnPrefix',[]}); -genesInfo=p.genesInfo; -rxnsCoeffs=p.rxnsCoeffs; -rxnsInfo=p.rxnsInfo; -metPrefix=p.metPrefix; -if isempty(metPrefix) - metPrefix = 'M_'; -end -rxnPrefix=p.rxnPrefix; -if isempty(rxnPrefix) - rxnPrefix = 'R_'; -end -if xor(strcmp(rxnsCoeffs,'none'), strcmp(rxnsInfo,'none')) - error('Provide both a ''rxnsInfo'' and a ''rxnsCoeffs'' file') -end -newModel=model; - -%% Metabolites -if ~strcmp(metsInfo,'none') - fid = fopen(metsInfo); - raw = textscan(fid,['%q' repmat(' %q',1,17)],'Delimiter','\t'); - fclose(fid); - metsToAdd.metNames = raw{1}(2:end); - metsToAdd.compartments = raw{2}(2:end); - metsToAdd.metFormulas = raw{3}(2:end); - metsToAdd.metCharges = cellfun(@str2num,raw{4}(2:end),'UniformOutput',false); - emptyEntry = cellfun(@isempty,metsToAdd.metCharges); - if all(emptyEntry) - metsToAdd = rmfield(metsToAdd,'metCharges'); - elseif any(emptyEntry) % If some charges are given, assume 0 for those without charges specified - metsToAdd.metCharges(emptyEntry) = {0}; - metsToAdd.metCharges = cell2mat(metsToAdd.metCharges); - else - metsToAdd.metCharges = cell2mat(metsToAdd.metCharges); - end - metsToAdd.inchis = raw{5}(2:end); - metsToAdd.metNotes = raw{6}(2:end); - - % Check if metabolite already exists (check by metName[comp]) - existingMets = []; - existingMetsIdx = []; - newMets = []; - for i=1:numel(metsToAdd.metNames) - metIdx = find(strcmp(newModel.metNames,metsToAdd.metNames{i})); - existMet = strcmp(metsToAdd.compartments{i},newModel.comps(newModel.metComps(metIdx))); - if any(existMet) - existingMets = [existingMets, i]; - existingMetsIdx = [existingMetsIdx, metIdx(existMet)]; - else - newMets = [newMets, i]; - end - end - - % Overwrite annotation of existing entries - if any(existingMets) - if isfield(newModel,'metFormulas') - newModel.metFormulas(existingMetsIdx) = metsToAdd.metFormulas(existingMets); - end - if isfield(newModel,'metCharges') && isfield(metsToAdd,'metCharges') - newModel.metCharges(existingMetsIdx) = metsToAdd.metCharges(existingMets); - end - if isfield(newModel,'inchis') - newModel.inchis(existingMetsIdx) = metsToAdd.inchis(existingMets); - end - newModel = extractAndAddMiriam(newModel,raw(7:end),existingMets,existingMetsIdx,'met'); - warning(['The following metabolites are already present in the model, '... - 'their annotation will be overwritten to match the metsInfo file. '... - 'If you do not particularly want to curate their annotations, it '... - 'would be better to removes these metabolites from metsInfo:\n\t\t%s'],... - strjoin(strcat(metsToAdd.metNames(existingMets),'[',metsToAdd.compartments(existingMets),']'),'\n\t\t')); - end - - % Continue with new metabolites - if numel(newMets)>0 - metsToAdd.metNames(existingMets) = []; - metsToAdd.compartments(existingMets) = []; - metsToAdd.metFormulas(existingMets) = []; - if all(cellfun(@isempty,metsToAdd.metFormulas)) - metsToAdd = rmfield(metsToAdd,'metFormulas'); - end - if isfield(metsToAdd,'metCharges') - metsToAdd.metCharges(existingMets) = []; - end - metsToAdd.inchis(existingMets) = []; - if all(cellfun(@isempty,metsToAdd.inchis)) - metsToAdd = rmfield(metsToAdd,'inchis'); - end - metsToAdd.metNotes(existingMets) = []; - if all(cellfun(@isempty,metsToAdd.metNotes)) - metsToAdd = rmfield(metsToAdd,'metNotes'); - end - % Add metabolites - newModel = addMets(newModel,metsToAdd,true,metPrefix); - addedIdx = numel(newModel.mets)-numel(newMets)+1:numel(newModel.mets); - newModel = extractAndAddMiriam(newModel,raw(7:end),newMets,addedIdx,'met'); - end -end -%% Genes -if ~strcmp(genesInfo,'none') - % Gather all data, first about reaction stoichiometries... - fid = fopen(genesInfo); - raw = textscan(fid,['%q' repmat(' %q',1,10)],'Delimiter','\t'); - fclose(fid); - genesToAdd.genes = raw{1}(2:end); - genesToAdd.geneShortNames = raw{2}(2:end); - - [~,notNewGene,existingGene] = intersect(genesToAdd.genes,newModel.genes); - if ~isempty(notNewGene) - warning(['The following genes are already present in the model, their annotation '... - 'will be overwritten to match the genesInfo file: \n\t\t%s'],... - strjoin(genesToAdd.genes(notNewGene),'\n\t\t')); - if isfield(newModel,'geneShortNames') - newModel.geneShortNames(existingGene) = genesToAdd.geneShortNames(notNewGene); - end - newModel = extractAndAddMiriam(newModel,raw(3:end),notNewGene,existingGene,'gene'); - end - - % Continue with new genes - toAdd = 1:numel(genesToAdd.genes); - toAdd(notNewGene) = []; - if ~isempty(toAdd) - genesToAdd.genes = genesToAdd.genes(toAdd); - genesToAdd.geneShortNames = genesToAdd.geneShortNames(toAdd); - if all(cellfun(@isempty,genesToAdd.geneShortNames)) - genesToAdd = rmfield(genesToAdd,'geneShortNames'); - end - % Add genes - newModel = addGenesRaven(newModel,genesToAdd); - addedIdx = numel(newModel.genes)-numel(toAdd)+1:numel(newModel.genes); - newModel = extractAndAddMiriam(newModel,raw(3:end),toAdd,addedIdx,'gene'); - end -end -%% Reactions -if ~any(strcmp({rxnsCoeffs,rxnsInfo},'none')) - % Gather all data, first about reaction stoichiometries... - fid = fopen(rxnsCoeffs); - raw = textscan(fid,'%q %q %q %q %f','Delimiter','\t','HeaderLines',1); - fclose(fid); - rxnCheck.coeffsIdx = str2double(raw{1}); - rxns.rxnNames = raw{2}; - rxns.metNames = raw{3}; - rxns.comps = raw{4}; - rxns.coeffs = raw{5}; - rxnCheck.coeffs = strcat(raw{1},'***',rxns.rxnNames); - - % ... and then additional reaction-specific data. - fid = fopen(rxnsInfo); - raw = textscan(fid,['%q' repmat(' %q',1,20)],'Delimiter','\t'); - fclose(fid); - rxnCheck.rxnsIdx = str2double(raw{1}(2:end)); - rxnCheck.rxns = strcat(raw{1}(2:end),'***',raw{2}(2:end)); - - notMatching = setxor(rxnCheck.coeffs,rxnCheck.rxns); - if numel(notMatching)>1 - error(['The following reactions ánd/or their indices are not matched '... - 'between the rxnsInfo and rxnsCoeffs files:\n\t\t%s'],... - strjoin(regexprep(notMatching,'^\d+***',''),'\n\t\t')) - end - - rxnsToAdd.rxnNames = raw{2}(2:end); - rxnsToAdd.grRules = raw{3}(2:end); - rxnsToAdd.lb = cellfun(@str2num,raw{4}(2:end)); - rxnsToAdd.ub = cellfun(@str2num,raw{5}(2:end)); - rxnsToAdd.rev = cellfun(@str2num,raw{6}(2:end)); - rxnsToAdd.subSystems = raw{7}(2:end); - rxnsToAdd.eccodes = raw{8}(2:end); - rxnsToAdd.rxnNotes = raw{9}(2:end); - rxnsToAdd.rxnReferences = raw{10}(2:end); - rxnsToAdd.rxnConfidenceScores = cellfun(@str2num,raw{11}(2:end),'UniformOutput',false); - emptyEntry = cellfun(@isempty,rxnsToAdd.rxnConfidenceScores); - if all(emptyEntry) - rxnsToAdd = rmfield(rxnsToAdd,'rxnConfidenceScores'); - elseif any(emptyEntry) % If some rxnConfidenceScores are given, assume 0 for those without rxnConfidenceScores specified - rxnsToAdd.rxnConfidenceScores(emptyEntry) = {0}; - rxnsToAdd.rxnConfidenceScores = cell2mat(rxnsToAdd.rxnConfidenceScores); - else - rxnsToAdd.rxnConfidenceScores = cell2mat(rxnsToAdd.rxnConfidenceScores); - end - - existingRxn=[]; - notNewRxn=[]; - for i=1:numel(rxnCheck.rxnsIdx) - rxnRows = find(rxnCheck.rxnsIdx(i)==rxnCheck.coeffsIdx); - rxnsToAdd.mets{i,1} = cell(1,1); - rxnsToAdd.stoichCoeffs{i,1} = cell(1,1); - for j=1:numel(rxnRows) - newMetComp = [rxns.metNames{rxnRows(j)}, '[', rxns.comps{rxnRows(j)}, ']']; - try - newMetComp = getIndexes(newModel,newMetComp,'metcomps'); - catch - error(['Not all metabolites in reaction "%s" are present in the '... - 'model or in the provided table with new metabolites.'],rxnsToAdd.rxnNames{i}) - end - rxnsToAdd.mets{i}(j) = newModel.mets(newMetComp); - rxnsToAdd.stoichCoeffs{i}{j} = rxns.coeffs(rxnRows(j)); - end - rxnsToAdd.stoichCoeffs{i}=cell2mat(rxnsToAdd.stoichCoeffs{i}); - %Check if the reaction not already exists, by stoichiometry of its - %products and reactants - metIdx = getIndexes(newModel,rxnsToAdd.mets{i},'mets'); - modelCoeffs = transpose(newModel.S(metIdx,:)); - modelCoeffs2 = find(~all(modelCoeffs==0,2)); - % Only reactions built from exactly these metabolites can be - % duplicates. Any model reaction that also involves other - % metabolites projects onto the same row of coefficients, but is a - % different reaction and must not be overwritten. - sameSupport = full(sum(newModel.S(:,modelCoeffs2)~=0,1))' == numel(metIdx); - modelCoeffs2 = modelCoeffs2(sameSupport); - modelCoeffs = modelCoeffs(modelCoeffs2,:); - [~, duplicateRxn] = intersect(modelCoeffs,rxnsToAdd.stoichCoeffs{i},'rows'); - if ~isempty(duplicateRxn) - existingRxn = [existingRxn,modelCoeffs2(duplicateRxn)]; - notNewRxn = [notNewRxn,i]; - end - end - - % If some reactions already existed, then replace its information - if ~isempty(existingRxn) - warning(['The following reactions are the same as existing model '... - 'reactions, their annotation will be overwritten to match the '... - 'rxnCoeffs file: \n\t\t(Existing reaction ID): New reaction name\n\t\t%s'],... - strjoin(strcat('(',newModel.rxns(existingRxn),{'): '},rxnsToAdd.rxnNames(notNewRxn)),'\n\t\t')); - newModel.rxnNames(existingRxn) = rxnsToAdd.rxnNames(notNewRxn); - newModel.lb(existingRxn) = rxnsToAdd.lb(notNewRxn); - newModel.ub(existingRxn) = rxnsToAdd.ub(notNewRxn); - newModel.rev(existingRxn) = rxnsToAdd.rev(notNewRxn); - if isfield(newModel,'subSystems') - newModel.subSystems(existingRxn) = rxnsToAdd.subSystems(notNewRxn); - end - if isfield(newModel,'eccodes') - newModel.eccodes(existingRxn) = rxnsToAdd.eccodes(notNewRxn); - end - if isfield(newModel,'rxnNotes') - newModel.rxnNotes(existingRxn) = rxnsToAdd.rxnNotes(notNewRxn); - end - if isfield(newModel,'rxnConfidenceScores') && isfield(rxnsToAdd,'rxnConfidenceScores') - newModel.rxnConfidenceScores(existingRxn) = rxnsToAdd.rxnConfidenceScores(notNewRxn); - end - emptyEntries = cellfun(@isempty,rxnsToAdd.grRules); - if ~all(emptyEntries) - newModel = changeGrRules(newModel,newModel.rxns(existingRxn(~emptyEntries)),rxnsToAdd.grRules(notNewRxn(~emptyEntries)),true); - end - newModel = extractAndAddMiriam(newModel,raw(11:end),notNewRxn,existingRxn,'rxn'); - end - - % Continue with new reactions - toAdd = 1:numel(rxnsToAdd.rxnNames); - toAdd(notNewRxn) = []; - if ~isempty(toAdd) - rxnsToAdd.rxnNames = rxnsToAdd.rxnNames(toAdd); - rxnsToAdd.lb = rxnsToAdd.lb(toAdd); - rxnsToAdd.ub = rxnsToAdd.ub(toAdd); - rxnsToAdd.rev = rxnsToAdd.rev(toAdd); - rxnsToAdd.subSystems = rxnsToAdd.subSystems(toAdd); - if all(cellfun(@isempty,rxnsToAdd.subSystems)) - rxnsToAdd = rmfield(rxnsToAdd,'subSystems'); - end - rxnsToAdd.eccodes = rxnsToAdd.eccodes(toAdd); - if all(cellfun(@isempty,rxnsToAdd.eccodes)) - rxnsToAdd = rmfield(rxnsToAdd,'eccodes'); - end - rxnsToAdd.rxnNotes = rxnsToAdd.rxnNotes(toAdd); - if all(cellfun(@isempty,rxnsToAdd.rxnNotes)) - rxnsToAdd = rmfield(rxnsToAdd,'rxnNotes'); - end - if isfield(rxnsToAdd,'rxnConfidenceScores') - rxnsToAdd.rxnConfidenceScores = rxnsToAdd.rxnConfidenceScores(toAdd); - end - rxnsToAdd.rxnReferences = rxnsToAdd.rxnReferences(toAdd); - if all(cellfun(@isempty,rxnsToAdd.rxnReferences)) - rxnsToAdd = rmfield(rxnsToAdd,'rxnReferences'); - end - rxnsToAdd.grRules = rxnsToAdd.grRules(toAdd); - rxnsToAdd.mets = rxnsToAdd.mets(toAdd); - rxnsToAdd.stoichCoeffs = rxnsToAdd.stoichCoeffs(toAdd); - rxnsToAdd.rxns = generateNewIds(model,'rxns',rxnPrefix,numel(rxnsToAdd.rxnNames)); - - newModel = addRxns(newModel,rxnsToAdd,1,[],false,false); - rxnsModelIdx = numel(newModel.rxns)-numel(toAdd)+1:numel(newModel.rxns); - newModel = extractAndAddMiriam(newModel,raw(11:end),toAdd,rxnsModelIdx,'rxn'); - end -end -end - -function newModel = extractAndAddMiriam(model,raw,inputIndex,modelIndex,type) -newModel=model; -miriamName = cell(numel(inputIndex),1); -miriamValues = cell(1,numel(inputIndex)); -for i=1:numel(raw) - miriamName{i} = raw{i}{1}; - miriamValues(1:numel(inputIndex),i) = raw{i}(inputIndex+1); -end -emptyMiriam = all(cellfun(@isempty,miriamValues),1); -miriamName(emptyMiriam) = []; -miriamValues(:,emptyMiriam) = []; -if ~isfield(newModel,[type 'Miriams']); - newModel.([type 'Miriams'])=cell(numel(newModel.([type 's'])),1); -end -if ~isempty(miriamName) - for i=1:numel(miriamName) - newModel = editMiriam(newModel,type,modelIndex,miriamName{i},miriamValues(:,i),'replace'); - end -end -end diff --git a/doc/INIT/ftINIT.html b/doc/INIT/ftINIT.html deleted file mode 100644 index 03405943..00000000 --- a/doc/INIT/ftINIT.html +++ /dev/null @@ -1,539 +0,0 @@ - - - - Description of ftINIT - - - - - - - - - -
Home > INIT > ftINIT.m
- - - -

ftINIT -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [model, metProduction, addedRxnsForTasks, deletedRxnsInINIT, fullMipRes] = ftINIT(prepData, tissue, celltype, hpaData, varargin)
-0002 % ftINIT  Generate a model using the ftINIT algorithm.
-0003 %
-0004 % Main function for generating a model using the ftINIT algorithm, based on
-0005 % proteomics and/or transcriptomics and/or metabolomics and/or metabolic tasks.
-0006 % The algorithm is not designed for running with metabolomics only. The
-0007 % function prepINITModel needs to be run first for the template model (such as
-0008 % the generic Human-GEM), but only needs to be run once. This function
-0009 % precalculates things independent of the omics to speed up the model
-0010 % generation process, and outputs the prepData, which is input to this
-0011 % function.
-0012 %
-0013 % Parameters
-0014 % ----------
-0015 % prepData : struct
-0016 %     the prepData for the model.
-0017 % tissue : char
-0018 %     tissue to score for. Should exist in either hpaData.tissues or
-0019 %     transcrData.tissues.
-0020 % celltype : char
-0021 %     cell type to score for. Should exist in either hpaData.celltypes or
-0022 %     transcrData.celltypes for this tissue (default is to use the max values
-0023 %     among all the cell types for the tissue).
-0024 % hpaData : struct
-0025 %     HPA data structure from parseHPA (optional if transcrData is supplied,
-0026 %     default []).
-0027 %
-0028 % Name-Value Arguments
-0029 % --------------------
-0030 % transcrData : struct
-0031 %     gene expression data structure (optional if hpaData is supplied, default
-0032 %     []). With fields:
-0033 %
-0034 %     - genes : cell array with the unique gene names.
-0035 %     - tissues : cell array with the tissue names. The list may not be unique,
-0036 %       as there can be multiple cell types per tissue.
-0037 %     - celltypes : cell array with the cell type names for each tissue.
-0038 %     - levels : GENESxTISSUES array with the expression level for each gene in
-0039 %       each tissue/celltype. NaN should be used when no measurement was
-0040 %       performed.
-0041 %     - threshold : a single value or a vector of gene expression thresholds,
-0042 %       above which genes are considered to be "expressed". (default 1; by
-0043 %       default, the mean expression levels of each gene across all tissues in
-0044 %       transcrData will be used as the threshold values).
-0045 %     - singleCells : binary value selecting whether to use the single-cell
-0046 %       algorithm to identify expressed genes. If used, specify cell
-0047 %       subpopulations in CELLTYPES (default []).
-0048 %     - plotResults : true if single cell probability distributions should be
-0049 %       plotted (default false).
-0050 % metabolomicsData : cell
-0051 %     cell array with metabolite names that the model should produce (default
-0052 %     []).
-0053 % INITSteps : cell
-0054 %     specifies the steps in the algorithm. For more info, see INITStepDesc and
-0055 %     getINITSteps (default getINITSteps(), which is the standard ftINIT).
-0056 % removeGenes : logical
-0057 %     if true, low-abundance genes will be removed from grRules, unless they
-0058 %     are the only gene associated with a reaction, or a subunit of an enzyme
-0059 %     complex (see "removeLowScoreGenes" function for details). If false,
-0060 %     grRules will not be modified; however, genes that were associated only
-0061 %     with removed reactions will not be present in the final model (default
-0062 %     true).
-0063 % useScoresForTasks : logical
-0064 %     true if the calculated reaction scores should be used as weights when
-0065 %     fitting to tasks (default true).
-0066 % paramsFT : struct
-0067 %     *obsolete option*.
-0068 % verbose : logical
-0069 %     if true, the MILP progression will be shown (default false).
-0070 % resolveTies : logical
-0071 %     if true, pin each step's degenerate MILP to a canonical answer instead
-0072 %     of relying on the solver Seed alone: hold the score objective at its
-0073 %     optimum, then minimise the count of removable ("on") reactions, then,
-0074 %     among the sparsest, minimise their summed reaction-id rank. Applies to
-0075 %     every main-extraction step (ftINITInternalAlg) and to the final
-0076 %     gap-filling step (fitTasks' resolveTies) -- see raven-gecko-parity#104
-0077 %     and raven-toolbox's resolve_ties (default false).
-0078 % proveAbsGap : double
-0079 %     if set, prove each step to this fixed *absolute* MILP gap in a single
-0080 %     solve, instead of the relative-gap escalation below (stp.MILPParams).
-0081 %     The escalation's final near-zero-objective run otherwise accepts an
-0082 %     arbitrary within-gap incumbent -- measured on genome-scale Human-GEM to
-0083 %     return a solution 2.0-4.0 objective units below the true optimum (see
-0084 %     raven-toolbox's prove_abs_gap and the ftINIT reproducibility study on
-0085 %     raven-docs). 1.0 is the recommended value: tighter (down to 0.05)
-0086 %     proves nothing further and costs substantially more runtime. Pairs
-0087 %     naturally with resolveTies (default [], i.e. use the escalation).
-0088 %
-0089 % Returns
-0090 % -------
-0091 % model : struct
-0092 %     the resulting model structure.
-0093 % metProduction : double
-0094 %     array that indicates which of the metabolites in metabolomicsData that
-0095 %     could be produced. Note that this is before the gap-filling process to
-0096 %     enable defined tasks. To see which metabolites that can be produced in
-0097 %     the final model, use canExchange:
-0098 %
-0099 %     - -2 : metabolite name not found in model.
-0100 %     - -1 : metabolite found, but it could not be produced.
-0101 %     - 1 : metabolite could be produced.
-0102 % addedRxnsForTasks : cell
-0103 %     cell array of the reactions which were added in order to perform the
-0104 %     tasks.
-0105 % deletedRxnsInINIT : cell
-0106 %     cell array of reactions deleted because they could not carry flux (INIT
-0107 %     requires a functional input model).
-0108 % fullMipRes : struct
-0109 %     the solver results from the last MILP step run.
-0110 %
-0111 % Notes
-0112 % -----
-0113 % Exchange metabolites should normally not be removed from the model when using
-0114 % this approach, since checkTasks/fitTasks rely on putting specific constraints
-0115 % for each task. The INIT algorithm will remove exchange metabolites if any are
-0116 % present. Use importModel(file,false) to import a model with exchange
-0117 % metabolites remaining.
-0118 %
-0119 
-0120 p=parseRAVENargs(varargin, {'transcrData',[]; 'metabolomicsData',[]; 'INITSteps',[]; 'removeGenes',[]; 'useScoresForTasks',[]; 'paramsFT',[]; 'verbose',false; 'resolveTies',false; 'proveAbsGap',[]});
-0121 transcrData=p.transcrData;
-0122 metabolomicsData=p.metabolomicsData;
-0123 INITSteps=p.INITSteps;
-0124 removeGenes=p.removeGenes;
-0125 useScoresForTasks=p.useScoresForTasks;
-0126 paramsFT=p.paramsFT;
-0127 verbose=p.verbose;
-0128 resolveTies=p.resolveTies;
-0129 proveAbsGap=p.proveAbsGap;
-0130 if isempty(INITSteps)
-0131     INITSteps = getINITSteps([],'1+1');
-0132 end
-0133 if isempty(removeGenes)
-0134     removeGenes = true;
-0135 end
-0136 if isempty(useScoresForTasks)
-0137     useScoresForTasks = true;
-0138 end
-0139 %Handle detected mets:
-0140 %Giving a bonus for secreting these metabolites does not work, since the
-0141 %metabolite secretion and uptake can be lost when we merge linearly dependent
-0142 %reactions. Instead, we need to figure out which reactions either produce or
-0143 %take up the mets, and give a bonus if any of them carry flux. To simplify
-0144 %things, we focus on reactions that produce the metabolite (since there must
-0145 %be one such reaction). We further reason that the direction does not matter -
-0146 %we can force one of these reactions in any direction - if it becomes a consumer, it will
-0147 %automatically force another producer on as well (otherwise we will have a net consumption).
-0148 
-0149 if (~isempty(metabolomicsData))
-0150     if length(unique(upper(metabolomicsData))) ~= length(metabolomicsData)
-0151         error('RAVEN:badInput', '%s', 'Metabolomics contains the same metabolite multiple times');
-0152     end
-0153     metData = false(numel(metabolomicsData), length(prepData.minModel.rxns)); %one row per metabolite that is a boolean vector
-0154     for i=1:numel(metabolomicsData)
-0155         %Get the matching mets
-0156         metSel = ismember(upper(prepData.refModel.metNames),upper(metabolomicsData{i}));
-0157         prodRxnsSel = any(prepData.refModel.S(metSel,:) > 0,1) | ... %direct producers
-0158                      (any(prepData.refModel.S(metSel,:) < 0,1) & prepData.refModel.rev.'); %reversible reactions that are consumers
-0159         %convert the production rxns from refModel to minModel
-0160         prepData.groupIds;
-0161         [~,ia,ib] = intersect(prepData.minModel.rxns,prepData.refModel.rxns);
-0162         grpIdsMerged = nan(length(prepData.minModel.rxns),1);
-0163         grpIdsMerged(ia) = prepData.groupIds(ib);
-0164         
-0165         groupIdsPos = unique(prepData.groupIds(prodRxnsSel));%gets all group ids which includes a production rxn
-0166         groupIdsPos = groupIdsPos(groupIdsPos ~= 0);%remove the 0 id, it means there is no group
-0167         %the other option is that there is a direct match between the rxn id in minModel and refModel:
-0168         posRxns = prepData.refModel.rxns(prodRxnsSel);
-0169         directMatch = ismember(prepData.minModel.rxns, posRxns).';
-0170         
-0171         metData(i,:) = ismember(grpIdsMerged, groupIdsPos).' | directMatch;
-0172     end
-0173     metData = sparse(metData);
-0174 else
-0175     metData = [];
-0176 end
-0177 
-0178 % Get rxn scores and adapt them to the minimized model
-0179 origRxnScores = scoreModel(prepData.refModel,hpaData,transcrData,tissue,celltype);
-0180 origRxnScores(origRxnScores > -0.1 & origRxnScores <= 0) = -0.1;%we don't want reaction scores that are exactly 0 (or close), this causes problems in the milp
-0181 origRxnScores(origRxnScores < 0.1 & origRxnScores > 0) = 0.1;
-0182 
-0183 rxnsTurnedOn = false(length(prepData.minModel.rxns),1);
-0184 fluxes = zeros(length(prepData.minModel.rxns),1);
-0185 
-0186 rxnsToIgnoreLastStep = [1;1;1;1;1;1;1;1];
-0187 
-0188 %We assume that all essential rxns are irrev - this is taken care of in
-0189 %prepINITModel. We then use an initial flux "from last run" of 0.1 for all
-0190 %reactions. This is used for knowing what flux should be forced through an
-0191 %essential rxn.
-0192 fluxes = ones(length(prepData.minModel.rxns), 1).*0.1;
-0193 
-0194 for initStep = 1:length(INITSteps)
-0195     disp(['ftINIT: Running step ' num2str(initStep)])
-0196     stp = INITSteps{initStep};
-0197     
-0198     if any ((rxnsToIgnoreLastStep - stp.RxnsToIgnoreMask) < 0)
-0199         error('RAVEN:badInput', '%s', 'RxnsToIgnoreMask may not cover rxns not covered in previous steps, but the other way around is fine.');
-0200     end
-0201     rxnsToIgnoreLastStep = stp.RxnsToIgnoreMask;
-0202     
-0203     mm = prepData.minModel;
-0204     
-0205     if (~isempty(stp.MetsToIgnore))
-0206         if (~isempty(stp.MetsToIgnore.simpleMets))
-0207             %Here, we remove simple metabolites that will not really affect the milp but
-0208             %are very common in the S matrix. For example H2O, H+, etc.
-0209             %It is also possible to leave compartments untouched, for example the i compartment in the mitochondria (for H+).
-0210             metsToRem = ismember(mm.metNames,stp.MetsToIgnore.simpleMets.mets);
-0211             compsToKeep = find(ismember(mm.comps, stp.MetsToIgnore.simpleMets.compsToKeep));
-0212             metsToRem = metsToRem & ~ismember(mm.metComps, compsToKeep);
-0213             mm.S(metsToRem,:) = 0;
-0214         end    
-0215     end
-0216 
-0217     %Set up the reaction scores and essential rxns
-0218     rxnsToIgnore = getRxnsFromPattern(stp.RxnsToIgnoreMask, prepData);
-0219     rxnScores = groupRxnScores(prepData.minModel, origRxnScores, prepData.refModel.rxns, prepData.groupIds, rxnsToIgnore);
-0220 
-0221     essentialRxns = prepData.essentialRxns;
-0222     toRev = false(numel(mm.rxns),1);
-0223     %Handle the results from previous steps ("ignore", "exclude", "essential")
-0224     if strcmp(stp.HowToUsePrevResults, 'exclude')
-0225         rxnScores(rxnsTurnedOn) = 0; %This is not used anymore in any step setup.
-0226     elseif strcmp(stp.HowToUsePrevResults, 'essential')
-0227         %Make all reversible reactions turned on in previous steps reversible
-0228         %in the direction that they were previously carrying flux
-0229         
-0230         %first reverse the reactions that need to be reversed
-0231         rev = mm.rev == 1;
-0232         toRev = rxnsTurnedOn & rev & fluxes < 0;
-0233         mm = reverseRxns(mm, mm.rxns(toRev));
-0234         
-0235         %Then make them irreversible
-0236         mm.rev(rxnsTurnedOn) = 0;
-0237         mm.lb(rxnsTurnedOn) = 0;
-0238 
-0239         essentialRxns = unique([prepData.essentialRxns;mm.rxns(rxnsTurnedOn)]);
-0240     end
-0241 
-0242     
-0243     fullMipRes = [];
-0244     lastError = [];
-0245     if ~isempty(proveAbsGap)
-0246         %One solve proven to this fixed absolute gap, replacing the relative-gap
-0247         %escalation below. The escalation's final near-zero-objective run otherwise
-0248         %accepts an arbitrary within-gap incumbent -- measured on genome-scale
-0249         %Human-GEM to cost 2.0-4.0 objective units (raven-toolbox's prove_abs_gap,
-0250         %raven-gecko-parity#104). Pairs naturally with resolveTies.
-0251         if ~isempty(stp.MILPParams) && isfield(stp.MILPParams{1}, 'TimeLimit')
-0252             params = struct('TimeLimit', stp.MILPParams{1}.TimeLimit);
-0253         else
-0254             params = struct('TimeLimit', 5000);
-0255         end
-0256         params.MIPGap = 0;
-0257         params.MIPGapAbs = proveAbsGap;
-0258         try
-0259             [deletedRxnsInINIT1, metProduction,fullMipRes,rxnsTurnedOn1,fluxes1] = ftINITInternalAlg(mm,rxnScores,metData,essentialRxns,5,stp.AllowMetSecr,stp.PosRevOff,params, [], fluxes, verbose, 0.1, resolveTies);
-0260             fluxes1(toRev) = -fluxes1(toRev);
-0261             success = true;
-0262             if fullMipRes.stat ~= 1
-0263                 %stat==2 (checkSolution.m): feasible but not proven within the time
-0264                 %limit -- an unproven incumbent, same caveat as the escalation path
-0265                 %below, but disclosed since proveAbsGap explicitly asks for a proof.
-0266                 EM=['ftINIT step ' num2str(initStep) ' hit the time limit before proving proveAbsGap; its kept reactions are an unproven incumbent. Raise TimeLimit, or loosen proveAbsGap.'];
-0267                 warning('RAVEN:warning', '%s', EM);
-0268             end
-0269         catch e
-0270             success = false;
-0271             lastError = e;
-0272         end
-0273     else
-0274         mipGap = 1;
-0275         first = true;
-0276         success = false;
-0277         for rn = 1:length(stp.MILPParams)
-0278             params = stp.MILPParams{rn};
-0279             if ~isfield(params, 'MIPGap')
-0280                 params.MIPGap = 0.0004;
-0281             end
-0282 
-0283             if ~isfield(params, 'TimeLimit')
-0284                 params.TimeLimit = 5000;
-0285             end
-0286 
-0287             if ~first
-0288                 %There is sometimes a problem with that the objective function becomes close to zero,
-0289                 %which leads to that a small percentage of that (which is the MIPGap sent in) is very small
-0290                 %and the MILP hence takes a lot of time to finish. We also therefore use an absolute MIP gap,
-0291                 %converted to a percentage using the last value of the objective function.
-0292                 params.MIPGap = min(max(params.MIPGap, stp.AbsMIPGaps{rn}/abs(lastObjVal)),1);
-0293                 params.seed = 1234;%use another seed, may work better
-0294 
-0295                 if mipGap <= params.MIPGap
-0296                     success = true;
-0297                     break; %we're done - this will not happen the first time
-0298                 else
-0299                     disp(['MipGap too high, trying with a different run. MipGap = ' num2str(mipGap) ' New MipGap Limit = ' num2str(params.MIPGap)])
-0300                 end
-0301             end
-0302 
-0303             first = false;
-0304 
-0305             %now run the MILP
-0306             try
-0307                 %The prodweight for metabolomics is set to 5. This value has not
-0308                 %been evaluated, but is assumed in the test cases - if changed, update the test case
-0309                 startVals = [];
-0310                 if ~isempty(fullMipRes)
-0311                     startVals = fullMipRes.full;
-0312                 end
-0313                 [deletedRxnsInINIT1, metProduction,fullMipRes,rxnsTurnedOn1,fluxes1] = ftINITInternalAlg(mm,rxnScores,metData,essentialRxns,5,stp.AllowMetSecr,stp.PosRevOff,params, startVals, fluxes, verbose, 0.1, resolveTies);
-0314                 %This is a bit tricky - since we reversed some reactions, those fluxes also need to be reversed
-0315                 fluxes1(toRev) = -fluxes1(toRev);
-0316 
-0317                 mipGap = fullMipRes.mipgap;
-0318                 lastObjVal = fullMipRes.obj;
-0319             catch e
-0320                 mipGap = Inf;
-0321                 lastObjVal = Inf; %we need to set something here, Inf leads to that this doesn't come into play
-0322                 lastError = e; %kept so a throw is not reported as a time-limit miss
-0323             end
-0324 
-0325             success = mipGap <= params.MIPGap;
-0326         end
-0327     end
-0328 
-0329     if ~success
-0330         if ~isempty(lastError)
-0331             %The MILP threw rather than returning a poor gap; reporting this
-0332             %as a time-limit miss sends users off to raise params.TimeLimit
-0333             %for what is often a solver or input problem.
-0334             error('RAVEN:badInput', '%s', ['Failed to find good enough solution within the time frame. The last MILP attempt failed with: ' lastError.message]);
-0335         else
-0336             error('RAVEN:badInput', '%s', ['Failed to find good enough solution within the time frame. MIPGap: ' num2str(mipGap)]);
-0337         end
-0338     end
-0339     
-0340     %save the reactions turned on and their fluxes for the next step
-0341     rxnsTurnedOn = rxnsTurnedOn | rxnsTurnedOn1.';
-0342     %The fluxes are a bit tricky - what if they change direction between the steps?
-0343     %The fluxes are used to determine the direction in which reactions are forced on
-0344     %(to simplify the problem it is good if they are unidirectional).
-0345     %We use the following strategy:
-0346     %1. Use the fluxes from the most recent step.
-0347     %2. If any flux is very low there (i.e. basically zero), use the flux from the previous steps
-0348     %This could in theory cause problems, but seems to work well practically
-0349     fluxesOld = fluxes;
-0350     fluxes = fluxes1;
-0351 end
-0352 
-0353 
-0354 %get the essential rxns
-0355 essential = ismember(prepData.minModel.rxns,prepData.essentialRxns);
-0356 %So, we only add reactions where the linearly merged scores are zero for all linearly dependent reactions
-0357 % (this cannot happen by chance, taken care of in the function groupRxnScores)
-0358 rxnsToIgn = rxnScores == 0; 
-0359 deletedRxnsInINITSel = ~(rxnsTurnedOn | rxnsToIgn | essential);
-0360 deletedRxnsInINIT = prepData.minModel.rxns(deletedRxnsInINITSel);
-0361 
-0362 %Here we need to figure out which original reactions (before the linear merge)
-0363 %that were removed. These are all reactions with the same group ids as the removed reactions
-0364 groupIdsRemoved = prepData.groupIds(ismember(prepData.refModel.rxns, deletedRxnsInINIT)); %can improve this slightly, use sel above
-0365 groupIdsRemoved = groupIdsRemoved(groupIdsRemoved ~= 0);%zero means that the reaction was not grouped, all with zeros are not a group!
-0366 rxnsToRem = union(prepData.refModel.rxns(ismember(prepData.groupIds,groupIdsRemoved)), deletedRxnsInINIT);%make a union here to include the ungrouped (unmerged) as well
-0367 
-0368 initModel = removeReactions(prepData.refModel,rxnsToRem,false,true);
-0369 
-0370 % remove metabolites separately to avoid removing those needed for tasks
-0371 unusedMets = initModel.mets(all(initModel.S == 0,2));
-0372 initModel = removeMets(initModel, setdiff(unusedMets, prepData.essentialMetsForTasks));
-0373 
-0374 %if printReport == true
-0375 %    printScores(initModel,"INIT model statistics",hpaData,transcrData,tissue,celltype);
-0376 %    printScores(removeReactions(cModel,setdiff(cModel.rxns,rxnsToRem),true,true),"Reactions deleted by INIT",hpaData,transcrData,tissue,celltype);
-0377 %end
-0378 
-0379 %The full model has exchange reactions in it. fitTasks with gapFillMode
-0380 %'preMerged' calls ftINITFillGaps, which automatically removes exchange
-0381 %metabolites (because it
-0382 %assumes that the reactions are constrained when appropriate). In this case the
-0383 %uptakes/outputs are retrieved from the task sheet instead. To prevent
-0384 %exchange reactions being used to fill gaps, they are deleted from the
-0385 %reference model here.
-0386 initModel.id = 'INITModel';
-0387 
-0388 %If gaps in the model should be filled using a task list
-0389 if ~isempty(prepData.taskStruct)
-0390     %Remove exchange reactions and reactions already included in the INIT
-0391     %model. Exchange rxns that are essential are kept.
-0392 
-0393     %At this stage the model is fully connected and most of the genes with
-0394     %good scores should have been included. The final gap-filling should
-0395     %take the scores of the genes into account, so that "rather bad"
-0396     %reactions are preferred to "very bad" reactions. However, reactions
-0397     %with positive scores will be included even if they are not connected
-0398     %in the current formulation. Therefore, such reactions will have to be
-0399     %assigned a small negative score instead.
-0400     exchRxns = getExchangeRxns(prepData.refModel);
-0401     refModelNoExc = removeReactions(prepData.refModelWithBM,exchRxns,false,true);
-0402     exchRxns = getExchangeRxns(initModel);
-0403     initModelNoExc = removeReactions(closeModel(initModel),exchRxns,false,true);
-0404     
-0405     if useScoresForTasks == true
-0406         %map the rxn scores to the model without exchange rxns
-0407         [~,ia,ib] = intersect(refModelNoExc.rxns,prepData.refModel.rxns);
-0408         rxnScores2nd = NaN(length(refModelNoExc.rxns),1);
-0409         rxnScores2nd(ia) = origRxnScores(ib);
-0410         %all(rxnScores2nd == refRxnScores);%should be the same, ok!
-0411         [outModel,addedRxnMat] = fitTasks(initModelNoExc,refModelNoExc,[], ...
-0412             'printOutput',true,'rxnScores',min(rxnScores2nd,-0.1), ...
-0413             'taskStructure',prepData.taskStruct,'gapFillMode','preMerged', ...
-0414             'params',paramsFT,'verbose',verbose,'resolveTies',resolveTies);
-0415     else
-0416         [outModel,addedRxnMat] = fitTasks(initModelNoExc,refModelNoExc,[], ...
-0417             'printOutput',true,'rxnScores',[], ...
-0418             'taskStructure',prepData.taskStruct,'gapFillMode','preMerged', ...
-0419             'params',paramsFT,'verbose',verbose,'resolveTies',resolveTies);
-0420     end
-0421     %if printReport == true
-0422     %    printScores(outModel,"Functional model statistics",hpaData,transcrData,tissue,celltype);
-0423     %    printScores(removeReactions(outModel,intersect(outModel.rxns,initModel.rxns),true,true),"Reactions added to perform the tasks",hpaData,transcrData,tissue,celltype);
-0424     %end
-0425     
-0426     addedRxnsForTasks = refModelNoExc.rxns(any(addedRxnMat,2));
-0427 else
-0428     outModel = initModel;
-0429     addedRxnMat = [];
-0430     addedRxnsForTasks = {};
-0431 end
-0432 
-0433 % The model can now perform all the tasks defined in the task list.
-0434 model = outModel;
-0435 
-0436 
-0437 % At this stage the model will contain some exchange reactions but probably
-0438 % not all (and maybe zero). This can be inconvenient, so all exchange
-0439 % reactions from the reference model are added, except for those which
-0440 % involve metabolites that are not in the model.
-0441 
-0442 %Start from the original model, and just remove the reactions that are no longer there (and keep exchange rxns). The model we got out
-0443 %from the problem is not complete, it does not have GRPs etc.
-0444 %The logic below is a bit complicated. We identify the reactions that should be removed from the full model as
-0445 %reactions that have been removed in the init model except the ones that were added back. In addition, we make
-0446 %sure that no exchange rxns are removed - they can be removed in the init model if they were linearly merged with other
-0447 %reactions that were decided to be removed from the model. We want to keep all exchange rxns to make sure the tasks can
-0448 %be performed also without manipulating the b vector in the model (which is what is done in the gap-filling).
-0449 exchRxns = getExchangeRxns(prepData.refModel);
-0450 deletedRxnsInINIT = setdiff(prepData.refModel.rxns,union(union(initModel.rxns, addedRxnsForTasks), exchRxns));
-0451 outModel = removeReactions(prepData.refModel, deletedRxnsInINIT, true); %we skip removing the genes for now, I'm not sure it is desirable
-0452 
-0453 % If requested, attempt to remove negative-score genes from the model,
-0454 % depending on their role (isozyme or complex subunit) in each grRule.
-0455 % See the "removeLowScoreGenes" function more more details, and to adjust
-0456 % any default parameters therein.
-0457 if ( removeGenes )
-0458     [~, geneScores] = scoreModel(outModel,hpaData,transcrData,tissue,celltype);
-0459     outModel = removeLowScoreGenes(outModel,geneScores);
-0460 end
-0461 
-0462 
-0463 model = outModel;
-0464 
-0465 end
-0466 
-0467 %This is for printing a summary of a model
-0468 function [rxnS, geneS] = printScores(model,name,hpaData,transcrData,tissue,celltype)
-0469     [a, b] = scoreModel(model,hpaData,transcrData,tissue,celltype);
-0470     rxnS = mean(a);
-0471     geneS = mean(b,'omitnan');
-0472     fprintf([name ':\n']);
-0473     fprintf(['\t' num2str(numel(model.rxns)) ' reactions, ' num2str(numel(model.genes)) ' genes\n']);
-0474     fprintf(['\tMean reaction score: ' num2str(rxnS) '\n']);
-0475     fprintf(['\tMean gene score: ' num2str(geneS) '\n']);
-0476     fprintf(['\tReactions with positive scores: ' num2str(100*sum(a>0)/numel(a)) '%%\n\n']);
-0477 end
-0478 
-0479 function rxnsToIgnore = getRxnsFromPattern(rxnsToIgnorePattern, prepData)
-0480     rxnsToIgnore = false(length(prepData.toIgnoreExch),1);
-0481     if rxnsToIgnorePattern(1) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreExch; end;
-0482     if rxnsToIgnorePattern(2) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreImportRxns; end;
-0483     if rxnsToIgnorePattern(3) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreSimpleTransp; end;
-0484     if rxnsToIgnorePattern(4) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreAdvTransp; end;
-0485     if rxnsToIgnorePattern(5) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreSpont; end;
-0486     if rxnsToIgnorePattern(6) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreS; end;
-0487     if rxnsToIgnorePattern(7) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreCustomRxns; end;
-0488     if rxnsToIgnorePattern(8) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreAllWithoutGPRs; end;
-0489 end
-0490
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/INIT/ftINITInternalAlg.html b/doc/INIT/ftINITInternalAlg.html deleted file mode 100644 index e6b9005a..00000000 --- a/doc/INIT/ftINITInternalAlg.html +++ /dev/null @@ -1,684 +0,0 @@ - - - - Description of ftINITInternalAlg - - - - - - - - - -
Home > INIT > ftINITInternalAlg.m
- - - -

ftINITInternalAlg -

- -

PURPOSE ^

-
ftINITInternalAlg
- -

SYNOPSIS ^

-
function [deletedRxns,metProduction,res,turnedOnRxns,fluxes]=ftINITInternalAlg(model,rxnScores,metData,essentialRxns,prodWeight,allowExcretion,remPosRev,params,startVals,fluxes,verbose,forceOn,resolveTies)
- -

DESCRIPTION ^

-
 ftINITInternalAlg
-    This function runs the MILP for a step in ftINIT.
-
-   model           a reference model structure
-   rxnScores       a vector of scores for the reactions in the model.
-                   Positive scores are reactions to keep and negative
-                   scores are reactions to exclude. Rxns set to 0 are excluded
-                   from the problem.
-   metData         boolean matrix with mets as rows and rxns as columns
-                   saying which reaction produces each detected met (optional, default [])
-   essentialRxns   cell array of reactions that are essential and that
-                   have to be in the resulting model. This is normally
-                   used when fitting a model to task (see fitTasks)
-   prodWeight      a score that determines the value of having
-                   net-production of metabolites. This is a way of having
-                   a more functional network as it provides a reason for
-                   including bad reactions for connectivity reasons. This
-                   score is for each metabolite, and the sum of these weights
-                   and the scores for the reactions is what is optimized
-   allowExcretion  true if excretion of all metabolites should be allowed.
-                   This results in fewer reactions being considered
-                   dead-ends, but all reactions in the resulting model may
-                   not be able to carry flux. If this is "false" then the
-                   equality constraints are taken from model.b. If the
-                   input model lacks exchange reactions then this should
-                   probably be "true", or a large proportion of the model
-                   would be excluded for connectivity reasons
-   remPosRev       If true, the positive reversible reactions are removed from the problem.
-                   This is used in step 1 of ftINIT
-   params          parameters for the MILP, for example MIPGap and TimeLimit
-   startVals       Start values for the MILP, typically used when rerunning
-                   with a higher MIPGap, to use the results from the previous
-                   run
-   fluxes          Fluxes from the last run.
-   verbose         If true, the MILP progression will be shown.
-   forceOn         minimum flux magnitude enforced through a reaction once
-                   it is turned on in the MILP (optional, default 0.1)
-   resolveTies     if true, pin the MILP's degenerate optimum to a canonical
-                   answer instead of relying on the solver Seed alone: hold
-                   the score objective at its optimum, then minimise the
-                   count of "on" removable (negative-score) reactions, then,
-                   among the sparsest, minimise their summed reaction-id
-                   rank. Positive-score reactions carry a continuous, not a
-                   free binary, on/off variable (pinned near 1 by the score
-                   objective), so only the negative-score ones are a genuine
-                   tie to resolve -- matches raven-toolbox's resolve_ties /
-                   _resolve_ties (raven-gecko-parity#104) (optional, default
-                   false)
-
-   deletedRxns     reactions which were deleted by the algorithm (only
-                   rxns included in the problem)
-   metProduction   array that indicates which of the
-                   metabolites in presentMets that could be
-                   produced
-                   0: metabolite could not be produced
-                   1: metabolite could be produced
-   res             The result from the MILP
-   turnedOnRxns    The reactions determined to be present (only 
-                   rxns included in the problem) 
-   fluxes          The fluxes from the MILP
-
-   This function is the actual implementation of the algorithm. See
-   ftINIT for a higher-level function for model reconstruction. 
-
- Usage: [deletedRxns,metProduction,res,turnedOnRxns,fluxes]=runINIT9(model,...
-           rxnScores,presentMets,essentialRxns,prodWeight,allowExcretion,...
-           remPosRev,params)
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: - - - - - -

SOURCE CODE ^

-
0001 function [deletedRxns,metProduction,res,turnedOnRxns,fluxes]=ftINITInternalAlg(model,rxnScores,metData,essentialRxns,prodWeight,allowExcretion,remPosRev,params,startVals,fluxes,verbose,forceOn,resolveTies)
-0002 % ftINITInternalAlg
-0003 %    This function runs the MILP for a step in ftINIT.
-0004 %
-0005 %   model           a reference model structure
-0006 %   rxnScores       a vector of scores for the reactions in the model.
-0007 %                   Positive scores are reactions to keep and negative
-0008 %                   scores are reactions to exclude. Rxns set to 0 are excluded
-0009 %                   from the problem.
-0010 %   metData         boolean matrix with mets as rows and rxns as columns
-0011 %                   saying which reaction produces each detected met (optional, default [])
-0012 %   essentialRxns   cell array of reactions that are essential and that
-0013 %                   have to be in the resulting model. This is normally
-0014 %                   used when fitting a model to task (see fitTasks)
-0015 %   prodWeight      a score that determines the value of having
-0016 %                   net-production of metabolites. This is a way of having
-0017 %                   a more functional network as it provides a reason for
-0018 %                   including bad reactions for connectivity reasons. This
-0019 %                   score is for each metabolite, and the sum of these weights
-0020 %                   and the scores for the reactions is what is optimized
-0021 %   allowExcretion  true if excretion of all metabolites should be allowed.
-0022 %                   This results in fewer reactions being considered
-0023 %                   dead-ends, but all reactions in the resulting model may
-0024 %                   not be able to carry flux. If this is "false" then the
-0025 %                   equality constraints are taken from model.b. If the
-0026 %                   input model lacks exchange reactions then this should
-0027 %                   probably be "true", or a large proportion of the model
-0028 %                   would be excluded for connectivity reasons
-0029 %   remPosRev       If true, the positive reversible reactions are removed from the problem.
-0030 %                   This is used in step 1 of ftINIT
-0031 %   params          parameters for the MILP, for example MIPGap and TimeLimit
-0032 %   startVals       Start values for the MILP, typically used when rerunning
-0033 %                   with a higher MIPGap, to use the results from the previous
-0034 %                   run
-0035 %   fluxes          Fluxes from the last run.
-0036 %   verbose         If true, the MILP progression will be shown.
-0037 %   forceOn         minimum flux magnitude enforced through a reaction once
-0038 %                   it is turned on in the MILP (optional, default 0.1)
-0039 %   resolveTies     if true, pin the MILP's degenerate optimum to a canonical
-0040 %                   answer instead of relying on the solver Seed alone: hold
-0041 %                   the score objective at its optimum, then minimise the
-0042 %                   count of "on" removable (negative-score) reactions, then,
-0043 %                   among the sparsest, minimise their summed reaction-id
-0044 %                   rank. Positive-score reactions carry a continuous, not a
-0045 %                   free binary, on/off variable (pinned near 1 by the score
-0046 %                   objective), so only the negative-score ones are a genuine
-0047 %                   tie to resolve -- matches raven-toolbox's resolve_ties /
-0048 %                   _resolve_ties (raven-gecko-parity#104) (optional, default
-0049 %                   false)
-0050 %
-0051 %   deletedRxns     reactions which were deleted by the algorithm (only
-0052 %                   rxns included in the problem)
-0053 %   metProduction   array that indicates which of the
-0054 %                   metabolites in presentMets that could be
-0055 %                   produced
-0056 %                   0: metabolite could not be produced
-0057 %                   1: metabolite could be produced
-0058 %   res             The result from the MILP
-0059 %   turnedOnRxns    The reactions determined to be present (only
-0060 %                   rxns included in the problem)
-0061 %   fluxes          The fluxes from the MILP
-0062 %
-0063 %   This function is the actual implementation of the algorithm. See
-0064 %   ftINIT for a higher-level function for model reconstruction.
-0065 %
-0066 % Usage: [deletedRxns,metProduction,res,turnedOnRxns,fluxes]=runINIT9(model,...
-0067 %           rxnScores,presentMets,essentialRxns,prodWeight,allowExcretion,...
-0068 %           remPosRev,params)
-0069 
-0070 if nargin < 12 || isempty(forceOn)
-0071     forceOn = 0.1;
-0072 end
-0073 if nargin < 13 || isempty(resolveTies)
-0074     resolveTies = false;
-0075 end
-0076 if isempty(essentialRxns)
-0077     essentialRxns={};
-0078 end
-0079 essentialRxns=essentialRxns(:);
-0080 if isempty(prodWeight)
-0081     prodWeight=0.5;
-0082 end
-0083 
-0084 
-0085 %The model should be in the reversible format and all relevant exchange
-0086 %reactions should be open
-0087 if isfield(model,'unconstrained')
-0088     EM='Exchange metabolites are still present in the model. Use simplifyModel if this is not intended';
-0089     warning('RAVEN:warning', '%s', EM);
-0090 end
-0091 
-0092 
-0093 essential = ismember(model.rxns,essentialRxns);
-0094 
-0095 %Some nice to have numbers
-0096 nMets=numel(model.mets);
-0097 nRxns=numel(model.rxns);
-0098 nRxnsWithOnOff = nRxns;
-0099 
-0100 %Reactions with score 0 will just be left in the model, and will not be part of the problem (but can carry flux).
-0101 %It is possible to set score = 0 for e.g. spontaneous reactions, exchange rxns, etc., which may not be that interesting
-0102 %to remove.
-0103 
-0104 %if makeIrrev is on, we can just as well skip all positive reversible rxns - they will be turned on without carrying any flux
-0105 %since they can form a loop within themself (fwd-rev)
-0106 if remPosRev
-0107     rxnScores(rxnScores > 0 & model.rev~=0) = 0;
-0108 end
-0109 
-0110 %Handle metabolomics
-0111 %A problem with the metabolomics is that some of the producer reactions
-0112 %for a metabolite could be excluded from the problem. We solve this by
-0113 %adding them with score 0. They can still be seen as positive, since they
-0114 %either do not matter (there is another producer) or they can contribute to an
-0115 %increased score.
-0116 revRxns=model.rev~=0; 
-0117 essRevRxns = find(revRxns & essential);
-0118 essIrrevRxns = find(~revRxns & essential);
-0119 
-0120 if ~isempty(metData)
-0121     %remove any metData rows that is connected to an essential rxn -
-0122     %these will be on regardless and will cause problems below.
-0123     containsEssential = any(metData(:,essential),2);
-0124     metData = metData(~containsEssential,:);
-0125 end
-0126 
-0127 if ~isempty(metData)
-0128     metRxns = any(metData,1).';
-0129     posRxns = rxnScores > 0 | ((rxnScores == 0) & metRxns);
-0130 else
-0131     posRxns = rxnScores > 0;
-0132 end
-0133 negRxns = rxnScores < 0;
-0134 
-0135 posRevRxns = find(posRxns & revRxns & ~essential);
-0136 negRevRxns = find(negRxns & revRxns & ~essential);
-0137 posIrrevRxns = find(posRxns & ~revRxns & ~essential);
-0138 negIrrevRxns = find(negRxns & ~revRxns & ~essential);
-0139 nPosRev = numel(posRevRxns);
-0140 nNegRev = numel(negRevRxns);
-0141 nPosIrrev = numel(posIrrevRxns);
-0142 nNegIrrev = numel(negIrrevRxns);
-0143 nEssRev = numel(essRevRxns);
-0144 nEssIrrev = numel(essIrrevRxns); %not used, but left for symmetry
-0145 nMetabolMets = size(metData,1);
-0146 
-0147 if ~isempty(metData)
-0148     metNegRev = negRxns & revRxns & ~essential & metRxns;
-0149     metNegIrrev = negRxns & ~revRxns & ~essential & metRxns;
-0150     nMetNegRev = sum(metNegRev);
-0151     nMetNegIrrev = sum(metNegIrrev);
-0152 end
-0153 
-0154 
-0155 milpModel = model;
-0156 
-0157 %These six categories need to be handled separately:
-0158 %
-0159 %PosIrrev (positive reaction score, irreversible):
-0160 %flux >= 0.1*Yi, flux - 0.1*Yi - VPI == 0, 0 <= Yi(onoff) <= 1, 0 <= VPI <= Inf
-0161 %The nice thing with rxns with positive score is that they do not require a boolean. The optimizer will
-0162 %strive for maximizing the Yi here, so it can be a continuous variable, where we just force a flux if
-0163 %the variable is on.
-0164 %PosRev:
-0165 % 1: Split up the flux into positive and negative: flux - vnrp + vnrn == 0, 0 <= vprp,vprn <= Inf.
-0166 % 2. Force one of the fluxes on: vprp + vprn >= 0.1*Yi, vprp + vprn - 0.1*Yi - vprv1 == 0. 0 <= Yi(onoff) <= 1, vprv1 >= 0
-0167 % 3. We need a bool to make sure that one of vprp and vprn are always zero:
-0168 %    vprb E {0,1}:  vprp <= 100*vprb (if bool is 0, vprp is zero): vprp - 100*vprb + vprv2 == 0, vprv2 >= 0
-0169 %    vprn <= (1-vprb)*100 (if bool is one, vprn is zero): vprn + 100*vprb + vprv3 == 0, -100 <= vprv3 <= inf
-0170 %Neg Irrev:
-0171 %For negative scores, we need to use an integer, I see no way around that.
-0172 % flux < 100*Yi, Yi E {0,1}. Flux - 100*Yi + vni == 0
-0173 %Neg Rev:
-0174 %Two cases:
-0175 %A: Without irrev model
-0176 % 1: Split up the flux into positive and negative: flux - vnrp + vnrn == 0, 0 <= vprp,vprn <= Inf.
-0177 % 2: Force the Yi (on/off) var on if the flux is on: vnrp + vnrn <= 0.1 * Yi, Yi E {0,1}: vnrp + vnrn - 0.1 * Yi + vnrv1 == 0, vnrv1 >= 0.
-0178 %B: Irrev model (Not used for now)
-0179 % 1. We now have two reactions, but still just boolean variable. We know that
-0180 %    the flux can only be positive, so we just say that flux1 + flux2 <= 0.1 * Yi, Yi E {0,1}: flux1 + flux2 - 0.1 * Yi + vnrv1 == 0, vnrv1 >= 0.
-0181 %    There is no need to prevent loops - there is no benefit for the objective to generate one, and it does not matter.
-0182 %Ess Irrev (essential rxn, i.e. forced to carry flux, irreversible):
-0183 %flux >= 0.1 - solved with lb = 0.1.
-0184 %Ess Rev (these are not really used):
-0185 % 1: Split up the flux into positive and negative: flux - verp + vern == 0, 0 <= verp,vern <= Inf.
-0186 % 2. Force one of the fluxes on: verp + vern >= 0.1, verp + vern - verv1 == 0. verv1 >= 0.1
-0187 % 3. We need a bool to make sure that one of verp and vern are always zero:
-0188 %    verb E {0,1}:  verp <= 100*verb (if bool is 0, verp is zero): verp - 100*verb + verv2 == 0, verv2 >= 0
-0189 %    vern <= (1-verb)*100 (if bool is one, vern is zero): vern + 100*verb + verv3 == 0, -100 <= verv3 <= inf
-0190 
-0191 % Now metabolomics
-0192 % The basic idea is that the bonus is gotten if any of the producer reactions are on. We can
-0193 % therefore use the "on" variables directly for the reactions that are included. We therefore add one variable per met, mon, which can be
-0194 % continuous and between 0 and 1. We then say that mon <= v1 + v2 + ... + vn, i.e. that
-0195 % mon + mv1 - v1 - v2 ... - vn == 0, 0 <= mon <= 1, 0 <= mv1 <= inf
-0196 % mon gets -prodWeight in the c vector (i.e. objective function)
-0197 % A tricky part is that some of the reactions producing a metabolite are left outside the problem
-0198 % This is solved by moving them into the problem, but with the score 0. They can still be treated as positive
-0199 % reactions. In the end, we are not interested if they are on though, but they may
-0200 % allow for production of a metabolite, giving no benefit of turning on another producer reaction.
-0201 % Another tricky thing is that some metabolite producers have negative score. There is
-0202 % no automatic mechanism for forcing flux on if the variable is on - this is simply
-0203 % not needed for negative variables - unless they are a metabolite producer. We therefore need
-0204 % to add an extra constraint there, similar to the case of positive. It gets a bit complicated.
-0205 % For reversible, we need a bool to make sure that one of vnrp and vnrn stays zero:
-0206 %    vnrbm E {0,1}:  vnrp <= 100*vnrbm (if bool is 0, vnrp is zero): vnrp - 100*vnrbm + vnrvm1 == 0, vnrvm1 >= 0
-0207 %    vnrn <= (1-vnrbm)*100 (if bool is one, vnrn is zero): vnrn + 100*vnrbm + vnrvm2 == 0, -100 <= vnrvm2 <= inf
-0208 % We then also say that vnrp + vnrn >= 0.1*Yi, vnrp + vnrn - 0.1*Yi - vnrvm3 == 0, vnrvm3 >= 0
-0209 % For irreversible, it is fairly straight-forward. We just say that flux >= 0.1*Yi, i.e. flux - 0.1*Yi - vnim == 0, 0 <= vnim <= Inf.
-0210 
-0211 
-0212 %The total matrix then looks like this. Note that we have ordered the categories, two reactions each,
-0213 %in the S matrix to make it easier to follow the figure. In practice, they come in random order, which
-0214 %is why the SEye variable is used further down.
-0215 %all categories below have two columns - the two column starts where label starts
-0216 %since the label cannot fit in two chars, they are on multiple lines
-0217 %
-0218 %The met variables and constraints are not in this figure for practical reasons.
-0219 %
-0220 %                           vprb  vprv3 vnrn  vern  verv2 mv1
-0221 % pi  ni  ei  Ypi Yni vpi vprn  vprv2 vnrp  verp  verb  mon
-0222 %   pr  nr  er  Ypr Ynr vprp  vprv1 vni   vnrv1 verv1 verv3
-0223 % SSSSSSSSSSSS0000000000000000000000000000000000000000000000
-0224 % SSSSSSSSSSSS0000000000000000000000000000000000000000000000 S block
-0225 % SSSSSSSSSSSS0000000000000000000000000000000000000000000000
-0226 % 1           N       -                                      Pos irrev block
-0227 %  1           N       -
-0228 %   1                   - 1                                  Pos rev block 1
-0229 %    1                   - 1
-0230 %               N       1 1   -                              Pos rev block 2
-0231 %                N       1 1   -
-0232 %                       1   M   1                            Pos rev block 3
-0233 %                        1   M   1
-0234 %                         1 C     1                          Pos rev block 4
-0235 %                          1 C     1
-0236 %     1           M                 1                        Neg irrev block
-0237 %      1           M                 1
-0238 %       1                             - 1                    Neg rev block 1
-0239 %        1                             - 1
-0240 %                   M                 1 1 1                  Neg rev block 2
-0241 %                    M                 1 1 1
-0242 %           1                               - 1              Ess rev block 1
-0243 %            1                               - 1
-0244 %                                           1 1 -            Ess rev block 2
-0245 %                                            1 1 -
-0246 %                                           1     M 1        Ess rev block 3
-0247 %                                            1     M 1
-0248 %                                             1   C   1      Ess rev block 4
-0249 %                                              1   C   1
-0250 %             --------                                  1 1  Met block - Here, we assume that all variables support each met.
-0251 %             --------                                   1 1             In practice, fewer of the "on" variables with -1 should be included
-0252 %
-0253 %
-0254 %M = -100
-0255 %N = -0.1
-0256 %D = 0.1
-0257 %C = 100
-0258 %- = -1
-0259 %
-0260 %It looks a bit different for the irrev model, but still quite similar
-0261 
-0262 %build the A matrix
-0263 %S row
-0264 %When forcing on essential rxns, use the flux value of the previous run (set to 0.1 the first time)
-0265 %Do not set it above 0.1, may starve something else out. Leave a margin of 1% from the last run.
-0266 forceOnLim = forceOn;
-0267 forceOnLimEss=min(abs(fluxes)*0.99,0.1);
-0268 forceOnLimEss(essIrrevRxns) = min(forceOnLimEss(essIrrevRxns), milpModel.ub(essIrrevRxns));
-0269 
-0270 % Per-reaction big-M: use each reaction own flux capacity (capped at 1000).
-0271 % This replaces the fixed big-M=100, which underestimates capacity for standard
-0272 % RAVEN models where ub=1000. Must be at least forceOnLim so the constraints are feasible.
-0273 bigMCap = 1000;
-0274 ubSafe = min(milpModel.ub, bigMCap); ubSafe(~isfinite(ubSafe)) = bigMCap;
-0275 lbSafe = min(-milpModel.lb, bigMCap); lbSafe(~isfinite(lbSafe)) = bigMCap; lbSafe = max(lbSafe, 0);
-0276 bigMPosRev   = max(max(ubSafe(posRevRxns),  lbSafe(posRevRxns)),  forceOnLim);
-0277 bigMNegIrrev = max(ubSafe(negIrrevRxns), forceOnLim);
-0278 bigMNegRev   = max(max(ubSafe(negRevRxns),  lbSafe(negRevRxns)),  forceOnLim);
-0279 bigMEssRev   = max(max(ubSafe(essRevRxns),  lbSafe(essRevRxns)),  forceOnLim);
-0280 
-0281 varsPerNegRev = 3;
-0282 
-0283 
-0284 %Figure out the number of variables needed for metabolomics
-0285 if ~isempty(metData)
-0286     nMetVars = 2*size(metData,1) + 4*nMetNegRev + nMetNegIrrev; %mon,mv1; vnrbm,vnrvm1,vnrvm2,vnrvm3; vnim
-0287 else
-0288     nMetVars = 0;
-0289 end
-0290 
-0291 
-0292 sRow = [milpModel.S sparse(nMets, nPosIrrev*2+nPosRev*7+nNegIrrev*2+nNegRev*(1+varsPerNegRev)+nEssRev*6 + nMetVars)];
-0293 sEye = speye(nRxns);
-0294 nYBlock = nPosIrrev+nPosRev+nNegIrrev+nNegRev;
-0295 piRows = [sEye(posIrrevRxns,:) speye(nPosIrrev)*-forceOnLim sparse(nPosIrrev,nPosRev+nNegIrrev+nNegRev) speye(nPosIrrev)*-1 sparse(nPosIrrev,nPosRev*6+nNegIrrev+nNegRev*varsPerNegRev+nEssRev*6+nMetVars)];
-0296 
-0297 prRows1 = [sEye(posRevRxns,:) sparse(nPosRev,nYBlock + nPosIrrev) speye(nPosRev)*-1 speye(nPosRev) sparse(nPosRev,nPosRev*4+nNegIrrev+nNegRev*varsPerNegRev+nEssRev*6+nMetVars)];
-0298 prRows2 = [sparse(nPosRev,nRxns + nPosIrrev) speye(nPosRev)*-forceOnLim sparse(nPosRev,nNegIrrev + nNegRev + nPosIrrev) speye(nPosRev) speye(nPosRev) sparse(nPosRev,nPosRev) speye(nPosRev)*-1 sparse(nPosRev,nPosRev*2+nNegIrrev+nNegRev*varsPerNegRev+nEssRev*6+nMetVars)];
-0299 prRows3 = [sparse(nPosRev,nRxns + nYBlock + nPosIrrev) speye(nPosRev) sparse(nPosRev,nPosRev) spdiags(-bigMPosRev,0,nPosRev,nPosRev) sparse(nPosRev,nPosRev) speye(nPosRev) sparse(nPosRev,nPosRev+nNegIrrev+nNegRev*varsPerNegRev+nEssRev*6+nMetVars)];
-0300 prRows4 = [sparse(nPosRev,nRxns + nYBlock + nPosIrrev + nPosRev) speye(nPosRev) spdiags(bigMPosRev,0,nPosRev,nPosRev) sparse(nPosRev,nPosRev*2) speye(nPosRev) sparse(nPosRev, nNegIrrev+nNegRev*varsPerNegRev+nEssRev*6+nMetVars)];
-0301 
-0302 niRows = [sEye(negIrrevRxns,:) sparse(nNegIrrev,nPosIrrev + nPosRev) spdiags(-bigMNegIrrev,0,nNegIrrev,nNegIrrev) sparse(nNegIrrev,nNegRev + nPosIrrev + nPosRev*6) speye(nNegIrrev) sparse(nNegIrrev,nNegRev*varsPerNegRev + nEssRev*6+nMetVars)];
-0303 
-0304 nrRows1 = [sEye(negRevRxns,:) sparse(nNegRev,nYBlock + nPosIrrev + nPosRev*6 + nNegIrrev) speye(nNegRev)*-1 speye(nNegRev) sparse(nNegRev,nNegRev + nEssRev*6+nMetVars)];
-0305 nrRows2 = [sparse(nNegRev,nRxns + nPosIrrev + nPosRev + nNegIrrev) spdiags(-bigMNegRev,0,nNegRev,nNegRev) sparse(nNegRev,nPosIrrev + nPosRev*6 + nNegIrrev) speye(nNegRev) speye(nNegRev) speye(nNegRev) sparse(nNegRev,nEssRev*6+nMetVars)];
-0306 nrRows = [nrRows1;nrRows2];
-0307 
-0308 erRows1 = [sEye(essRevRxns,:) sparse(nEssRev,nYBlock + nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev*varsPerNegRev) speye(nEssRev)*-1 speye(nEssRev) sparse(nEssRev, nEssRev*4+nMetVars)];
-0309 erRows2 = [sparse(nEssRev,nRxns + nYBlock + nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev*varsPerNegRev) speye(nEssRev) speye(nEssRev) speye(nEssRev)*-1 sparse(nEssRev, nEssRev*3+nMetVars)];
-0310 erRows3 = [sparse(nEssRev,nRxns + nYBlock + nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev*varsPerNegRev) speye(nEssRev) sparse(nEssRev, nEssRev*2) spdiags(-bigMEssRev,0,nEssRev,nEssRev) speye(nEssRev) sparse(nEssRev, nEssRev+nMetVars)];
-0311 erRows4 = [sparse(nEssRev,nRxns + nYBlock + nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev*varsPerNegRev + nEssRev) speye(nEssRev) sparse(nEssRev, nEssRev) spdiags(bigMEssRev,0,nEssRev,nEssRev) sparse(nEssRev, nEssRev) speye(nEssRev) sparse(nEssRev, nMetVars)];
-0312 
-0313 %now the mets
-0314 if ~isempty(metData)
-0315     %Order of rxn "on" vars: Ypi Ypr Yni Ynr. This is a bit messy, since they are not in the
-0316     %same order as the reactions in the S matrix or metData. We therefore resort the vars in
-0317     %metData to match the order of the vars.
-0318     srtMetData = sparse([metData(:,posIrrevRxns) metData(:,posRevRxns) metData(:,negIrrevRxns) metData(:,negRevRxns)]);
-0319     
-0320     %First the setup for giving bonus if the met is included.
-0321     %The mon vars come first followed by the mv1 vars
-0322     metRows1 = [sparse(nMetabolMets,nRxns) -srtMetData sparse(nMetabolMets,  nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev*varsPerNegRev + nEssRev*6) speye(nMetabolMets) speye(nMetabolMets) sparse(nMetabolMets, 4*nMetNegRev + nMetNegIrrev)];
-0323     %Then negative rev:
-0324     % Variable order: eye(vnrbm),eye(vnrvm1),eye(vnrvm2),eye(vnrvm3)
-0325     % vnrp,vnrn are the two first variables among the neg rev vars.
-0326     % For reversible, we need a bool to make sure that one of vnrp and vnrn stays zero:
-0327     %    vnrbm E {0,1}:  vnrp <= 100*vnrbm (if bool is 0, vnrp is zero): vnrp - 100*vnrbm + vnrvm1 == 0, vnrvm1 >= 0 (metRows2)
-0328     %    vnrn <= (1-vnrbm)*100 (if bool is one, vnrn is zero): vnrn + 100*vnrbm + vnrvm2 == 0, -100 <= vnrvm2 <= inf (metRows3)
-0329     % We then also say that vnrp + vnrn >= 0.1*Yi, -0.1*Yi + vnrp + vnrn - vnrvm3 == 0, vnrvm3 >= 0 (metRows4)
-0330     nrEye = speye(nNegRev);
-0331     %Force a column: when no met reaction is negative-reversible, indexing a
-0332     %scalar bigMNegRev with an all-false mask returns a 0-by-0 empty, which
-0333     %the spdiags calls below reject (they need a 0-by-1 column).
-0334     bigMMetNegRev = bigMNegRev(metNegRev(negRevRxns));
-0335     bigMMetNegRev = bigMMetNegRev(:);
-0336     %vnrp - M*vnrbm + vnrvm1 == 0
-0337     metRows2 = [sparse(nMetNegRev,nRxns + nYBlock + nPosIrrev + nPosRev*6 + nNegIrrev) ... %zeros up to vnrp
-0338                 nrEye(metNegRev(negRevRxns),:) ... %vnrp
-0339                 sparse(nMetNegRev, nNegRev*(varsPerNegRev-1) + nEssRev*6 + nMetabolMets*2) ... %zeros up to vnrbm
-0340                 spdiags(-bigMMetNegRev,0,nMetNegRev,nMetNegRev) ... %vnrbm
-0341                 speye(nMetNegRev) ... % vnrvm1
-0342                 sparse(nMetNegRev, nMetNegRev*2 + nMetNegIrrev)];%fill up the rest with zeros
-0343     %vnrn + M*vnrbm + vnrvm2 == 0
-0344     metRows3 = [sparse(nMetNegRev,nRxns + nYBlock + nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev) ... %zeros up to vnrn
-0345                 nrEye(metNegRev(negRevRxns),:) ... %vnrp
-0346                 sparse(nMetNegRev, nNegRev*(varsPerNegRev-2) + nEssRev*6 + nMetabolMets*2) ... %zeros up to vnrbm
-0347                 spdiags(bigMMetNegRev,0,nMetNegRev,nMetNegRev) ... %vnrbm
-0348                 sparse(nMetNegRev, nMetNegRev) ... %zeros up to vnrvm2
-0349                 speye(nMetNegRev) ... % vnrvm2
-0350                 sparse(nMetNegRev, nMetNegRev + nMetNegIrrev)];%fill up the rest with zeros
-0351     %-0.1*Yi + vnrp + vnrn - vnrvm3 == 0
-0352     metRows4 = [sparse(nMetNegRev,nRxns + nPosIrrev + nPosRev + nNegIrrev) ... %zeros up to Yi for neg rev
-0353                 nrEye(metNegRev(negRevRxns),:)*-0.1 ... %Yi for met neg rev
-0354                 sparse(nMetNegRev, nPosIrrev + nPosRev*6 + nNegIrrev) ... %zeros up to vnrp
-0355                 nrEye(metNegRev(negRevRxns),:) ... %vnrp
-0356                 nrEye(metNegRev(negRevRxns),:) ... %vnrn
-0357                 sparse(nMetNegRev, nNegRev*(varsPerNegRev-2) + nEssRev*6 + nMetabolMets*2 + nMetNegRev*3) ...%zeros up to vnrvm3
-0358                 speye(nMetNegRev)*-1 ... % vnrvm3
-0359                 sparse(nMetNegRev, nMetNegIrrev)];%fill up the rest with zeros
-0360     
-0361     %Then negative irrev, i.e. flux - 0.1*Yi - vnim == 0
-0362     niEye = speye(nNegIrrev);
-0363     metRows5 = [sEye(metNegIrrev,:) ... %flux
-0364                 sparse(nMetNegIrrev, nPosIrrev + nPosRev) ... % zeros up to Yi for neg irrev
-0365                 niEye(metNegIrrev(negIrrevRxns),:)*-0.1 ... %Yi for met neg irrev
-0366                 sparse(nMetNegIrrev, nNegRev + nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev*varsPerNegRev + nEssRev*6 + nMetabolMets*2 + 4*nMetNegRev) ... %zeros up to vnim
-0367                 -speye(nMetNegIrrev) ]; %vnim
-0368     metRows = [metRows1;metRows2;metRows3;metRows4;metRows5];
-0369     metVarC = [ones(nMetabolMets,1)*-prodWeight;zeros(nMetVars - nMetabolMets,1)];
-0370     %vnrbm is boolean, so between 0 and 1
-0371     %vnrvm1 >= 0
-0372     %-100 <= vnrvm2 <= inf
-0373     % vnrvm3 >= 0
-0374     %0 <= vnim <= Inf
-0375     metLb = [zeros(nMetabolMets*2 + 2*nMetNegRev,1);-bigMMetNegRev;zeros(nMetNegRev + nMetNegIrrev,1)];
-0376     metUb = [ones(nMetabolMets,1);inf(nMetabolMets,1);ones(nMetNegRev,1);inf(3*nMetNegRev + nMetNegIrrev,1)];
-0377     metVartype = [repmat('C', 1, nMetabolMets*2), ...
-0378                   repmat('B', 1, nMetNegRev), ...
-0379                   repmat('C', 1, 3*nMetNegRev + nMetNegIrrev)];
-0380 
-0381 else
-0382     metRows = [];
-0383     metVarC = [];
-0384     metLb = [];
-0385     metUb = [];
-0386     metVartype = [];
-0387 end
-0388 
-0389 prob.a = [sRow;piRows;prRows1;prRows2;prRows3;prRows4;niRows;nrRows;erRows1;erRows2;erRows3;erRows4;metRows];
-0390 prob.A = prob.a;
-0391 prob.b = zeros(size(prob.A,1),1);
-0392 prob.c = [zeros(nRxns,1);rxnScores(posIrrevRxns)*-1;rxnScores(posRevRxns)*-1;rxnScores(negIrrevRxns)*-1;rxnScores(negRevRxns)*-1;zeros(nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev*varsPerNegRev + nEssRev*6,1);metVarC];
-0393 prob.lb = [milpModel.lb;zeros(nYBlock + nPosIrrev + 5*nPosRev,1);-bigMPosRev;zeros(nNegIrrev+nNegRev*varsPerNegRev+nEssRev*2,1);ones(nEssRev,1)*forceOnLim;zeros(nEssRev*2,1);-bigMEssRev;metLb];
-0394 prob.lb(essIrrevRxns) = forceOnLimEss(essIrrevRxns);%force flux for the ess irrev rxns - this is the only way these are handled
-0395 prob.ub = [milpModel.ub;ones(nYBlock,1);inf(nPosIrrev+nPosRev*2,1);ones(nPosRev,1);inf(3*nPosRev+nNegIrrev+varsPerNegRev*nNegRev+3*nEssRev,1);ones(nEssRev,1);inf(2*nEssRev,1);metUb];
-0396 
-0397 prob.vartype = [repmat('C', 1, nRxns + nPosIrrev + nPosRev), ...
-0398                 repmat('B', 1, nNegIrrev + nNegRev), ...
-0399                 repmat('C', 1, nPosIrrev + 2*nPosRev), ... 
-0400                 repmat('B', 1, nPosRev), ...
-0401                 repmat('C', 1, 3*nPosRev + nNegIrrev + varsPerNegRev*nNegRev + 3*nEssRev), ...
-0402                 repmat('B', 1, nEssRev), ...
-0403                 repmat('C', 1, 2*nEssRev), ...
-0404                 metVartype];
-0405             
-0406 onoffVarInd = (1:(nPosIrrev + nPosRev + nNegIrrev + nNegRev)) + nRxns;
-0407 onoffPosIrrev = onoffVarInd(1:nPosIrrev);
-0408 onoffPosRev = onoffVarInd((1:nPosRev)+nPosIrrev);
-0409 onoffNegIrrev = onoffVarInd((1:nNegIrrev)+nPosIrrev+nPosRev);
-0410 onoffNegRev = onoffVarInd((1:nNegRev)+nPosIrrev+nPosRev+nNegIrrev);
-0411 
-0412 metVarInd = (1:nMetabolMets) + (length(prob.vartype) - nMetVars);
-0413 
-0414 if allowExcretion
-0415     %S*v >= 0: a metabolite may be produced in excess of what is consumed,
-0416     %and the surplus is implicitly excreted. 'L' would say S*v <= 0, which
-0417     %lets a metabolite be consumed without ever being produced -- free
-0418     %uptake, the opposite of excretion, and enough for the MILP to switch a
-0419     %reaction on by conjuring its substrates out of nothing.
-0420     prob.csense = [repmat('G', 1, length(milpModel.mets)), ...
-0421                   repmat('E', 1, length(prob.b) - length(milpModel.mets))];
-0422 else
-0423     %One character per row: optimizeProb maps csense elementwise for glpk and
-0424     %cobra, so a scalar '=' only happens to work on gurobi.
-0425     prob.csense = repmat('E', 1, length(prob.b));
-0426 end
-0427 
-0428 params.intTol = 10^-7; %This value is very important. If set too low
-0429                        %there is a risk that gurobi fails due to numerical
-0430                        %issues - this happened for Gurobi v. 10.0 with TestModelL.
-0431                        %On the other hand, it should not be too large
-0432                        %either. With this value, fluxes of 10-7 can slip
-0433                        %through, which should be fine. Another option if
-0434                        %this becomes a problem is to set NumericFocus=2,
-0435                        %which makes the solver slower but fixes the issue
-0436                        %with TestModelL.
-0437 
-0438 prob.osense = 1; %minimize
-0439 
-0440 if ~isempty(startVals)
-0441      prob.start = startVals; %This doesn't work...
-0442 end
-0443 
-0444 res=optimizeProb(prob,params,verbose);
-0445 
-0446 
-0447 if ~checkSolution(res)
-0448     if strcmp(res.origStat, 'TIME_LIMIT')
-0449         EM='Time limit reached without finding a solution. Try increasing the TimeLimit parameter.';
-0450     else
-0451         EM='The problem is infeasible';
-0452     end
-0453     error('RAVEN:badInput', '%s', EM);
-0454 end
-0455 
-0456 if resolveTies && (nNegIrrev + nNegRev) > 0
-0457     %This MILP is highly degenerate: many reaction subsets reach the same
-0458     %score optimum, and the solver returns an arbitrary one -- reproducible
-0459     %for a fixed solver build + Seed, but fragile to a solver version bump
-0460     %(raven-gecko-parity#104). This runs a lexicographic phase 2, holding
-0461     %the score objective at its optimum with a floor constraint: first
-0462     %minimise the count of "on" removable reactions (the sparsest optimum),
-0463     %then, among the sparsest, minimise their summed reaction-id rank
-0464     %(prefer lower ids). Only the negative-score "on" variables (onoffNegIrrev,
-0465     %onoffNegRev) are a genuine tie to resolve: positive-score ones are
-0466     %continuous, pinned near 1 by the score objective rather than a free
-0467     %binary choice. Ports raven-toolbox's _resolve_ties (raven-toolbox#114).
-0468     negCols = [onoffNegIrrev, onoffNegRev];
-0469     negRxnIdx = [negIrrevRxns; negRevRxns];
-0470     primaryObj = full(prob.c' * res.full);
-0471     tol = max(abs(primaryObj)*1e-7, 1e-7);
-0472 
-0473     floorRow = prob.c';
-0474     tieProb = prob;
-0475     tieProb.A = [prob.A; floorRow];
-0476     tieProb.a = tieProb.A;
-0477     tieProb.b = [prob.b; primaryObj+tol];
-0478     tieProb.csense = [prob.csense 'L'];
-0479 
-0480     %An integer objective needs only a cheap absolute-gap proof, not a tiny
-0481     %relative one -- mirrors raven-toolbox's own MIPGap=0/MIPGapAbs=0.4 for
-0482     %the same two phases.
-0483     tieParams = params;
-0484     tieParams.MIPGap = 0;
-0485     tieParams.MIPGapAbs = 0.4;
-0486 
-0487     unproven = {};
-0488 
-0489     %Phase 2a: fewest "on" removable reactions among the score-optimal solutions.
-0490     countObj = zeros(size(prob.c));
-0491     countObj(negCols) = 1;
-0492     tieProb.c = countObj;
-0493     res2a = optimizeProb(tieProb,tieParams,verbose);
-0494     [isFeasible2a, isOptimal2a] = checkSolution(res2a);
-0495     if ~isOptimal2a
-0496         unproven{end+1} = 'phase2a-parsimony';
-0497     end
-0498     if isFeasible2a
-0499         kmin = full(countObj'*res2a.full);
-0500 
-0501         %Phase 2b: among the sparsest, prefer lower reaction ids -- a
-0502         %deterministic tie-break independent of solver/seed.
-0503         countRow = sparse(1,size(tieProb.A,2));
-0504         countRow(negCols) = 1;
-0505         tieProb2b = tieProb;
-0506         tieProb2b.A = [tieProb.A; countRow];
-0507         tieProb2b.a = tieProb2b.A;
-0508         tieProb2b.b = [tieProb.b; kmin+0.5];
-0509         tieProb2b.csense = [tieProb.csense 'L'];
-0510 
-0511         [~, sortOrder] = sort(model.rxns(negRxnIdx));
-0512         ranks = zeros(numel(negRxnIdx),1);
-0513         ranks(sortOrder) = (1:numel(negRxnIdx))';
-0514         idObj = zeros(size(prob.c));
-0515         idObj(negCols) = ranks;
-0516         tieProb2b.c = idObj;
-0517         res2b = optimizeProb(tieProb2b,tieParams,verbose);
-0518         [isFeasible2b, isOptimal2b] = checkSolution(res2b);
-0519         if ~isOptimal2b
-0520             unproven{end+1} = 'phase2b-idrank';
-0521         end
-0522         if isFeasible2b
-0523             %Adopt the tie-resolved solution for the on/off read-out below.
-0524             res = res2b;
-0525         end
-0526     end
-0527     if ~isempty(unproven)
-0528         EM = ['ftINITInternalAlg tie resolution did not converge (' strjoin(unproven,', ') '): the selection among equally score-optimal solutions is itself an unproven incumbent, so resolveTies has reduced but not removed the run-to-run spread. Raise params.TimeLimit for a proven tie-break.'];
-0529         warning('RAVEN:warning', '%s', EM);
-0530     end
-0531 end
-0532 
-0533 %get the on/off vals
-0534 onoff = ones(nRxnsWithOnOff,1);%standard is on, i.e. all reactions not included in the problem + ess is on
-0535 onoff(posIrrevRxns) = res.full(onoffPosIrrev);
-0536 onoff(posRevRxns) = res.full(onoffPosRev);
-0537 onoff(negIrrevRxns) = res.full(onoffNegIrrev);
-0538 onoff(negRevRxns) = res.full(onoffNegRev);
-0539 
-0540 onoff2 = zeros(nRxnsWithOnOff,1);%standard is off, i.e. all reactions not included in the problem + ess is off
-0541 onoff2(posIrrevRxns) = res.full(onoffPosIrrev);
-0542 onoff2(posRevRxns) = res.full(onoffPosRev);
-0543 onoff2(negIrrevRxns) = res.full(onoffNegIrrev);
-0544 onoff2(negRevRxns) = res.full(onoffNegRev);
-0545 
-0546 
-0547 %investigate a bit - this code could be used for fault finding. We have
-0548 %this code commented out, but it is definitely worth testing this if for example
-0549 %the solver is changed.
-0550 %problematic = onoff < 0.99 & onoff > 0.01 & (rxnScores ~= 0).';
-0551 %if sum(problematic) > 0
-0552 %    disp("There are reactions that are in between on and off in the MILP. This may be due to the MILP parameter settings, the tolerances may be too large.")
-0553 %    disp(["No problematic reactions: ", num2str(sum(problematic))])
-0554 %end
-0555 
-0556 %so, it is only positive that are problematic. Likely because the 0.1 is too large. Test to change to 0.01
-0557 %unique(onoff(onoff < 0.99 & onoff > 0.01))
-0558 
-0559 %Get all reactions used in the irreversible model
-0560 %The reaction score check is for metabolomics - we add those reactions as
-0561 %positive even though the score is 0. And, we do not want them included in the
-0562 %results.
-0563 deletedRxns=(onoff < 0.5).' & (rxnScores ~= 0).'; 
-0564 turnedOnRxns=(onoff2 >= 0.5).' & (rxnScores ~= 0).';
-0565 
-0566 fluxes = res.full(1:nRxns);
-0567 
-0568 %extract the met data
-0569 metProduction = logical(round(res.full(metVarInd)));
-0570 
-0571 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/INIT/mergeLinear.html b/doc/INIT/mergeLinear.html deleted file mode 100644 index b1dbb106..00000000 --- a/doc/INIT/mergeLinear.html +++ /dev/null @@ -1,284 +0,0 @@ - - - - Description of mergeLinear - - - - - - - - - -
Home > INIT > mergeLinear.m
- - - -

mergeLinear -

- -

PURPOSE ^

-
mergeLinear
- -

SYNOPSIS ^

-
function [reducedModel,origRxnIds,groupIds,reversedRxns]=mergeLinear(model, noMergeRxns)
- -

DESCRIPTION ^

-
 mergeLinear
-   Simplifies a model by merging rxns with linear dependencies. There are two
-   differences from the function in simplifyModel:
-   1) Here we return which reactions were merged
-   2) Here we allow reversible reactions to be merged as well, which will 
-      probably reduce the size of the model more.
-
-   model                 a model structure
-   noMergeRxns           Cell array with reaction IDs that are not allowed to be
-                         merged
-
-   reducedModel          an updated model structure
-   origRxnIds            Cell array: original rxn ids, used together with the groupIds variable
-   groupIds              Vector of ids: merged rxns have the same id, same length as origRxnIds
-                         0 means a reaction was not merged.
-   reversedRxns          Vector of booleans, saying for each rxn if it is reversed in the combined rxns.
-
- Usage: [reducedModel,origRxnIds,groupIds]=mergeLinear(model,noMergeRxns)
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • prepINITModel prepINITModel Precompute omics-independent data for ftINIT.
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [reducedModel,origRxnIds,groupIds,reversedRxns]=mergeLinear(model, noMergeRxns)
-0002 % mergeLinear
-0003 %   Simplifies a model by merging rxns with linear dependencies. There are two
-0004 %   differences from the function in simplifyModel:
-0005 %   1) Here we return which reactions were merged
-0006 %   2) Here we allow reversible reactions to be merged as well, which will
-0007 %      probably reduce the size of the model more.
-0008 %
-0009 %   model                 a model structure
-0010 %   noMergeRxns           Cell array with reaction IDs that are not allowed to be
-0011 %                         merged
-0012 %
-0013 %   reducedModel          an updated model structure
-0014 %   origRxnIds            Cell array: original rxn ids, used together with the groupIds variable
-0015 %   groupIds              Vector of ids: merged rxns have the same id, same length as origRxnIds
-0016 %                         0 means a reaction was not merged.
-0017 %   reversedRxns          Vector of booleans, saying for each rxn if it is reversed in the combined rxns.
-0018 %
-0019 % Usage: [reducedModel,origRxnIds,groupIds]=mergeLinear(model,noMergeRxns)
-0020 
-0021 
-0022 reducedModel=model;
-0023 
-0024 
-0025 reducedModel.genes={};
-0026 reducedModel.rxnGeneMat=sparse(numel(reducedModel.rxns),0);
-0027 reducedModel.grRules(:)={''};
-0028 
-0029 if isfield(reducedModel,'geneShortNames')
-0030     reducedModel.geneShortNames={};
-0031 end
-0032 if isfield(reducedModel,'proteins')
-0033     reducedModel.proteins={};
-0034 end
-0035 if isfield(reducedModel,'geneMiriams')
-0036     reducedModel.geneMiriams={};
-0037 end
-0038 if isfield(reducedModel,'geneComps')
-0039     reducedModel.geneComps=[];
-0040 end
-0041 
-0042 nextGroupId = 1;
-0043 origRxnIds = reducedModel.rxns;
-0044 groupIds = zeros(numel(reducedModel.rxns),1);
-0045 reversedRxns = false(numel(reducedModel.rxns),1);
-0046 %rxn id -> original index lookup, built once rather than re-scanning all
-0047 %of origRxnIds (via strcmp) for every merge candidate examined below.
-0048 origRxnIdMap = containers.Map(origRxnIds,num2cell(1:numel(origRxnIds)));
-0049 
-0050 %Loop through and iteratively group linear reactions
-0051 while 1
-0052     %Get the banned reaction indexes. Note that the indexes will change
-0053     %in each iteration, but the names will not as they won't be merged
-0054     %with any other reaction
-0055     bannedIndexes=getIndexes(reducedModel,noMergeRxns,'rxns');
-0056 
-0057     %Select all metabolites that are only present as reactants/products
-0058     %in one reaction
-0059     twoNonZero = find(sum(reducedModel.S ~= 0, 2) == 2);
-0060 
-0061     mergedSome=false;
-0062 
-0063     %Loop through each of them and see if the reactions should be
-0064     %merged
-0065     for i=1:numel(twoNonZero)
-0066         involvedRxns=find(reducedModel.S(twoNonZero(i),:));
-0067         %Check that we can have one positive and one negative
-0068         pos = sum(reducedModel.S(twoNonZero(i),involvedRxns).' > 0 | reducedModel.rev(involvedRxns));
-0069         neg = sum(reducedModel.S(twoNonZero(i),involvedRxns).' < 0 | reducedModel.rev(involvedRxns));
-0070         
-0071                 
-0072         %Check so that one or both of the reactions haven't been merged
-0073         %already
-0074         if numel(involvedRxns)==2 && isempty(intersect(bannedIndexes,involvedRxns)) && pos >= 1 && neg >= 1
-0075             %first, take care of a special case: If the first reaction is producing the metabolite and if it is reversible,
-0076             %and the second is also producing it and is not reversible, change the order - the code below will not work otherwise
-0077             if reducedModel.rev(involvedRxns(1)) && (~reducedModel.rev(involvedRxns(2))) && ...
-0078                (reducedModel.S(twoNonZero(i),involvedRxns(1)) > 0) && (reducedModel.S(twoNonZero(i),involvedRxns(2)) > 0)
-0079                 involvedRxns = flip(involvedRxns);
-0080             end
-0081             
-0082             %first make sure the first reaction is producing the metabolite
-0083             if reducedModel.S(twoNonZero(i),involvedRxns(1)) < 0
-0084                 %it is not producing the metabolite - fix that
-0085                 %first choice: use the second reaction as producer if it is producing
-0086                 if reducedModel.S(twoNonZero(i),involvedRxns(2)) > 0
-0087                     involvedRxns = flip(involvedRxns);%make the second the first
-0088                 else
-0089                     %now we know that the second reaction is not producing, so we can safely try to make the first a producer
-0090                     if reducedModel.rev(involvedRxns(1)) == 1
-0091                         [reducedModel,reversedRxns] = flipRxn(reducedModel, involvedRxns(1), groupIds, reversedRxns);
-0092                     else %ok, finally try to flip the second reaction
-0093                         if reducedModel.rev(involvedRxns(2)) == 1
-0094                             [reducedModel,reversedRxns] = flipRxn(reducedModel, involvedRxns(2), groupIds, reversedRxns);
-0095                             involvedRxns = flip(involvedRxns);%make the second the first
-0096                         else
-0097                             error('We should never end up here!');
-0098                         end
-0099                     end
-0100                 end
-0101             end
-0102             %Now, make sure the second rxn is a consumer
-0103             if reducedModel.S(twoNonZero(i),involvedRxns(2)) > 0
-0104                 if reducedModel.rev(involvedRxns(2)) == 1
-0105                     [reducedModel,reversedRxns] = flipRxn(reducedModel, involvedRxns(2), groupIds, reversedRxns);
-0106                 else
-0107                     error('We should never end up here!');
-0108                 end
-0109             end
-0110 
-0111             %Calculate how many times the second reaction has to be
-0112             %multiplied before being merged with the first
-0113             stoichRatio=abs(reducedModel.S(twoNonZero(i),involvedRxns(1))/reducedModel.S(twoNonZero(i),involvedRxns(2)));
-0114 
-0115             %Add the second to the first
-0116             reducedModel.S(:,involvedRxns(1))=reducedModel.S(:,involvedRxns(1))+reducedModel.S(:,involvedRxns(2))*stoichRatio;
-0117 
-0118             %Clear the second reaction
-0119             reducedModel.S(:,involvedRxns(2))=0;
-0120 
-0121             %This is to prevent numerical issues. It should be 0
-0122             %already
-0123             reducedModel.S(twoNonZero(i),involvedRxns(1))=0;
-0124 
-0125             %At this point the second reaction is certain to be deleted
-0126             %in a later step and can therefore be ignored
-0127 
-0128             %Recalculate the bounds for the new reaction. This can be
-0129             %problematic since the scale of the bounds may change
-0130             %dramatically. Let the most constraining reaction determine
-0131             %the new bound
-0132             lb1=reducedModel.lb(involvedRxns(1));
-0133             lb2=reducedModel.lb(involvedRxns(2));
-0134             ub1=reducedModel.ub(involvedRxns(1));
-0135             ub2=reducedModel.ub(involvedRxns(2));
-0136 
-0137             if lb2~=-inf
-0138                 reducedModel.lb(involvedRxns(1))=max(lb1,lb2/stoichRatio);
-0139             end
-0140             if ub2~=inf
-0141                 reducedModel.ub(involvedRxns(1))=min(ub1,ub2/stoichRatio);
-0142             end
-0143             
-0144             %take care of the .rev flag - it could be that the combined rxn changes from rev to irrev
-0145             reducedModel.rev(involvedRxns(1)) = reducedModel.rev(involvedRxns(1))*reducedModel.rev(involvedRxns(2));%this is a way to do an "and" operation with 0 and 1 numbers
-0146 
-0147             %Then recalculate the objective coefficient. The resulting
-0148             %coefficient is the weighted sum of the previous
-0149             reducedModel.c(involvedRxns(1))=reducedModel.c(involvedRxns(1))+reducedModel.c(involvedRxns(2))*stoichRatio;
-0150 
-0151             %store which reactions that have been merged
-0152             rxnInd1 = origRxnIdMap(reducedModel.rxns{involvedRxns(1)});
-0153             rxnInd2 = origRxnIdMap(reducedModel.rxns{involvedRxns(2)});
-0154             grpId = max(groupIds(rxnInd1),groupIds(rxnInd2));
-0155             if grpId == 0
-0156                grpId = nextGroupId;
-0157                nextGroupId = nextGroupId + 1;
-0158             end
-0159 
-0160             if groupIds(rxnInd1) ~= grpId
-0161                if groupIds(rxnInd1) == 0
-0162                    %not merged before, just set the group id
-0163                    groupIds(rxnInd1) = grpId;
-0164                else
-0165                    %merged before - all rxns with the same group id should be changed
-0166                    groupIds(groupIds == groupIds(rxnInd1)) = grpId;
-0167                end
-0168             end
-0169             if groupIds(rxnInd2) ~= grpId
-0170                if groupIds(rxnInd2) == 0
-0171                    %not merged before, just set the group id
-0172                    groupIds(rxnInd2) = grpId;
-0173                else
-0174                    %merged before - all rxns with the same group id should be changed
-0175                    groupIds(groupIds == groupIds(rxnInd2)) = grpId;
-0176                end
-0177             end
-0178 
-0179             %Iterate again
-0180             mergedSome=true;
-0181         end
-0182     end
-0183 
-0184     %All possible reactions merged
-0185     if mergedSome==false
-0186         break;
-0187     end
-0188 
-0189     %Now delete all reactions that involve no metabolites
-0190     I=find(sum(reducedModel.S~=0,1)==0);
-0191 
-0192     %Remove reactions
-0193     reducedModel=removeReactions(reducedModel,I);
-0194 
-0195     %Remove metabolites
-0196     notInUse=sum(reducedModel.S~=0,2)==0;
-0197     reducedModel=removeMets(reducedModel,notInUse);
-0198 end
-0199     
-0200 function [model1,reversedRxns1] = flipRxn(model1, rxnInd, groupIds1, reversedRxns1)
-0201     model1.S(:,rxnInd) = model1.S(:,rxnInd)*-1;
-0202     %swap the bounds
-0203     ub = model1.ub(rxnInd);
-0204     model1.ub(rxnInd) = -model1.lb(rxnInd);
-0205     model1.lb(rxnInd) = -ub;
-0206     %flip the objective
-0207     model1.c(rxnInd) = -model1.c(rxnInd);
-0208     
-0209     %now take care of the reversedRxns - if this is a group, reverse all of the
-0210     %reactions in the group in the reversedRxns index - they will all be reversed at the
-0211     %same time since they are the same rxn.
-0212     rxnIndices = rxnInd;
-0213     if groupIds1(rxnInd) > 0
-0214         rxnIndices = find(groupIds1 == groupIds1(rxnInd));
-0215     end
-0216     reversedRxns1(rxnIndices) = ~reversedRxns1(rxnIndices);
-0217 end
-0218 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/INIT/prepINITModel.html b/doc/INIT/prepINITModel.html deleted file mode 100644 index 6cfd0428..00000000 --- a/doc/INIT/prepINITModel.html +++ /dev/null @@ -1,434 +0,0 @@ - - - - Description of prepINITModel - - - - - - - - - -
Home > INIT > prepINITModel.m
- - - -

prepINITModel -

- -

PURPOSE ^

-
prepINITModel Precompute omics-independent data for ftINIT.
- -

SYNOPSIS ^

-
function prepData = prepINITModel(origRefModel, taskStruct, varargin)
- -

DESCRIPTION ^

-
 prepINITModel  Precompute omics-independent data for ftINIT.
-
- Runs time-consuming calculation steps that are not dependent on the RNA-Seq
- data.
-
- Parameters
- ----------
- origRefModel : struct
-     the model to use. Expected to be something such as Human-GEM, Mouse-GEM,
-     etc.
- taskStruct : struct
-     the essential tasks. Can be loaded with for example
-     taskStruct = parseTaskList('../data/metabolicTasks_Essential.txt').
-
- Name-Value Arguments
- --------------------
- spontRxnNames : cell
-     the spontaneous rxns (default {}).
- convertGenes : logical
-     if true the genes are converted to gene names (from ENSEMBL). This
-     relies on translateGrRules, which is distributed with Human-GEM rather
-     than with RAVEN, so Human-GEM has to be on the MATLAB path (default
-     false).
- customRxnsToIgnore : cell
-     these reactions can be ignored in the ignore mask (specifying b7=1)
-     (default {}).
- extComp : char
-     name of the external compartment, typically 's' or 'e'. This is used for
-     identifying exch and import rxns (default 'e').
- skipScaling : logical
-     if true the scaling step is not run on the minimal model. The scaling is
-     there to remove large differences between the stoichiometric coefficients
-     within a reaction, since such differences creates numerical issues in the
-     ftINIT algorithm due to limitations in solver resolution. However, the
-     current scaling step is also risky and may lead to that some reactions
-     cannot carry flux, since it changes the stoichiometry of the reactions
-     with large differences. If you experience problems where the solution is
-     infeasible, it may be worth trying to turn off the scaling. Note that it
-     is only the minModel that is scaled, the scaling will not be present in
-     the final model (default false).
- runParallel : logical
-     true to run the task-essentiality check in Step 3 (checkTasks) across
-     parallel workers, since each task is independent of the others (see
-     parallelWorkersRAVEN). Default false, matching the previous (serial)
-     behaviour.
-
- Returns
- -------
- prepData : struct
-     the resulting prepData structure which is used as input to ftINIT.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function prepData = prepINITModel(origRefModel, taskStruct, varargin)
-0002 % prepINITModel  Precompute omics-independent data for ftINIT.
-0003 %
-0004 % Runs time-consuming calculation steps that are not dependent on the RNA-Seq
-0005 % data.
-0006 %
-0007 % Parameters
-0008 % ----------
-0009 % origRefModel : struct
-0010 %     the model to use. Expected to be something such as Human-GEM, Mouse-GEM,
-0011 %     etc.
-0012 % taskStruct : struct
-0013 %     the essential tasks. Can be loaded with for example
-0014 %     taskStruct = parseTaskList('../data/metabolicTasks_Essential.txt').
-0015 %
-0016 % Name-Value Arguments
-0017 % --------------------
-0018 % spontRxnNames : cell
-0019 %     the spontaneous rxns (default {}).
-0020 % convertGenes : logical
-0021 %     if true the genes are converted to gene names (from ENSEMBL). This
-0022 %     relies on translateGrRules, which is distributed with Human-GEM rather
-0023 %     than with RAVEN, so Human-GEM has to be on the MATLAB path (default
-0024 %     false).
-0025 % customRxnsToIgnore : cell
-0026 %     these reactions can be ignored in the ignore mask (specifying b7=1)
-0027 %     (default {}).
-0028 % extComp : char
-0029 %     name of the external compartment, typically 's' or 'e'. This is used for
-0030 %     identifying exch and import rxns (default 'e').
-0031 % skipScaling : logical
-0032 %     if true the scaling step is not run on the minimal model. The scaling is
-0033 %     there to remove large differences between the stoichiometric coefficients
-0034 %     within a reaction, since such differences creates numerical issues in the
-0035 %     ftINIT algorithm due to limitations in solver resolution. However, the
-0036 %     current scaling step is also risky and may lead to that some reactions
-0037 %     cannot carry flux, since it changes the stoichiometry of the reactions
-0038 %     with large differences. If you experience problems where the solution is
-0039 %     infeasible, it may be worth trying to turn off the scaling. Note that it
-0040 %     is only the minModel that is scaled, the scaling will not be present in
-0041 %     the final model (default false).
-0042 % runParallel : logical
-0043 %     true to run the task-essentiality check in Step 3 (checkTasks) across
-0044 %     parallel workers, since each task is independent of the others (see
-0045 %     parallelWorkersRAVEN). Default false, matching the previous (serial)
-0046 %     behaviour.
-0047 %
-0048 % Returns
-0049 % -------
-0050 % prepData : struct
-0051 %     the resulting prepData structure which is used as input to ftINIT.
-0052 
-0053 
-0054 p=parseRAVENargs(varargin, {'spontRxnNames',{}; 'convertGenes',false; 'customRxnsToIgnore',{}; 'extComp','e'; 'skipScaling',false; 'runParallel',false});
-0055 spontRxnNames=p.spontRxnNames;
-0056 convertGenes=p.convertGenes;
-0057 customRxnsToIgnore=p.customRxnsToIgnore;
-0058 extComp=p.extComp;
-0059 skipScaling=p.skipScaling;
-0060 runParallel=p.runParallel;
-0061 disp('Step 1: Gene rules')
-0062 [origRefModel.grRules, origRefModel.rxnGeneMat] = standardizeGrRules(origRefModel, true);
-0063 
-0064 if convertGenes %For mouse we might want to translate in the opposite direction - this has to be done before calling this function in that case.
-0065     if ~exist('translateGrRules','file')
-0066         EM=['convertGenes requires translateGrRules, which is distributed with ' ...
-0067             'Human-GEM and not with RAVEN. Add Human-GEM to the MATLAB path, or ' ...
-0068             'convert the gene identifiers before calling prepINITModel.'];
-0069         error('RAVEN:badInput', '%s', EM);
-0070     end
-0071     [origRefModel.grRules, origRefModel.genes, origRefModel.rxnGeneMat] = translateGrRules(origRefModel.grRules, 'Name');
-0072 end
-0073 
-0074 
-0075 % Get list of all dead-end/inacessible/constrained reactions in the model.
-0076 % We don't merge linear dependent reactions here, that will happen later.
-0077 % We also don't remove reversibility here, we don't want that in the final
-0078 % models produced. Therefore, this is done as a separate step.
-0079 % This takes quite some time to run
-0080 disp('Step 2: First simplification')
-0081 [~,deletedDeadEndRxns] = simplifyModel(origRefModel,true,false,true,true,true);
-0082 
-0083 % get reduced model
-0084 cModel = removeReactions(origRefModel,deletedDeadEndRxns,false,true);
-0085 
-0086 
-0087 %Then, run the tasks to find out which reactions are essential. These rxns
-0088 %will be forced to carry flux in the optimization later
-0089 
-0090 % determine reactions essential for tasks - takes ~10 min
-0091 % need to add boundary mets first - but only to a temporary model, we don't
-0092 % want those for further processing
-0093 disp('Step 3: Check tasks (~10 min)')
-0094 if ~isempty(taskStruct)
-0095     bModel = closeModel(cModel);
-0096     [taskReport, essentialRxnMat, ~, essentialFluxes] = checkTasks(bModel,[],true,false,true,taskStruct,'runParallel',runParallel);
-0097 
-0098     %extract the essential rxns:
-0099     sel = sum(essentialRxnMat,2) > 0;
-0100     selInd = find(sel);
-0101     essentialRxns = bModel.rxns(selInd);%382 rxns for human-GEM
-0102 
-0103     %Find metabolites present in taskStruct. We want to avoid removing
-0104     %these metabolites from the final model (even though they may no longer
-0105     %participate in any reacitons) so that the final model is still able to
-0106     %complete all of the tasks without any errors.
-0107     taskMets = union(vertcat(taskStruct.inputs),vertcat(taskStruct.outputs));
-0108     taskMets = union(taskMets, parseRxnEqu(vertcat(taskStruct.equations)));
-0109     modelMets = strcat(cModel.metNames,'[',cModel.comps(cModel.metComps),']');
-0110     [inModel,metInd] = ismember(taskMets,modelMets);
-0111     essentialMetsForTasks = cModel.mets(metInd(inModel));%118 mets
-0112 
-0113     %Remove tasks that cannot be performed
-0114     taskStruct(taskReport.ok == false) = [];
-0115 
-0116 else
-0117     essentialRxns = {};
-0118     essentialMetsForTasks = {};
-0119     taskReport = {};
-0120 end
-0121 
-0122 
-0123 % remove metabolites separately to avoid removing those needed for tasks
-0124 unusedMets = cModel.mets(all(cModel.S == 0,2));%1248 in Human-GEM
-0125 cModel = removeMets(cModel, setdiff(unusedMets,essentialMetsForTasks));
-0126 
-0127 if ~isempty(taskStruct)
-0128 
-0129     %Here, we decide on a direction in which the essential reactions should be forced.
-0130     essentialRevDir = false(length(essentialRxns),1);
-0131     pp = zeros(length(essentialRxns),1);
-0132     nn = zeros(length(essentialRxns),1);
-0133     for i = 1:length(selInd)
-0134         pos = sum(essentialFluxes(selInd(i),essentialRxnMat(selInd(i),:)) > 0);
-0135         neg = sum(essentialFluxes(selInd(i),essentialRxnMat(selInd(i),:)) < 0);
-0136         essentialRevDir(i) = pos < neg;
-0137         pp(i) = pos;
-0138         nn(i) = neg;
-0139     end
-0140 
-0141     %sum(pp>0 & nn>0) %0, so it is not a big problem that different tasks share essential
-0142     %reactions but in different directions, at least no such cases exist now, so we ignore this for now.
-0143 
-0144     %Now create a minimal model for the MILP - we want to make this as small as we can, but we
-0145     %don't want to use this later when we create the final models - just for the first MILP.
-0146     %We don't want boundary metabolites in here either.
-0147 
-0148     %We do a trick with the essential rxns here:
-0149     %What we want is to get rid of the reversibility of the reactions, because reversible reactions require
-0150     %an integer each in the MILP, and we can also get the case that it may be cheaper to force flux in the wrong direction, which
-0151     %will probably not give the gap-filling of reactions that we want.
-0152     %So, we simply change the reversible reactions to irreversible, and flip their direction if the direction
-0153     %to force flux in is negative. We don't need to do anything else, runINIT will then handle all essential rxns
-0154     %as irreversible (and will not add any integers etc. for them).
-0155 
-0156     minModel1 = cModel;
-0157     %for the essential rxns where we should force flux in the positive direction, just make them irreversible
-0158     %for the negative direction, we do the same, but also flip the direction of the reaction
-0159 
-0160     %first flip the reactions with negative direction
-0161     %constructEquations(minModel1,minModel1.rxns(selInd(essentialRevDir))) %looks good
-0162     minModel1 = reverseRxns(minModel1, minModel1.rxns(selInd(essentialRevDir)));
-0163     %constructEquations(minModel2,minModel2.rxns(selInd(essentialRevDir))) %looks good
-0164 
-0165     %Then make them irreversible
-0166     minModel1.rev(selInd) = 0;
-0167     minModel1.lb(selInd) = 0;
-0168 else
-0169     minModel1 = cModel;
-0170 end
-0171 %Now, remove reversibility on any reactions that can only be run in one direction - that simplifies the problem
-0172 %This takes a long time to run (at least an hour or so).
-0173 %This is an important step, we go from 5533 to 3734 reversible rxns, which reduces the size of the MILP,
-0174 %since irreversible rxns are easier to handle.
-0175 disp('Step 4: Second simplification (~1 hour)')
-0176 minModel2 = simplifyModel(minModel1,false,false,false,false,false,false,true);
-0177 
-0178 disp('Step 5: Final work')
-0179 
-0180 %Now, merge all linear dependent rxns - this reduces the size of the model a lot.
-0181 %Size goes from 11888 rxns to 7922 rxns for human-GEM, so this is an important step
-0182 [minModel3,origRxnIds,groupIds]=mergeLinear(minModel2, {});
-0183 
-0184 %we now need to figure out what happened to the essential rxns in the merge step above.
-0185 %Note that it would be ideal to run checkTasks on the result from mergeLinear, but that doesn't work.
-0186 %The exchange rxns have in many cases been merged with other reactions, making it difficult to run
-0187 %the tasks. So, instead we run checkTasks before, and figure out which merged reactions the
-0188 %essential reactions belong to now
-0189 
-0190 if ~isempty(taskStruct)
-0191 
-0192     %find all potential rxns
-0193     rxnIndOrig = ismember(origRxnIds, essentialRxns);
-0194     ids = unique(groupIds(rxnIndOrig));
-0195     ids = ids(ids > 0);%0 means it has not been merged
-0196     rxnCandidates = unique([essentialRxns;origRxnIds(ismember(groupIds, ids))]);%388, essentialRxns is 382
-0197     newEssentialRxns = minModel3.rxns(ismember(minModel3.rxns,rxnCandidates));%196 in Human-GEM
-0198     %setdiff(newEssentialRxns, essentialRxns)%only 2, that is very few
-0199     %setdiff(essentialRxns, newEssentialRxns)%188, that is very many
-0200     %It is probably the case that many essential reactions are linearly dependent on one another, which makes sense.
-0201 else
-0202     newEssentialRxns = {};
-0203 end
-0204 %What is left now is to remove some rxns from the problem, such as exchange rxns and spontaneous rxns.
-0205 %We set the rxn scores of these to exactly 0, and make sure no other scores are zero.
-0206 %runINIT will then handle this, we do >0 and <0 to get positive and negative scores, the rest
-0207 %are not included in the problem.
-0208 %But, this has to happen in the scoring - we identify the reactions in the origrxns here, and then fix this
-0209 %in the scoring for each sample (the function groupRxnScores handles this and is run for each sample).
-0210 
-0211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-0212 %identify the reactions to ignore in the minModel2
-0213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-0214 
-0215 %Identify reactions that should be ignored by tINIT, i.e. tINIT will not remove these regardless of score
-0216 
-0217 
-0218 [~,exchRxnInd] = getExchangeRxns(minModel2);
-0219 toIgnoreExch = false(numel(minModel2.rxns),1);
-0220 toIgnoreExch(exchRxnInd) = true;
-0221 toIgnoreImportRxns = false(numel(minModel2.rxns),1);
-0222 toIgnoreSimpleTransp = false(numel(minModel2.rxns),1);
-0223 toIgnoreAdvTransp = false(numel(minModel2.rxns),1);
-0224 toIgnoreS = false(numel(minModel2.rxns),1);
-0225 
-0226 
-0227 
-0228 %sum(toIgnore) %1500 for Human-GEM
-0229 %find simple transport reactions with no GPRs:
-0230 %We really only skip the reactions with two metabolites
-0231 %where they are the same but different compartments - only skip the transport into the cell for now
-0232 numMets = sum(minModel2.S ~= 0, 1);
-0233 scomp = find(strcmp(minModel2.comps,extComp));
-0234 for i = 1:length(minModel2.rxns)
-0235     %check that it has no GPR and that the rxn has two metabolites
-0236     if strcmp(minModel2.grRules(i),'') && (numMets(i) == 2)
-0237       metsComps = minModel2.metComps(minModel2.S(:,i) ~= 0);
-0238       metNames = minModel2.metNames(minModel2.S(:,i) ~= 0);
-0239       if metsComps(1) ~= metsComps(2) && (metsComps(1) == scomp || metsComps(2) == scomp) %different compartments, one is the extComp
-0240           if strcmp(metNames{1}, metNames{2}) %the same metabolite, it is a transport reaction
-0241               toIgnoreImportRxns(i) = true;
-0242           end
-0243       elseif metsComps(1) ~= metsComps(2) %different compartments, no check for extComp
-0244           if strcmp(metNames{1}, metNames{2}) %the same metabolite, it is a transport reaction
-0245               toIgnoreSimpleTransp(i) = true;
-0246           end
-0247       end
-0248     else %check for advanced
-0249         metsInd = find(minModel2.S(:,i) ~= 0);
-0250         %check that we have an even number of mets that is larger than 2 and that we don't have a GPR
-0251         if rem(length(metsInd),2) == 0 && length(metsInd) > 2 && isempty(minModel2.grRules{i})
-0252             %Now check that we have pairs of metabolites that are transported
-0253             SVals = full(minModel2.S(metsInd,i));
-0254             metNames = minModel2.metNames(metsInd);
-0255             comps = minModel2.metComps(metsInd);
-0256             success = true;
-0257             while ~isempty(metNames)
-0258                 if length(metNames) < 2 %this should not happen
-0259                    error('We should never arrive here!');
-0260                 end
-0261                 metMatch = find(strcmp(metNames(2:length(metNames)),metNames{1})) + 1;
-0262                 if length(metMatch) ~= 1 %we want a match, and only one (if there is some other complex transport rxn, we skip that)
-0263                    success = false;
-0264                    break;
-0265                 end
-0266                 if (SVals(1) + SVals(metMatch)) ~= 0 %the stoichiometry is not right
-0267                    success = false;
-0268                    break;
-0269                 end
-0270                 if comps(1) == comps(metMatch) %they must be in different compartments
-0271                    success = false;
-0272                    break;
-0273                 end
-0274                 %now remove the pair and continue with the next
-0275                 SVals([1;metMatch]) = [];
-0276                 metNames([1;metMatch]) = [];
-0277                 comps([1;metMatch]) = [];
-0278             end
-0279             toIgnoreAdvTransp(i) = success;
-0280         end          
-0281     end
-0282 end
-0283 
-0284 %Spontaneous reactions:
-0285 toIgnoreSpont = ismember(minModel2.rxns, spontRxnNames);%only 10 rxns in human-GEM
-0286 
-0287 %rxns without GPRs in the s compartment (outside the cell)
-0288 sCompInd = find(strcmp(minModel2.comps, extComp));
-0289 for i = 1:length(minModel2.rxns)
-0290     metsInd = find(minModel2.S(:,i) ~= 0);
-0291     %check that it has more than 1 metabolite (not exch rxn) and no GPR
-0292     if length(metsInd) > 1 && isempty(minModel2.grRules{i})
-0293         if  all(minModel2.metComps(metsInd) == sCompInd)
-0294             toIgnoreS(i) = true;
-0295         end
-0296     end
-0297 end
-0298 
-0299 toIgnoreAllWithoutGPRs = cellfun(@isempty,minModel2.grRules);
-0300 
-0301 
-0302 %sum(toIgnore)%in total 2375 rxns in human-GEM
-0303 %sum(toIgnoreAllTransp)%in total 3337 rxns in human-GEM, so 1,000 more
-0304 
-0305 %Now, try to scale the model to become more favorable for the solver.
-0306 %We try to make all fluxes as similar as possible.
-0307 %      There is room for improvement here, this function is pretty simple.
-0308 %      It only rescales reactions, while it would be possible to rescale
-0309 %      metabolites as well (ROS => kROS, albumin => millialbumin, etc., but
-0310 %      scaled freely with a mathematical method). It could potentially open up
-0311 %      for using less strict margins in the MILP, and make it run faster.
-0312 if (skipScaling)
-0313     scaledMinModel = minModel3;
-0314 else
-0315     scaledMinModel = rescaleModelForINIT(minModel3);
-0316     scaledMinModel.ub(scaledMinModel.ub > 0) = 1000;
-0317     scaledMinModel.lb(scaledMinModel.lb < 0) = -1000;
-0318 end
-0319 % create data structure with pre-processing results
-0320 prepData.taskReport = taskReport;
-0321 prepData.essentialRxns = newEssentialRxns; %essential rxns in the minModel
-0322 prepData.taskStruct = taskStruct;
-0323 prepData.refModel = cModel;
-0324 prepData.minModel = scaledMinModel;
-0325 prepData.refModelWithBM = closeModel(cModel); %do this here so we don't have to do this for each sample
-0326 prepData.groupIds = groupIds;
-0327 prepData.essentialMetsForTasks = essentialMetsForTasks;
-0328 
-0329 prepData.toIgnoreExch = toIgnoreExch;
-0330 prepData.toIgnoreImportRxns = toIgnoreImportRxns;
-0331 prepData.toIgnoreSimpleTransp = toIgnoreSimpleTransp;
-0332 prepData.toIgnoreAdvTransp = toIgnoreAdvTransp;
-0333 prepData.toIgnoreSpont = toIgnoreSpont;
-0334 prepData.toIgnoreS = toIgnoreS;
-0335 prepData.toIgnoreCustomRxns = ismember(minModel2.rxns, customRxnsToIgnore);
-0336 prepData.toIgnoreAllWithoutGPRs = toIgnoreAllWithoutGPRs;
-0337 
-0338 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/INIT/rescaleModelForINIT.html b/doc/INIT/rescaleModelForINIT.html deleted file mode 100644 index 8c9a7af3..00000000 --- a/doc/INIT/rescaleModelForINIT.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - Description of rescaleModelForINIT - - - - - - - - - -
Home > INIT > rescaleModelForINIT.m
- - - -

rescaleModelForINIT -

- -

PURPOSE ^

-
rescaleModelForINIT Rescale a model to avoid large stoichiometric differences.
- -

SYNOPSIS ^

-
function model=rescaleModelForINIT(model, varargin)
- -

DESCRIPTION ^

-
 rescaleModelForINIT  Rescale a model to avoid large stoichiometric differences.
-
- Rescales the MILP problem in ftINIT to avoid large differences in flux
- magnitudes between reactions. Such differences cause among other things
- difficulties regarding tolerances for integer variables. For now it just
- scales down all reactions with high stoichiometric coefficients.
-
- Parameters
- ----------
- model : struct
-     the model to be modified (input and output).
-
- Name-Value Arguments
- --------------------
- maxStoichDiff : double
-     all reactions with a stoichiometric coefficient higher than this will be
-     scaled down (default 25).
-
- Returns
- -------
- model : struct
-     the rescaled model structure.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • prepINITModel prepINITModel Precompute omics-independent data for ftINIT.
- - - - -

SOURCE CODE ^

-
0001 function model=rescaleModelForINIT(model, varargin)
-0002 % rescaleModelForINIT  Rescale a model to avoid large stoichiometric differences.
-0003 %
-0004 % Rescales the MILP problem in ftINIT to avoid large differences in flux
-0005 % magnitudes between reactions. Such differences cause among other things
-0006 % difficulties regarding tolerances for integer variables. For now it just
-0007 % scales down all reactions with high stoichiometric coefficients.
-0008 %
-0009 % Parameters
-0010 % ----------
-0011 % model : struct
-0012 %     the model to be modified (input and output).
-0013 %
-0014 % Name-Value Arguments
-0015 % --------------------
-0016 % maxStoichDiff : double
-0017 %     all reactions with a stoichiometric coefficient higher than this will be
-0018 %     scaled down (default 25).
-0019 %
-0020 % Returns
-0021 % -------
-0022 % model : struct
-0023 %     the rescaled model structure.
-0024 
-0025 p=parseRAVENargs(varargin, {'maxStoichDiff',25});
-0026 maxStoichDiff=p.maxStoichDiff;
-0027 
-0028 %Define maxMinRatio to be the ratio between the largest and smallest
-0029 %stoichiometric coefficients in each reaction.
-0030 %Scale all rxns with maxMinRatio > maxStoichDiff - just set all coeffs that are
-0031 %larger than maxStoichDiff*minVal to that value, and center the mean coeff to 1 for all rxns.
-0032 SAbs = abs(model.S);
-0033 for i = 1:numel(model.rxns)
-0034     tmp = SAbs(:,i);
-0035     tmp = tmp(tmp ~= 0);
-0036     mn = min(tmp);
-0037     %modify matrix
-0038     sign = ones(numel(model.mets),1);
-0039     sign(model.S(:,i) < 0) = -1;
-0040     sel = SAbs(:,i) > maxStoichDiff*mn;
-0041     model.S(sel,i) = sign(sel) .* maxStoichDiff*mn;
-0042 end
-0043 %center around 1
-0044 boolMat = model.S ~= 0;
-0045 absMat = abs(model.S);
-0046 rxnScales = sum(boolMat,1)./sum(absMat,1);
-0047 
-0048 model.S = model.S .* rxnScales;
-0049 
-0050 %{
-0051 if (nargin < 2)
-0052     maxStoichVal = 250;
-0053 end
-0054 
-0055 
-0056 %find all reactions containing high stoichiometry coefficients
-0057 maxCoeffs = max(model.S, [], 1);
-0058 rxnsToCheck = maxCoeffs > maxStoichVal;
-0059 
-0060 %for debugging:
-0061 %constructEquations(model, model.rxns(rxnsToCheck))
-0062 
-0063 rxnsToCheckInd = find(rxnsToCheck);
-0064 
-0065 %We just scale all reactions so they don't have a coefficient > maxStoichVal for now
-0066 
-0067 %do this with a loop, doesn't really matter
-0068 for rxnInd = rxnsToCheckInd
-0069     %find the lowest index which is not a pointless metabolite, such as H+, H2O etc.
-0070     rxnS = model.S(:,rxnInd);
-0071     %metInd = find(rxnS ~= 0);
-0072     
-0073     largestCoeff = max(abs(rxnS));
-0074     scaleFactor = maxStoichVal/largestCoeff;
-0075     model.S(:,rxnInd) = model.S(:,rxnInd) .* scaleFactor;
-0076 end
-0077 %}
-0078 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/INIT/tINIT/getINITModel.html b/doc/INIT/tINIT/getINITModel.html deleted file mode 100644 index c7b0392b..00000000 --- a/doc/INIT/tINIT/getINITModel.html +++ /dev/null @@ -1,549 +0,0 @@ - - - - Description of getINITModel - - - - - - - - - -
Home > INIT > tINIT > getINITModel.m
- - - -

getINITModel -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [model, metProduction, essentialRxnsForTasks, addedRxnsForTasks, deletedDeadEndRxns, deletedRxnsInINIT, taskReport]=getINITModel(refModel, tissue, varargin)
-0002 % getINITModel  Generate a model using the original tINIT algorithm.
-0003 %
-0004 % Generates a model using the tINIT algorithm, based on proteomics and/or
-0005 % transcriptomics and/or metabolomics and/or metabolic tasks (PLoS Comput Biol.
-0006 % 2012;8(5):e1002518, Mol Syst Biol. 2014;10:721).
-0007 %
-0008 % ftINIT is the recommended method for context-specific model extraction. Use
-0009 % this function to reproduce models built with the original tINIT. It remains
-0010 % supported, and issues a RAVEN:legacyMethod notice once per session, which
-0011 % warning('off','RAVEN:legacyMethod') silences.
-0012 %
-0013 % Not all settings are reachable through this function; runINIT and fitTasks
-0014 % can be called individually for the ones that are not.
-0015 %
-0016 % Parameters
-0017 % ----------
-0018 % refModel : struct
-0019 %     a model structure. The model should be in the closed form (no exchange
-0020 %     reactions open). Import using import(filename,false). If the model is not
-0021 %     loaded using importModel, it might be that there is no "unconstrained"
-0022 %     field. In that case, manually add the field like:
-0023 %     model.unconstrained=false(numel(model.mets),1).
-0024 % tissue : char
-0025 %     tissue to score for. Should exist in either hpaData.tissues or
-0026 %     arrayData.tissues.
-0027 %
-0028 % Name-Value Arguments
-0029 % --------------------
-0030 % celltype : char
-0031 %     cell type to score for. Should exist in either hpaData.celltypes or
-0032 %     arrayData.celltypes for this tissue (default is to use the best values
-0033 %     among all the cell types for the tissue).
-0034 % hpaData : struct
-0035 %     HPA data structure from parseHPA (optional if arrayData is supplied,
-0036 %     default []).
-0037 % arrayData : struct
-0038 %     gene expression data structure (optional if hpaData is supplied, default
-0039 %     []). With fields:
-0040 %
-0041 %     - genes : cell array with the unique gene names.
-0042 %     - tissues : cell array with the tissue names. The list may not be unique,
-0043 %       as there can be multiple cell types per tissue.
-0044 %     - celltypes : cell array with the cell type names for each tissue.
-0045 %     - levels : GENESxTISSUES array with the expression level for each gene in
-0046 %       each tissue/celltype. NaN should be used when no measurement was
-0047 %       performed.
-0048 %     - threshold : a single value or a vector of gene expression thresholds,
-0049 %       above which genes are considered to be "expressed". (default, by
-0050 %       default the mean expression levels of each gene across all tissues in
-0051 %       arrayData will be used as the threshold values).
-0052 %     - singleCells : binary value selecting whether to use the single-cell
-0053 %       algorithm to identify expressed genes. If used, specify cell
-0054 %       subpopulations in CELLTYPES (default []).
-0055 %     - plotResults : true if single cell probability distributions should be
-0056 %       plotted (default false).
-0057 % metabolomicsData : cell
-0058 %     cell array with metabolite names that the model should produce (default
-0059 %     []).
-0060 % taskFile : char
-0061 %     a task list in Excel format. See parseTaskList for details (default []).
-0062 % useScoresForTasks : logical
-0063 %     true if the calculated reaction scores should be used as weights in the
-0064 %     fitting to tasks (default true).
-0065 % printReport : logical
-0066 %     true if a report should be printed to the screen (default true).
-0067 % taskStructure : struct
-0068 %     task structure as from parseTaskList. Can be used as an alternative way
-0069 %     to define tasks when Excel sheets are not suitable. Overrides taskFile
-0070 %     (default []).
-0071 % params : struct
-0072 %     parameter structure for use by the INIT solver (default []).
-0073 % paramsFT : struct
-0074 %     parameter structure for the fitTasks step (default []).
-0075 % seed : double
-0076 %     seed for the random number generator used to break ties among
-0077 %     genes with equally uninformative (-Inf) scores, so which gene is
-0078 %     kept is reproducible across runs (default: current rng state is
-0079 %     left untouched).
-0080 %
-0081 % Returns
-0082 % -------
-0083 % model : struct
-0084 %     the resulting model structure.
-0085 % metProduction : double
-0086 %     array that indicates which of the metabolites in metabolomicsData that
-0087 %     could be produced. Note that this is before the gap-filling process to
-0088 %     enable defined tasks. To see which metabolites that can be produced in
-0089 %     the final model, use canExchange:
-0090 %
-0091 %     - -2 : metabolite name not found in model.
-0092 %     - -1 : metabolite found, but it could not be produced.
-0093 %     - 1 : metabolite could be produced.
-0094 % essentialRxnsForTasks : cell
-0095 %     cell array of the reactions which were essential to perform the tasks.
-0096 % addedRxnsForTasks : cell
-0097 %     cell array of the reactions which were added in order to perform the
-0098 %     tasks.
-0099 % deletedDeadEndRxns : cell
-0100 %     cell array of reactions deleted because they could not carry flux (INIT
-0101 %     requires a functional input model).
-0102 % deletedRxnsInINIT : cell
-0103 %     cell array of the reactions which were deleted by the INIT algorithm.
-0104 % taskReport : struct
-0105 %     structure with the results for each task. With fields:
-0106 %
-0107 %     - id : cell array with the id of the task.
-0108 %     - description : cell array with the description of the task.
-0109 %     - ok : boolean array with true if the task was successful.
-0110 %     - essential : cell array with cell arrays of essential reactions for the
-0111 %       task.
-0112 %     - gapfill : cell array of cell arrays of reactions included in the
-0113 %       gap-filling for the task.
-0114 %
-0115 % Notes
-0116 % -----
-0117 % Exchange metabolites should normally not be removed from the model when using
-0118 % this approach, since checkTasks/fitTasks rely on putting specific constraints
-0119 % for each task. The INIT algorithm will remove exchange metabolites if any are
-0120 % present. Use importModel(file,false) to import a model with exchange
-0121 % metabolites remaining.
-0122 %
-0123 % See Also
-0124 % --------
-0125 % ftINIT, prepINITModel, runINIT, scoreModel
-0126 
-0127 legacyMethodNotice('getINITModel','ftINIT');
-0128 
-0129 p=parseRAVENargs(varargin, {'celltype',[]; 'hpaData',[]; 'arrayData',[]; 'metabolomicsData',[]; 'taskFile',[]; 'useScoresForTasks',[]; 'printReport',[]; 'taskStructure',[]; 'params',[]; 'paramsFT',[]; 'seed',[]});
-0130 if ~isempty(p.seed)
-0131     rng(p.seed);
-0132 end
-0133 celltype=p.celltype;
-0134 hpaData=p.hpaData;
-0135 arrayData=p.arrayData;
-0136 metabolomicsData=p.metabolomicsData;
-0137 taskFile=p.taskFile;
-0138 useScoresForTasks=p.useScoresForTasks;
-0139 printReport=p.printReport;
-0140 taskStructure=p.taskStructure;
-0141 params=p.params;
-0142 paramsFT=p.paramsFT;
-0143 if ~isempty(celltype)
-0144     celltype=char(celltype);
-0145 end
-0146 if ~isempty(taskFile)
-0147     taskFile=char(taskFile);
-0148 end
-0149 if isempty(useScoresForTasks)
-0150     useScoresForTasks=true;
-0151 end
-0152 if isempty(printReport)
-0153     printReport=true;
-0154 end
-0155 
-0156 %Check that the model is in the closed form
-0157 if ~isfield(refModel,'unconstrained')
-0158     EM='Exchange metabolites should normally not be removed from the model when using getINITModel. Use importModel(file,false) to import a model with exchange metabolites remaining (see the documentation for details)';
-0159     error('RAVEN:badInput', '%s', EM);
-0160 end
-0161 
-0162 %Create the task structure if not supplied
-0163 if any(taskFile) && isempty(taskStructure)
-0164     taskStructure=parseTaskList(taskFile);
-0165 end
-0166 
-0167 
-0168 % sc-tINIT to identify confidence levels of gene expression
-0169 if ~isempty(arrayData) && isfield(arrayData,'singleCells')
-0170     if arrayData.singleCells == 1
-0171         % Check to ensure cell type is defined
-0172         if ~isfield(arrayData,'celltypes')
-0173             warning('RAVEN:warning', '%s', 'arrayData must contain cell type information if sc-tINIT is to be used');
-0174         end
-0175         if ~ismember(upper(celltype),upper(arrayData.celltypes))
-0176             error('RAVEN:badInput', '%s', 'The cell type name does not match');
-0177         end
-0178         
-0179         % Analyze only cell type of interest
-0180         J= strcmpi(arrayData.celltypes,celltype);
-0181         
-0182         % Analyze only genes included in the reference model
-0183         I=ismember(arrayData.genes,refModel.genes);
-0184         
-0185         % Convert expression data to population fractions
-0186         binary_levels = arrayData.levels(I,J)~=0; % Classify each gene as detected (1) or not (0) in each cell
-0187         cell_count_levels = sum(binary_levels,2); % Number of cells expressing each transcript
-0188         cell_frac_levels = cell_count_levels/size(binary_levels,2); % Number of cells expressing each transcript
-0189 
-0190         % Bin cell_frac_counts manually
-0191         x = 0:.01:1;
-0192         for(i = 1:length(x))
-0193             cell_frac_count(i) = sum(round(cell_frac_levels,2)==x(i));
-0194         end
-0195         
-0196         % Fit four beta distributions
-0197         cell_frac_count(cell_frac_count==0) = NaN; % Remove zeros from optimization
-0198         cell_frac_count(1) = NaN; % Remove non-expressed genes from optimization
-0199         x_lim = 1; % Somewhat arbitrary parameter to fit left tail of distr.
-0200         myfun = @(par) sum((cell_frac_count(1:find(x>=x_lim,1)) - ...
-0201             abs(par(1))*betapdf(x(1:find(x>=x_lim,1)),abs(par(2)),abs(par(3))) - ...
-0202             abs(par(4))*betapdf(x(1:find(x>=x_lim,1)),abs(par(5)),abs(par(6))) - ...
-0203             abs(par(7))*betapdf(x(1:find(x>=x_lim,1)),abs(par(8)),abs(par(9))) - ...
-0204             abs(par(10))*betapdf(x(1:find(x>=x_lim,1)),abs(par(11)),abs(par(12)))).^2, 'omitnan');
-0205         
-0206         par0 = [4,2,100,7,2,30,7,5,20,5,15,20];
-0207         opts = optimset('Display','off');
-0208         [par,f_val] = fminsearch(myfun,par0,opts);
-0209         par = abs(par);
-0210         
-0211         % Plot results
-0212         if (isfield(arrayData,'plotResults'))
-0213             if arrayData.plotResults == true
-0214                 figure(); hold on; plot(x,cell_frac_count,'ko','MarkerSize',5);
-0215                 plot(x,abs(par(1))*betapdf(x,abs(par(2)),abs(par(3))),'b-','LineWidth',1)
-0216                 plot(x,abs(par(4))*betapdf(x,abs(par(5)),abs(par(6))),'b-','LineWidth',1)
-0217                 plot(x,abs(par(7))*betapdf(x,abs(par(8)),abs(par(9))),'b-','LineWidth',1)
-0218                 plot(x,abs(par(10))*betapdf(x,abs(par(11)),abs(par(12))),'b-','LineWidth',1)
-0219                 plot(x,abs(par(1))*betapdf(x,abs(par(2)),abs(par(3))) + ...
-0220                     abs(par(4))*betapdf(x,abs(par(5)),abs(par(6))) + ...
-0221                     abs(par(7))*betapdf(x,abs(par(8)),abs(par(9))) + ...
-0222                     abs(par(10))*betapdf(x,abs(par(11)),abs(par(12))),'-','Color',[.5 .5 .5],'LineWidth',2)
-0223                 xlabel('Expression Probability');ylabel('# of genes');set(gca,'FontSize',14,'LineWidth',1.25);
-0224                 title('Expression prediction','FontSize',18,'FontWeight','bold')
-0225             end
-0226         end
-0227         
-0228         % Score genes based on population expression (p = .05)
-0229         exprs_cutoff_1 = find(cdf('beta',x,par(2),par(3)) >.95,1)-1; % Find index of no confidence genes
-0230         exprs_cutoff_2 = find(cdf('beta',x,par(5),par(6)) >.95,1)-1; % Find index of low confidence genes
-0231         exprs_cutoff_3 = find(cdf('beta',x,par(8),par(9)) >.95,1)-1; % Find index of low confidence genes
-0232         exprs_cutoffs = sort([exprs_cutoff_1,exprs_cutoff_2,exprs_cutoff_3]);
-0233         gene_scores = cell_frac_levels*0;
-0234         gene_scores(cell_frac_levels <= x(exprs_cutoffs(1))) = 4; % Not detected
-0235         gene_scores(logical((cell_frac_levels >= x(exprs_cutoffs(1))).*(cell_frac_levels < x(exprs_cutoffs(2))))) = 3; % Low detection
-0236         gene_scores(logical((cell_frac_levels >= x(exprs_cutoffs(2))).*(cell_frac_levels < x(exprs_cutoffs(3))))) = 2; % Medium detection
-0237         gene_scores(cell_frac_levels > x(exprs_cutoffs(3))) = 1; % High detection
-0238 
-0239         % Replace hpaData with singleCellData
-0240         if printReport==true
-0241             %warning()'s '%s' passes the message through literally, so its
-0242             %"\n" must be resolved to a real newline beforehand, not left
-0243             %to print as a literal backslash-n.
-0244             warning('RAVEN:warning', '%s', sprintf('Single cell data is not currently compatible with HPA data. \n         Replacing hpaData with single cell-based scoring.'));
-0245         end
-0246         hpaData.genes = arrayData.genes;
-0247         hpaData.tissues = arrayData.tissues;
-0248         hpaData.celltypes = arrayData.celltypes;
-0249         hpaData.levels = [{'High'},{'Medium'},{'Low'},{'None'}];
-0250         hpaData.gene2Level = zeros(length(arrayData.genes),length(arrayData.celltypes));
-0251         for i = 1:length(find(J))
-0252             find_var = find(J,i);
-0253             hpaData.gene2Level(I,find_var(end)) = gene_scores;
-0254         end
-0255         
-0256         % Remove arrayData from the analysis
-0257         clear arrayData
-0258         arrayData=[];
-0259     end
-0260 end
-0261 
-0262 
-0263 if printReport==true
-0264     if any(celltype)
-0265         fprintf(['***Generating model for: ' tissue ' - ' celltype '\n']);
-0266     else
-0267         fprintf(['***Generating model for: ' tissue '\n']);
-0268     end
-0269     fprintf('-Using the legacy tINIT algorithm; ftINIT is the recommended method\n');
-0270     if ~isempty(hpaData)
-0271         fprintf('-Using HPA data\n');
-0272     end
-0273     if ~isempty(arrayData)
-0274         fprintf('-Using array data\n');
-0275     end
-0276     if ~isempty(metabolomicsData)
-0277         fprintf('-Using metabolomics data\n');
-0278     end
-0279     if ~isempty(taskFile) || ~isempty(taskStructure)
-0280         fprintf('-Using metabolic tasks\n');
-0281     end
-0282     fprintf('\n');
-0283     
-0284     printScores(refModel,'Reference model statistics',hpaData,arrayData,tissue,celltype);
-0285 end
-0286 
-0287 %Remove dead-end reactions to speed up the optimization and to
-0288 %differentiate between reactions removed by INIT and those that are
-0289 %dead-end
-0290 [~, deletedDeadEndRxns]=simplifyModel(refModel,true,false,true,true,true);
-0291 cModel=removeReactions(refModel,deletedDeadEndRxns,false,true);
-0292 
-0293 %Store the connected model like this to keep track of stuff
-0294 if printReport==true
-0295     printScores(cModel,'Pruned model statistics',hpaData,arrayData,tissue,celltype);
-0296 end
-0297 
-0298 %If tasks have been defined, then go through them and get essential
-0299 %reactions
-0300 if ~isempty(taskStructure)
-0301     [taskReport, essentialRxnMat]=checkTasks(cModel,[],printReport,true,true,taskStructure);
-0302     
-0303     essentialRxnsForTasks=cModel.rxns(any(essentialRxnMat,2));
-0304     
-0305     %Remove tasks that cannot be performed
-0306     taskStructure(taskReport.ok==false)=[];
-0307     if printReport==true
-0308         printScores(removeReactions(cModel,setdiff(cModel.rxns,essentialRxnsForTasks),true,true),'Reactions essential for tasks',hpaData,arrayData,tissue,celltype);
-0309     end
-0310 else
-0311     essentialRxnsForTasks={};
-0312 end
-0313 
-0314 %Score the connected model. isozymeScoring/complexScoring use one operator
-0315 %for both and/or (a rule reduces to a single statistic over its genes) and
-0316 %dataPrecedence is per-reaction, matching the original tINIT algorithm.
-0317 [rxnScores, geneScores]=scoreModel(cModel,hpaData,arrayData,tissue,celltype, ...
-0318     'isozymeScoring','max','complexScoring','max','dataPrecedence','reaction');
-0319 %scoreModel marks an unmeasured gene NaN (removeLowScoreGenes reads that as
-0320 %"no evidence" and leaves it be); this algorithm instead needs -Inf, so an
-0321 %unmeasured gene never ranks equal-or-above a measured one further down.
-0322 geneScores(isnan(geneScores))=-Inf;
-0323 
-0324 %Run the INIT algorithm. The exchange reactions that are used in the final
-0325 %reactions will be open, which does not fit with the last step. Therefore
-0326 %delete reactions from the original model instead of taking the output. The
-0327 %default implementation does not constrain reversible reactions to only
-0328 %carry flux in one direction. Runs without the constraints on reversibility
-0329 %and with all output allowed. This is to reduce the complexity of the
-0330 %problem.
-0331 [~, deletedRxnsInINIT, metProduction]=runINIT(simplifyModel(cModel),rxnScores,metabolomicsData,essentialRxnsForTasks,0,true,false,params);
-0332 initModel=removeReactions(cModel,deletedRxnsInINIT,true,true);
-0333 if printReport==true
-0334     printScores(initModel,'INIT model statistics',hpaData,arrayData,tissue,celltype);
-0335     printScores(removeReactions(cModel,setdiff(cModel.rxns,deletedRxnsInINIT),true,true),'Reactions deleted by INIT',hpaData,arrayData,tissue,celltype);
-0336 end
-0337 
-0338 %The full model has exchange reactions in it. fitTasks calls on fillGaps,
-0339 %which automatically removes exchange metabolites (because it assumes that
-0340 %the reactions are constrained when appropriate). In this case the
-0341 %uptakes/outputs are retrieved from the task sheet instead. To prevent
-0342 %exchange reactions being used to fill gaps, they are delete from the
-0343 %reference model here.
-0344 initModel.id='INITModel';
-0345 
-0346 %If gaps in the model should be filled using a task list
-0347 if ~isempty(taskStructure)
-0348     %Remove exchange reactions and reactions already included in the INIT
-0349     %model
-0350     refModelNoExc=removeReactions(refModel,union(initModel.rxns,getExchangeRxns(refModel)),true,true);
-0351     
-0352     %At this stage the model is fully connected and most of the genes with
-0353     %good scores should have been included. The final gap-filling should
-0354     %take the scores of the genes into account, so that "rather bad"
-0355     %reactions are preferred to "very bad" reactions. However, reactions
-0356     %with positive scores will be included even if they are not connected
-0357     %in the current formulation. Therefore, such reactions will have to be
-0358     %assigned a small negative score instead.
-0359     if useScoresForTasks==true
-0360         refRxnScores=scoreModel(refModelNoExc,hpaData,arrayData,tissue,celltype, ...
-0361             'isozymeScoring','max','complexScoring','max','dataPrecedence','reaction');
-0362         [outModel, addedRxnMat]=fitTasks(initModel,refModelNoExc,[],true,min(refRxnScores,-0.1),taskStructure,paramsFT);
-0363     else
-0364         [outModel, addedRxnMat]=fitTasks(initModel,refModelNoExc,[],true,[],taskStructure,paramsFT);
-0365     end
-0366     if printReport==true
-0367         printScores(outModel,'Functional model statistics',hpaData,arrayData,tissue,celltype);
-0368         printScores(removeReactions(outModel,intersect(outModel.rxns,initModel.rxns),true,true),'Reactions added to perform the tasks',hpaData,arrayData,tissue,celltype);
-0369     end
-0370     
-0371     addedRxnsForTasks=refModelNoExc.rxns(any(addedRxnMat,2));
-0372 else
-0373     outModel=initModel;
-0374     addedRxnMat=[];
-0375     addedRxnsForTasks={};
-0376 end
-0377 
-0378 %The model can now perform all the tasks defined in the task list. The
-0379 %algorithm cannot deal with gene-complexes at the moment. It is therefore
-0380 %ok to remove bad genes from a reaction (as long as at least one gene is
-0381 %kept)
-0382 model=outModel;
-0383 
-0384 [~, I]=ismember(model.genes,cModel.genes); %All should be found
-0385 %This is a little weird way to make sure that only one bad gene is included
-0386 %if there are no good ones (since all -Inf==max(-Inf))
-0387 geneScores(isinf(geneScores))=-1000+rand(sum(isinf(geneScores)),1);
-0388 
-0389 model.grRules(:)={''};
-0390 for i=1:numel(model.rxns)
-0391     ids=find(model.rxnGeneMat(i,:));
-0392     if numel(ids)>1
-0393         scores=geneScores(I(ids));
-0394         %Only keep the positive ones if possible
-0395         model.rxnGeneMat(i,ids(~(scores>0 | scores==max(scores))))=0;
-0396     end
-0397     %Rewrite the grRules to be only OR
-0398     if isfield(model,'grRules')
-0399         J=find(model.rxnGeneMat(i,:));
-0400         for j=1:numel(J)
-0401             model.grRules{i}=[model.grRules{i} '(' model.genes{J(j)} ')'];
-0402             if j<numel(J)
-0403                 model.grRules{i}=[model.grRules{i} ' or '];
-0404             end
-0405         end
-0406     end
-0407 end
-0408 
-0409 %Find all genes that are not used and delete them
-0410 I=sum(model.rxnGeneMat)==0;
-0411 model.genes(I)=[];
-0412 model.rxnGeneMat(:,I)=[];
-0413 if isfield(model,'geneShortNames')
-0414     model.geneShortNames(I)=[];
-0415 end
-0416 if isfield(model,'proteins')
-0417     model.proteins(I)=[];
-0418 end
-0419 if isfield(model,'geneMiriams')
-0420     model.geneMiriams(I)=[];
-0421 end
-0422 if isfield(model,'geneFrom')
-0423     model.geneFrom(I)=[];
-0424 end
-0425 if isfield(model,'geneComps')
-0426     model.geneComps(I)=[];
-0427 end
-0428 
-0429 %At this stage the model will contain some exchange reactions but probably
-0430 %not all (and maybe zero). This can be inconvenient, so all exchange
-0431 %reactions from the reference model are added, except for those which
-0432 %involve metabolites that are not in the model.
-0433 
-0434 %First delete and included exchange reactions in order to prevent the order
-0435 %from changing
-0436 model=removeReactions(model,getExchangeRxns(model));
-0437 
-0438 %Create a model with only the exchange reactions in refModel
-0439 excModel=removeReactions(refModel,setdiff(refModel.rxns,getExchangeRxns(refModel)),true,true);
-0440 
-0441 %Find the metabolites there which are not exchange metabolites and which do
-0442 %not exist in the output model
-0443 I=~ismember(excModel.mets,model.mets) & excModel.unconstrained==0;
-0444 
-0445 %Then find those reactions and delete them
-0446 [~, J]=find(excModel.S(I,:));
-0447 excModel=removeReactions(excModel,J,true,true);
-0448 
-0449 %Merge with the output model
-0450 model=mergeModels({model;excModel},'metNames');
-0451 model.id='INITModel';
-0452 model.name=['Automatically generated model for ' tissue];
-0453 if any(celltype)
-0454     model.name=[model.name ' - ' celltype];
-0455 end
-0456 
-0457 if printReport==true
-0458     printScores(model,'Final model statistics',hpaData,arrayData,tissue,celltype);
-0459 end
-0460 
-0461 %Add information about essential reactions and reactions included for
-0462 %gap-filling and return a taskReport
-0463 if ~isempty(taskStructure)
-0464     I=find(taskReport.ok); %Ignore failed tasks
-0465     for i=1:numel(I)
-0466         taskReport.essential{I(i),1}=cModel.rxns(essentialRxnMat(:,I(i)));
-0467         taskReport.gapfill{I(i),1}=refModelNoExc.rxns(addedRxnMat(:,i));
-0468     end
-0469 else
-0470     taskReport=[];
-0471 end
-0472 
-0473 %Fix grRules and reconstruct rxnGeneMat
-0474 [grRules,rxnGeneMat] = standardizeGrRules(model,true);
-0475 model.grRules = grRules;
-0476 model.rxnGeneMat = rxnGeneMat;
-0477 end
-0478 
-0479 %This is for printing a summary of a model
-0480 function [rxnS, geneS]=printScores(model,name,hpaData,arrayData,tissue,celltype)
-0481 [a, b]=scoreModel(model,hpaData,arrayData,tissue,celltype, ...
-0482     'isozymeScoring','max','complexScoring','max','dataPrecedence','reaction');
-0483 b(isnan(b))=-Inf;
-0484 rxnS=mean(a);
-0485 geneS=mean(b(~isinf(b)));
-0486 fprintf([name ':\n']);
-0487 fprintf(['\t' num2str(numel(model.rxns)) ' reactions, ' num2str(numel(model.genes)) ' genes\n']);
-0488 fprintf(['\tMean reaction score: ' num2str(rxnS) '\n']);
-0489 fprintf(['\tMean gene score: ' num2str(geneS) '\n']);
-0490 fprintf(['\tReactions with positive scores: ' num2str(100*sum(a>0)/numel(a)) '%%\n\n']);
-0491 end
-0492 
-0493 %Local beta probability density function, equivalent to the Statistics and
-0494 %Machine Learning Toolbox betapdf but using only base MATLAB (beta). This
-0495 %avoids a dependency on that toolbox. The density is zero outside [0,1].
-0496 function y=betapdf(x,a,b)
-0497 y=zeros(size(x));
-0498 k=x>=0 & x<=1;
-0499 y(k)=(x(k).^(a-1)).*((1-x(k)).^(b-1))/beta(a,b);
-0500 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/INIT/tINIT/runINIT.html b/doc/INIT/tINIT/runINIT.html deleted file mode 100644 index 035f8e0b..00000000 --- a/doc/INIT/tINIT/runINIT.html +++ /dev/null @@ -1,482 +0,0 @@ - - - - Description of runINIT - - - - - - - - - -
Home > INIT > tINIT > runINIT.m
- - - -

runINIT -

- -

PURPOSE ^

-
runINIT Solve the MILP of the original INIT algorithm.
- -

SYNOPSIS ^

-
function [outModel, deletedRxns, metProduction, fValue]=runINIT(model,varargin)
- -

DESCRIPTION ^

-
 runINIT  Solve the MILP of the original INIT algorithm.
-
- Generates a model using the INIT algorithm, based on proteomics and/or
- transcriptomics and/or metabolomics and/or metabolic tasks. This function is
- the actual implementation of the algorithm; see getINITModel for the
- higher-level reconstruction function, and PLoS Comput Biol. 2012;8(5):e1002518
- for the formulation.
-
- ftINIT is the recommended method for context-specific model extraction, and
- solves a different MILP in ftINITInternalAlg. This function remains
- supported, and issues a RAVEN:legacyMethod notice once per session, which
- warning('off','RAVEN:legacyMethod') silences.
-
- Parameters
- ----------
- model : struct
-     a reference model structure.
-
- Name-Value Arguments
- --------------------
- rxnScores : double
-     a vector of scores for the reactions in the model. Positive scores are
-     reactions to keep and negative scores are reactions to exclude (default
-     all 0.0).
- presentMets : cell
-     cell array with unique metabolite names that the model should produce
-     (default []).
- essentialRxns : cell
-     cell array of reactions that are essential and that have to be in the
-     resulting model. This is normally used when fitting a model to task (see
-     fitTasks) (default []).
- prodWeight : double
-     a score that determines the value of having net-production of
-     metabolites. This is a way of having a more functional network as it
-     provides a reason for including bad reactions for connectivity reasons.
-     This score is for each metabolite, and the sum of these weights and the
-     scores for the reactions is what is optimized (default 0.5).
- allowExcretion : logical
-     true if excretion of all metabolites should be allowed. This results in
-     fewer reactions being considered dead-ends, but all reactions in the
-     resulting model may not be able to carry flux. If this is "false" then
-     the equality constraints are taken from model.b. If the input model
-     lacks exchange reactions then this should probably be "true", or a large
-     proportion of the model would be excluded for connectivity reasons
-     (default false).
- noRevLoops : logical
-     true if reversible reactions should be constrained to only carry flux in
-     one direction. This prevents reversible reactions from being wrongly
-     assigned as connected (the forward and backward reactions can form a loop
-     and therefore appear connected), but it makes the problem significantly
-     more computationally intensive to solve (two more integer constraints per
-     reversible reaction) (default false).
- params : struct
-     parameter structure for use by optimizeProb.
-
- Returns
- -------
- outModel : struct
-     the resulting model structure.
- deletedRxns : cell
-     reactions which were deleted by the algorithm.
- metProduction : double
-     array that indicates which of the metabolites in presentMets that could
-     be produced:
-
-     - -2 : metabolite name not found in model.
-     - -1 : metabolite found, but it could not be produced.
-     - 1 : metabolite could be produced.
- fValue : double
-     objective value (sum of (the negative of) reaction scores for the
-     included reactions and prodWeight*number of produced metabolites).
-
- See Also
- --------
- getINITModel, ftINITInternalAlg, ftINIT
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: - - - - - -

SOURCE CODE ^

-
0001 function [outModel, deletedRxns, metProduction, fValue]=runINIT(model,varargin)
-0002 % runINIT  Solve the MILP of the original INIT algorithm.
-0003 %
-0004 % Generates a model using the INIT algorithm, based on proteomics and/or
-0005 % transcriptomics and/or metabolomics and/or metabolic tasks. This function is
-0006 % the actual implementation of the algorithm; see getINITModel for the
-0007 % higher-level reconstruction function, and PLoS Comput Biol. 2012;8(5):e1002518
-0008 % for the formulation.
-0009 %
-0010 % ftINIT is the recommended method for context-specific model extraction, and
-0011 % solves a different MILP in ftINITInternalAlg. This function remains
-0012 % supported, and issues a RAVEN:legacyMethod notice once per session, which
-0013 % warning('off','RAVEN:legacyMethod') silences.
-0014 %
-0015 % Parameters
-0016 % ----------
-0017 % model : struct
-0018 %     a reference model structure.
-0019 %
-0020 % Name-Value Arguments
-0021 % --------------------
-0022 % rxnScores : double
-0023 %     a vector of scores for the reactions in the model. Positive scores are
-0024 %     reactions to keep and negative scores are reactions to exclude (default
-0025 %     all 0.0).
-0026 % presentMets : cell
-0027 %     cell array with unique metabolite names that the model should produce
-0028 %     (default []).
-0029 % essentialRxns : cell
-0030 %     cell array of reactions that are essential and that have to be in the
-0031 %     resulting model. This is normally used when fitting a model to task (see
-0032 %     fitTasks) (default []).
-0033 % prodWeight : double
-0034 %     a score that determines the value of having net-production of
-0035 %     metabolites. This is a way of having a more functional network as it
-0036 %     provides a reason for including bad reactions for connectivity reasons.
-0037 %     This score is for each metabolite, and the sum of these weights and the
-0038 %     scores for the reactions is what is optimized (default 0.5).
-0039 % allowExcretion : logical
-0040 %     true if excretion of all metabolites should be allowed. This results in
-0041 %     fewer reactions being considered dead-ends, but all reactions in the
-0042 %     resulting model may not be able to carry flux. If this is "false" then
-0043 %     the equality constraints are taken from model.b. If the input model
-0044 %     lacks exchange reactions then this should probably be "true", or a large
-0045 %     proportion of the model would be excluded for connectivity reasons
-0046 %     (default false).
-0047 % noRevLoops : logical
-0048 %     true if reversible reactions should be constrained to only carry flux in
-0049 %     one direction. This prevents reversible reactions from being wrongly
-0050 %     assigned as connected (the forward and backward reactions can form a loop
-0051 %     and therefore appear connected), but it makes the problem significantly
-0052 %     more computationally intensive to solve (two more integer constraints per
-0053 %     reversible reaction) (default false).
-0054 % params : struct
-0055 %     parameter structure for use by optimizeProb.
-0056 %
-0057 % Returns
-0058 % -------
-0059 % outModel : struct
-0060 %     the resulting model structure.
-0061 % deletedRxns : cell
-0062 %     reactions which were deleted by the algorithm.
-0063 % metProduction : double
-0064 %     array that indicates which of the metabolites in presentMets that could
-0065 %     be produced:
-0066 %
-0067 %     - -2 : metabolite name not found in model.
-0068 %     - -1 : metabolite found, but it could not be produced.
-0069 %     - 1 : metabolite could be produced.
-0070 % fValue : double
-0071 %     objective value (sum of (the negative of) reaction scores for the
-0072 %     included reactions and prodWeight*number of produced metabolites).
-0073 %
-0074 % See Also
-0075 % --------
-0076 % getINITModel, ftINITInternalAlg, ftINIT
-0077 
-0078 legacyMethodNotice('runINIT','ftINIT');
-0079 
-0080 p=parseRAVENargs(varargin, {'rxnScores',[]; 'presentMets',[]; 'essentialRxns',[]; 'prodWeight',[]; 'allowExcretion',false; 'noRevLoops',false; 'params',[]; 'eps',1});
-0081 rxnScores=p.rxnScores;
-0082 presentMets=p.presentMets;
-0083 essentialRxns=p.essentialRxns;
-0084 prodWeight=p.prodWeight;
-0085 allowExcretion=p.allowExcretion;
-0086 noRevLoops=p.noRevLoops;
-0087 params=p.params;
-0088 initEps=p.eps;
-0089 if isempty(rxnScores)
-0090     rxnScores=zeros(numel(model.rxns),1);
-0091 end
-0092 if isempty(presentMets)
-0093     presentMets={};
-0094 else
-0095     presentMets=convertCharArray(presentMets);
-0096 end
-0097 if isempty(essentialRxns)
-0098     essentialRxns={};
-0099 else
-0100     essentialRxns=convertCharArray(essentialRxns);
-0101 end
-0102 if isempty(prodWeight)
-0103     prodWeight=0.5;
-0104 end
-0105 
-0106 if numel(presentMets)~=numel(unique(presentMets))
-0107     EM='Duplicate metabolite names in presentMets';
-0108     error('RAVEN:badInput', '%s', EM);
-0109 end
-0110 
-0111 %Default is that the metabolites cannot be produced
-0112 if ~isempty(presentMets)
-0113     metProduction=ones(numel(presentMets),1)*-2;
-0114     presentMets=upper(presentMets);
-0115     pmIndexes=find(ismember(presentMets,upper(model.metNames)));
-0116     metProduction(pmIndexes)=-1; %Then set that they are at least found
-0117 else
-0118     metProduction=[];
-0119     pmIndexes=[];
-0120 end
-0121 
-0122 %The model should be in the reversible format and all relevant exchange
-0123 %reactions should be open
-0124 if isfield(model,'unconstrained')
-0125     EM='Exchange metabolites are still present in the model. Use simplifyModel if this is not intended';
-0126     warning('RAVEN:warning', '%s', EM);
-0127 end
-0128 
-0129 %The irreversible reactions that are essential must have a flux and are
-0130 %therefore not optimized for using MILP, which reduces the problem size.
-0131 %However, reversible reactions must have a flux in one direction, so they
-0132 %have to stay in the problem. The essentiality constraint on reversible
-0133 %reactions is implemented in the same manner as for reversible reactions
-0134 %when noRevLoops==true, but with the additional constraint that C ub=-1.
-0135 %This forces one of the directions to be active.
-0136 revRxns=find(model.rev~=0);
-0137 essentialReversible=find(ismember(model.rxns(revRxns),essentialRxns));
-0138 essentialRxns=intersect(essentialRxns,model.rxns(model.rev==0));
-0139 
-0140 %Convert the model to irreversible
-0141 irrevModel=convertToIrrev(model);
-0142 rxnScores=[rxnScores;rxnScores(model.rev==1)];
-0143 %These are used if noRevLoops is true
-0144 if noRevLoops==true
-0145     forwardIndexes=find(model.rev~=0);
-0146     backwardIndexes=(numel(model.rxns)+1:numel(irrevModel.rxns))';
-0147 else
-0148     %Then they should only be used for essential reversible reactions
-0149     forwardIndexes=revRxns(essentialReversible);
-0150     backwardIndexes=essentialReversible+numel(model.rxns);
-0151 end
-0152 
-0153 %Get the indexes of the essential reactions and remove them from the
-0154 %scoring vector
-0155 essentialIndex=find(ismember(irrevModel.rxns,essentialRxns));
-0156 rxnScores(essentialIndex)=[];
-0157 
-0158 %Go through each of the presentMets (if they exist) and modify the S matrix
-0159 %so that each reaction which produces any of them also produces a
-0160 %corresponding fake metabolite and the opposite in the reverse direction.
-0161 
-0162 %This is to deal with the fact that there is no compartment info regarding
-0163 %the presentMets. This modifies the irrevModel structure, but that is fine
-0164 %since it is the model structure that is returned.
-0165 if any(pmIndexes)
-0166     irrevModel.metNames=upper(irrevModel.metNames);
-0167     % num2str on the whole column right-aligns every row to a common width,
-0168     % embedding padding spaces once the indices span more than one digit
-0169     % count; format each one independently.
-0170     metsToAdd.mets=arrayfun(@(x) sprintf('FAKEFORPM%d',x), pmIndexes, 'UniformOutput', false);
-0171     metsToAdd.metNames=metsToAdd.mets;
-0172     metsToAdd.compartments=irrevModel.comps{1};
-0173     
-0174     %There is no constraints on the metabolites yet, since maybe not all of
-0175     %them could be produced
-0176     irrevModel=addMets(irrevModel,metsToAdd);
-0177 end
-0178 
-0179 %Modify the matrix
-0180 for i=1:numel(pmIndexes)
-0181     %Get the matching mets
-0182     I=ismember(irrevModel.metNames,presentMets(pmIndexes(i)));
-0183     
-0184     %Find the reactions where any of them are used.
-0185     [~, K, L]=find(irrevModel.S(I,:));
-0186     
-0187     %This ugly loop is to avoid problems if a metabolite occurs several
-0188     %times in one reaction
-0189     KK=unique(K);
-0190     LL=zeros(numel(KK),1);
-0191     for j=1:numel(KK)
-0192         LL(j)=sum(L(K==KK(j)));
-0193     end
-0194     irrevModel.S(numel(irrevModel.mets)-numel(pmIndexes)+i,KK)=LL;
-0195 end
-0196 
-0197 %Some nice to have numbers
-0198 nMets=numel(irrevModel.mets);
-0199 nRxns=numel(irrevModel.rxns);
-0200 nEssential=numel(essentialIndex);
-0201 nNonEssential=nRxns-nEssential;
-0202 nonEssentialIndex=setdiff(1:nRxns,essentialIndex);
-0203 % Per-reaction big-M: use each reaction upper bound, capped at 1000.
-0204 % Must be at least initEps so the inclusion constraint is feasible.
-0205 nonEssUBs = min(irrevModel.ub(nonEssentialIndex), 1000);
-0206 nonEssUBs = max(nonEssUBs(:), initEps);
-0207 S=irrevModel.S;
-0208 
-0209 %Add so that each non-essential reaction produces one unit of a fake
-0210 %metabolite
-0211 temp=sparse(1:nRxns,1:nRxns,1);
-0212 temp(essentialIndex,:)=[];
-0213 S=[S;temp];
-0214 
-0215 %Add another set of reactions (will be binary) which also produce these
-0216 %fake metabolites, but with a stoichiometry of 1000
-0217 temp=sparse(1:nNonEssential,1:nNonEssential,nonEssUBs);
-0218 temp=[sparse(nMets,nNonEssential);temp];
-0219 S=[S temp];
-0220 
-0221 %Add reactions for net-production of (real) metabolites
-0222 if prodWeight~=0
-0223     temp=[speye(nMets-numel(pmIndexes))*-1;sparse(nNonEssential+numel(pmIndexes),nMets-numel(pmIndexes))];
-0224     S=[S temp];
-0225     %To keep the number of reactions added like this
-0226     nNetProd=nMets-numel(pmIndexes);
-0227 else
-0228     nNetProd=0;
-0229 end
-0230 
-0231 %Add constraints so that reversible reactions can only be used in one
-0232 %direction. This is done by adding the fake metabolites A, B, C for each
-0233 %reversible reaction in the following manner
-0234 % forward: A + .. => ... backwards: B + ... => ... int1: C => 1000 A int2:
-0235 % C => 1000 B A ub=999.9 B ub=999.9 C lb=-1 int1 and int2 are binary
-0236 if any(forwardIndexes)
-0237     nRevBounds=numel(forwardIndexes);
-0238     
-0239     %Add the A metabolites for the forward reactions and the B metabolites
-0240     %for the reverse reactions
-0241     I=speye(numel(irrevModel.rxns))*-1;
-0242     temp=[I(forwardIndexes,:);I(backwardIndexes,:)];
-0243     
-0244     %Padding
-0245     temp=[temp sparse(size(temp,1),size(S,2)-numel(irrevModel.rxns))];
-0246     
-0247     %Add the int1 & int2 reactions that produce A and B
-0248     temp=[temp speye(nRevBounds*2)*1000];
-0249     
-0250     %And add that they also consume C
-0251     temp=[temp;[sparse(nRevBounds,size(S,2)) speye(nRevBounds)*-1 speye(nRevBounds)*-1]];
-0252     
-0253     %Add the new reactions and metabolites
-0254     S=[S sparse(size(S,1),nRevBounds*2)];
-0255     S=[S;temp];
-0256 else
-0257     nRevBounds=0;
-0258 end
-0259 
-0260 %Add so that the essential reactions must have a small flux and that the
-0261 %binary ones (and net-production reactions) may have zero flux. The integer
-0262 %reactions for reversible reactions have [0 1]
-0263 prob.blx=[irrevModel.lb;zeros(nNonEssential+nNetProd+nRevBounds*2,1)];
-0264 prob.blx(essentialIndex)=min(max(initEps,prob.blx(essentialIndex)),irrevModel.ub(essentialIndex));
-0265 
-0266 %Add so that the binary ones and net-production reactions can have at the
-0267 %most flux 1.0
-0268 prob.bux=[irrevModel.ub;ones(nNonEssential+nNetProd+nRevBounds*2,1)];
-0269 
-0270 %Add that the fake metabolites must be produced in a small amount and that
-0271 %the A and B metabolites for reversible reactions can be [0 999.9] and C
-0272 %metabolites [-1 0]
-0273 prob.blc=[irrevModel.b(:,1);ones(nNonEssential,1)*initEps;zeros(nRevBounds*2,1);ones(nRevBounds,1)*-1];
-0274 
-0275 %Add that normal metabolites can be freely excreted if
-0276 %allowExcretion==true, and that the fake ones can be excreted 1000 units at
-0277 %most. C metabolites for essential reversible reactions should have an
-0278 %upper bound of -1. If noRevLoops is false, then add this constraint for
-0279 %all the reactions instead.
-0280 if noRevLoops==true
-0281     revUB=zeros(nRevBounds,1);
-0282     revUB(essentialReversible)=-1;
-0283 else
-0284     revUB=ones(nRevBounds,1)*-1;
-0285 end
-0286 if allowExcretion==true
-0287     metUB=inf(nMets,1);
-0288 else
-0289     metUB=irrevModel.b(:,min(size(irrevModel.b,2),2));
-0290 end
-0291 prob.buc=[metUB;nonEssUBs;ones(nRevBounds*2,1)*999.9;revUB];
-0292 
-0293 %Add objective coefficients for the binary reactions. The negative is used
-0294 %since we are minimizing. The negative is taken for the prodWeight as well,
-0295 %to be consistent with the syntax that positive scores are good
-0296 prob.c=[zeros(nRxns,1);rxnScores;ones(nNetProd,1)*prodWeight*-1;zeros(nRevBounds*2,1)];
-0297 prob.a=S;
-0298 
-0299 % adapt problem structure for cobra-style solver
-0300 prob.c=[prob.c;zeros(size(prob.a,1),1)];
-0301 prob.A=[prob.a -speye(size(prob.a,1))];
-0302 prob.b=zeros(size(prob.a,1), 1);
-0303 prob.ub=[prob.bux; prob.buc];
-0304 prob.osense=1;
-0305 prob.csense=char(zeros(1,size(prob.a,1)));
-0306 prob.csense(:)='E';
-0307 
-0308 %Add that the binary reactions may only take integer values. This has to be
-0309 %declared before the presentMets loop below: optimizeProb reads vartype on
-0310 %every call, and the producibility test is only meaningful against the same
-0311 %integer problem that is solved at the end.
-0312 prob.vartype = repmat('C', 1, size(prob.A, 2));
-0313 allInt=[(nRxns+1):(nRxns+nNonEssential) size(S,2)-nRevBounds*2+1:size(S,2)];
-0314 prob.vartype(allInt) = 'B';
-0315 
-0316 %We still do not know which of the presentMets that can be produced. Go
-0317 %through them, force production, and see if the problem can be solved
-0318 for i=1:numel(pmIndexes)
-0319     prob.blc(numel(irrevModel.mets)-numel(pmIndexes)+i)=1;
-0320     prob.lb=[prob.blx; prob.blc];
-0321     res=optimizeProb(prob,params);
-0322     isFeasible=checkSolution(res);
-0323     if ~isFeasible
-0324         %Reset the constraint again
-0325         prob.blc(numel(irrevModel.mets)-numel(pmIndexes)+i)=0;
-0326     else
-0327         %Metabolite produced
-0328         metProduction(pmIndexes(i))=1;
-0329     end
-0330 end
-0331 prob.lb=[prob.blx; prob.blc];
-0332 
-0333 % solve problem
-0334 res=optimizeProb(prob,params);
-0335 
-0336 %Problem should not be infeasible, but it is possible that the time limit
-0337 %was reached before finding any solutions.
-0338 if ~checkSolution(res)
-0339     if strcmp(res.origStat, 'TIME_LIMIT')
-0340         EM='Time limit reached without finding a solution. Try increasing the TimeLimit parameter.';
-0341     else
-0342         EM='The problem is infeasible';
-0343     end
-0344     error('RAVEN:badInput', '%s', EM);
-0345 end
-0346 
-0347 fValue=res.obj;
-0348 
-0349 %Get all reactions used in the irreversible model
-0350 usedRxns=(nonEssentialIndex(res.full(nRxns+1:nRxns+nNonEssential)<0.1))';
-0351 
-0352 %Map to reversible model IDs
-0353 usedRxns=[usedRxns(usedRxns<=numel(model.rxns));revRxns(usedRxns(usedRxns>numel(model.rxns))-numel(model.rxns))];
-0354 
-0355 %Then get the ones that are not used in either direction or is essential
-0356 I=true(numel(model.rxns),1);
-0357 I(usedRxns)=false;
-0358 I(essentialIndex)=false;
-0359 deletedRxns=model.rxns(I);
-0360 outModel=removeReactions(model,I,true,true);
-0361 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/analysis/FSEOF.html b/doc/analysis/FSEOF.html deleted file mode 100644 index 3a198a6d..00000000 --- a/doc/analysis/FSEOF.html +++ /dev/null @@ -1,265 +0,0 @@ - - - - Description of FSEOF - - - - - - - - - -
Home > analysis > FSEOF.m
- - - -

FSEOF -

- -

PURPOSE ^

-
FSEOF Flux Scanning based on Enforced Objective Flux.
- -

SYNOPSIS ^

-
function targets=FSEOF(model,biomassRxn,targetRxn,varargin)
- -

DESCRIPTION ^

-
 FSEOF  Flux Scanning based on Enforced Objective Flux.
-
- Implements the Flux Scanning based on Enforced Objective Flux algorithm.
- This function writes a tab-delimited file or prints to the command
- window. If an output has been specified (targets), it will also generate
- a structure indicating for each model reaction whether it is identified
- by FSEOF as a target and the slope of the reaction when switching from
- biomass formation to product formation.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- biomassRxn : char
-     reaction ID of the biomass formation or growth reaction.
- targetRxn : char
-     reaction ID of the target reaction.
-
- Name-Value Arguments
- --------------------
- iterations : double
-     number of iterations (default 10).
- coefficient : double
-     ratio of optimal target reaction flux, must be less than 1
-     (default 0.9).
- outputFile : char
-     output filename (default prints to command window).
- corrThreshold : double
-     minimum absolute Pearson correlation coefficient between a
-     reaction absolute flux and the enforced product flux for the
-     reaction to be selected as a target. Values in [0, 1]; higher
-     values require a more linear response and suppress LP alternative-
-     optima noise (default 0.9).
-
- Returns
- -------
- targets : struct
-     structure with information for identified targets, with fields:
-
-     - logical : logical array, true for amplification targets (backward
-       compatible alias for amplify).
-     - amplify : logical array, reactions whose absolute flux increases
-       with enforced product flux.
-     - knockdown : logical array, reactions driven toward but not to zero.
-     - knockout : logical array, reactions driven to zero at max product.
-     - slope : numeric array with per-reaction regression slopes of
-       absolute flux versus enforced product flux (all reactions, not
-       just targets).
-
- Examples
- --------
-     targets = FSEOF(model, biomassRxn, targetRxn, iterations, ...
-                     coefficient, outputFile);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function targets=FSEOF(model,biomassRxn,targetRxn,varargin)
-0002 % FSEOF  Flux Scanning based on Enforced Objective Flux.
-0003 %
-0004 % Implements the Flux Scanning based on Enforced Objective Flux algorithm.
-0005 % This function writes a tab-delimited file or prints to the command
-0006 % window. If an output has been specified (targets), it will also generate
-0007 % a structure indicating for each model reaction whether it is identified
-0008 % by FSEOF as a target and the slope of the reaction when switching from
-0009 % biomass formation to product formation.
-0010 %
-0011 % Parameters
-0012 % ----------
-0013 % model : struct
-0014 %     a model structure.
-0015 % biomassRxn : char
-0016 %     reaction ID of the biomass formation or growth reaction.
-0017 % targetRxn : char
-0018 %     reaction ID of the target reaction.
-0019 %
-0020 % Name-Value Arguments
-0021 % --------------------
-0022 % iterations : double
-0023 %     number of iterations (default 10).
-0024 % coefficient : double
-0025 %     ratio of optimal target reaction flux, must be less than 1
-0026 %     (default 0.9).
-0027 % outputFile : char
-0028 %     output filename (default prints to command window).
-0029 % corrThreshold : double
-0030 %     minimum absolute Pearson correlation coefficient between a
-0031 %     reaction absolute flux and the enforced product flux for the
-0032 %     reaction to be selected as a target. Values in [0, 1]; higher
-0033 %     values require a more linear response and suppress LP alternative-
-0034 %     optima noise (default 0.9).
-0035 %
-0036 % Returns
-0037 % -------
-0038 % targets : struct
-0039 %     structure with information for identified targets, with fields:
-0040 %
-0041 %     - logical : logical array, true for amplification targets (backward
-0042 %       compatible alias for amplify).
-0043 %     - amplify : logical array, reactions whose absolute flux increases
-0044 %       with enforced product flux.
-0045 %     - knockdown : logical array, reactions driven toward but not to zero.
-0046 %     - knockout : logical array, reactions driven to zero at max product.
-0047 %     - slope : numeric array with per-reaction regression slopes of
-0048 %       absolute flux versus enforced product flux (all reactions, not
-0049 %       just targets).
-0050 %
-0051 % Examples
-0052 % --------
-0053 %     targets = FSEOF(model, biomassRxn, targetRxn, iterations, ...
-0054 %                     coefficient, outputFile);
-0055 
-0056 biomassRxn=char(biomassRxn);
-0057 targetRxn=char(targetRxn);
-0058 
-0059 p=parseRAVENargs(varargin, {'iterations',10; 'coefficient',0.9; 'outputFile',[]; 'corrThreshold',0.9});
-0060 iterations=p.iterations;
-0061 coefficient=p.coefficient;
-0062 outputFile=p.outputFile;
-0063 corrThreshold=p.corrThreshold;
-0064 
-0065 %Find out the maximum theoretical yield of target reaction
-0066 model=setParam(model,'obj',targetRxn,1);
-0067 sol=solveLP(model,1);
-0068 targetMax=sol.f*coefficient;
-0069 
-0070 model=setParam(model,'obj',biomassRxn,1);
-0071 
-0072 nRxns=length(model.rxns);
-0073 results=zeros(nRxns,iterations);
-0074 enforcedFlux=(1:iterations)*targetMax/iterations;
-0075 
-0076 %Run pFBA at each enforced target flux level
-0077 for i=1:iterations
-0078     model=setParam(model,'lb',targetRxn,enforcedFlux(i));
-0079     sol=solveLP(model,1);
-0080     results(:,i)=sol.x;
-0081 end
-0082 
-0083 %Compute per-reaction regression slope (FS4) and Pearson correlation (FS1)
-0084 %on absolute flux values to treat forward/reverse symmetrically.
-0085 absResults=abs(results);
-0086 slope=zeros(nRxns,1);
-0087 rCoeff=zeros(nRxns,1);
-0088 hasVariation = iterations > 1 && std(enforcedFlux) > 0;
-0089 if hasVariation
-0090     for num=1:nRxns
-0091         fluxTrack=absResults(num,:);
-0092         if any(fluxTrack > 1e-10) && std(fluxTrack) > 0
-0093             p_fit=polyfit(enforcedFlux,fluxTrack,1);
-0094             slope(num)=p_fit(1);
-0095             r=corrcoef(enforcedFlux,fluxTrack);
-0096             rCoeff(num)=r(1,2);
-0097         end
-0098     end
-0099 end
-0100 
-0101 %Classify targets: select reactions whose absolute flux is sufficiently
-0102 %correlated with the enforced product flux (FS1), then split by direction
-0103 %and whether any flux remains at maximum enforcement (FS2).
-0104 isCorrelated = abs(rCoeff) >= corrThreshold;
-0105 isAmplify    = isCorrelated & slope > 0;
-0106 isKnockdown  = isCorrelated & slope < 0 & absResults(:,end) > 1e-8;
-0107 isKnockout   = isCorrelated & slope < 0 & absResults(:,end) <= 1e-8;
-0108 
-0109 rxnDirection=sign(results(:,1));
-0110 
-0111 %Generating output
-0112 formatSpec='%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n';
-0113 if ~isempty(outputFile)
-0114     outputFile=char(outputFile);
-0115     fid=fopen(outputFile,'w');
-0116     fprintf(fid,formatSpec,'Type','Slope','rowID','Enzyme ID','Enzyme Name','Subsystems','Direction','Gr Rule');
-0117 else
-0118     fid=-1;
-0119     fprintf(formatSpec,'Type','Slope','rowID','Enzyme ID','Enzyme Name','Subsystems','Direction','Gr Rule');
-0120 end
-0121 
-0122 targetTypes  = {'Amplify','Knockdown','Knockout'};
-0123 targetArrays = {isAmplify, isKnockdown, isKnockout};
-0124 
-0125 for t=1:3
-0126     flag=targetArrays{t};
-0127     for num=1:nRxns
-0128         if flag(num)
-0129             A_type=targetTypes{t};
-0130             A0=num2str(slope(num));
-0131             A1=num2str(num);
-0132             A2=char(model.rxns(num));
-0133             A3=char(model.rxnNames(num));
-0134             if isfield(model,'subSystems') && ~isempty(model.subSystems{num})
-0135                 if ~any(cellfun(@(x) iscell(x), model.subSystems))
-0136                     subSys=cellfun(@(x) {x}, model.subSystems, 'uni', 0);
-0137                 else
-0138                     subSys=model.subSystems;
-0139                 end
-0140                 A4=char(strjoin(subSys{num},';'));
-0141             else
-0142                 A4='';
-0143             end
-0144             A5=num2str(model.rev(num)*rxnDirection(num));
-0145             A6=char(model.grRules(num));
-0146             if fid == -1
-0147                 fprintf(formatSpec,A_type,A0,A1,A2,A3,A4,A5,A6);
-0148             else
-0149                 fprintf(fid,formatSpec,A_type,A0,A1,A2,A3,A4,A5,A6);
-0150             end
-0151         end
-0152     end
-0153 end
-0154 
-0155 if fid ~= -1
-0156     fclose(fid);
-0157 end
-0158 
-0159 if nargout == 1
-0160     targets.logical   = isAmplify;
-0161     targets.amplify   = isAmplify;
-0162     targets.knockdown = isKnockdown;
-0163     targets.knockout  = isKnockout;
-0164     targets.slope     = slope;
-0165 end
-0166 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/analysis/analyzeSampling.html b/doc/analysis/analyzeSampling.html deleted file mode 100644 index 75424dc5..00000000 --- a/doc/analysis/analyzeSampling.html +++ /dev/null @@ -1,267 +0,0 @@ - - - - Description of analyzeSampling - - - - - - - - - -
Home > analysis > analyzeSampling.m
- - - -

analyzeSampling -

- -

PURPOSE ^

-
analyzeSampling Compare flux change significance with expression change.
- -

SYNOPSIS ^

-
function scores=analyzeSampling(Tex, df, solutionsA, solutionsB, varargin)
- -

DESCRIPTION ^

-
 analyzeSampling  Compare flux change significance with expression change.
-
- Compares the significance of change in flux between two conditions with
- the significance of change in gene expression.
-
- Parameters
- ----------
- Tex : double
-     a vector of t-scores for the change in gene expression for each
-     reaction. This score could be the Student t between the two
-     conditions, or you can calculate it from a p-value (by computing the
-     inverse of the so called error function). If you choose the second
-     alternative you should be aware that the transcripts that increased
-     in expression level should have positive values and those who
-     decreased in expression level should have negative values (the
-     p-values only tell you if the fluxes changed or not but not in which
-     direction).
- df : double
-     the degrees of freedom in the t-test.
- solutionsA : double
-     random solutions for the reference condition (as generated by
-     randomSampling).
- solutionsB : double
-     random solutions for the test condition (as generated by
-     randomSampling).
-
- Name-Value Arguments
- --------------------
- printResults : logical
-     prints the most significant reactions in each category
-     (default false).
-
- Returns
- -------
- scores : double
-     a Nx3 column matrix with the probabilities of a reaction:
-
-     1.  changing both in flux and expression in the same direction
-     2.  changing in expression but not in flux
-     3.  changing in flux but not in expression or changing in opposed
-         directions in flux and expression
-
- Examples
- --------
-     scores = analyzeSampling(Tex, df, solutionsA, solutionsB, ...
-                              printResults);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • getFluxZ getFluxZ Calculate Z scores between two sets of random flux distributions.
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function scores=analyzeSampling(Tex, df, solutionsA, solutionsB, varargin)
-0002 % analyzeSampling  Compare flux change significance with expression change.
-0003 %
-0004 % Compares the significance of change in flux between two conditions with
-0005 % the significance of change in gene expression.
-0006 %
-0007 % Parameters
-0008 % ----------
-0009 % Tex : double
-0010 %     a vector of t-scores for the change in gene expression for each
-0011 %     reaction. This score could be the Student t between the two
-0012 %     conditions, or you can calculate it from a p-value (by computing the
-0013 %     inverse of the so called error function). If you choose the second
-0014 %     alternative you should be aware that the transcripts that increased
-0015 %     in expression level should have positive values and those who
-0016 %     decreased in expression level should have negative values (the
-0017 %     p-values only tell you if the fluxes changed or not but not in which
-0018 %     direction).
-0019 % df : double
-0020 %     the degrees of freedom in the t-test.
-0021 % solutionsA : double
-0022 %     random solutions for the reference condition (as generated by
-0023 %     randomSampling).
-0024 % solutionsB : double
-0025 %     random solutions for the test condition (as generated by
-0026 %     randomSampling).
-0027 %
-0028 % Name-Value Arguments
-0029 % --------------------
-0030 % printResults : logical
-0031 %     prints the most significant reactions in each category
-0032 %     (default false).
-0033 %
-0034 % Returns
-0035 % -------
-0036 % scores : double
-0037 %     a Nx3 column matrix with the probabilities of a reaction:
-0038 %
-0039 %     1.  changing both in flux and expression in the same direction
-0040 %     2.  changing in expression but not in flux
-0041 %     3.  changing in flux but not in expression or changing in opposed
-0042 %         directions in flux and expression
-0043 %
-0044 % Examples
-0045 % --------
-0046 %     scores = analyzeSampling(Tex, df, solutionsA, solutionsB, ...
-0047 %                              printResults);
-0048 
-0049 p=parseRAVENargs(varargin, {'printResults',false});
-0050 printResults=p.printResults;
-0051 
-0052 nRxns=numel(Tex);
-0053 pM=zeros(nRxns,1);
-0054 pH=zeros(nRxns,1);
-0055 pR=zeros(nRxns,1);
-0056 
-0057 %Check that the number of reactions is the same in both expression and flux
-0058 if nRxns~=size(solutionsA,1)
-0059     EM='The number of reactions must be the same in Tex as in solutionsA';
-0060     error('RAVEN:badInput', '%s', EM);
-0061 end
-0062 
-0063 %Get the Z-score and mean for the solutions
-0064 mA=mean(solutionsA,2);
-0065 mB=mean(solutionsB,2);
-0066 Zf=getFluxZ(solutionsA, solutionsB);
-0067 
-0068 %Clear up the tex if there are elements that are NaN or +/- Inf.
-0069 I=isnan(Tex) | isinf(Tex);
-0070 if any(I)
-0071     EM='There are t-scores that are NaN or +/- Inf. These values are changed to 0.0';
-0072     warning('RAVEN:warning', '%s', EM);
-0073 end
-0074 Tex(I)=0;
-0075 
-0076 for i=1:nRxns
-0077     %Check how the flux has changed. The means are needed because to
-0078     %differentiate between a positive flux changing to a smaller flux and a
-0079     %negative flux changing to a more negative flux (which is a larger
-0080     %flux)
-0081     I=mB(i)/mA(i);
-0082     if I<0
-0083         pM(i)=erf(abs(Zf(i)));
-0084         pH(i)=(1-pM(i))*(2*tcdf(abs(Tex(i)),df)-1);
-0085         pR(i)=0;
-0086     else
-0087         if mB(i)<0
-0088             Zf(i)=Zf(i)*-1;
-0089         end
-0090     end
-0091 
-0092     I=Zf(i)/Tex(i);
-0093     if I<0
-0094         pM(i)=erf(abs(Zf(i)));
-0095         pH(i)=(1-pM(i))*(2*tcdf(abs(Tex(i)),df)-1);
-0096         pR(i)=0;
-0097     else
-0098         pR(i)=erf(abs(Zf(i)))*(2*tcdf(abs(Tex(i)),df)-1);
-0099         pH(i)=(2*tcdf(abs(Tex(i)),df)-1)*(1-erf(abs(Zf(i))));
-0100         pM(i)=erf(abs(Zf(i)))*(1-(2*tcdf(abs(Tex(i)),df)-1));
-0101     end
-0102 end
-0103 
-0104 scores=[pR pH pM];
-0105 
-0106 if printResults==true
-0107     fprintf('TOP SCORING REACTIONS\n\n');
-0108     %The top 10 hits in the first category
-0109     [I, J]=sort(pR,'descend');
-0110     fprintf('Reactions which change both in flux and expression in the same direction\nReaction\tProbability\n');
-0111     for i=1:10
-0112         fprintf([num2str(J(i)) '\t' num2str(I(i)) '\n']);
-0113     end
-0114 
-0115     %The top 10 hits in the first category
-0116     [I, J]=sort(pH,'descend');
-0117     fprintf('\nReactions which change in expression but not in flux\nReaction\tProbability\n');
-0118     for i=1:10
-0119         fprintf([num2str(J(i)) '\t' num2str(I(i)) '\n']);
-0120     end
-0121 
-0122     %The top 10 hits in the first category
-0123     [I, J]=sort(pM,'descend');
-0124     fprintf('\nReactions which change in flux but not in expression, or in opposed directions in flux and expression\nReaction\tProbability\n');
-0125     for i=1:10
-0126         fprintf([num2str(J(i)) '\t' num2str(I(i)) '\n']);
-0127     end
-0128 end
-0129 end
-0130 
-0131 function p = tcdf(t,v,uflag)
-0132 % TCDF Student T cumulative distribution function (cdf).
-0133 %
-0134 % P = TCDF(X,V) computes the cdf for Student T distribution
-0135 % with V degrees of freedom, at the values in X. V must be a
-0136 % scalar or have the same size as T.
-0137 %
-0138 % P = TCDF(X,V,"upper") computes it for the upper tail instead
-0139 % of the lower tail.
-0140 %
-0141 % Alternative to the TCDF function in the Matlab statistics toolbox, for use
-0142 % when that toolbox is not installed. Adapted from
-0143 % https://github.com/fieldtrip/fieldtrip/blob/master/external/stats/tcdf.m,
-0144 % originally by Gordon Smyth, University of Queensland.
-0145 
-0146 if v <= 0, error('Degrees of freedom must be positive.'); end
-0147 
-0148 % resize v if necessary
-0149 if all(size(v)==1)
-0150     v = ones(size(t))*v;
-0151 end
-0152 
-0153 %check for NaNs - do not do calculations on them, give those out as NaNs
-0154 if any( not(isfinite(t(:))) | not(isfinite(v(:))) )
-0155     sel = find(isfinite(t) & isfinite(v));
-0156     x=nan(size(t));
-0157     p=nan(size(t));
-0158     x(sel) = t(sel).^2 ./ (v(sel) + t(sel).^2) ;
-0159     p(sel) = 0.5 .* ( 1 + sign(t(sel)) .* betainc( x(sel), 0.5, 0.5*v(sel) ) );
-0160 else
-0161     x = t.^2 ./ (v + t.^2) ;
-0162     p = 0.5 .* ( 1 + sign(t) .* betainc( x, 0.5, 0.5*v ) );
-0163 end
-0164 
-0165 if nargin==3
-0166     if strcmp(uflag, 'upper')
-0167         % compute it for the upper tail instead of the lower tail
-0168         p = 1-p;
-0169     else
-0170         error('incorrect specification of uflag');
-0171     end
-0172 end
-0173 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/analysis/compareFluxes.html b/doc/analysis/compareFluxes.html deleted file mode 100644 index c6448840..00000000 --- a/doc/analysis/compareFluxes.html +++ /dev/null @@ -1,303 +0,0 @@ - - - - Description of compareFluxes - - - - - - - - - -
Home > analysis > compareFluxes.m
- - - -

compareFluxes -

- -

PURPOSE ^

-
compareFluxes Find the most significant flux changes between two conditions.
- -

SYNOPSIS ^

-
function result = compareFluxes(model, fluxes1, fluxes2, varargin)
- -

DESCRIPTION ^

-
 compareFluxes  Find the most significant flux changes between two conditions.
-
- Compares two flux vectors from the same model and returns all reactions
- that changed, sorted by absolute flux difference. Classifies reactions as
- turned on, turned off, reversed direction, or changed magnitude.
-
- Parameters
- ----------
- model : struct
-     RAVEN model structure.
- fluxes1 : double
-     reference flux vector (condition A, e.g. wild-type).
- fluxes2 : double
-     comparison flux vector (condition B, e.g. mutant or knockin).
-
- Name-Value Arguments
- --------------------
- cutoff : double
-     minimum |flux| to consider a reaction active (default 1e-8).
- metaboliteList : cell
-     cell array of metabolite names. Only reactions involving at least one
-     of these metabolites are compared; every other reaction is left out of
-     the result entirely. Matching is against model.metNames and is case
-     insensitive. A name that matches no metabolite is reported as a warning
-     rather than an error (default all reactions).
- nMax : double
-     maximum rows printed in the summary table (default 20).
- verbose : logical
-     print the summary table (default true).
-
- Returns
- -------
- result : struct
-     .turnedOn  — cell, rxn IDs newly active in fluxes2
-     .turnedOff — cell, rxn IDs silenced in fluxes2
-     .flipped   — cell, rxn IDs that reversed sign between conditions
-     .changed   — struct with fields (all reactions with |dflux|>cutoff,
-                  sorted by |dflux| descending):
-                    .rxn       — cell of reaction IDs
-                    .flux1     — double, flux in condition A
-                    .flux2     — double, flux in condition B
-                    .absDelta  — double, |flux2 - flux1|
-                    .relChange — double, (flux2-flux1)/|flux1|; NaN when
-                                 flux1 is near-zero (reaction emerged)
-                    .type      — cell, 'on'|'off'|'flip'|'' per reaction
-
- Examples
- --------
-     solA = solveLP(model);
-     modelAna = setParam(model, 'ub', 'O2exchange', 0);
-     solB = solveLP(modelAna);
-     result = compareFluxes(model, solA.x, solB.x)
-     result = compareFluxes(model, solA.x, solB.x, 'nMax', 50)
-     result = compareFluxes(model, solA.x, solB.x, 'metaboliteList', {'ATP','NADH'})
-
- See also
-     walkFluxes, traceFluxPath, modelSummary
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function result = compareFluxes(model, fluxes1, fluxes2, varargin)
-0002 % compareFluxes  Find the most significant flux changes between two conditions.
-0003 %
-0004 % Compares two flux vectors from the same model and returns all reactions
-0005 % that changed, sorted by absolute flux difference. Classifies reactions as
-0006 % turned on, turned off, reversed direction, or changed magnitude.
-0007 %
-0008 % Parameters
-0009 % ----------
-0010 % model : struct
-0011 %     RAVEN model structure.
-0012 % fluxes1 : double
-0013 %     reference flux vector (condition A, e.g. wild-type).
-0014 % fluxes2 : double
-0015 %     comparison flux vector (condition B, e.g. mutant or knockin).
-0016 %
-0017 % Name-Value Arguments
-0018 % --------------------
-0019 % cutoff : double
-0020 %     minimum |flux| to consider a reaction active (default 1e-8).
-0021 % metaboliteList : cell
-0022 %     cell array of metabolite names. Only reactions involving at least one
-0023 %     of these metabolites are compared; every other reaction is left out of
-0024 %     the result entirely. Matching is against model.metNames and is case
-0025 %     insensitive. A name that matches no metabolite is reported as a warning
-0026 %     rather than an error (default all reactions).
-0027 % nMax : double
-0028 %     maximum rows printed in the summary table (default 20).
-0029 % verbose : logical
-0030 %     print the summary table (default true).
-0031 %
-0032 % Returns
-0033 % -------
-0034 % result : struct
-0035 %     .turnedOn  — cell, rxn IDs newly active in fluxes2
-0036 %     .turnedOff — cell, rxn IDs silenced in fluxes2
-0037 %     .flipped   — cell, rxn IDs that reversed sign between conditions
-0038 %     .changed   — struct with fields (all reactions with |dflux|>cutoff,
-0039 %                  sorted by |dflux| descending):
-0040 %                    .rxn       — cell of reaction IDs
-0041 %                    .flux1     — double, flux in condition A
-0042 %                    .flux2     — double, flux in condition B
-0043 %                    .absDelta  — double, |flux2 - flux1|
-0044 %                    .relChange — double, (flux2-flux1)/|flux1|; NaN when
-0045 %                                 flux1 is near-zero (reaction emerged)
-0046 %                    .type      — cell, 'on'|'off'|'flip'|'' per reaction
-0047 %
-0048 % Examples
-0049 % --------
-0050 %     solA = solveLP(model);
-0051 %     modelAna = setParam(model, 'ub', 'O2exchange', 0);
-0052 %     solB = solveLP(modelAna);
-0053 %     result = compareFluxes(model, solA.x, solB.x)
-0054 %     result = compareFluxes(model, solA.x, solB.x, 'nMax', 50)
-0055 %     result = compareFluxes(model, solA.x, solB.x, 'metaboliteList', {'ATP','NADH'})
-0056 %
-0057 % See also
-0058 %     walkFluxes, traceFluxPath, modelSummary
-0059 
-0060 p = parseRAVENargs(varargin, {'cutoff',1e-8; 'metaboliteList',[]; 'nMax',20; 'verbose',true});
-0061 cutoff         = p.cutoff;
-0062 metaboliteList = p.metaboliteList;
-0063 nMax           = p.nMax;
-0064 verbose        = p.verbose;
-0065 
-0066 f1    = fluxes1(:);
-0067 f2    = fluxes2(:);
-0068 nRxns = numel(model.rxns);
-0069 
-0070 if numel(f1) ~= nRxns || numel(f2) ~= nRxns
-0071     error('RAVEN:badInput', ...
-0072         'Flux vectors must have %d elements (numel(model.rxns)).', nRxns);
-0073 end
-0074 
-0075 active1 = abs(f1) > cutoff;
-0076 active2 = abs(f2) > cutoff;
-0077 delta   = f2 - f1;
-0078 
-0079 % Restrict to reactions involving one of metaboliteList, if given. Reactions
-0080 % left out are treated as unchanged, so they appear in no output field.
-0081 inList = true(nRxns, 1);
-0082 if ~isempty(metaboliteList)
-0083     metaboliteList = convertCharArray(metaboliteList);
-0084     inList = false(nRxns, 1);
-0085     notFound = {};
-0086     for i = 1:numel(metaboliteList)
-0087         metIdx = find(strcmpi(metaboliteList{i}, model.metNames));
-0088         if isempty(metIdx)
-0089             notFound{end+1} = metaboliteList{i}; %#ok<AGROW>
-0090         else
-0091             inList(any(model.S(metIdx,:) ~= 0, 1)) = true;
-0092         end
-0093     end
-0094     if ~isempty(notFound)
-0095         warning('RAVEN:warning', '%s', ...
-0096             ravenList('No metabolite in the model is named:', notFound));
-0097     end
-0098     active1(~inList) = false;
-0099     active2(~inList) = false;
-0100     delta(~inList)   = 0;
-0101 end
-0102 
-0103 onMask   = ~active1 &  active2;
-0104 offMask  =  active1 & ~active2;
-0105 flipMask =  active1 &  active2 & sign(f1) ~= sign(f2);
-0106 
-0107 result.turnedOn  = model.rxns(onMask);
-0108 result.turnedOff = model.rxns(offMask);
-0109 result.flipped   = model.rxns(flipMask);
-0110 
-0111 % All changed reactions sorted by |delta| descending
-0112 changedMask = abs(delta) > cutoff;
-0113 [~, si]     = sort(abs(delta(changedMask)), 'descend');
-0114 idx = find(changedMask);
-0115 idx = idx(si);
-0116 
-0117 % Relative change: (f2-f1)/|f1|; NaN when reference flux is near-zero
-0118 denom = abs(f1(idx));
-0119 denom(denom < cutoff) = NaN;
-0120 relChange = delta(idx) ./ denom;
-0121 
-0122 % Type label per changed reaction
-0123 changedTypes = repmat({''}, numel(idx), 1);
-0124 for k = 1:numel(idx)
-0125     ri = idx(k);
-0126     if onMask(ri)
-0127         changedTypes{k} = 'on';
-0128     elseif offMask(ri)
-0129         changedTypes{k} = 'off';
-0130     elseif flipMask(ri)
-0131         changedTypes{k} = 'flip';
-0132     end
-0133 end
-0134 
-0135 result.changed.rxn       = model.rxns(idx);
-0136 result.changed.flux1     = f1(idx);
-0137 result.changed.flux2     = f2(idx);
-0138 result.changed.absDelta  = abs(delta(idx));
-0139 result.changed.relChange = relChange;
-0140 result.changed.type      = changedTypes;
-0141 
-0142 if verbose
-0143     printTable_(model, result, nMax, nnz(inList));
-0144 end
-0145 end
-0146 
-0147 %--------------------------------------------------------------------------
-0148 function printTable_(model, r, nMax, nConsidered)
-0149     W   = 72;
-0150     bar = repmat('=', 1, W);
-0151     sep = repmat('-', 1, W);
-0152     nC  = numel(r.changed.rxn);
-0153 
-0154     fprintf('\n%s\n', bar);
-0155     fprintf('  Flux comparison: %d of %d reactions changed\n', nC, nConsidered);
-0156     nOn  = numel(r.turnedOn);
-0157     nOff = numel(r.turnedOff);
-0158     nFlp = numel(r.flipped);
-0159     if nOn + nOff + nFlp > 0
-0160         fprintf('  (on: %d  |  off: %d  |  reversed: %d)\n', nOn, nOff, nFlp);
-0161     end
-0162 
-0163     if nC == 0
-0164         fprintf('%s\n\n', bar);
-0165         return
-0166     end
-0167 
-0168     nShow    = min(nC, nMax);
-0169     showRxns = r.changed.rxn(1:nShow);
-0170     eqns     = constructEquations(model, showRxns);
-0171 
-0172     fprintf('%s\n', sep);
-0173     fprintf('  %-20s  %10s  %10s  %10s  %7s  %-5s  %-25s  %s\n', ...
-0174         'Reaction', 'Cond.1', 'Cond.2', '|dflux|', 'drel%', 'Type', 'Name', 'Equation');
-0175     fprintf('  %s\n', sep);
-0176 
-0177     for i = 1:nShow
-0178         rel = r.changed.relChange(i);
-0179         if isnan(rel)
-0180             relStr = '    new';
-0181         else
-0182             relStr = sprintf('%+6.0f%%', rel * 100);
-0183         end
-0184         ri = find(strcmp(model.rxns, showRxns{i}), 1);
-0185         nm = '';
-0186         if isfield(model,'rxnNames') && ri <= numel(model.rxnNames)
-0187             nm = char(model.rxnNames{ri});
-0188         end
-0189         if numel(nm) > 25, nm = [nm(1:22) '...']; end
-0190         fprintf('  %-20s  %+10.4g  %+10.4g  %10.4g  %7s  %-5s  %-25s  %s\n', ...
-0191             showRxns{i}, r.changed.flux1(i), r.changed.flux2(i), ...
-0192             r.changed.absDelta(i), relStr, r.changed.type{i}, nm, eqns{i});
-0193     end
-0194     if nC > nMax
-0195         fprintf('  ... (%d more, use nMax to show all)\n', nC - nMax);
-0196     end
-0197     fprintf('%s\n\n', bar);
-0198 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/analysis/findGeneDeletions.html b/doc/analysis/findGeneDeletions.html deleted file mode 100644 index 7eb35845..00000000 --- a/doc/analysis/findGeneDeletions.html +++ /dev/null @@ -1,233 +0,0 @@ - - - - Description of findGeneDeletions - - - - - - - - - -
Home > analysis > findGeneDeletions.m
- - - -

findGeneDeletions -

- -

PURPOSE ^

-
findGeneDeletions Delete genes and track the resulting fluxes.
- -

SYNOPSIS ^

-
function [genes, fluxes, originalGenes, details, grRatioMuts]=findGeneDeletions(model,varargin)
- -

DESCRIPTION ^

-
 findGeneDeletions  Delete genes and track the resulting fluxes.
-
- Deletes genes, optimizes the model by FBA, and keeps track of the
- resulting fluxes. This is used for identifying gene deletion targets.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- testType : char
-     single/double gene deletion (default "sgd"):
-
-     - "sgd" : single gene deletion
-     - "dgd" : double gene deletion
-
- Returns
- -------
- genes : double
-     a matrix with the genes that were deleted in each optimization (the
-     gene indexes in originalGenes). Each row corresponds to a column in
-     fluxes.
- fluxes : double
-     a matrix with the resulting fluxes. Double deletions that result in
-     an unsolvable problem have all zero flux. Single deletions that
-     result in an unsolvable problem are indicated in details instead.
- originalGenes : cell
-     simply the genes in the input model. Included for simple
-     presentation of the output.
- details : double
-     not all genes will be deleted in all analyses. It is for example not
-     necessary to delete genes for dead end reactions. This is a vector
-     with details about each gene in originalGenes and why or why not it
-     was deleted:
-
-     - 1 : was deleted
-     - 2 : proved lethal in sgd (single gene deletion)
-     - 3 : redundant, no longer used
-     - 4 : involved in dead-end reaction
- grRatioMuts : double
-     growth rate ratio between mutated strain and wild type, matching the
-     originalGenes(genes) mutants. Note that this does not directly map
-     to model.genes, as is the case for COBRA getEssentialGenes. However,
-     this can be obtained by afterwards running:
-
-         grRatio=zeros(1,numel(model.genes));
-         grRatio(genes)=grRatioMuts;
-
- Examples
- --------
-     [genes, fluxes, originalGenes, details, grRatioMuts]=...
-         findGeneDeletions(model,testType);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function [genes, fluxes, originalGenes, details, grRatioMuts]=findGeneDeletions(model,varargin)
-0002 % findGeneDeletions  Delete genes and track the resulting fluxes.
-0003 %
-0004 % Deletes genes, optimizes the model by FBA, and keeps track of the
-0005 % resulting fluxes. This is used for identifying gene deletion targets.
-0006 %
-0007 % Parameters
-0008 % ----------
-0009 % model : struct
-0010 %     a model structure.
-0011 %
-0012 % Name-Value Arguments
-0013 % --------------------
-0014 % testType : char
-0015 %     single/double gene deletion (default "sgd"):
-0016 %
-0017 %     - "sgd" : single gene deletion
-0018 %     - "dgd" : double gene deletion
-0019 %
-0020 % Returns
-0021 % -------
-0022 % genes : double
-0023 %     a matrix with the genes that were deleted in each optimization (the
-0024 %     gene indexes in originalGenes). Each row corresponds to a column in
-0025 %     fluxes.
-0026 % fluxes : double
-0027 %     a matrix with the resulting fluxes. Double deletions that result in
-0028 %     an unsolvable problem have all zero flux. Single deletions that
-0029 %     result in an unsolvable problem are indicated in details instead.
-0030 % originalGenes : cell
-0031 %     simply the genes in the input model. Included for simple
-0032 %     presentation of the output.
-0033 % details : double
-0034 %     not all genes will be deleted in all analyses. It is for example not
-0035 %     necessary to delete genes for dead end reactions. This is a vector
-0036 %     with details about each gene in originalGenes and why or why not it
-0037 %     was deleted:
-0038 %
-0039 %     - 1 : was deleted
-0040 %     - 2 : proved lethal in sgd (single gene deletion)
-0041 %     - 3 : redundant, no longer used
-0042 %     - 4 : involved in dead-end reaction
-0043 % grRatioMuts : double
-0044 %     growth rate ratio between mutated strain and wild type, matching the
-0045 %     originalGenes(genes) mutants. Note that this does not directly map
-0046 %     to model.genes, as is the case for COBRA getEssentialGenes. However,
-0047 %     this can be obtained by afterwards running:
-0048 %
-0049 %         grRatio=zeros(1,numel(model.genes));
-0050 %         grRatio(genes)=grRatioMuts;
-0051 %
-0052 % Examples
-0053 % --------
-0054 %     [genes, fluxes, originalGenes, details, grRatioMuts]=...
-0055 %         findGeneDeletions(model,testType);
-0056 
-0057 originalModel=model;
-0058 p=parseRAVENargs(varargin, {'testType',[]});
-0059 testType=p.testType;
-0060 if isempty(testType)
-0061     testType='sgd';
-0062 else
-0063     testType=char(testType);
-0064 end
-0065 
-0066 %Check that the test type is correct
-0067 if ~strcmpi(testType,'sgd') && ~strcmpi(testType,'dgd')
-0068     EM='Incorrect test type';
-0069     error('RAVEN:badInput', '%s', EM);
-0070 end
-0071 
-0072 originalGenes=model.genes;
-0073 details=zeros(numel(model.genes),1);
-0074 
-0075 %First simplify the model to reduce the size
-0076 model=simplifyModel(model,true,false,true,true);
-0077 model=removeReactions(model,{},true,true); %Removes unused genes
-0078 details(~ismember(originalGenes,model.genes))=4;
-0079 
-0080 [~, geneMapping]=ismember(model.genes,originalGenes);
-0081 growthWT=solveLP(model);
-0082 growthWT=growthWT.f;
-0083 
-0084 %Do single deletion. This is done here since the double deletion depends
-0085 %on which single deletions prove lethal (to reduce the size of the system)
-0086 if strcmpi(testType,'sgd') || strcmpi(testType,'dgd')
-0087     fluxes=zeros(numel(model.rxns),numel(model.genes));
-0088     grRatioMuts=zeros(1,numel(model.genes));
-0089     for i=1:numel(model.genes)
-0090         %Constrain all reactions involving the gene to 0
-0091         tempModel=removeGenes(model,i,false,false,false);
-0092         sol=solveLP(tempModel);
-0093 
-0094         %If the optimization terminated successfully
-0095         if sol.stat==1
-0096             fluxes(:,i)=sol.x;
-0097             grRatioMuts(i)=sol.f/growthWT;
-0098             details(geneMapping(i))=1;
-0099         else
-0100             fluxes(:,i)=0;
-0101             grRatioMuts(i)=0;
-0102             details(geneMapping(i))=2;
-0103         end
-0104     end
-0105     genes=geneMapping;
-0106 end
-0107 
-0108 %For double deletions
-0109 if strcmpi(testType,'dgd')
-0110     %This is a little lazy but it is fine. Check which genes have already
-0111     %been deleted in "sgd" analysis.
-0112     [~, I]=ismember(originalGenes(details==1),model.genes);
-0113     genesToModify=nchoosek(I,2);
-0114     genes=geneMapping(genesToModify);
-0115     grRatioMuts=zeros(1,numel(genes));
-0116     fluxes=sparse(numel(model.rxns),size(genesToModify,1));
-0117     for i=1:size(genesToModify,1)
-0118         tempModel=removeGenes(model,genesToModify(i,:),false,false,false);
-0119         sol=solveLP(tempModel);
-0120 
-0121         if sol.stat==1
-0122             fluxes(:,i)=sol.x;
-0123             grRatioMuts(i)=sol.f/growthWT;
-0124         end
-0125     end
-0126 end
-0127 
-0128 %Map back to the old model
-0129 [~, I]=ismember(model.rxns,originalModel.rxns);
-0130 temp=fluxes;
-0131 fluxes=sparse(numel(originalModel.rxns),size(temp,2));
-0132 fluxes(I,:)=temp;
-0133 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/analysis/getMinimalMedium.html b/doc/analysis/getMinimalMedium.html deleted file mode 100644 index 90d0bbc2..00000000 --- a/doc/analysis/getMinimalMedium.html +++ /dev/null @@ -1,275 +0,0 @@ - - - - Description of getMinimalMedium - - - - - - - - - -
Home > analysis > getMinimalMedium.m
- - - -

getMinimalMedium -

- -

PURPOSE ^

-
getMinimalMedium Find the minimal set of uptake reactions for growth.
- -

SYNOPSIS ^

-
function [medium, mediumIdx] = getMinimalMedium(model, varargin)
- -

DESCRIPTION ^

-
 getMinimalMedium  Find the minimal set of uptake reactions for growth.
-
- Uses MILP to find the smallest set of exchange reactions that can carry
- uptake flux (lb < 0) while still allowing the model to achieve a target
- growth rate. Unlike iterative heuristics, the MILP formulation finds the
- globally minimal medium.
-
- Parameters
- ----------
- model : struct
-     RAVEN model structure.
-
- Name-Value Arguments
- --------------------
- minGrowth : double
-     minimum objective value (growth rate) the medium must support.
-     Defaults to 10 % of the unconstrained optimum computed automatically.
-     Set explicitly to avoid an extra FBA call (e.g. 'minGrowth', 0.1).
- verbose : logical
-     print the resulting minimal medium (default true).
-
- Returns
- -------
- medium : cell
-     cell array of reaction IDs that form the minimal medium. Empty if
-     no feasible medium can support the requested growth.
- mediumIdx : double
-     column vector of reaction indices corresponding to medium.
-
- Examples
- --------
-     [med, idx] = getMinimalMedium(model)
-     [med, idx] = getMinimalMedium(model, 'minGrowth', 0.05)
-     [med, idx] = getMinimalMedium(model, 'verbose', false)
-
- Notes
- -----
- Only exchange reactions with lb < 0 are considered as candidate uptake
- reactions. Exchange reactions that are always secretion-only (lb >= 0) or
- that are completely blocked are not included.
-
- The MILP formulation:
-   minimise  sum(y_i)
-   s.t.      S v = 0
-             c' v >= minGrowth
-             v_i >= lb_i * y_i   for each candidate uptake reaction i
-             lb_j <= v_j <= ub_j for all reactions j
-             y_i in {0, 1}
-
- A y_i = 0 forces reaction i to carry non-negative flux (no net uptake);
- y_i = 1 allows it to carry flux as low as lb_i.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [medium, mediumIdx] = getMinimalMedium(model, varargin)
-0002 % getMinimalMedium  Find the minimal set of uptake reactions for growth.
-0003 %
-0004 % Uses MILP to find the smallest set of exchange reactions that can carry
-0005 % uptake flux (lb < 0) while still allowing the model to achieve a target
-0006 % growth rate. Unlike iterative heuristics, the MILP formulation finds the
-0007 % globally minimal medium.
-0008 %
-0009 % Parameters
-0010 % ----------
-0011 % model : struct
-0012 %     RAVEN model structure.
-0013 %
-0014 % Name-Value Arguments
-0015 % --------------------
-0016 % minGrowth : double
-0017 %     minimum objective value (growth rate) the medium must support.
-0018 %     Defaults to 10 % of the unconstrained optimum computed automatically.
-0019 %     Set explicitly to avoid an extra FBA call (e.g. 'minGrowth', 0.1).
-0020 % verbose : logical
-0021 %     print the resulting minimal medium (default true).
-0022 %
-0023 % Returns
-0024 % -------
-0025 % medium : cell
-0026 %     cell array of reaction IDs that form the minimal medium. Empty if
-0027 %     no feasible medium can support the requested growth.
-0028 % mediumIdx : double
-0029 %     column vector of reaction indices corresponding to medium.
-0030 %
-0031 % Examples
-0032 % --------
-0033 %     [med, idx] = getMinimalMedium(model)
-0034 %     [med, idx] = getMinimalMedium(model, 'minGrowth', 0.05)
-0035 %     [med, idx] = getMinimalMedium(model, 'verbose', false)
-0036 %
-0037 % Notes
-0038 % -----
-0039 % Only exchange reactions with lb < 0 are considered as candidate uptake
-0040 % reactions. Exchange reactions that are always secretion-only (lb >= 0) or
-0041 % that are completely blocked are not included.
-0042 %
-0043 % The MILP formulation:
-0044 %   minimise  sum(y_i)
-0045 %   s.t.      S v = 0
-0046 %             c' v >= minGrowth
-0047 %             v_i >= lb_i * y_i   for each candidate uptake reaction i
-0048 %             lb_j <= v_j <= ub_j for all reactions j
-0049 %             y_i in {0, 1}
-0050 %
-0051 % A y_i = 0 forces reaction i to carry non-negative flux (no net uptake);
-0052 % y_i = 1 allows it to carry flux as low as lb_i.
-0053 
-0054 p = parseRAVENargs(varargin, {'minGrowth',[]; 'verbose',true});
-0055 minGrowth = p.minGrowth;
-0056 verbose   = p.verbose;
-0057 
-0058 medium    = {};
-0059 mediumIdx = zeros(0,1);
-0060 
-0061 % ---- Resolve minGrowth ----
-0062 if isempty(minGrowth)
-0063     sol0 = solveLP(model);
-0064     if sol0.stat ~= 1
-0065         error('RAVEN:infeasible', ...
-0066             'getMinimalMedium: model is infeasible under current bounds.');
-0067     end
-0068     minGrowth = 0.1 * sol0.f;
-0069 end
-0070 
-0071 if minGrowth <= 0
-0072     warning('getMinimalMedium:zeroGrowth', ...
-0073         'minGrowth <= 0; the MILP will accept a zero-growth solution.');
-0074 end
-0075 
-0076 % ---- Identify candidate uptake reactions ----
-0077 [~, exchIdx] = getExchangeRxns(model);
-0078 % Only consider exchange reactions with lb < 0 (able to take up)
-0079 lb_cap = max(model.lb(exchIdx), -1000);  % cap at -1000 for numerical safety
-0080 isUptake = lb_cap < 0;
-0081 uptakeIdx = exchIdx(isUptake);           % indices into model.rxns
-0082 uptakeLb  = lb_cap(isUptake);           % effective lower bounds (negative)
-0083 n_uptake  = numel(uptakeIdx);
-0084 
-0085 if n_uptake == 0
-0086     if verbose
-0087         fprintf('getMinimalMedium: no exchange reactions with lb < 0 found.\n');
-0088     end
-0089     return
-0090 end
-0091 
-0092 % ---- Build MILP ----
-0093 n_mets = numel(model.mets);
-0094 n_rxns = numel(model.rxns);
-0095 n_vars = n_rxns + n_uptake;   % [flux vars | binary uptake indicators]
-0096 
-0097 % Objective: minimise sum(y)
-0098 obj = [zeros(n_rxns,1); ones(n_uptake,1)];
-0099 
-0100 % 1. Steady-state: S*v = 0   (n_mets rows)
-0101 A_ss = [model.S, sparse(n_mets, n_uptake)];
-0102 b_ss = zeros(n_mets, 1);
-0103 
-0104 % 2. Growth lower bound: c'*v >= minGrowth   (1 row)
-0105 A_grw = [model.c', sparse(1, n_uptake)];
-0106 b_grw = minGrowth;
-0107 
-0108 % 3. Uptake coupling: v_i - lb_i*y_k >= 0   (n_uptake rows)
-0109 %    coefficient of v_i (in flux block) = 1
-0110 %    coefficient of y_k (in binary block, k-th uptake var) = -lb_i (> 0)
-0111 rows  = (1:n_uptake)';
-0112 colsV = uptakeIdx;
-0113 colsY = n_rxns + (1:n_uptake)';
-0114 A_upt = sparse([rows; rows], [colsV; colsY], [ones(n_uptake,1); -uptakeLb], n_uptake, n_vars);
-0115 b_upt = zeros(n_uptake, 1);
-0116 
-0117 % Assemble — prob.a is the "original" A (without solver slack columns) and
-0118 % is used by optimizeProb to know how many variables to return in res.full.
-0119 prob.A       = [A_ss; A_grw; A_upt];
-0120 prob.a       = prob.A;
-0121 prob.b       = [b_ss; b_grw; b_upt];
-0122 prob.csense  = [repmat('E',1,n_mets), 'G', repmat('G',1,n_uptake)];
-0123 prob.c       = obj;
-0124 prob.osense  = 1;   % minimise
-0125 lb_forProb              = model.lb;
-0126 lb_forProb(uptakeIdx)   = uptakeLb;   % cap at -1000 for numerical safety
-0127 prob.lb      = [lb_forProb; zeros(n_uptake,1)];
-0128 prob.ub      = [model.ub; ones(n_uptake,1)];
-0129 prob.vartype = [repmat('C', 1, n_rxns), repmat('I', 1, n_uptake)];
-0130 
-0131 % ---- Solve ----
-0132 sol = optimizeProb(prob, [], false);
-0133 isFeasible = checkSolution(sol);
-0134 
-0135 if ~isFeasible
-0136     if verbose
-0137         fprintf('getMinimalMedium: MILP returned no feasible solution.\n');
-0138         fprintf('  The model may not be able to grow at minGrowth=%.4g with any medium.\n', minGrowth);
-0139     end
-0140     return
-0141 end
-0142 
-0143 % ---- Extract result ----
-0144 y_sol     = sol.full(n_rxns + 1 : end);
-0145 inMedium  = y_sol > 0.5;
-0146 mediumIdx = uptakeIdx(inMedium);
-0147 medium    = model.rxns(mediumIdx);
-0148 
-0149 if verbose
-0150     printMedium_(model, medium, mediumIdx, minGrowth, n_uptake);
-0151 end
-0152 end
-0153 
-0154 %--------------------------------------------------------------------------
-0155 function printMedium_(model, medium, mediumIdx, minGrowth, nCandidates)
-0156     W = 62;
-0157     fprintf('\n%s\n', repmat('=', 1, W));
-0158     fprintf('  Minimal medium  (%d of %d candidate uptake reactions)\n', ...
-0159         numel(medium), nCandidates);
-0160     fprintf('  Target growth: %.4g\n', minGrowth);
-0161     fprintf('%s\n', repmat('-', 1, W));
-0162     if isempty(medium)
-0163         fprintf('  (none — model can grow without any uptake)\n');
-0164     else
-0165         for i = 1:numel(medium)
-0166             ri  = mediumIdx(i);
-0167             nm  = '';
-0168             if isfield(model,'rxnNames') && ri <= numel(model.rxnNames)
-0169                 nm = char(model.rxnNames{ri});
-0170             end
-0171             if numel(nm) > 35, nm = [nm(1:32) '...']; end
-0172             fprintf('  %-20s  %s\n', medium{i}, nm);
-0173         end
-0174     end
-0175     fprintf('%s\n\n', repmat('=', 1, W));
-0176 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/analysis/haveFlux.html b/doc/analysis/haveFlux.html deleted file mode 100644 index 9968ae6f..00000000 --- a/doc/analysis/haveFlux.html +++ /dev/null @@ -1,187 +0,0 @@ - - - - Description of haveFlux - - - - - - - - - -
Home > analysis > haveFlux.m
- - - -

haveFlux -

- -

PURPOSE ^

-
haveFlux Check which reactions can carry a flux.
- -

SYNOPSIS ^

-
function I=haveFlux(model,varargin)
- -

DESCRIPTION ^

-
 haveFlux  Check which reactions can carry a flux.
-
- Checks which reactions can carry a (positive or negative) flux. Is used as
- a faster version of getAllowedBounds if it is only interesting whether the
- reactions can carry a flux or not.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- cutOff : double
-     the flux value that a reaction has to carry to be identified as
-     positive (default 10^-6).
- rxns : cell or logical or double
-     either a cell array of IDs, a logical vector with the same number of
-     elements as metabolites in the model, or a vector of indexes (default
-     model.rxns).
- seed : double
-     seed for the random number generator used to shuffle the order in
-     which reactions are tested, so that order is reproducible across
-     runs (default: current rng state is left untouched).
-
- Returns
- -------
- I : logical
-     logical array with true if the corresponding reaction can carry a
-     flux.
-
- Examples
- --------
-     I = haveFlux(model, cutOff, rxns);
-
- Notes
- -----
- If a model has +/- Inf bounds then those are replaced with an arbitary
- large value of +/- 10000 prior to solving.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function I=haveFlux(model,varargin)
-0002 % haveFlux  Check which reactions can carry a flux.
-0003 %
-0004 % Checks which reactions can carry a (positive or negative) flux. Is used as
-0005 % a faster version of getAllowedBounds if it is only interesting whether the
-0006 % reactions can carry a flux or not.
-0007 %
-0008 % Parameters
-0009 % ----------
-0010 % model : struct
-0011 %     a model structure.
-0012 %
-0013 % Name-Value Arguments
-0014 % --------------------
-0015 % cutOff : double
-0016 %     the flux value that a reaction has to carry to be identified as
-0017 %     positive (default 10^-6).
-0018 % rxns : cell or logical or double
-0019 %     either a cell array of IDs, a logical vector with the same number of
-0020 %     elements as metabolites in the model, or a vector of indexes (default
-0021 %     model.rxns).
-0022 % seed : double
-0023 %     seed for the random number generator used to shuffle the order in
-0024 %     which reactions are tested, so that order is reproducible across
-0025 %     runs (default: current rng state is left untouched).
-0026 %
-0027 % Returns
-0028 % -------
-0029 % I : logical
-0030 %     logical array with true if the corresponding reaction can carry a
-0031 %     flux.
-0032 %
-0033 % Examples
-0034 % --------
-0035 %     I = haveFlux(model, cutOff, rxns);
-0036 %
-0037 % Notes
-0038 % -----
-0039 % If a model has +/- Inf bounds then those are replaced with an arbitary
-0040 % large value of +/- 10000 prior to solving.
-0041 
-0042 p=parseRAVENargs(varargin, {'cutOff',10^-6; 'rxns',[]; 'seed',[]});
-0043 if ~isempty(p.seed)
-0044     rng(p.seed);
-0045 end
-0046 cutOff=p.cutOff;
-0047 rxns=p.rxns;
-0048 if isempty(cutOff)
-0049     cutOff=10^-6;
-0050 end
-0051 if isempty(rxns)
-0052     rxns=model.rxns;
-0053 elseif ~islogical(rxns) && ~isnumeric(rxns)
-0054     rxns=convertCharArray(rxns);
-0055 end
-0056 
-0057 %This is since we're maximizing for the sum of fluxes, which isn't possible
-0058 %when there are infinite bounds
-0059 model.lb(model.lb==-inf)=-10000;
-0060 model.ub(model.ub==inf)=10000;
-0061 
-0062 %Get the reaction IDs. A bit of an awkward way, but fine.
-0063 indexes=getIndexes(model,rxns,'rxns');
-0064 rxns=model.rxns(indexes);
-0065 
-0066 %First remove all dead-end reactions
-0067 smallModel=simplifyModel(model,false,false,true,true);
-0068 
-0069 %Get the indexes of the reactions in the connected model
-0070 indexes=getIndexes(smallModel,intersect(smallModel.rxns,rxns),'rxns');
-0071 J=false(numel(indexes),1);
-0072 mixIndexes=indexes(randperm(numel(indexes)));
-0073 
-0074 %Maximize for all fluxes first to get fewer rxns to test
-0075 smallModel.c=ones(numel(smallModel.c),1);
-0076 sol=solveLP(smallModel);
-0077 if ~isempty(sol.x)
-0078     J(abs(sol.x(mixIndexes))>cutOff)=true;
-0079 end
-0080 
-0081 %Loop through and maximize then minimize each rxn if it does not already
-0082 %have a flux
-0083 Z=zeros(numel(smallModel.c),1);
-0084 hsSolOut=[];
-0085 for i=[1 -1]
-0086     for j=1:numel(J)
-0087         if J(j)==false
-0088             %Only minimize for reversible reactions
-0089             if i==1 || smallModel.rev(mixIndexes(j))~=0
-0090                 smallModel.c=Z;
-0091                 smallModel.c(mixIndexes(j))=i;
-0092                 [sol, hsSolOut]=solveLP(smallModel,0,[],hsSolOut);
-0093                 if any(sol.x)
-0094                     J(abs(sol.x(mixIndexes))>cutOff)=true;
-0095                 end
-0096             end
-0097         end
-0098     end
-0099 end
-0100 
-0101 I=ismember(rxns,smallModel.rxns(mixIndexes(J)));
-0102 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/analysis/index.html b/doc/analysis/index.html deleted file mode 100644 index a5ae7e63..00000000 --- a/doc/analysis/index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - Index for Directory analysis - - - - - - - - - - -
< Master indexIndex for analysis >
- -

Index for analysis

- -

Matlab files in this directory:

- -
 FSEOFFSEOF Flux Scanning based on Enforced Objective Flux.
 analyzeSamplinganalyzeSampling Compare flux change significance with expression change.
 compareFluxescompareFluxes Find the most significant flux changes between two conditions.
 findGeneDeletionsfindGeneDeletions Delete genes and track the resulting fluxes.
 getAllSubGraphsgetAllSubGraphs Get all metabolic subgraphs in a model.
 getAllowedBoundsgetAllowedBounds Return the minimal and maximal fluxes through reactions.
 getEssentialRxnsgetEssentialRxns Calculate the essential reactions for a solvable model.
 getFluxZgetFluxZ Calculate Z scores between two sets of random flux distributions.
 getMinimalMediumgetMinimalMedium Find the minimal set of uptake reactions for growth.
 haveFluxhaveFlux Check which reactions can carry a flux.
 randomSamplingrandomSampling Sample the flux solution space (entry point for all samplers).
 reporterMetabolitesreporterMetabolites Identify metabolites around which transcriptional changes occur.
 runDynamicFBArunDynamicFBA Perform dynamic FBA using the static optimization approach.
 runPhenotypePhasePlanerunPhenotypePhasePlane Run phenotype phase plane analysis and plot the results.
 runProductionEnveloperunProductionEnvelope Calculate the byproduct secretion envelope.
 runRobustnessAnalysisrunRobustnessAnalysis Perform robustness analysis for a reaction and objective.
 runSimpleOptKnockrunSimpleOptKnock Simple OptKnock for growth-coupled production.
 sampleACHRsampleACHR Artificially Centered Hit-and-Run flux sampling.
 sampleCHRRsampleCHRR Coordinate Hit-and-Run with Rounding flux sampling.
 sampleChebyshevCentersampleChebyshevCenter Centre of the largest ball inscribed in a polytope.
 sampleMaxVolEllipsesampleMaxVolEllipse Maximum-volume inscribed ellipsoid of a polytope.
 sampleWarmupPointssampleWarmupPoints FVA warmup vertices for ACHR sampling.
 traceFluxPathtraceFluxPath Find the highest-flux-fraction path between two reactions.
 walkFluxeswalkFluxes Interactively navigate a flux distribution reaction by reaction.
- - - - -
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/analysis/randomSampling.html b/doc/analysis/randomSampling.html deleted file mode 100644 index 037b52c1..00000000 --- a/doc/analysis/randomSampling.html +++ /dev/null @@ -1,430 +0,0 @@ - - - - Description of randomSampling - - - - - - - - - -
Home > analysis > randomSampling.m
- - - -

randomSampling -

- -

PURPOSE ^

-
randomSampling Sample the flux solution space (entry point for all samplers).
- -

SYNOPSIS ^

-
function [solutions, goodRxns, info]=randomSampling(model,varargin)
- -

DESCRIPTION ^

-
 randomSampling  Sample the flux solution space (entry point for all samplers).
-
- Dispatches to one of three sampling methods via the 'method' argument:
-   'achr' (default)  Artificially Centered Hit-and-Run; near-uniform MCMC
-                     sampling of the polytope interior (see sampleACHR).
-   'chrr'            Coordinate Hit-and-Run with Rounding; near-uniform MCMC
-                     with maximum-volume-ellipsoid rounding, for better mixing
-                     on thin/ill-conditioned polytopes such as enzyme-
-                     constrained models (see sampleCHRR).
-   'randomObjective' the random-objective vertex method of Bordel et al. (2010)
-                     PLoS Comput Biol (doi:10.1371/journal.pcbi.1000859): each
-                     sample maximises a small random objective, returning a
-                     polytope vertex.
-
- The 'achr'/'chrr' methods draw the (near-)uniform interior distribution; the
- 'randomObjective' method draws diverse vertices. Arguments below marked
- [randomObjective] apply only to that method; 'thinning'/'nBurnin' apply only
- to the MCMC methods.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- nSamples : double
-     the number of solutions to return (default 1000).
- method : char
-     sampling method: 'achr' (default), 'chrr', or 'randomObjective'.
- thinning : double
-     MCMC steps between recorded samples for 'achr'/'chrr' (default 100).
- nBurnin : double
-     burn-in steps discarded before the first sample for 'chrr' (default 1000).
- replaceBoundsWithInf : logical
-     [randomObjective]
-     replace the largest upper bounds with Inf and the smallest lower
-     bounds with -Inf. This is needed to get solutions without loops if
-     your model has, for example, 1000/-1000 as arbitrarily large bounds.
-     If your model only has "biologically relevant" bounds, then set this
-     to false (default true).
- supressErrors : logical
-     the program will halt if it has problems finding non-zero solutions
-     which are not involved in loops. This could be because the constraints
-     on the model are too relaxed (such as unlimited glucose uptake) or too
-     strict (such as too many and too narrow constraints) (default false).
- runParallel : logical
-     speed up calculations by parallel processing. Requires the MATLAB
-     Parallel Computing Toolbox. If this is not installed, the calculations
-     will not be parallelized, regardless of what is indicated as
-     runParallel (default true).
- goodRxns : double
-     vector of indexes of those reactions that are not involved in loops
-     and can be used as random objective functions, as generated by a
-     previous run of randomSampling on the same model (default empty).
- minFlux : logical
-     determines if a second optimization should be performed for each
-     random sample, to minimize the number of fluxes and thereby prevent
-     loops. Typically, loops are averaged out when a large number of
-     samples are taken, but this is not always the case (default false).
- nObjectives : double
-     number of reactions included in the random objective function per
-     sample iteration (default 2).
- seed : double
-     random seed for reproducibility. If empty, the current MATLAB random
-     state is used (default empty).
-
- Returns
- -------
- solutions : double
-     matrix with the solutions.
- goodRxns : double
-     [randomObjective] vector of indexes of those reactions that are not
-     involved in loops or always carry zero flux and can be used as random
-     objective functions. Empty ([]) for the 'achr' and 'chrr' methods.
- info : struct
-     [chrr] the diagnostics from sampleCHRR, including mveConverged (whether
-     the maximum-volume ellipsoid rounding reached tolerance). Empty ([])
-     for the 'achr' and 'randomObjective' methods.
-
- Notes
- -----
- The solutions are generated by maximizing (with random weights) for a
- random set of nObjectives reactions (default 2). For reversible reactions
- it randomly chooses between maximizing and minimizing.
-
- If the model is a GECKO v3+ ecModel, then usage_prot reactions are not
- selected for sampling, instead focusing on sampling from the metabolic
- aspects that form the solution space.
-
- Examples
- --------
-     % Near-uniform interior sampling (default, ACHR):
-     solutions = randomSampling(model, 1000);
-     % Coordinate hit-and-run with rounding:
-     solutions = randomSampling(model, 1000, 'method', 'chrr', 'seed', 1);
-     % Historical random-objective vertex method:
-     solutions = randomSampling(model, 1000, 'method', 'randomObjective');
-
- See also
- --------
- sampleACHR, sampleCHRR, sampleMaxVolEllipse
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • getAllowedBounds getAllowedBounds Return the minimal and maximal fluxes through reactions.
  • sampleACHR sampleACHR Artificially Centered Hit-and-Run flux sampling.
  • sampleCHRR sampleCHRR Coordinate Hit-and-Run with Rounding flux sampling.
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function [solutions, goodRxns, info]=randomSampling(model,varargin)
-0002 % randomSampling  Sample the flux solution space (entry point for all samplers).
-0003 %
-0004 % Dispatches to one of three sampling methods via the 'method' argument:
-0005 %   'achr' (default)  Artificially Centered Hit-and-Run; near-uniform MCMC
-0006 %                     sampling of the polytope interior (see sampleACHR).
-0007 %   'chrr'            Coordinate Hit-and-Run with Rounding; near-uniform MCMC
-0008 %                     with maximum-volume-ellipsoid rounding, for better mixing
-0009 %                     on thin/ill-conditioned polytopes such as enzyme-
-0010 %                     constrained models (see sampleCHRR).
-0011 %   'randomObjective' the random-objective vertex method of Bordel et al. (2010)
-0012 %                     PLoS Comput Biol (doi:10.1371/journal.pcbi.1000859): each
-0013 %                     sample maximises a small random objective, returning a
-0014 %                     polytope vertex.
-0015 %
-0016 % The 'achr'/'chrr' methods draw the (near-)uniform interior distribution; the
-0017 % 'randomObjective' method draws diverse vertices. Arguments below marked
-0018 % [randomObjective] apply only to that method; 'thinning'/'nBurnin' apply only
-0019 % to the MCMC methods.
-0020 %
-0021 % Parameters
-0022 % ----------
-0023 % model : struct
-0024 %     a model structure.
-0025 %
-0026 % Name-Value Arguments
-0027 % --------------------
-0028 % nSamples : double
-0029 %     the number of solutions to return (default 1000).
-0030 % method : char
-0031 %     sampling method: 'achr' (default), 'chrr', or 'randomObjective'.
-0032 % thinning : double
-0033 %     MCMC steps between recorded samples for 'achr'/'chrr' (default 100).
-0034 % nBurnin : double
-0035 %     burn-in steps discarded before the first sample for 'chrr' (default 1000).
-0036 % replaceBoundsWithInf : logical
-0037 %     [randomObjective]
-0038 %     replace the largest upper bounds with Inf and the smallest lower
-0039 %     bounds with -Inf. This is needed to get solutions without loops if
-0040 %     your model has, for example, 1000/-1000 as arbitrarily large bounds.
-0041 %     If your model only has "biologically relevant" bounds, then set this
-0042 %     to false (default true).
-0043 % supressErrors : logical
-0044 %     the program will halt if it has problems finding non-zero solutions
-0045 %     which are not involved in loops. This could be because the constraints
-0046 %     on the model are too relaxed (such as unlimited glucose uptake) or too
-0047 %     strict (such as too many and too narrow constraints) (default false).
-0048 % runParallel : logical
-0049 %     speed up calculations by parallel processing. Requires the MATLAB
-0050 %     Parallel Computing Toolbox. If this is not installed, the calculations
-0051 %     will not be parallelized, regardless of what is indicated as
-0052 %     runParallel (default true).
-0053 % goodRxns : double
-0054 %     vector of indexes of those reactions that are not involved in loops
-0055 %     and can be used as random objective functions, as generated by a
-0056 %     previous run of randomSampling on the same model (default empty).
-0057 % minFlux : logical
-0058 %     determines if a second optimization should be performed for each
-0059 %     random sample, to minimize the number of fluxes and thereby prevent
-0060 %     loops. Typically, loops are averaged out when a large number of
-0061 %     samples are taken, but this is not always the case (default false).
-0062 % nObjectives : double
-0063 %     number of reactions included in the random objective function per
-0064 %     sample iteration (default 2).
-0065 % seed : double
-0066 %     random seed for reproducibility. If empty, the current MATLAB random
-0067 %     state is used (default empty).
-0068 %
-0069 % Returns
-0070 % -------
-0071 % solutions : double
-0072 %     matrix with the solutions.
-0073 % goodRxns : double
-0074 %     [randomObjective] vector of indexes of those reactions that are not
-0075 %     involved in loops or always carry zero flux and can be used as random
-0076 %     objective functions. Empty ([]) for the 'achr' and 'chrr' methods.
-0077 % info : struct
-0078 %     [chrr] the diagnostics from sampleCHRR, including mveConverged (whether
-0079 %     the maximum-volume ellipsoid rounding reached tolerance). Empty ([])
-0080 %     for the 'achr' and 'randomObjective' methods.
-0081 %
-0082 % Notes
-0083 % -----
-0084 % The solutions are generated by maximizing (with random weights) for a
-0085 % random set of nObjectives reactions (default 2). For reversible reactions
-0086 % it randomly chooses between maximizing and minimizing.
-0087 %
-0088 % If the model is a GECKO v3+ ecModel, then usage_prot reactions are not
-0089 % selected for sampling, instead focusing on sampling from the metabolic
-0090 % aspects that form the solution space.
-0091 %
-0092 % Examples
-0093 % --------
-0094 %     % Near-uniform interior sampling (default, ACHR):
-0095 %     solutions = randomSampling(model, 1000);
-0096 %     % Coordinate hit-and-run with rounding:
-0097 %     solutions = randomSampling(model, 1000, 'method', 'chrr', 'seed', 1);
-0098 %     % Historical random-objective vertex method:
-0099 %     solutions = randomSampling(model, 1000, 'method', 'randomObjective');
-0100 %
-0101 % See also
-0102 % --------
-0103 % sampleACHR, sampleCHRR, sampleMaxVolEllipse
-0104 
-0105 p=parseRAVENargs(varargin, {'nSamples',[]; 'replaceBoundsWithInf',[]; 'supressErrors',[]; 'runParallel',[]; 'goodRxns',[]; 'minFlux',[]; 'nObjectives',[]; 'seed',[]; 'method',[]; 'thinning',[]; 'nBurnin',[]});
-0106 nSamples=p.nSamples;
-0107 replaceBoundsWithInf=p.replaceBoundsWithInf;
-0108 supressErrors=p.supressErrors;
-0109 runParallel=p.runParallel;
-0110 goodRxns=p.goodRxns;
-0111 minFlux=p.minFlux;
-0112 nObjectives=p.nObjectives;
-0113 seed=p.seed;
-0114 method=p.method;
-0115 thinning=p.thinning;
-0116 nBurnin=p.nBurnin;
-0117 info=[]; %only populated by the 'chrr' method
-0118 if isempty(nSamples)
-0119     nSamples=1000;
-0120 end
-0121 if isempty(method)
-0122     method='achr';
-0123 end
-0124 
-0125 % ---- Dispatch to the requested sampler ----
-0126 % 'achr'/'chrr' are near-uniform MCMC samplers of the polytope interior,
-0127 % implemented in sampleACHR / sampleCHRR. 'randomObjective' is the
-0128 % random-objective vertex method (Bordel 2010), implemented below; goodRxns is
-0129 % meaningful only for that method (empty otherwise).
-0130 switch lower(method)
-0131     case 'achr'
-0132         solutions = sampleACHR(model, nSamples, thinning, [], seed);
-0133         goodRxns = [];
-0134         return;
-0135     case 'chrr'
-0136         [solutions, info] = sampleCHRR(model, nSamples, thinning, nBurnin, seed);
-0137         goodRxns = [];
-0138         return;
-0139     case {'randomobjective', 'objective'}
-0140         % fall through to the random-objective implementation below
-0141     otherwise
-0142         error('RAVEN:badInput', ...
-0143             ['randomSampling: unknown method ''%s''; expected ''achr'', ' ...
-0144              '''chrr'', or ''randomObjective''.'], method);
-0145 end
-0146 
-0147 if isempty(replaceBoundsWithInf)
-0148     replaceBoundsWithInf=true;
-0149 end
-0150 if isempty(supressErrors)
-0151     supressErrors=false;
-0152 end
-0153 if isempty(runParallel)
-0154     runParallel=true;
-0155 end
-0156 if isempty(minFlux)
-0157     minFlux=false;
-0158 end
-0159 if isempty(nObjectives)
-0160     nObjectives=2;
-0161 end
-0162 
-0163 nW = parallelWorkersRAVEN(runParallel);
-0164 
-0165 nObjRxns=nObjectives;
-0166 
-0167 %Simplify the model to speed stuff up a little. Keep original mapping
-0168 originalRxns=model.rxns;
-0169 model=simplifyModel(model,false,false,true,true);
-0170 [~, simpToOrig] = ismember(model.rxns, originalRxns);
-0171 
-0172 %goodRxns is part of the public API and stored as original-model indices.
-0173 %Convert to simplified-model indices for use in the detection and sampling
-0174 %loops below, dropping any reactions that were removed by simplification.
-0175 if ~isempty(goodRxns)
-0176     [tf, simpIdx] = ismember(originalRxns(goodRxns), model.rxns);
-0177     goodRxns = double(simpIdx(tf));
-0178 end
-0179 
-0180 %Check that the model is feasible given the constraints
-0181 [sol,~]=solveLP(model);
-0182 if isempty(sol.x)
-0183     EM='The model has no feasible solution, likely due to incompatible constraints';
-0184     error('RAVEN:badInput', '%s', EM);
-0185 elseif sol.f==0
-0186     warning('The model objective function cannot reach a non-zero value. This might be intended, so randomSampling will continue, but this could indicate problems with your model')
-0187 end
-0188 
-0189 nRxns = numel(model.rxns);
-0190 %Reactions which can be involved in loops should not be optimized for.
-0191 %Check which reactions reach an arbitrary high upper bound. This is done
-0192 %on the original bounded model so that loop detection is meaningful
-0193 %(Inf-bounded reactions would always reach the threshold).
-0194 if isempty(goodRxns)
-0195     [minF, maxF] = getAllowedBounds(model, 'runParallel', runParallel);
-0196     goodRxns = true(nRxns, 1);
-0197     % Reactions that reach the model's own highest bound are involved in
-0198     % loops. The threshold has to be derived from the model, as the bound
-0199     % replacement below does: hardcoding ±1000 matches nothing on a model
-0200     % whose bounds are on another scale (ecModels, 100-scale bounds), so no
-0201     % reaction would be excluded and loops would be sampled while the
-0202     % function reports success.
-0203     loopUb = max(model.ub);
-0204     loopLb = min(model.lb);
-0205     goodRxns(maxF == Inf | maxF > loopUb*0.999) = false;
-0206     if loopLb < 0
-0207         goodRxns(minF == -Inf | minF < loopLb*0.999) = false;
-0208     end
-0209     % Reactions that cannot carry any non-zero flux
-0210     goodRxns(~(maxF > 0 | minF < 0)) = false;
-0211     % In ecModels do not sample from usage_prot reactions
-0212     if isfield(model,'ec')
-0213         goodRxns(startsWith(model.rxns,'usage_prot_')) = false;
-0214     end
-0215     goodRxns = find(goodRxns);
-0216 end
-0217 
-0218 %Change the bounds to +/- Inf to prevent loops in the sampled solutions.
-0219 %Applied after goodRxns detection so that loop-detection uses original bounds.
-0220 if replaceBoundsWithInf==true
-0221     model.ub(model.ub==max(model.ub))=Inf;
-0222     if min(model.lb)<0 % Only negative lower bounds should be set to -Inf
-0223         model.lb(model.lb==min(model.lb))=-Inf;
-0224     end
-0225 end
-0226 
-0227 %Seed the RNG here so that only the sampling loop is affected, not the
-0228 %model simplification or goodRxns detection above.
-0229 if ~isempty(seed)
-0230     rng(seed);
-0231 end
-0232 
-0233 %Reserve space for a solution matrix
-0234 sols = zeros(numel(model.rxns),nSamples);
-0235 sols = num2cell(sols,1);
-0236 
-0237 %Main loop
-0238 if nSamples > 0
-0239 
-0240     PB = progressReport(nSamples,'Performing random sampling');
-0241     parfor (i = 1:nSamples, nW)
-0242         badSolutions = 1;
-0243         tmpModel = model;
-0244         while lt(0,badSolutions)
-0245             rxns = goodRxns(randperm(numel(goodRxns), nObjRxns));
-0246             tmpModel.c = zeros(numel(tmpModel.rxns),1);
-0247 
-0248             multipliers = 2*randi(2,nObjRxns,1)-3;  % random draws from {-1, 1}
-0249             multipliers(tmpModel.ub(rxns)<0) = 1;
-0250 
-0251             tmpModel.c(rxns) = rand(nObjRxns,1).*multipliers;
-0252 
-0253             if true(minFlux)
-0254                 sol=solveLP(tmpModel,1);
-0255             else
-0256                 sol=solveLP(tmpModel,0);
-0257             end
-0258             if any(sol.x) && abs(sol.f)>10^-8
-0259                 sols{i} = sol.x;
-0260                 badSolutions = 0;
-0261             else
-0262                 badSolutions = badSolutions+1;
-0263                 %If it only finds bad solutions then throw an error.
-0264                 if badSolutions == 100 && supressErrors==false
-0265                     error('The program is having problems finding non-zero solutions (ignoring reactions that might be involved in loops). Review the constraints on your model. Set supressErrors to true to ignore this error');
-0266                 end
-0267             end
-0268         end
-0269         count(PB);
-0270     end
-0271 
-0272     %Map to original model
-0273     sols = cell2mat(sols);
-0274     solutions=zeros(numel(originalRxns),nSamples);
-0275     solutions(simpToOrig,:)=sols;
-0276 else
-0277     solutions=[];
-0278 end
-0279 %Return goodRxns as original-model indices so callers can pass them back
-0280 %safely across separate calls (which each re-simplify the model internally).
-0281 goodRxns = double(simpToOrig(goodRxns));
-0282 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/analysis/reporterMetabolites.html b/doc/analysis/reporterMetabolites.html deleted file mode 100644 index 232585cc..00000000 --- a/doc/analysis/reporterMetabolites.html +++ /dev/null @@ -1,311 +0,0 @@ - - - - Description of reporterMetabolites - - - - - - - - - -
Home > analysis > reporterMetabolites.m
- - - -

reporterMetabolites -

- -

PURPOSE ^

-
reporterMetabolites Identify metabolites around which transcriptional changes occur.
- -

SYNOPSIS ^

-
function repMets=reporterMetabolites(model,genes,genePValues,varargin)
- -

DESCRIPTION ^

-
 reporterMetabolites  Identify metabolites around which transcriptional changes occur.
-
- The Reporter Metabolites algorithm for identifying metabolites around
- which transcriptional changes occur.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- genes : cell
-     a cell array of gene names (should match with model.genes).
- genePValues : double
-     P-values for differential expression of the genes.
-
- Name-Value Arguments
- --------------------
- printResults : logical
-     true if the top 20 Reporter Metabolites should be printed to the
-     screen (default false).
- outputFile : char
-     the results are printed to this file (default none).
- geneFoldChanges : double
-     log-fold changes for the genes. If supplied, then Reporter
-     Metabolites are calculated for only up/down-regulated genes in
-     addition to the full test (default none).
-
- Returns
- -------
- repMets : struct
-     an array of structures with the following fields:
-
-     - test : a string that describes the genes used to calculate the
-       Reporter Metabolites ("all", "only up", or "only down"). The two
-       latter structures are only calculated if geneFoldChanges are
-       supplied.
-     - mets : a cell array of metabolite IDs for the metabolites for which
-       a score could be calculated.
-     - metZScores : Z-scores for differential expression around each
-       metabolite in "mets".
-     - metPValues : P-values for differential expression around each
-       metabolite in "mets".
-     - metNGenes : number of neighbouring genes for each metabolite in
-       "mets".
-     - meanZ : average Z-scores for the genes around each metabolite in
-       "mets".
-     - stdZ : standard deviations of the Z-scores around each metabolite
-       in "mets".
-
- Examples
- --------
-     repMets = reporterMetabolites(model, genes, genePValues, ...
-         printResults, outputFile, geneFoldChanges);
-
- Notes
- -----
- For details about the algorithm, see Patil KR, Nielsen J, Uncovering
- transcriptional regulation of metabolism by using metabolic network
- topology. Proc. Natl Acad. Sci. USA 2005;102:2685-2689.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • reporterMetabolites reporterMetabolites Identify metabolites around which transcriptional changes occur.
-This function is called by: -
    -
  • reporterMetabolites reporterMetabolites Identify metabolites around which transcriptional changes occur.
- - - - -

SOURCE CODE ^

-
0001 function repMets=reporterMetabolites(model,genes,genePValues,varargin)
-0002 % reporterMetabolites  Identify metabolites around which transcriptional changes occur.
-0003 %
-0004 % The Reporter Metabolites algorithm for identifying metabolites around
-0005 % which transcriptional changes occur.
-0006 %
-0007 % Parameters
-0008 % ----------
-0009 % model : struct
-0010 %     a model structure.
-0011 % genes : cell
-0012 %     a cell array of gene names (should match with model.genes).
-0013 % genePValues : double
-0014 %     P-values for differential expression of the genes.
-0015 %
-0016 % Name-Value Arguments
-0017 % --------------------
-0018 % printResults : logical
-0019 %     true if the top 20 Reporter Metabolites should be printed to the
-0020 %     screen (default false).
-0021 % outputFile : char
-0022 %     the results are printed to this file (default none).
-0023 % geneFoldChanges : double
-0024 %     log-fold changes for the genes. If supplied, then Reporter
-0025 %     Metabolites are calculated for only up/down-regulated genes in
-0026 %     addition to the full test (default none).
-0027 %
-0028 % Returns
-0029 % -------
-0030 % repMets : struct
-0031 %     an array of structures with the following fields:
-0032 %
-0033 %     - test : a string that describes the genes used to calculate the
-0034 %       Reporter Metabolites ("all", "only up", or "only down"). The two
-0035 %       latter structures are only calculated if geneFoldChanges are
-0036 %       supplied.
-0037 %     - mets : a cell array of metabolite IDs for the metabolites for which
-0038 %       a score could be calculated.
-0039 %     - metZScores : Z-scores for differential expression around each
-0040 %       metabolite in "mets".
-0041 %     - metPValues : P-values for differential expression around each
-0042 %       metabolite in "mets".
-0043 %     - metNGenes : number of neighbouring genes for each metabolite in
-0044 %       "mets".
-0045 %     - meanZ : average Z-scores for the genes around each metabolite in
-0046 %       "mets".
-0047 %     - stdZ : standard deviations of the Z-scores around each metabolite
-0048 %       in "mets".
-0049 %
-0050 % Examples
-0051 % --------
-0052 %     repMets = reporterMetabolites(model, genes, genePValues, ...
-0053 %         printResults, outputFile, geneFoldChanges);
-0054 %
-0055 % Notes
-0056 % -----
-0057 % For details about the algorithm, see Patil KR, Nielsen J, Uncovering
-0058 % transcriptional regulation of metabolism by using metabolic network
-0059 % topology. Proc. Natl Acad. Sci. USA 2005;102:2685-2689.
-0060 
-0061 genes=convertCharArray(genes);
-0062 p=parseRAVENargs(varargin, {'printResults',false; 'outputFile',[]; 'geneFoldChanges',[]});
-0063 printResults=p.printResults;
-0064 outputFile=p.outputFile;
-0065 geneFoldChanges=p.geneFoldChanges;
-0066 
-0067 %Check some stuff
-0068 if numel(genes)~=numel(genePValues)
-0069     EM='The number of genes and the number of Z-scores must be the same';
-0070     error('RAVEN:badInput', '%s', EM);
-0071 end
-0072 if ~isfield(model,'rxnGeneMat')
-0073     EM='The model structure must have a rxnGeneMat field';
-0074     error('RAVEN:badInput', '%s', EM);
-0075 end
-0076 
-0077 %Remove the genes which are not in the model
-0078 genePValues(~ismember(genes,model.genes))=[];
-0079 if any(geneFoldChanges)
-0080     geneFoldChanges(~ismember(genes,model.genes))=[];
-0081 end
-0082 genes(~ismember(genes,model.genes))=[];
-0083 
-0084 %Remove the genes that has NA P-value
-0085 genes(isnan(genePValues))=[];
-0086 if any(geneFoldChanges)
-0087     geneFoldChanges(isnan(genePValues))=[];
-0088 end
-0089 genePValues(isnan(genePValues))=[];
-0090 
-0091 %Convert p-values to Z-scores. Uses base MATLAB erfcinv instead of the
-0092 %Statistics Toolbox norminv: -norminv(p) == sqrt(2)*erfcinv(2*p).
-0093 geneZScores=sqrt(2)*erfcinv(2*genePValues);
-0094 
-0095 %This is to prevent errors if the p-values are exactly 0 or 1
-0096 geneZScores(geneZScores==-inf)=-15;
-0097 geneZScores(geneZScores==inf)=15;
-0098 
-0099 %For each metabolite, calculate the aggregate Z-score and keep track of the
-0100 %number of neighbouring genes
-0101 metZScores=nan(numel(model.mets),1);
-0102 metNGenes=nan(numel(model.mets),1);
-0103 meanZ=nan(numel(model.mets),1);
-0104 stdZ=nan(numel(model.mets),1);
-0105 %model.genes -> genes(filtered list) index, computed once rather than
-0106 %re-scanning the whole "genes" list (via ismember) for every metabolite,
-0107 %which is quadratic in a genome-scale model's mets x genes.
-0108 [~,geneIdxInList]=ismember(model.genes,genes);
-0109 for i=1:numel(model.mets)
-0110     %Get the involved rxns
-0111     I=model.S(i,:);
-0112 
-0113     %Get the involved genes
-0114     [~, J]=find(model.rxnGeneMat(I~=0,:));
-0115 
-0116     %Find the genes in the gene list
-0117     K=unique(geneIdxInList(J));
-0118     K=K(K>0);
-0119     
-0120     %Calculate the aggregated Z-score for the metabolite
-0121     if any(K)
-0122         metZScores(i)=sum(geneZScores(K))/sqrt(numel(K));
-0123         meanZ(i)=mean(geneZScores(K));
-0124         stdZ(i)=std(geneZScores(K));
-0125         metNGenes(i)=numel(K);
-0126     end
-0127 end
-0128 
-0129 %Remove the metabolites which have no Z-scores
-0130 mets=model.mets(~isnan(metZScores));
-0131 metNames=model.metNames(~isnan(metZScores));
-0132 metZScores(isnan(metZScores))=[];
-0133 metNGenes(isnan(metNGenes))=[];
-0134 meanZ(isnan(meanZ))=[];
-0135 stdZ(isnan(stdZ))=[];
-0136 
-0137 %Correct for background using the closed-form moments of the sampling
-0138 %distribution. For n genes drawn with replacement from the scored-gene pool
-0139 %(mean mu, std sigma), the aggregate Z = sum(z)/sqrt(n) has:
-0140 %  E[Z] = sqrt(n)*mu  and  Std[Z] = sigma  (independent of n)
-0141 %so the corrected score is (metZ - sqrt(n)*mu) / sigma.
-0142 mu = mean(geneZScores);
-0143 sigma = std(geneZScores);
-0144 metZScores = (metZScores - sqrt(metNGenes) .* mu) ./ sigma;
-0145 
-0146 %Calculate the P-values (upper-tail of the standard normal). Uses the base
-0147 %MATLAB erfc so this does not depend on the Statistics Toolbox normcdf:
-0148 %1 - normcdf(z) == 0.5*erfc(z/sqrt(2)).
-0149 metPValues=0.5*erfc(metZScores/sqrt(2));
-0150 
-0151 %Sort the results
-0152 [metZScores, I]=sort(metZScores,'descend');
-0153 mets=mets(I);
-0154 metNames=metNames(I);
-0155 metPValues=metPValues(I);
-0156 metNGenes=metNGenes(I);
-0157 meanZ=meanZ(I);
-0158 stdZ=stdZ(I);
-0159 
-0160 %Prepare the output
-0161 repMets.mets=mets;
-0162 repMets.metNames=metNames;
-0163 repMets.metZScores=metZScores;
-0164 repMets.metPValues=metPValues;
-0165 repMets.metNGenes=metNGenes;
-0166 repMets.meanZ=meanZ;
-0167 repMets.stdZ=stdZ;
-0168 
-0169 %Call this function recursively if geneFoldChanges has been specified
-0170 repMets(1).test='all';
-0171 if any(geneFoldChanges)
-0172     repMets=[repMets;reporterMetabolites(model,genes(geneFoldChanges>=0),genePValues(geneFoldChanges>=0))];
-0173     repMets=[repMets;reporterMetabolites(model,genes(geneFoldChanges<0),genePValues(geneFoldChanges<0))];
-0174     repMets(2).test='only up';
-0175     repMets(3).test='only down';
-0176 end
-0177 
-0178 %This is for printing results to the screen. For printing a full list of
-0179 %all scores, specify a output file
-0180 %Escapes metabolite text before it is spliced into an fprintf template,
-0181 %since a literal "%" in e.g. a metabolite name would otherwise be misread
-0182 %as a format directive and swallow the remainder of the line.
-0183 esc=@(s) strrep(s,'%','%%');
-0184 
-0185 if printResults==true
-0186     for i=1:numel(repMets)
-0187         fprintf(['TOP 20 REPORTER METABOLITES\nTEST TYPE: ' repMets(i).test '\nID\tNAME\tP-VALUE\n']);
-0188         for j=1:min(20,numel(repMets(i).mets))
-0189             fprintf([esc(repMets(i).mets{j}) '\t' esc(repMets(i).metNames{j}) '\t' num2str(repMets(i).metPValues(j)) '\n']);
-0190         end
-0191         fprintf('\n');
-0192     end
-0193 end
-0194 
-0195 %Print results to file
-0196 if any(outputFile)
-0197     fid=fopen(outputFile,'w');
-0198     for i=1:numel(repMets)
-0199         fprintf(fid,['REPORTER METABOLITES USING TEST TYPE: ' repMets(i).test '\n']);
-0200         fprintf(fid,'ID\tNAME\tZ-SCORE\tP-VALUE\tNUMBER OF NEIGHBOURS\tAVERAGE Z-SCORE\tSTD Z-SCORE\n');
-0201         for j=1:numel(repMets(i).mets)
-0202             fprintf(fid,[esc(repMets(i).mets{j}) '\t' esc(repMets(i).metNames{j}) '\t' num2str(repMets(i).metZScores(j)) '\t' num2str(repMets(i).metPValues(j)) '\t' num2str(repMets(i).metNGenes(j)) '\t' num2str(repMets(i).meanZ(j)) '\t' num2str(repMets(i).stdZ(j)) '\n']);
-0203         end
-0204     end
-0205     fclose(fid);
-0206 end
-0207 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/analysis/runRobustnessAnalysis.html b/doc/analysis/runRobustnessAnalysis.html deleted file mode 100644 index 3c95349a..00000000 --- a/doc/analysis/runRobustnessAnalysis.html +++ /dev/null @@ -1,172 +0,0 @@ - - - - Description of runRobustnessAnalysis - - - - - - - - - -
Home > analysis > runRobustnessAnalysis.m
- - - -

runRobustnessAnalysis -

- -

PURPOSE ^

-
runRobustnessAnalysis Perform robustness analysis for a reaction and objective.
- -

SYNOPSIS ^

-
function [controlFlux, objFlux] = runRobustnessAnalysis(model, controlRxn, varargin)
- -

DESCRIPTION ^

-
 runRobustnessAnalysis  Perform robustness analysis for a reaction and objective.
-
- Performs robustness analysis for a reaction of interest and an objective
- of interest.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- controlRxn : char
-     reaction of interest whose value is to be controlled.
-
- Name-Value Arguments
- --------------------
- nPoints : double
-     number of points to show on plot (default 20).
- objRxn : char
-     reaction identifier of objective to be maximized (default uses the
-     objective defined in the model).
- plotRedCost : logical
-     whether reduced cost should also be plotted (default false).
-
- Returns
- -------
- controlFlux : double
-     flux values of the reaction of interest, ranging from its minimum to
-     its maximum value.
- objFlux : double
-     optimal values of objective reaction at each control reaction flux
-     value.
-
- Examples
- --------
-     [controlFlux, objFlux] = runRobustnessAnalysis(model, controlRxn, ...
-         nPoints, objRxn);
-
- Notes
- -----
- Modified from COBRA Toolbox robustnessAnalysis.m.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function [controlFlux, objFlux] = runRobustnessAnalysis(model, controlRxn, varargin)
-0002 % runRobustnessAnalysis  Perform robustness analysis for a reaction and objective.
-0003 %
-0004 % Performs robustness analysis for a reaction of interest and an objective
-0005 % of interest.
-0006 %
-0007 % Parameters
-0008 % ----------
-0009 % model : struct
-0010 %     a model structure.
-0011 % controlRxn : char
-0012 %     reaction of interest whose value is to be controlled.
-0013 %
-0014 % Name-Value Arguments
-0015 % --------------------
-0016 % nPoints : double
-0017 %     number of points to show on plot (default 20).
-0018 % objRxn : char
-0019 %     reaction identifier of objective to be maximized (default uses the
-0020 %     objective defined in the model).
-0021 % plotRedCost : logical
-0022 %     whether reduced cost should also be plotted (default false).
-0023 %
-0024 % Returns
-0025 % -------
-0026 % controlFlux : double
-0027 %     flux values of the reaction of interest, ranging from its minimum to
-0028 %     its maximum value.
-0029 % objFlux : double
-0030 %     optimal values of objective reaction at each control reaction flux
-0031 %     value.
-0032 %
-0033 % Examples
-0034 % --------
-0035 %     [controlFlux, objFlux] = runRobustnessAnalysis(model, controlRxn, ...
-0036 %         nPoints, objRxn);
-0037 %
-0038 % Notes
-0039 % -----
-0040 % Modified from COBRA Toolbox robustnessAnalysis.m.
-0041 
-0042 p=parseRAVENargs(varargin, {'nPoints',20; 'objRxn',[]; 'plotRedCost',false});
-0043 nPoints=p.nPoints;
-0044 objRxn=p.objRxn;
-0045 plotRedCost=p.plotRedCost;
-0046 if isempty(objRxn)
-0047     baseModel = model;
-0048 else
-0049     baseModel = setParam(model,'obj',objRxn,1);
-0050 end
-0051 
-0052 if any(ismember(model.rxns,controlRxn))
-0053     controlRxnIdx = getIndexes(model,controlRxn,'rxns');
-0054     tmpModel = setParam(model,'obj',controlRxnIdx,1);
-0055     solMax = solveLP(tmpModel);
-0056     solMax = solMax.x(logical(tmpModel.c));
-0057     tmpModel.c = -tmpModel.c;
-0058     solMin = solveLP(tmpModel);
-0059     solMin = solMin.x(logical(tmpModel.c));
-0060 else
-0061     error('Control reaction does not exist!');
-0062 end
-0063 
-0064 objFlux = zeros(nPoints,1);
-0065 redCost = zeros(nPoints,1);
-0066 controlFlux = linspace(solMin,solMax,nPoints)';
-0067 
-0068 
-0069 PB = progressReport(length(controlFlux),'Running robustness analysis');
-0070 for i=1:length(controlFlux)
-0071     modelControlled = setParam(baseModel,'eq',controlRxnIdx,controlFlux(i));
-0072     solControlled = solveLP(modelControlled);
-0073     objFlux(i) = solControlled.x(logical(modelControlled.c));
-0074     redCost(i) = solControlled.rCost(controlRxnIdx);
-0075     count(PB);
-0076 end
-0077 
-0078 if plotRedCost
-0079     yyaxis right
-0080     plot(controlFlux,redCost)
-0081     ylabel([strrep(controlRxn,'_','-') ' reduced cost']);
-0082     yyaxis left
-0083 end
-0084 plot(controlFlux,objFlux)
-0085 xlabel(strrep(controlRxn,'_','-'));
-0086 ylabel('Objective');
-0087 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/analysis/runSimpleOptKnock.html b/doc/analysis/runSimpleOptKnock.html deleted file mode 100644 index 498f7693..00000000 --- a/doc/analysis/runSimpleOptKnock.html +++ /dev/null @@ -1,218 +0,0 @@ - - - - Description of runSimpleOptKnock - - - - - - - - - -
Home > analysis > runSimpleOptKnock.m
- - - -

runSimpleOptKnock -

- -

PURPOSE ^

-
runSimpleOptKnock Simple OptKnock for growth-coupled production.
- -

SYNOPSIS ^

-
function out = runSimpleOptKnock(model, targetRxn, biomassRxn, varargin)
- -

DESCRIPTION ^

-
 runSimpleOptKnock  Simple OptKnock for growth-coupled production.
-
- Simple OptKnock algorithm that checks all gene or reaction deletions for
- growth-coupled metabolite production, by testing all possible
- combinations. This is not defined as MILP, and is therefore slow (but
- simple).
-
- Parameters
- ----------
- model : struct
-     a model structure.
- targetRxn : char
-     identifier of target reaction.
- biomassRxn : char
-     identifier of biomass reaction.
-
- Name-Value Arguments
- --------------------
- deletions : cell
-     cell array with gene or reaction identifiers that should be
-     considered for knockout (default model.rxns).
- genesOrRxns : char
-     string indicating whether deletions parameter is given with 'genes'
-     or 'rxns' identifiers (default 'rxns').
- maxNumKO : double
-     maximum number of simultaneous knockouts (default 1).
- minGrowth : double
-     minimum growth rate (default 0.05).
-
- Returns
- -------
- out : struct
-     structure with deletion strategies that result in growth-coupled
-     production, with fields:
-
-     - KO : cell array with gene(s) or reaction(s) to be deleted.
-     - growthRate : vector with growth rates after deletion.
-     - prodRate : vector with production rates after deletion.
-
- Examples
- --------
-     out = runSimpleOptKnock(model, targetRxn, biomassRxn, deletions, ...
-         genesOrRxns, maxNumKO, minGrowth);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function out = runSimpleOptKnock(model, targetRxn, biomassRxn, varargin)
-0002 % runSimpleOptKnock  Simple OptKnock for growth-coupled production.
-0003 %
-0004 % Simple OptKnock algorithm that checks all gene or reaction deletions for
-0005 % growth-coupled metabolite production, by testing all possible
-0006 % combinations. This is not defined as MILP, and is therefore slow (but
-0007 % simple).
-0008 %
-0009 % Parameters
-0010 % ----------
-0011 % model : struct
-0012 %     a model structure.
-0013 % targetRxn : char
-0014 %     identifier of target reaction.
-0015 % biomassRxn : char
-0016 %     identifier of biomass reaction.
-0017 %
-0018 % Name-Value Arguments
-0019 % --------------------
-0020 % deletions : cell
-0021 %     cell array with gene or reaction identifiers that should be
-0022 %     considered for knockout (default model.rxns).
-0023 % genesOrRxns : char
-0024 %     string indicating whether deletions parameter is given with 'genes'
-0025 %     or 'rxns' identifiers (default 'rxns').
-0026 % maxNumKO : double
-0027 %     maximum number of simultaneous knockouts (default 1).
-0028 % minGrowth : double
-0029 %     minimum growth rate (default 0.05).
-0030 %
-0031 % Returns
-0032 % -------
-0033 % out : struct
-0034 %     structure with deletion strategies that result in growth-coupled
-0035 %     production, with fields:
-0036 %
-0037 %     - KO : cell array with gene(s) or reaction(s) to be deleted.
-0038 %     - growthRate : vector with growth rates after deletion.
-0039 %     - prodRate : vector with production rates after deletion.
-0040 %
-0041 % Examples
-0042 % --------
-0043 %     out = runSimpleOptKnock(model, targetRxn, biomassRxn, deletions, ...
-0044 %         genesOrRxns, maxNumKO, minGrowth);
-0045 
-0046 p=parseRAVENargs(varargin, {'deletions',[]; 'genesOrRxns','rxns'; 'maxNumKO',1; 'minGrowth',0.05});
-0047 deletions=p.deletions;
-0048 genesOrRxns=p.genesOrRxns;
-0049 maxNumKO=p.maxNumKO;
-0050 minGrowth=p.minGrowth;
-0051 if isempty(deletions)
-0052     params.deletions = model.rxns;
-0053 else
-0054     params.deletions = deletions;
-0055 end
-0056 params.genesOrRxns = genesOrRxns;
-0057 params.maxNumKO = maxNumKO;
-0058 params.minGrowth = minGrowth;
-0059 
-0060 % Number of deletions
-0061 out.KO         = cell(0,params.maxNumKO); % The KO genes/rxns
-0062 out.growthRate = zeros(0);
-0063 out.prodRate   = zeros(0);
-0064 out.score      = zeros(0);
-0065 
-0066 params.biomassIdx  = getIndexes(model,biomassRxn,'rxns');
-0067 params.targetIdx   = getIndexes(model,targetRxn,'rxns');
-0068 
-0069 model = setParam(model,'obj',params.biomassIdx,1);
-0070 [solWT, hsSol] = solveLP(model);
-0071 WT.minScore = solWT.x(params.targetIdx)*solWT.f;
-0072 
-0073 params.PB = progressReport(numel(params.deletions),'Running simple OptKnock analysis');
-0074 KO=zeros(1,params.maxNumKO);
-0075 [~,~,out,~] = knockoutIteration(model,params,WT,out,params.maxNumKO,KO,[],hsSol);
-0076 
-0077 if size(out.KO,2)>1
-0078     singleKO = cellfun(@isempty,out.KO(:,1));
-0079     if any(singleKO)
-0080         singleKO = out.KO{singleKO,2};
-0081         singleKO = strcmp(out.KO(:,1),singleKO);
-0082         out.KO(singleKO,:) = [];
-0083         out.growthRate(singleKO,:) = [];
-0084         out.prodRate(singleKO,:) = [];
-0085         out.score(singleKO,:) = [];
-0086     end
-0087 end
-0088 
-0089 
-0090 params.PB.done;
-0091 end
-0092 
-0093 function [model,iteration,out,KO,hsSol] = knockoutIteration(model,params,WT,out,iteration,KO,minScore,hsSol)
-0094 if nargin<7 || isempty(minScore)
-0095     minScore = WT.minScore;
-0096 end
-0097 iteration = iteration - 1;
-0098 for i = 1:numel(params.deletions)
-0099     if iteration+1==params.maxNumKO
-0100         params.PB.update(i);
-0101     end
-0102     KO(iteration+1)=i;
-0103     switch params.genesOrRxns
-0104         case 'rxns'
-0105             modelKO = setParam(model,'eq',params.deletions{i},0);
-0106         case 'genes'
-0107             modelKO = removeGenes(model,params.deletions{i},false,false,false);
-0108     end
-0109     solKO = solveLP(modelKO,0,[],hsSol);
-0110     if ~isempty(solKO.f)
-0111         growthRate = solKO.f;
-0112         prodRate   = solKO.x(params.targetIdx);
-0113         prodRate(prodRate<1e-10)=0; % Filter out results from solver tolerance
-0114         if growthRate > params.minGrowth
-0115             if growthRate*prodRate > minScore*1.01
-0116                 out.KO(end+1,find(KO)) = transpose(params.deletions(KO(find(KO))));
-0117                 out.growthRate(end+1,1) = growthRate;
-0118                 out.prodRate(end+1,1)   = prodRate;
-0119                 out.score(end+1,1)      = growthRate*prodRate;
-0120             end
-0121             if iteration>0
-0122                 [~,~,out] = knockoutIteration(modelKO,params,WT,out,iteration,KO,growthRate*prodRate,hsSol);
-0123             end
-0124         end
-0125     end
-0126 end
-0127 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/analysis/sampleACHR.html b/doc/analysis/sampleACHR.html deleted file mode 100644 index dfc28729..00000000 --- a/doc/analysis/sampleACHR.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - Description of sampleACHR - - - - - - - - - -
Home > analysis > sampleACHR.m
- - - -

sampleACHR -

- -

PURPOSE ^

-
sampleACHR Artificially Centered Hit-and-Run flux sampling.
- -

SYNOPSIS ^

-
function solutions = sampleACHR(model, nSamples, thinning, warmup, seed)
- -

DESCRIPTION ^

-
 sampleACHR  Artificially Centered Hit-and-Run flux sampling.
-
- Draws nSamples flux vectors approximately uniformly from the flux polytope
- {v : S*v = 0, lb <= v <= ub}, using the ACHR algorithm (Kaufman & Smith 1998).
- No rounding is performed, so on strongly elongated polytopes (e.g.
- enzyme-constrained models) mixing is slower than sampleCHRR; prefer CHRR there.
-
- Parameters
- ----------
- model : struct
-     a RAVEN model structure.
- nSamples : double
-     number of flux vectors to return (default 1000).
- thinning : double
-     Markov-chain steps between recorded samples (default 100).
- warmup : double
-     nPoints-by-nRxns warmup vertices (default: generated by sampleWarmupPoints).
- seed : double
-     random seed for reproducibility (default: current RNG state).
-
- Returns
- -------
- solutions : double
-     nRxns-by-nSamples matrix of sampled flux distributions.
-
- See also
- --------
- randomSampling, sampleCHRR, sampleWarmupPoints
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
  • randomSampling randomSampling Sample the flux solution space (entry point for all samplers).
- - - - -

SOURCE CODE ^

-
0001 function solutions = sampleACHR(model, nSamples, thinning, warmup, seed)
-0002 % sampleACHR  Artificially Centered Hit-and-Run flux sampling.
-0003 %
-0004 % Draws nSamples flux vectors approximately uniformly from the flux polytope
-0005 % {v : S*v = 0, lb <= v <= ub}, using the ACHR algorithm (Kaufman & Smith 1998).
-0006 % No rounding is performed, so on strongly elongated polytopes (e.g.
-0007 % enzyme-constrained models) mixing is slower than sampleCHRR; prefer CHRR there.
-0008 %
-0009 % Parameters
-0010 % ----------
-0011 % model : struct
-0012 %     a RAVEN model structure.
-0013 % nSamples : double
-0014 %     number of flux vectors to return (default 1000).
-0015 % thinning : double
-0016 %     Markov-chain steps between recorded samples (default 100).
-0017 % warmup : double
-0018 %     nPoints-by-nRxns warmup vertices (default: generated by sampleWarmupPoints).
-0019 % seed : double
-0020 %     random seed for reproducibility (default: current RNG state).
-0021 %
-0022 % Returns
-0023 % -------
-0024 % solutions : double
-0025 %     nRxns-by-nSamples matrix of sampled flux distributions.
-0026 %
-0027 % See also
-0028 % --------
-0029 % randomSampling, sampleCHRR, sampleWarmupPoints
-0030 
-0031 if nargin < 2 || isempty(nSamples); nSamples = 1000; end
-0032 if nargin < 3 || isempty(thinning); thinning = 100;  end
-0033 if nargin < 4 || isempty(warmup);   warmup = sampleWarmupPoints(model); end
-0034 if nargin < 5;                      seed = [];        end
-0035 if ~isempty(seed)
-0036     rng(seed);
-0037 end
-0038 
-0039 nRxns = numel(model.rxns);
-0040 lb = model.lb(:);
-0041 ub = model.ub(:);
-0042 nW = size(warmup, 1);
-0043 
-0044 center = mean(warmup, 1)';             % nRxns-by-1
-0045 prev = center;
-0046 nTotal = 0;
-0047 dirTol = 1e-12;
-0048 
-0049 solutions = zeros(nRxns, nSamples);
-0050 rec = 0;
-0051 nSteps = thinning*nSamples;
-0052 
-0053 for stepno = 1:nSteps
-0054     pidx = randi(nW);
-0055     delta = warmup(pidx, :)' - center;
-0056 
-0057     posD = delta > dirTol;
-0058     negD = delta < -dirTol;
-0059     ubp = ub - prev;
-0060     lbp = lb - prev;
-0061     % alpha range keeping lb <= prev + alpha*delta <= ub
-0062     upperLims = [ubp(posD)./delta(posD); lbp(negD)./delta(negD)];
-0063     lowerLims = [lbp(posD)./delta(posD); ubp(negD)./delta(negD)];
-0064     if isempty(upperLims); tHi = 0; else; tHi = min(upperLims); end
-0065     if isempty(lowerLims); tLo = 0; else; tLo = max(lowerLims); end
-0066 
-0067     if tHi > tLo
-0068         alpha = tLo + rand*(tHi - tLo);
-0069         prev = prev + alpha*delta;
-0070     end
-0071 
-0072     % Running centre (quasi-Markovian ACHR: centre converges and steadies).
-0073     center = (nTotal*center + prev) / (nTotal + 1);
-0074     nTotal = nTotal + 1;
-0075 
-0076     if mod(stepno, thinning) == 0
-0077         rec = rec + 1;
-0078         solutions(:, rec) = prev;
-0079     end
-0080 end
-0081 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/analysis/sampleCHRR.html b/doc/analysis/sampleCHRR.html deleted file mode 100644 index a1c724bd..00000000 --- a/doc/analysis/sampleCHRR.html +++ /dev/null @@ -1,250 +0,0 @@ - - - - Description of sampleCHRR - - - - - - - - - -
Home > analysis > sampleCHRR.m
- - - -

sampleCHRR -

- -

PURPOSE ^

-
sampleCHRR Coordinate Hit-and-Run with Rounding flux sampling.
- -

SYNOPSIS ^

-
function [solutions, info] = sampleCHRR(model, nSamples, thinning, nBurnin, seed, fixedWidthTol)
- -

DESCRIPTION ^

-
 sampleCHRR  Coordinate Hit-and-Run with Rounding flux sampling.
-
- Draws nSamples flux vectors approximately uniformly from the flux polytope
- {v : S*v = 0, lb <= v <= ub}, using the CHRR algorithm (Haraldsdottir et al.
- 2017, Bioinformatics 33:1741). The polytope is rounded before sampling, so
- mixing stays fast even on strongly elongated polytopes (e.g.
- enzyme-constrained models), unlike sampleACHR.
-
- Set any constraints you want to condition on (e.g. a biomass lower bound or
- measured exchange fluxes) on the model before calling.
-
- Parameters
- ----------
- model : struct
-     a RAVEN model structure.
- nSamples : double
-     number of flux vectors to return (default 1000).
- thinning : double
-     Markov-chain steps between recorded samples (default 100).
- nBurnin : double
-     burn-in steps discarded before the first recorded sample (default 1000).
- seed : double
-     random seed for reproducibility (default: current RNG state).
- fixedWidthTol : double
-     a reaction whose allowed flux range is narrower than this is treated as
-     stoichiometrically fixed and folded into the equality system, keeping the
-     reduced polytope full-dimensional (default 1e-7).
-
- Returns
- -------
- solutions : double
-     nRxns-by-nSamples matrix of sampled flux distributions.
- info : struct
-     .nDimensions  — dimension of the sampled flux polytope
-     .mveConverged — whether MVE rounding reached tolerance
-     .fixedRxns    — IDs of reactions folded in as fixed
-
- See also
- --------
- randomSampling, sampleACHR, sampleMaxVolEllipse
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
  • randomSampling randomSampling Sample the flux solution space (entry point for all samplers).
- - - - -

SOURCE CODE ^

-
0001 function [solutions, info] = sampleCHRR(model, nSamples, thinning, nBurnin, seed, fixedWidthTol)
-0002 % sampleCHRR  Coordinate Hit-and-Run with Rounding flux sampling.
-0003 %
-0004 % Draws nSamples flux vectors approximately uniformly from the flux polytope
-0005 % {v : S*v = 0, lb <= v <= ub}, using the CHRR algorithm (Haraldsdottir et al.
-0006 % 2017, Bioinformatics 33:1741). The polytope is rounded before sampling, so
-0007 % mixing stays fast even on strongly elongated polytopes (e.g.
-0008 % enzyme-constrained models), unlike sampleACHR.
-0009 %
-0010 % Set any constraints you want to condition on (e.g. a biomass lower bound or
-0011 % measured exchange fluxes) on the model before calling.
-0012 %
-0013 % Parameters
-0014 % ----------
-0015 % model : struct
-0016 %     a RAVEN model structure.
-0017 % nSamples : double
-0018 %     number of flux vectors to return (default 1000).
-0019 % thinning : double
-0020 %     Markov-chain steps between recorded samples (default 100).
-0021 % nBurnin : double
-0022 %     burn-in steps discarded before the first recorded sample (default 1000).
-0023 % seed : double
-0024 %     random seed for reproducibility (default: current RNG state).
-0025 % fixedWidthTol : double
-0026 %     a reaction whose allowed flux range is narrower than this is treated as
-0027 %     stoichiometrically fixed and folded into the equality system, keeping the
-0028 %     reduced polytope full-dimensional (default 1e-7).
-0029 %
-0030 % Returns
-0031 % -------
-0032 % solutions : double
-0033 %     nRxns-by-nSamples matrix of sampled flux distributions.
-0034 % info : struct
-0035 %     .nDimensions  — dimension of the sampled flux polytope
-0036 %     .mveConverged — whether MVE rounding reached tolerance
-0037 %     .fixedRxns    — IDs of reactions folded in as fixed
-0038 %
-0039 % See also
-0040 % --------
-0041 % randomSampling, sampleACHR, sampleMaxVolEllipse
-0042 
-0043 if nargin < 2 || isempty(nSamples);      nSamples = 1000;     end
-0044 if nargin < 3 || isempty(thinning);      thinning = 100;      end
-0045 if nargin < 4 || isempty(nBurnin);       nBurnin = 1000;      end
-0046 if nargin < 5;                           seed = [];           end
-0047 if nargin < 6 || isempty(fixedWidthTol); fixedWidthTol = 1e-7; end
-0048 if ~isempty(seed)
-0049     rng(seed);
-0050 end
-0051 
-0052 nRxns = numel(model.rxns);
-0053 S  = full(model.S);
-0054 lb = model.lb(:);
-0055 ub = model.ub(:);
-0056 
-0057 % ---- Fold implicitly-fixed reactions into equalities ----
-0058 % A reaction with ~zero flux range is determined by stoichiometry; keeping it as
-0059 % a box constraint would make the reduced polytope lower-dimensional and the MVE
-0060 % solve singular. Fix it at its (unique) value instead.
-0061 [minF, maxF] = getAllowedBounds(model);
-0062 minF = minF(:); maxF = maxF(:);
-0063 width = maxF - minF;
-0064 fixed = width < fixedWidthTol;
-0065 fixedVals = (maxF + minF) / 2;
-0066 fixedIdx = find(fixed);
-0067 
-0068 Aeq = S;
-0069 beq = zeros(size(S, 1), 1);
-0070 if ~isempty(fixedIdx)
-0071     blk = zeros(numel(fixedIdx), nRxns);
-0072     for k = 1:numel(fixedIdx)
-0073         blk(k, fixedIdx(k)) = 1;
-0074     end
-0075     Aeq = [Aeq; blk];
-0076     beq = [beq; fixedVals(fixedIdx)];
-0077 end
-0078 
-0079 % Particular solution v0 and nullspace basis N: v = v0 + N*x.
-0080 % v0 is ANY particular solution; the sampled flux distribution is invariant to
-0081 % its choice and to the nullspace basis.
-0082 % lsqminnorm rather than '\': Aeq is square whenever the number of metabolites
-0083 % plus the number of folded-in fixed reactions equals the number of reactions,
-0084 % and it is then rank-deficient, for which '\' returns NaN. Those NaNs travel
-0085 % into the polytope and surface much later as a misleading "empty interior"
-0086 % error from sampleChebyshevCenter.
-0087 v0 = lsqminnorm(Aeq, beq);
-0088 N  = null(Aeq);
-0089 d  = size(N, 2);
-0090 
-0091 info = struct();
-0092 info.fixedRxns = model.rxns(fixedIdx);
-0093 
-0094 if d == 0
-0095     % Flux space is a single point — nothing to sample.
-0096     solutions = repmat(v0, 1, nSamples);
-0097     info.nDimensions = 0;
-0098     info.mveConverged = true;
-0099     return;
-0100 end
-0101 
-0102 % ---- Build the full-dimensional polytope in x (free reactions only) ----
-0103 free = ~fixed;
-0104 Nf = N(free, :);
-0105 A_full = [Nf; -Nf];
-0106 b_full = [ub(free) - v0(free); v0(free) - lb(free)];
-0107 % Drop rows whose direction vanished in the nullspace (constraint is constant).
-0108 keep = sqrt(sum(A_full.^2, 2)) > 1e-10;
-0109 A_full = A_full(keep, :);
-0110 b_full = b_full(keep);
-0111 
-0112 % ---- Round with the MVE ----
-0113 x0 = sampleChebyshevCenter(A_full, b_full);
-0114 [center, E, converged] = sampleMaxVolEllipse(A_full, b_full, x0);
-0115 info.nDimensions = d;
-0116 info.mveConverged = converged;
-0117 if ~converged
-0118     warning('RAVEN:warning', '%s', ['The maximum-volume ellipsoid rounding ' ...
-0119         'did not converge; the samples may be poorly mixed. Inspect the ' ...
-0120         'mveConverged field of the second output.']);
-0121 end
-0122 
-0123 % Rounded polytope {y : A_r*y <= b_r}, which contains the unit ball.
-0124 A_r = A_full * E;
-0125 b_r = b_full - A_full*center;
-0126 
-0127 % ---- Coordinate hit-and-run on the rounded polytope ----
-0128 y = zeros(d, 1);
-0129 s = b_r - A_r*y;                       % maintained slack, > 0 at start
-0130 solutions = zeros(nRxns, nSamples);
-0131 rec = 0;
-0132 total = nBurnin + thinning*nSamples;
-0133 colTol = 1e-12;
-0134 
-0135 for stepno = 1:total
-0136     j = randi(d);
-0137     col = A_r(:, j);
-0138     pos = col > colTol;
-0139     neg = col < -colTol;
-0140     ratios = s ./ col;
-0141     if any(pos); tHi = min(ratios(pos)); else; tHi = inf;  end
-0142     if any(neg); tLo = max(ratios(neg)); else; tLo = -inf; end
-0143     if ~isfinite(tHi) || ~isfinite(tLo) || tHi <= tLo
-0144         continue;                      % unbounded/degenerate coordinate; skip
-0145     end
-0146     t = tLo + rand*(tHi - tLo);
-0147     y(j) = y(j) + t;
-0148     s = s - col*t;
-0149 
-0150     if stepno > nBurnin && mod(stepno - nBurnin, thinning) == 0
-0151         rec = rec + 1;
-0152         x = center + E*y;
-0153         solutions(:, rec) = v0 + N*x;
-0154     end
-0155 end
-0156 
-0157 if rec < nSamples
-0158     if rec >= 1
-0159         solutions(:, rec+1:end) = repmat(solutions(:, rec), 1, nSamples - rec);
-0160     else
-0161         solutions = repmat(v0, 1, nSamples);
-0162     end
-0163 end
-0164 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/analysis/traceFluxPath.html b/doc/analysis/traceFluxPath.html deleted file mode 100644 index 8984bc4c..00000000 --- a/doc/analysis/traceFluxPath.html +++ /dev/null @@ -1,515 +0,0 @@ - - - - Description of traceFluxPath - - - - - - - - - -
Home > analysis > traceFluxPath.m
- - - -

traceFluxPath -

- -

PURPOSE ^

-
traceFluxPath Find the highest-flux-fraction path between two reactions.
- -

SYNOPSIS ^

-
function [pathRxns, pathMets, cumFrac] = traceFluxPath(model, fluxes, fromRxn, toRxn, varargin)
- -

DESCRIPTION ^

-
 traceFluxPath  Find the highest-flux-fraction path between two reactions.
-
- Traces how flux flows from one reaction to another through the metabolic
- network, quantifying what fraction of the source reaction's flux reaches
- the target. At each metabolite junction the flux is split proportionally
- among all consuming reactions; the path with the highest cumulative
- fraction is returned and printed.
-
- By default the search excludes currency metabolites (ATP, NAD, H2O, etc.)
- so the returned path represents material (carbon-skeleton) flow rather
- than cofactor shortcuts. Set traceMaterial=false to disable this.
-
- Parameters
- ----------
- model : struct
-     RAVEN model structure.
- fluxes : double
-     flux vector (length = numel(model.rxns)).
- fromRxn : char
-     source reaction ID.
- toRxn : char
-     target reaction ID.
-
- Name-Value Arguments
- --------------------
- cutoff : double
-     minimum |flux| for a reaction to be considered flux-carrying
-     (default 1e-8).
- maxHops : double
-     maximum number of reactions in the path; longer paths are pruned
-     (default 30).
- verbose : logical
-     print the path to the command window (default true).
- traceMaterial : logical
-     exclude common currency/cofactor metabolites (ATP, ADP, NAD, NADH,
-     H2O, CoA, Pi, CO2, …) so the path follows material (carbon-skeleton)
-     flux rather than energy-carrier shortcuts (default true).
- carbonOnly : logical
-     additionally require each intermediate metabolite to contain at least
-     one carbon atom, inferred from model.metFormulas. Drops H+, H2O, Pi,
-     O2, NH3, CO2 that are not already caught by the currency list.
-     Silently ignored when model.metFormulas is absent (default false).
- excludeMets : cell of char
-     extra metabolite names or IDs (after stripping compartment suffix) to
-     exclude, in addition to the built-in currency list (default {}).
-
- Returns
- -------
- pathRxns : cell
-     cell array of reaction IDs on the best path, including fromRxn and
-     toRxn. Empty if no path is found.
- pathMets : cell
-     metabolite IDs at each junction (numel = numel(pathRxns)-1).
- cumFrac : double
-     fraction of fromRxn's flux that routes through this exact path
-     (0 = no path found, 1 = all flux reaches toRxn directly).
-
- Examples
- --------
-     [p, m, f] = traceFluxPath(model, sol.x, 'PFK', 'ATPS4rpp')
-     % disable material filter to see all connections including ATP/ADP:
-     [p, m, f] = traceFluxPath(model, sol.x, 'PFK', 'ATPS4rpp', 'traceMaterial', false)
-     % add model-specific cofactors to the exclusion list:
-     [p, m, f] = traceFluxPath(model, sol.x, 'PFK', 'CS', 'excludeMets', {'acetyl-CoA'})
-
- Notes
- -----
- The algorithm follows metabolites in the forward direction only: it
- traces the metabolites that fromRxn PRODUCES and finds which consuming
- reactions eventually reach toRxn. If fromRxn and toRxn are not connected
- in the forward flux direction (e.g. they are in parallel branches), an
- empty path is returned. A best-first (greedy) search ensures the
- highest-fraction path is found efficiently; a visited set prevents
- revisiting reactions.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [pathRxns, pathMets, cumFrac] = traceFluxPath(model, fluxes, fromRxn, toRxn, varargin)
-0002 % traceFluxPath  Find the highest-flux-fraction path between two reactions.
-0003 %
-0004 % Traces how flux flows from one reaction to another through the metabolic
-0005 % network, quantifying what fraction of the source reaction's flux reaches
-0006 % the target. At each metabolite junction the flux is split proportionally
-0007 % among all consuming reactions; the path with the highest cumulative
-0008 % fraction is returned and printed.
-0009 %
-0010 % By default the search excludes currency metabolites (ATP, NAD, H2O, etc.)
-0011 % so the returned path represents material (carbon-skeleton) flow rather
-0012 % than cofactor shortcuts. Set traceMaterial=false to disable this.
-0013 %
-0014 % Parameters
-0015 % ----------
-0016 % model : struct
-0017 %     RAVEN model structure.
-0018 % fluxes : double
-0019 %     flux vector (length = numel(model.rxns)).
-0020 % fromRxn : char
-0021 %     source reaction ID.
-0022 % toRxn : char
-0023 %     target reaction ID.
-0024 %
-0025 % Name-Value Arguments
-0026 % --------------------
-0027 % cutoff : double
-0028 %     minimum |flux| for a reaction to be considered flux-carrying
-0029 %     (default 1e-8).
-0030 % maxHops : double
-0031 %     maximum number of reactions in the path; longer paths are pruned
-0032 %     (default 30).
-0033 % verbose : logical
-0034 %     print the path to the command window (default true).
-0035 % traceMaterial : logical
-0036 %     exclude common currency/cofactor metabolites (ATP, ADP, NAD, NADH,
-0037 %     H2O, CoA, Pi, CO2, …) so the path follows material (carbon-skeleton)
-0038 %     flux rather than energy-carrier shortcuts (default true).
-0039 % carbonOnly : logical
-0040 %     additionally require each intermediate metabolite to contain at least
-0041 %     one carbon atom, inferred from model.metFormulas. Drops H+, H2O, Pi,
-0042 %     O2, NH3, CO2 that are not already caught by the currency list.
-0043 %     Silently ignored when model.metFormulas is absent (default false).
-0044 % excludeMets : cell of char
-0045 %     extra metabolite names or IDs (after stripping compartment suffix) to
-0046 %     exclude, in addition to the built-in currency list (default {}).
-0047 %
-0048 % Returns
-0049 % -------
-0050 % pathRxns : cell
-0051 %     cell array of reaction IDs on the best path, including fromRxn and
-0052 %     toRxn. Empty if no path is found.
-0053 % pathMets : cell
-0054 %     metabolite IDs at each junction (numel = numel(pathRxns)-1).
-0055 % cumFrac : double
-0056 %     fraction of fromRxn's flux that routes through this exact path
-0057 %     (0 = no path found, 1 = all flux reaches toRxn directly).
-0058 %
-0059 % Examples
-0060 % --------
-0061 %     [p, m, f] = traceFluxPath(model, sol.x, 'PFK', 'ATPS4rpp')
-0062 %     % disable material filter to see all connections including ATP/ADP:
-0063 %     [p, m, f] = traceFluxPath(model, sol.x, 'PFK', 'ATPS4rpp', 'traceMaterial', false)
-0064 %     % add model-specific cofactors to the exclusion list:
-0065 %     [p, m, f] = traceFluxPath(model, sol.x, 'PFK', 'CS', 'excludeMets', {'acetyl-CoA'})
-0066 %
-0067 % Notes
-0068 % -----
-0069 % The algorithm follows metabolites in the forward direction only: it
-0070 % traces the metabolites that fromRxn PRODUCES and finds which consuming
-0071 % reactions eventually reach toRxn. If fromRxn and toRxn are not connected
-0072 % in the forward flux direction (e.g. they are in parallel branches), an
-0073 % empty path is returned. A best-first (greedy) search ensures the
-0074 % highest-fraction path is found efficiently; a visited set prevents
-0075 % revisiting reactions.
-0076 
-0077 p = parseRAVENargs(varargin, {'cutoff',1e-8; 'maxHops',30; 'verbose',true; ...
-0078     'traceMaterial',true; 'carbonOnly',false; 'excludeMets',{}});
-0079 cutoff        = p.cutoff;
-0080 maxHops       = p.maxHops;
-0081 verbose       = p.verbose;
-0082 traceMaterial = p.traceMaterial;
-0083 carbonOnly    = p.carbonOnly;
-0084 excludeMets   = p.excludeMets;
-0085 if ischar(excludeMets), excludeMets = {excludeMets}; end
-0086 
-0087 % ---- Build exclusion set ----
-0088 if traceMaterial
-0089     currencyList = [defaultCurrencyMets_(), excludeMets(:)'];
-0090 else
-0091     currencyList = excludeMets(:)';
-0092 end
-0093 
-0094 % Pre-compute carbon counts per metabolite (for carbonOnly filter)
-0095 hasCarbonFilter = false;
-0096 carbonCounts    = [];
-0097 if carbonOnly
-0098     if isfield(model,'metFormulas') && ~isempty(model.metFormulas)
-0099         carbonCounts    = cellfun(@countCarbon_, model.metFormulas);
-0100         hasCarbonFilter = true;
-0101     else
-0102         warning('traceFluxPath:noFormulas', ...
-0103             'carbonOnly requires model.metFormulas; the option is ignored.');
-0104     end
-0105 end
-0106 
-0107 % ---- Resolve reactions ----
-0108 pathRxns = {};
-0109 pathMets = {};
-0110 cumFrac  = 0;
-0111 
-0112 fromIdx = resolveRxn_(model, fromRxn);
-0113 toIdx   = resolveRxn_(model, toRxn);
-0114 
-0115 if fromIdx == toIdx
-0116     pathRxns = model.rxns(fromIdx);
-0117     pathMets = {};
-0118     cumFrac  = 1;
-0119     if verbose, printPath_(model, fluxes, pathRxns, pathMets, cumFrac, ...
-0120             fromRxn, toRxn, traceMaterial || ~isempty(excludeMets) || carbonOnly); end
-0121     return
-0122 end
-0123 
-0124 nRxns = numel(model.rxns);
-0125 
-0126 % ---- Best-first search (highest cumulative fraction first) ----
-0127 % Queue entries are structs: .rxn (idx), .frac (cumFrac), .rpath, .mpath
-0128 bestAtNode = zeros(nRxns,1);
-0129 bestAtNode(fromIdx) = 1.0;
-0130 
-0131 queue = makeEntry_(fromIdx, 1.0, fromIdx, zeros(0,1));
-0132 
-0133 bestFrac  = 0;
-0134 bestRPath = [];
-0135 bestMPath = [];
-0136 
-0137 while ~isempty(queue)
-0138     % Pop highest-fraction entry
-0139     entry = queue(1);
-0140     queue(1) = [];
-0141 
-0142     cur   = entry.rxn;
-0143     frac  = entry.frac;
-0144     rpath = entry.rpath;
-0145     mpath = entry.mpath;
-0146 
-0147     if cur == toIdx
-0148         if frac > bestFrac
-0149             bestFrac  = frac;
-0150             bestRPath = rpath;
-0151             bestMPath = mpath;
-0152         end
-0153         continue
-0154     end
-0155 
-0156     if numel(rpath) > maxHops, continue; end
-0157 
-0158     % Find metabolites net-produced by cur. Iterate only the structurally
-0159     % nonzero entries of the (sparse) column instead of every metabolite
-0160     % in the model, most of which a reaction never touches.
-0161     col  = model.S(:, cur);
-0162     fcur = fluxes(cur);
-0163     for m = find(col)'
-0164         if col(m) * fcur <= 0, continue; end   % skip consumed or zero-contribution mets
-0165 
-0166         % ---- Material-flux filters ----
-0167         if ~isempty(currencyList) && isCurrencyMet_(model, m, currencyList)
-0168             continue
-0169         end
-0170         if hasCarbonFilter && carbonCounts(m) == 0
-0171             continue
-0172         end
-0173 
-0174         % Reactions that net-consume this metabolite
-0175         row  = full(model.S(m, :));
-0176         fVec = fluxes(:)';
-0177         consumers = find(row .* fVec < -cutoff);
-0178         consumers = consumers(~ismember(consumers, rpath));  % no revisiting
-0179         if isempty(consumers), continue; end
-0180 
-0181         totalCons = sum(abs(row(consumers) .* fVec(consumers)));
-0182 
-0183         for ci = 1:numel(consumers)
-0184             nr      = consumers(ci);
-0185             frac_r  = abs(row(nr) * fluxes(nr)) / totalCons;
-0186             newFrac = frac * frac_r;
-0187 
-0188             if newFrac > bestAtNode(nr) || nr == toIdx
-0189                 bestAtNode(nr) = max(bestAtNode(nr), newFrac);
-0190                 newEntry = makeEntry_(nr, newFrac, [rpath, nr], [mpath; m]);
-0191 
-0192                 % Insert in decreasing-fraction order
-0193                 if isempty(queue)
-0194                     queue = newEntry;
-0195                 else
-0196                     fracs = [queue.frac];
-0197                     pos   = find(fracs < newFrac, 1);
-0198                     if isempty(pos)
-0199                         queue(end+1) = newEntry; %#ok<AGROW>
-0200                     else
-0201                         queue = [queue(1:pos-1), newEntry, queue(pos:end)];
-0202                     end
-0203                 end
-0204             end
-0205         end
-0206     end
-0207 end
-0208 
-0209 % ---- Package and display ----
-0210 cumFrac = bestFrac;
-0211 if ~isempty(bestRPath)
-0212     pathRxns = model.rxns(bestRPath);
-0213     pathMets = model.mets(bestMPath);
-0214 end
-0215 
-0216 if verbose
-0217     materialMode = traceMaterial || ~isempty(excludeMets) || carbonOnly;
-0218     printPath_(model, fluxes, pathRxns, pathMets, cumFrac, fromRxn, toRxn, materialMode);
-0219 end
-0220 end
-0221 
-0222 %--------------------------------------------------------------------------
-0223 function e = makeEntry_(rxn, frac, rpath, mpath)
-0224     e.rxn   = rxn;
-0225     e.frac  = frac;
-0226     e.rpath = rpath;
-0227     e.mpath = mpath;
-0228 end
-0229 
-0230 function printPath_(model, fluxes, pathRxns, pathMets, cumFrac, fromRxn, toRxn, materialMode)
-0231     W   = 66;
-0232     bar = repmat('=', 1, W);
-0233     fprintf('\n%s\n', bar);
-0234     fprintf('  Flux path: %s \x2192 %s\n', fromRxn, toRxn);
-0235     if materialMode
-0236         fprintf('  (material flux — currency metabolites excluded)\n');
-0237     end
-0238     fprintf('%s\n', bar);
-0239 
-0240     if isempty(pathRxns)
-0241         fprintf('  No forward flux path found between these reactions.\n');
-0242         if materialMode
-0243             fprintf('  Tip: try traceMaterial=false to include cofactor connections,\n');
-0244             fprintf('       or excludeMets={} to see what the filter removed.\n');
-0245         else
-0246             fprintf('  (They may be in parallel branches or connected only in reverse.)\n');
-0247         end
-0248         fprintf('%s\n\n', bar);
-0249         return
-0250     end
-0251 
-0252     % One-line path with junction fractions
-0253     parts = pathRxns{1};
-0254     for i = 2:numel(pathRxns)
-0255         mi = find(strcmp(model.mets, pathMets{i-1}));
-0256         row = full(model.S(mi, :));
-0257         consumers = find(row .* fluxes' < -1e-12);
-0258         if isempty(consumers)
-0259             pct = NaN;
-0260         else
-0261             totalCons = sum(abs(row(consumers) .* fluxes(consumers)'));
-0262             ni = find(strcmp(model.rxns, pathRxns{i}));
-0263             pct = abs(row(ni) * fluxes(ni)) / totalCons * 100;
-0264         end
-0265         mLbl = getMetLabel_(model, mi);
-0266         if isnan(pct)
-0267             parts = [parts sprintf(' \x2192[%s]\x2192 %s', mLbl, pathRxns{i})]; %#ok<AGROW>
-0268         else
-0269             parts = [parts sprintf(' \x2192[%s: %.0f%%]\x2192 %s', mLbl, pct, pathRxns{i})]; %#ok<AGROW>
-0270         end
-0271     end
-0272     fprintf('\n  %s\n\n', parts);
-0273 
-0274     fprintf('  Cumulative fraction: %.2f%%\n', cumFrac*100);
-0275     fprintf('  Path length:         %d reactions\n', numel(pathRxns));
-0276 
-0277     fprintf('\n  Reactions:\n');
-0278     eqns = constructEquations(model, pathRxns);
-0279     for i = 1:numel(pathRxns)
-0280         ri  = find(strcmp(model.rxns, pathRxns{i}));
-0281         nm  = '';
-0282         if isfield(model,'rxnNames') && ri <= numel(model.rxnNames)
-0283             nm = char(model.rxnNames{ri});
-0284         end
-0285         if numel(nm) > 25, nm = [nm(1:22) '...']; end
-0286         fprintf('  %2d. %-14s  flux: %+10.4g   %-25s  %s\n', i, pathRxns{i}, fluxes(ri), nm, eqns{i});
-0287     end
-0288     fprintf('\n%s\n\n', bar);
-0289 end
-0290 
-0291 function idx = resolveRxn_(model, rxn)
-0292     if isnumeric(rxn)
-0293         idx = rxn;
-0294     else
-0295         idx = find(strcmp(model.rxns, char(rxn)));
-0296         if isempty(idx)
-0297             error('Reaction ''%s'' not found in model.', rxn);
-0298         end
-0299         idx = idx(1);
-0300     end
-0301 end
-0302 
-0303 function s = getMetLabel_(model, metIdx)
-0304     if isfield(model,'metNames') && ~isempty(model.metNames{metIdx})
-0305         s = model.metNames{metIdx};
-0306     else
-0307         s = model.mets{metIdx};
-0308     end
-0309 end
-0310 
-0311 function tf = isCurrencyMet_(model, m, currencyList)
-0312 % Returns true if metabolite m matches any entry in currencyList.
-0313 % Matches against metNames (case-insensitive exact) and against the
-0314 % metabolite ID after stripping compartment suffixes like [c], [m], _c.
-0315     targets = {};
-0316     if isfield(model,'metNames') && m <= numel(model.metNames) && ~isempty(model.metNames{m})
-0317         targets{end+1} = lower(strtrim(model.metNames{m}));
-0318     end
-0319     % Strip compartment from ID: [c], (c), _c, _C at end
-0320     rawId = model.mets{m};
-0321     stripped = regexprep(rawId, '[\[\(][^\]\)]*[\]\)]$|_[a-zA-Z]\d*$', '');
-0322     targets{end+1} = lower(stripped);
-0323 
-0324     for i = 1:numel(currencyList)
-0325         p = lower(strtrim(currencyList{i}));
-0326         for j = 1:numel(targets)
-0327             if strcmp(targets{j}, p)
-0328                 tf = true;
-0329                 return
-0330             end
-0331         end
-0332     end
-0333     tf = false;
-0334 end
-0335 
-0336 function nC = countCarbon_(formula)
-0337 % Count carbon atoms in a molecular formula string like 'C6H12O6'.
-0338     if isempty(formula)
-0339         nC = 0; return
-0340     end
-0341     tok = regexp(char(formula), 'C(\d*)', 'tokens', 'once');
-0342     if isempty(tok)
-0343         nC = 0;
-0344     elseif isempty(tok{1})
-0345         nC = 1;
-0346     else
-0347         nC = str2double(tok{1});
-0348     end
-0349 end
-0350 
-0351 function lst = defaultCurrencyMets_()
-0352 % Common currency / cofactor metabolites matched case-insensitively.
-0353 % Bare comment lines inside {} act as row separators in older MATLAB, so
-0354 % all comments are placed after ... on data lines instead.
-0355     lst = { ...
-0356         'ATP','ADP','AMP','dATP','dADP','dAMP', ... % adenosine phosphates
-0357         'atp','adp','amp','datp','dadp','damp', ...
-0358         'GTP','GDP','GMP','dGTP','dGDP','dGMP', ... % other NTPs/NDPs/NMPs
-0359         'CTP','CDP','CMP','dCTP','dCDP','dCMP', ...
-0360         'UTP','UDP','UMP','dUTP','dUDP','dUMP', ...
-0361         'TTP','TDP','TMP','dTTP','dTDP','dTMP', ...
-0362         'gtp','gdp','gmp','dgtp','dgdp','dgmp', ...
-0363         'ctp','cdp','cmp','dctp','dcdp','dcmp', ...
-0364         'utp','udp','ump','dutp','dudp','dump', ...
-0365         'ttp','tdp','tmp','dttp','dtdp','dtmp', ...
-0366         'NAD','NADH','NADP','NADPH','NAD+','NADP+', ... % nicotinamide cofactors
-0367         'nad','nadh','nadp','nadph', ...
-0368         'FAD','FADH2','FMN','FMNH2', ... % flavin cofactors
-0369         'fad','fadh2','fmn','fmnh2', ...
-0370         'CoA','coenzyme A','coenzyme a','Coenzyme A', ... % coenzyme A
-0371         'coa', ...
-0372         'H2O','water','H+','proton','OH-','hydroxide', ... % water / proton
-0373         'h2o','h','oh1', ...
-0374         'phosphate','orthophosphate','pyrophosphate','diphosphate', ... % inorganic phosphate
-0375         'pi','ppi','pp', ...
-0376         'CO2','carbon dioxide','bicarbonate','HCO3-', ... % carbon dioxide
-0377         'co2','hco3', ...
-0378         'O2','oxygen','NH3','ammonia','NH4+','ammonium','nitrogen', ... % oxygen / nitrogen
-0379         'o2','nh3','nh4', ...
-0380         'sulfate','sulfite','sulfide','thiosulfate', ... % sulfur species
-0381         'thioredoxin','thioredoxin-SH','thioredoxin-S2', ... % redox carriers
-0382         'trdrd','trdox', ...
-0383         'ferredoxin','ferredoxin reduced','ferredoxin oxidized', ...
-0384         'fdred','fdox', ...
-0385         'ubiquinol','ubiquinone','menaquinol','menaquinone', ...
-0386         'q8h2','q8','mqn8','mql8', ...
-0387         'lipoamide','dihydrolipoamide', ...
-0388         'S-adenosyl-L-methionine','S-adenosylhomocysteine', ... % one-carbon donors
-0389         'sam','sah','amet','ahcys', ...
-0390         '5-methyltetrahydrofolate','tetrahydrofolate','dihydrofolate', ...
-0391         'thf','dhf','mlthf','methf' ...
-0392     };
-0393 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/analysis/walkFluxes.html b/doc/analysis/walkFluxes.html deleted file mode 100644 index 7ee884e0..00000000 --- a/doc/analysis/walkFluxes.html +++ /dev/null @@ -1,286 +0,0 @@ - - - - Description of walkFluxes - - - - - - - - - -
Home > analysis > walkFluxes.m
- - - -

walkFluxes -

- -

PURPOSE ^

-
walkFluxes Interactively navigate a flux distribution reaction by reaction.
- -

SYNOPSIS ^

-
function walkFluxes(model, fluxes, startRxn, varargin)
- -

DESCRIPTION ^

-
 walkFluxes  Interactively navigate a flux distribution reaction by reaction.
-
- Starting from a chosen reaction, displays all flux-carrying reactions
- connected through shared metabolites, grouped by metabolite and labelled
- with each neighbour's role (produces / consumes). Select a neighbour by
- number to step to it; build a history and rewind with 'b'.
-
- Parameters
- ----------
- model : struct
-     RAVEN model structure.
- fluxes : double
-     flux vector (length = numel(model.rxns)).
- startRxn : char or double
-     starting reaction ID (string) or column index.
-
- Name-Value Arguments
- --------------------
- cutoff : double
-     minimum |flux| to display a reaction as a neighbour (default 1e-8).
- maxPerMet : double
-     maximum neighbour reactions displayed per metabolite (default 8).
-
- Examples
- --------
-     sol = solveLP(model);
-     walkFluxes(model, sol.x, 'BIOMASS_Ec_iJO1366_core_53p95M')
-     walkFluxes(model, sol.x, 'PFK', 'cutoff', 1e-6)
-
- Notes
- -----
- Navigation commands:
-   <number>  — step to that neighbour reaction
-   b         — go back to the previous reaction
-   q         — quit
-
- This function is interactive and reads from the terminal at every step;
- it errors immediately under -batch, in deployed code, or otherwise without
- a real terminal, rather than blocking forever waiting for input that will
- never arrive.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function walkFluxes(model, fluxes, startRxn, varargin)
-0002 % walkFluxes  Interactively navigate a flux distribution reaction by reaction.
-0003 %
-0004 % Starting from a chosen reaction, displays all flux-carrying reactions
-0005 % connected through shared metabolites, grouped by metabolite and labelled
-0006 % with each neighbour's role (produces / consumes). Select a neighbour by
-0007 % number to step to it; build a history and rewind with 'b'.
-0008 %
-0009 % Parameters
-0010 % ----------
-0011 % model : struct
-0012 %     RAVEN model structure.
-0013 % fluxes : double
-0014 %     flux vector (length = numel(model.rxns)).
-0015 % startRxn : char or double
-0016 %     starting reaction ID (string) or column index.
-0017 %
-0018 % Name-Value Arguments
-0019 % --------------------
-0020 % cutoff : double
-0021 %     minimum |flux| to display a reaction as a neighbour (default 1e-8).
-0022 % maxPerMet : double
-0023 %     maximum neighbour reactions displayed per metabolite (default 8).
-0024 %
-0025 % Examples
-0026 % --------
-0027 %     sol = solveLP(model);
-0028 %     walkFluxes(model, sol.x, 'BIOMASS_Ec_iJO1366_core_53p95M')
-0029 %     walkFluxes(model, sol.x, 'PFK', 'cutoff', 1e-6)
-0030 %
-0031 % Notes
-0032 % -----
-0033 % Navigation commands:
-0034 %   <number>  — step to that neighbour reaction
-0035 %   b         — go back to the previous reaction
-0036 %   q         — quit
-0037 %
-0038 % This function is interactive and reads from the terminal at every step;
-0039 % it errors immediately under -batch, in deployed code, or otherwise without
-0040 % a real terminal, rather than blocking forever waiting for input that will
-0041 % never arrive.
-0042 
-0043 isBatch = false;
-0044 try
-0045     isBatch = batchStartupOptionUsed;
-0046 catch
-0047 end
-0048 if isBatch || isdeployed
-0049     error('walkFluxes:nonInteractive', ...
-0050         ['walkFluxes is interactive and reads from the terminal at every step, ' ...
-0051          'so it cannot run under -batch or in deployed code. Run it in an ' ...
-0052          'interactive MATLAB session instead.']);
-0053 end
-0054 
-0055 p = parseRAVENargs(varargin, {'cutoff',1e-8; 'maxPerMet',8});
-0056 cutoff    = p.cutoff;
-0057 maxPerMet = p.maxPerMet;
-0058 
-0059 rxnIdx  = resolveRxn_(model, startRxn);
-0060 history = zeros(0,1);
-0061 W       = 68;
-0062 
-0063 while true
-0064     % ---- Header ----
-0065     fprintf('\n%s\n', repmat('=', 1, W));
-0066     f   = fluxes(rxnIdx);
-0067     rID = model.rxns{rxnIdx};
-0068     rName = getRxnName_(model, rxnIdx);
-0069     if isempty(rName)
-0070         fprintf('  [%s]  flux: %+.6g\n', rID, f);
-0071     else
-0072         fprintf('  [%s]  %s\n  flux: %+.6g\n', rID, rName, f);
-0073     end
-0074     eqn = constructEquations(model, model.rxns(rxnIdx));
-0075     fprintf('  %s\n', eqn{1});
-0076     if isfield(model,'grRules') && rxnIdx <= numel(model.grRules) ...
-0077             && ~isempty(model.grRules{rxnIdx})
-0078         fprintf('  genes: %s\n', model.grRules{rxnIdx});
-0079     end
-0080     fprintf('%s\n', repmat('=', 1, W));
-0081 
-0082     % ---- Collect neighbours grouped by metabolite ----
-0083     col         = full(model.S(:, rxnIdx));
-0084     nonzeroMets = find(abs(col) > 0);
-0085 
-0086     % One number per unique reaction across all metabolite groups
-0087     seenRxns   = containers.Map('KeyType','double','ValueType','double');
-0088     allNeighbors = [];
-0089 
-0090     for mi = 1:numel(nonzeroMets)
-0091         m    = nonzeroMets(mi);
-0092         netF = col(m) * f;   % >0: current rxn produces m; <0: consumes m
-0093 
-0094         if abs(netF) < cutoff, continue; end
-0095 
-0096         if netF < 0
-0097             role = 'consumed';
-0098         else
-0099             role = 'produced';
-0100         end
-0101 
-0102         % Other flux-carrying reactions involving this metabolite
-0103         row   = full(model.S(m, :));
-0104         carry = find(abs(row .* fluxes') > cutoff);
-0105         carry = carry(carry ~= rxnIdx);
-0106         if isempty(carry), continue; end
-0107 
-0108         [~, si] = sort(abs(fluxes(carry)), 'descend');
-0109         carry   = carry(si(1:min(end, maxPerMet)));
-0110 
-0111         mLbl = getMetLabel_(model, m);
-0112         fprintf('\n  %s  [%s, %.4g]\n', mLbl, role, abs(netF));
-0113 
-0114         for ci = 1:numel(carry)
-0115             nr         = carry(ci);
-0116             neighNetF  = row(nr) * fluxes(nr);  % >0: nr produces m; <0: consumes m
-0117             neighRole  = 'produces';
-0118             if neighNetF < 0, neighRole = 'consumes'; end
-0119 
-0120             nrName = getRxnName_(model, nr);
-0121             if numel(nrName) > 28, nrName = [nrName(1:25) '...']; end
-0122 
-0123             if isKey(seenRxns, nr)
-0124                 n = seenRxns(nr);   % re-use existing number; don't re-add
-0125             else
-0126                 n = numel(allNeighbors) + 1;
-0127                 seenRxns(nr)     = n;
-0128                 allNeighbors(end+1) = nr; %#ok<AGROW>
-0129             end
-0130 
-0131             fprintf('  %3d. %-14s  %+9.4g  %-8s  %s\n', ...
-0132                 n, model.rxns{nr}, fluxes(nr), neighRole, nrName);
-0133         end
-0134     end
-0135 
-0136     fprintf('\n');
-0137     nN = numel(allNeighbors);
-0138 
-0139     if nN == 0
-0140         fprintf('  (no flux-carrying neighbours at this cutoff)\n');
-0141         fprintf('  b: go back   q: quit\n');
-0142         resp = strtrim(input('  > ', 's'));
-0143     else
-0144         fprintf('  1-%d: step to reaction   b: go back   q: quit\n', nN);
-0145         resp = strtrim(input('  > ', 's'));
-0146     end
-0147 
-0148     if strcmpi(resp, 'q')
-0149         fprintf('Navigator closed.\n');
-0150         return
-0151 
-0152     elseif strcmpi(resp, 'b')
-0153         if isempty(history)
-0154             fprintf('  (no history)\n');
-0155         else
-0156             rxnIdx     = history(end);
-0157             history(end) = [];
-0158         end
-0159 
-0160     else
-0161         n = str2double(resp);
-0162         if ~isnan(n) && n >= 1 && n <= nN && n == round(n)
-0163             history(end+1) = rxnIdx; %#ok<AGROW>
-0164             rxnIdx         = allNeighbors(n);
-0165         else
-0166             fprintf('  Enter a number 1-%d, ''b'', or ''q''.\n', max(nN,1));
-0167         end
-0168     end
-0169 end
-0170 end
-0171 
-0172 %--------------------------------------------------------------------------
-0173 function idx = resolveRxn_(model, rxn)
-0174     if isnumeric(rxn)
-0175         idx = rxn;
-0176     else
-0177         idx = find(strcmp(model.rxns, char(rxn)));
-0178         if isempty(idx)
-0179             error('Reaction ''%s'' not found in model.', rxn);
-0180         end
-0181         idx = idx(1);
-0182     end
-0183 end
-0184 
-0185 function s = getRxnName_(model, rxnIdx)
-0186     s = '';
-0187     if isfield(model,'rxnNames') && rxnIdx <= numel(model.rxnNames)
-0188         s = char(model.rxnNames{rxnIdx});
-0189     end
-0190 end
-0191 
-0192 function s = getMetLabel_(model, metIdx)
-0193     if isfield(model,'metNames') && ~isempty(model.metNames{metIdx})
-0194         s = model.metNames{metIdx};
-0195     else
-0196         s = model.mets{metIdx};
-0197     end
-0198 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/annotation/assignSBOterms.html b/doc/annotation/assignSBOterms.html deleted file mode 100644 index bac884a1..00000000 --- a/doc/annotation/assignSBOterms.html +++ /dev/null @@ -1,281 +0,0 @@ - - - - Description of assignSBOterms - - - - - - - - - -
Home > annotation > assignSBOterms.m
- - - -

assignSBOterms -

- -

PURPOSE ^

-
assignSBOterms Assign SBO terms to metabolites and reactions.
- -

SYNOPSIS ^

-
function model = assignSBOterms(model, varargin)
- -

DESCRIPTION ^

-
 assignSBOterms  Assign SBO terms to metabolites and reactions.
-
- Assign SBO terms to metabolites and reactions following a generic rule
- set. The MATLAB counterpart of yeast-GEM's legacy addSBOterms;
- organism-agnostic, parameterised entirely by opts.
-
- SBO is written via editMiriam(..., 'fill') so pre-existing SBO
- annotations are preserved.
-
- Parameters
- ----------
- model : struct
-     RAVEN model struct.
-
- Name-Value Arguments
- --------------------
- opts : struct
-     Struct with any of the following fields. Missing fields take the
-     defaults shown:
-
-     - biomassMetNames : {'biomass','DNA','RNA','protein','carbohydrate',
-       'lipid','cofactor','ion'}
-     - biomassMetSuffixes : {' backbone',' chain'}
-     - biomassRxnName : 'biomass pseudoreaction'
-     - ngamRxnName : 'non-growth associated maintenance reaction'
-     - pseudoreactionSubstrings : {'pseudoreaction','SLIME rxn'}
-     - onlyLastReactionForPseudo : false. yeast-GEM bug-compat flag that
-       replicates the legacy `for i=numel(...)` typo (pseudoreaction SBOs
-       applied only to the last reaction). Off by default; turn ON for
-       byte-equivalent yeast-GEM output.
-
- Returns
- -------
- model : struct
-     Modified model.
-
- Examples
- --------
-     model = assignSBOterms(model);
-     model = assignSBOterms(model, struct('onlyLastReactionForPseudo', true));
-
- Notes
- -----
- Metabolites:
-
-     SBO:0000649 (Biomass) when met.name is in opts.biomassMetNames, or
-     ends with any of opts.biomassMetSuffixes. Otherwise SBO:0000247
-     (Simple chemical).
-
- Reactions (default → override → pseudoreaction override):
-
-     SBO:0000176 (Metabolic reaction) default.
-     Single-reactant reactions become:
-         SBO:0000627 (exchange) if the lone metabolite is extracellular
-             (compartment 'e' or compartment name containing
-             'extracellular'),
-         SBO:0000632 (sink) if coef < 0,
-         SBO:0000628 (demand) otherwise.
-     Transport reactions (detected by opts.transportDetector or the
-         default heuristic: same metName in ≥ 2 compartments in a single
-         reaction) → SBO:0000655.
-     Reactions whose name matches opts.biomassRxnName → SBO:0000629.
-     Reactions whose name matches opts.ngamRxnName → SBO:0000630.
-     Reactions whose name contains any of opts.pseudoreactionSubstrings
-         → SBO:0000395.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • editMiriam editMiriam Change MIRIAM annotation fields, one type at a time.
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function model = assignSBOterms(model, varargin)
-0002 % assignSBOterms  Assign SBO terms to metabolites and reactions.
-0003 %
-0004 % Assign SBO terms to metabolites and reactions following a generic rule
-0005 % set. The MATLAB counterpart of yeast-GEM's legacy addSBOterms;
-0006 % organism-agnostic, parameterised entirely by opts.
-0007 %
-0008 % SBO is written via editMiriam(..., 'fill') so pre-existing SBO
-0009 % annotations are preserved.
-0010 %
-0011 % Parameters
-0012 % ----------
-0013 % model : struct
-0014 %     RAVEN model struct.
-0015 %
-0016 % Name-Value Arguments
-0017 % --------------------
-0018 % opts : struct
-0019 %     Struct with any of the following fields. Missing fields take the
-0020 %     defaults shown:
-0021 %
-0022 %     - biomassMetNames : {'biomass','DNA','RNA','protein','carbohydrate',
-0023 %       'lipid','cofactor','ion'}
-0024 %     - biomassMetSuffixes : {' backbone',' chain'}
-0025 %     - biomassRxnName : 'biomass pseudoreaction'
-0026 %     - ngamRxnName : 'non-growth associated maintenance reaction'
-0027 %     - pseudoreactionSubstrings : {'pseudoreaction','SLIME rxn'}
-0028 %     - onlyLastReactionForPseudo : false. yeast-GEM bug-compat flag that
-0029 %       replicates the legacy `for i=numel(...)` typo (pseudoreaction SBOs
-0030 %       applied only to the last reaction). Off by default; turn ON for
-0031 %       byte-equivalent yeast-GEM output.
-0032 %
-0033 % Returns
-0034 % -------
-0035 % model : struct
-0036 %     Modified model.
-0037 %
-0038 % Examples
-0039 % --------
-0040 %     model = assignSBOterms(model);
-0041 %     model = assignSBOterms(model, struct('onlyLastReactionForPseudo', true));
-0042 %
-0043 % Notes
-0044 % -----
-0045 % Metabolites:
-0046 %
-0047 %     SBO:0000649 (Biomass) when met.name is in opts.biomassMetNames, or
-0048 %     ends with any of opts.biomassMetSuffixes. Otherwise SBO:0000247
-0049 %     (Simple chemical).
-0050 %
-0051 % Reactions (default → override → pseudoreaction override):
-0052 %
-0053 %     SBO:0000176 (Metabolic reaction) default.
-0054 %     Single-reactant reactions become:
-0055 %         SBO:0000627 (exchange) if the lone metabolite is extracellular
-0056 %             (compartment 'e' or compartment name containing
-0057 %             'extracellular'),
-0058 %         SBO:0000632 (sink) if coef < 0,
-0059 %         SBO:0000628 (demand) otherwise.
-0060 %     Transport reactions (detected by opts.transportDetector or the
-0061 %         default heuristic: same metName in ≥ 2 compartments in a single
-0062 %         reaction) → SBO:0000655.
-0063 %     Reactions whose name matches opts.biomassRxnName → SBO:0000629.
-0064 %     Reactions whose name matches opts.ngamRxnName → SBO:0000630.
-0065 %     Reactions whose name contains any of opts.pseudoreactionSubstrings
-0066 %         → SBO:0000395.
-0067 
-0068 p=parseRAVENargs(varargin, {'opts',[]});
-0069 opts=p.opts;
-0070 if isempty(opts)
-0071     opts = struct();
-0072 end
-0073 opts = applyDefaults(opts);
-0074 
-0075 % Metabolite SBO ------------------------------------------------------
-0076 metsSBO = cell(size(model.mets));
-0077 for i = 1:length(model.mets)
-0078     metName = model.metNames{i};
-0079     if any(strcmp(opts.biomassMetNames, metName)) || endsWithAny(metName, opts.biomassMetSuffixes)
-0080         metsSBO{i} = 'SBO:0000649';
-0081     else
-0082         metsSBO{i} = 'SBO:0000247';
-0083     end
-0084 end
-0085 
-0086 % Reaction SBO --------------------------------------------------------
-0087 rxnSBO = cell(size(model.rxns));
-0088 rxnSBO(:) = {'SBO:0000176'};
-0089 
-0090 % Single-reactant reactions
-0091 reactantNumber = sum(model.S ~= 0, 1);
-0092 singleRxns = find(reactantNumber == 1);
-0093 for k = 1:numel(singleRxns)
-0094     idx = singleRxns(k);
-0095     metRow = find(model.S(:, idx));
-0096     compName = model.compNames{model.metComps(metRow)};
-0097     compShort = model.comps{model.metComps(metRow)};
-0098     if strcmp(compShort, 'e') || strcmp(compName, 'extracellular')
-0099         rxnSBO{idx} = 'SBO:0000627';
-0100     elseif sum(model.S(:, idx)) < 0
-0101         rxnSBO{idx} = 'SBO:0000632';
-0102     else
-0103         rxnSBO{idx} = 'SBO:0000628';
-0104     end
-0105 end
-0106 
-0107 % Transport reactions
-0108 if isfield(opts, 'transportRxnIdxs') && ~isempty(opts.transportRxnIdxs)
-0109     transportIdxs = opts.transportRxnIdxs;
-0110 else
-0111     transportIdxs = getTransportRxns(model);
-0112 end
-0113 rxnSBO(transportIdxs) = {'SBO:0000655'};
-0114 
-0115 % Pseudoreaction overrides
-0116 if opts.onlyLastReactionForPseudo
-0117     pseudoTargets = numel(model.rxns);
-0118 else
-0119     pseudoTargets = 1:numel(model.rxns);
-0120 end
-0121 for ii = pseudoTargets
-0122     name = model.rxnNames{ii};
-0123     if strcmp(name, opts.biomassRxnName)
-0124         rxnSBO{ii} = 'SBO:0000629';
-0125     elseif strcmp(name, opts.ngamRxnName)
-0126         rxnSBO{ii} = 'SBO:0000630';
-0127     else
-0128         for k = 1:numel(opts.pseudoreactionSubstrings)
-0129             if contains(name, opts.pseudoreactionSubstrings{k})
-0130                 rxnSBO{ii} = 'SBO:0000395';
-0131                 break;
-0132             end
-0133         end
-0134     end
-0135 end
-0136 
-0137 model = editMiriam(model, 'met', 'all', 'sbo', metsSBO, 'fill');
-0138 model = editMiriam(model, 'rxn', 'all', 'sbo', rxnSBO, 'fill');
-0139 end
-0140 
-0141 
-0142 function opts = applyDefaults(opts)
-0143 defaults = struct( ...
-0144     'biomassMetNames', {{'biomass','DNA','RNA','protein','carbohydrate','lipid','cofactor','ion'}}, ...
-0145     'biomassMetSuffixes', {{' backbone',' chain'}}, ...
-0146     'biomassRxnName', 'biomass pseudoreaction', ...
-0147     'ngamRxnName', 'non-growth associated maintenance reaction', ...
-0148     'pseudoreactionSubstrings', {{'pseudoreaction','SLIME rxn'}}, ...
-0149     'onlyLastReactionForPseudo', false);
-0150 fields = fieldnames(defaults);
-0151 for k = 1:numel(fields)
-0152     f = fields{k};
-0153     if ~isfield(opts, f) || isempty(opts.(f))
-0154         opts.(f) = defaults.(f);
-0155     end
-0156 end
-0157 end
-0158 
-0159 
-0160 function tf = endsWithAny(s, suffixes)
-0161 tf = false;
-0162 for i = 1:numel(suffixes)
-0163     if endsWith(s, suffixes{i})
-0164         tf = true;
-0165         return;
-0166     end
-0167 end
-0168 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/annotation/editMiriam.html b/doc/annotation/editMiriam.html deleted file mode 100644 index 5a11c397..00000000 --- a/doc/annotation/editMiriam.html +++ /dev/null @@ -1,227 +0,0 @@ - - - - Description of editMiriam - - - - - - - - - -
Home > annotation > editMiriam.m
- - - -

editMiriam -

- -

PURPOSE ^

-
editMiriam Change MIRIAM annotation fields, one type at a time.
- -

SYNOPSIS ^

-
function model=editMiriam(model,type,object,miriamName,miriams,keep)
- -

DESCRIPTION ^

-
 editMiriam  Change MIRIAM annotation fields, one type at a time.
-
- Parameters
- ----------
- model : struct
-     model structure.
- type : char
-     'met', 'rxn', 'gene' or 'comp' dependent on which objects the
-     annotations should be assigned to.
- object : cell or logical or double or char
-     either a cell array of IDs, a logical vector with the same number of
-     elements as the type (see above) in the model, a vector of indexes,
-     or 'all'.
- miriamName : char
-     string specifying the namespace of the identifier, for instance
-     'bigg.metabolite'. Should be a valid prefix from identifiers.org
-     (e.g. https://registry.identifiers.org/registry/bigg.metabolite).
- miriams : char or cell
-     string or cell array of strings with annotation identifiers, e.g.
-     '12dgr161'.
- keep : char
-     one of the following strings, specifying what should be done if an
-     object already has existing MIRIAM annotations with the same
-     miriamName:
-
-     - 'replace' : discard all existing annotations, all will be
-       overwritten, even if the new annotation is an empty field. Should
-       only be used if you do not want to keep any of the old annotation
-       with the same miriamName.
-     - 'fill' : only add annotations to those objects that did not yet
-       have an annotation with that miriamName. Otherwise, the existing
-       annotation is kept, even if it is different from the suggested new
-       annotation.
-     - 'add' : keep all existing annotations, and add any new
-       annotations, after removing duplicates.
-     - 'remove' : remove all annotations with the given miriamName from
-       the specified objects. The miriams argument is ignored.
-
- Returns
- -------
- model : struct
-     model structure with updated MIRIAM annotation field.
-
- Examples
- --------
-     model = editMiriam(model, type, object, miriamName, miriams, keep);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • extractMiriam extractMiriam Unpack MIRIAM annotations into a human-readable table.
-This function is called by: -
    -
  • assignSBOterms assignSBOterms Assign SBO terms to metabolites and reactions.
- - - - -

SOURCE CODE ^

-
0001 function model=editMiriam(model,type,object,miriamName,miriams,keep)
-0002 % editMiriam  Change MIRIAM annotation fields, one type at a time.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % model : struct
-0007 %     model structure.
-0008 % type : char
-0009 %     'met', 'rxn', 'gene' or 'comp' dependent on which objects the
-0010 %     annotations should be assigned to.
-0011 % object : cell or logical or double or char
-0012 %     either a cell array of IDs, a logical vector with the same number of
-0013 %     elements as the type (see above) in the model, a vector of indexes,
-0014 %     or 'all'.
-0015 % miriamName : char
-0016 %     string specifying the namespace of the identifier, for instance
-0017 %     'bigg.metabolite'. Should be a valid prefix from identifiers.org
-0018 %     (e.g. https://registry.identifiers.org/registry/bigg.metabolite).
-0019 % miriams : char or cell
-0020 %     string or cell array of strings with annotation identifiers, e.g.
-0021 %     '12dgr161'.
-0022 % keep : char
-0023 %     one of the following strings, specifying what should be done if an
-0024 %     object already has existing MIRIAM annotations with the same
-0025 %     miriamName:
-0026 %
-0027 %     - 'replace' : discard all existing annotations, all will be
-0028 %       overwritten, even if the new annotation is an empty field. Should
-0029 %       only be used if you do not want to keep any of the old annotation
-0030 %       with the same miriamName.
-0031 %     - 'fill' : only add annotations to those objects that did not yet
-0032 %       have an annotation with that miriamName. Otherwise, the existing
-0033 %       annotation is kept, even if it is different from the suggested new
-0034 %       annotation.
-0035 %     - 'add' : keep all existing annotations, and add any new
-0036 %       annotations, after removing duplicates.
-0037 %     - 'remove' : remove all annotations with the given miriamName from
-0038 %       the specified objects. The miriams argument is ignored.
-0039 %
-0040 % Returns
-0041 % -------
-0042 % model : struct
-0043 %     model structure with updated MIRIAM annotation field.
-0044 %
-0045 % Examples
-0046 % --------
-0047 %     model = editMiriam(model, type, object, miriamName, miriams, keep);
-0048 miriamName=char(miriamName);
-0049 miriams=convertCharArray(miriams);
-0050 
-0051 %Check 'keep' input
-0052 keep=char(keep);
-0053 if ~any(strcmp(keep,{'replace','fill','add','remove'}))
-0054     error('Invalid ''keep'', should be ''replace'',''fill'',''add'',''remove''.')
-0055 end
-0056 %Check 'type' input
-0057 type=char(type);
-0058 if ~any(strcmp(type,{'met','gene','rxn','comp'}))
-0059     error('Invalid ''type'', should be ''met'', ''gene'', ''rxn'' or ''comp''.')
-0060 end
-0061 %Check 'object' input
-0062 if islogical(object)
-0063     idxInModel=find(object);
-0064 elseif isnumeric(object)
-0065     idxInModel=object;
-0066 else
-0067     object=convertCharArray(object);
-0068     if numel(object)==1 && strcmp(object{1},'all')
-0069         idxInModel=1:numel(model.([type,'s']));
-0070     else
-0071         idxInModel=getIndexes(model,object,[type,'s']);
-0072         if ~all(idxInModel)
-0073             error('RAVEN:badInput', '%s', ravenList('The following objects cannot be found in the model: ', object(~idxInModel)))
-0074         end
-0075     end
-0076 end
-0077 %Check 'miriams' input (skip for 'remove' — miriams argument is not used)
-0078 if ~strcmp(keep,'remove')
-0079     if numel(miriams)==1 && numel(idxInModel)~=1
-0080         miriams=repmat(miriams,numel(idxInModel),1);
-0081     elseif numel(miriams)~=numel(idxInModel)
-0082         error('The number of annotations does not match the number of objects.')
-0083     end
-0084 end
-0085 
-0086 miriamFieldName=[type,'Miriams'];
-0087 
-0088 if isfield(model,miriamFieldName)
-0089     [extractedMiriams,extractedMiriamNames]=extractMiriam(model.(miriamFieldName)(idxInModel),'all');
-0090 else
-0091     extractedMiriams=cell(numel(idxInModel),1);
-0092     extractedMiriamNames={miriamName};
-0093 end
-0094 
-0095 [~, midx]=ismember(miriamName,extractedMiriamNames);
-0096 if strcmp(keep,'remove')
-0097     if midx>0
-0098         extractedMiriams(:,midx)={''};
-0099     end
-0100     % Nothing to do when midx==0: miriamName not present
-0101 elseif midx==0
-0102     midx=numel(extractedMiriamNames)+1;
-0103     extractedMiriamNames(end+1)={miriamName};
-0104     extractedMiriams(:,end+1)=miriams;
-0105 elseif strcmp(keep,'replace')
-0106     extractedMiriams(:,midx)=miriams;
-0107 else
-0108     noMiriam=cellfun(@isempty,extractedMiriams(:,midx));
-0109     extractedMiriams(noMiriam,midx)=miriams(noMiriam);
-0110     if strcmp(keep,'add') % Skipped when only filling empty entries
-0111         existingMiriams=[split(extractedMiriams(~noMiriam,midx),'; '), miriams(~noMiriam)];
-0112         uniqueMiriams=cell(size(existingMiriams,1),1);
-0113         for i=1:size(existingMiriams,1)
-0114             uniqueMiriams{i,1}=strjoin(unique(existingMiriams(i,:)), {'; '});
-0115         end
-0116         extractedMiriams(~noMiriam,midx)=uniqueMiriams;
-0117     end
-0118 end
-0119 
-0120 % Make Miriam field again
-0121 for i=1:numel(idxInModel)
-0122     miriam.name=cell(1,1);
-0123     miriam.value=cell(1,1);
-0124     for j=1:numel(extractedMiriamNames)
-0125         if ~isempty(extractedMiriams{i,j})
-0126             values=strsplit(extractedMiriams{i,j},'; ');
-0127             miriam.name(end+1:end+numel(values),1)=extractedMiriamNames(j);
-0128             miriam.value(end+1:end+numel(values),1)=values;
-0129         end
-0130     end
-0131     miriam.name(1)=[];
-0132     miriam.value(1)=[];
-0133     model.(miriamFieldName){idxInModel(i),1}=miriam;
-0134 end
-0135 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/annotation/extractMiriam.html b/doc/annotation/extractMiriam.html deleted file mode 100644 index 12586d45..00000000 --- a/doc/annotation/extractMiriam.html +++ /dev/null @@ -1,174 +0,0 @@ - - - - Description of extractMiriam - - - - - - - - - -
Home > annotation > extractMiriam.m
- - - -

extractMiriam -

- -

PURPOSE ^

-
extractMiriam Unpack MIRIAM annotations into a human-readable table.
- -

SYNOPSIS ^

-
function [miriams,extractedMiriamNames]=extractMiriam(modelMiriams,varargin)
- -

DESCRIPTION ^

-
 extractMiriam  Unpack MIRIAM annotations into a human-readable table.
-
- Unpacks metMiriams, rxnMiriams, geneMiriams or compMiriams into a
- human-readable cell array, formatted as in Excel, with the columns split
- so each miriam name gets its own column.
-
- Parameters
- ----------
- modelMiriams : cell
-     a miriam structure (e.g. model.metMiriams) for one or multiple
-     metabolites.
-
- Name-Value Arguments
- --------------------
- miriamNames : cell or char
-     cell array with miriam names to be extracted (default 'all', meaning
-     that annotation for all miriam names found in modelMiriams will be
-     extracted).
-
- Returns
- -------
- miriams : cell
-     a cell array with extracted miriams. If several miriam names are
-     requested, the corresponding information is saved in different
-     columns. If there are several ids available for the same entity
-     (metabolite, gene, reaction or compartment), they are concatenated
-     into one column. The total number of columns represents the number
-     of unique miriam names per entity.
- extractedMiriamNames : cell
-     cell array with extracted miriam names.
-
- Examples
- --------
-     [miriams, extractedMiriamNames] = extractMiriam(modelMiriams, miriamNames);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • editMiriam editMiriam Change MIRIAM annotation fields, one type at a time.
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [miriams,extractedMiriamNames]=extractMiriam(modelMiriams,varargin)
-0002 % extractMiriam  Unpack MIRIAM annotations into a human-readable table.
-0003 %
-0004 % Unpacks metMiriams, rxnMiriams, geneMiriams or compMiriams into a
-0005 % human-readable cell array, formatted as in Excel, with the columns split
-0006 % so each miriam name gets its own column.
-0007 %
-0008 % Parameters
-0009 % ----------
-0010 % modelMiriams : cell
-0011 %     a miriam structure (e.g. model.metMiriams) for one or multiple
-0012 %     metabolites.
-0013 %
-0014 % Name-Value Arguments
-0015 % --------------------
-0016 % miriamNames : cell or char
-0017 %     cell array with miriam names to be extracted (default 'all', meaning
-0018 %     that annotation for all miriam names found in modelMiriams will be
-0019 %     extracted).
-0020 %
-0021 % Returns
-0022 % -------
-0023 % miriams : cell
-0024 %     a cell array with extracted miriams. If several miriam names are
-0025 %     requested, the corresponding information is saved in different
-0026 %     columns. If there are several ids available for the same entity
-0027 %     (metabolite, gene, reaction or compartment), they are concatenated
-0028 %     into one column. The total number of columns represents the number
-0029 %     of unique miriam names per entity.
-0030 % extractedMiriamNames : cell
-0031 %     cell array with extracted miriam names.
-0032 %
-0033 % Examples
-0034 % --------
-0035 %     [miriams, extractedMiriamNames] = extractMiriam(modelMiriams, miriamNames);
-0036 
-0037 p=parseRAVENargs(varargin, {'miriamNames',[]});
-0038 miriamNames=p.miriamNames;
-0039 if isempty(miriamNames) || (ischar(miriamNames) && strcmp(miriamNames,'all'))
-0040     extractAllTypes=true;
-0041 else
-0042     extractAllTypes=false;
-0043     miriamNames=convertCharArray(miriamNames);
-0044 end
-0045 
-0046 %The annotation for all miriam names should be extracted
-0047 if extractAllTypes
-0048     miriamNames={''};
-0049     for i=1:numel(modelMiriams)
-0050         if ~isempty(modelMiriams{i,1})
-0051             for j=1:numel(modelMiriams{i,1}.name)
-0052                 miriamNames{numel(miriamNames)+1,1}=modelMiriams{i,1}.name{j};
-0053             end
-0054         end
-0055     end
-0056     miriamNames=sort(unique(miriamNames));
-0057 end
-0058 
-0059 %Aggregate the final cell array table with extracted miriam information
-0060 miriams=cell(numel(modelMiriams),0);
-0061 for i=1:numel(miriamNames)
-0062     miriams=[miriams, extractMiriamType(modelMiriams,miriamNames{i})];
-0063 end
-0064 extractedMiriamNames=miriamNames;
-0065 
-0066 end
-0067 
-0068 function miriams=extractMiriamType(modelMiriams,miriamName)
-0069 %Create an empty cell array for ids vector
-0070 tempMiriams = cell([size(modelMiriams,1) 1]);
-0071 %Obtain the list of relevant miriam ids. Several entries may have
-0072 %several miriam ids, such ids are kept in additional columns
-0073 for i=1:numel(modelMiriams)
-0074     if (~isempty(modelMiriams{i,1})) && any(strcmp(modelMiriams{i,1}.name,miriamName))
-0075         for j=1:numel(modelMiriams{i,1}.name)
-0076             if strcmp(modelMiriams{i,1}.name(j),miriamName)
-0077                 tempMiriams(i,j) = modelMiriams{i,1}.value(j);
-0078             end
-0079         end
-0080     else
-0081         modelMiriams{i,1} = '';
-0082     end
-0083 end
-0084 
-0085 %Concatenate multiple ids per miriam name in one column with semicolon as
-0086 %separator
-0087 miriams = cell([size(tempMiriams,1) 1]);
-0088 notEmpty=~cellfun(@isempty,tempMiriams);
-0089 for i=1:size(miriams,1)
-0090     miriams{i}=strjoin(tempMiriams(i,notEmpty(i,:)),{'; '});
-0091 end
-0092 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/biomass/fitParameters.html b/doc/biomass/fitParameters.html deleted file mode 100644 index 62776151..00000000 --- a/doc/biomass/fitParameters.html +++ /dev/null @@ -1,249 +0,0 @@ - - - - Description of fitParameters - - - - - - - - - -
Home > biomass > fitParameters.m
- - - -

fitParameters -

- -

PURPOSE ^

-
fitParameters Fit stoichiometric parameters such as maintenance ATP to measured data.
- -

SYNOPSIS ^

-
function [parameters, fitnessScore, exitFlag, newModel]=fitParameters(model,xRxns,xValues,rxnsToFit,valuesToFit,parameterPositions,varargin)
- -

DESCRIPTION ^

-
 fitParameters  Fit stoichiometric parameters such as maintenance ATP to measured data.
-
- Searches for the parameter values that minimise the residual sum of squares
- between the fluxes the model predicts and the measured ones, using
- fminsearch (Nelder-Mead). Each candidate parameter set is written into the
- S matrix at the positions given by parameterPositions, and the model is
- solved once per data point.
-
- Use this to *derive* a value from measurements; use setGAM to *apply* a
- known growth-associated maintenance value to a biomass pseudoreaction.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- xRxns : cell
-     cell array with the IDs of the reactions that will be fixed for each
-     data point.
- xValues : double
-     matrix with the corresponding values for each xRxns (columns are
-     reactions).
- rxnsToFit : cell
-     cell array with the IDs of reactions that will be fitted to.
- valuesToFit : double
-     matrix with the corresponding values for each rxnsToFit (columns are
-     reactions).
- parameterPositions : struct
-     structure that determines where the parameters are in the
-     stoichiometric matrix, with fields:
-
-     - position : cell array of vectors where each element contains the
-       positions in the S-matrix for that parameter
-     - isNegative : cell array of vectors where the elements are true if
-       that position should be the negative of the fitted value (to
-       differentiate between production/consumption)
-
- Name-Value Arguments
- --------------------
- fitToRatio : logical
-     if the ratio of simulated to measured values should be fitted
-     instead of the absolute value. Used to prevent large fluxes from
-     having too large an impact (default true).
- initialGuess : double
-     initial guess of the parameters (default ones).
- plotFitting : logical
-     true if the resulting fitting should be plotted (default false).
-
- Returns
- -------
- parameters : double
-     fitted parameters in the same order as in parameterPositions.
- fitnessScore : double
-     the corresponding residual sum of squares.
- exitFlag : double
-     exit status returned by fminsearch.
- newModel : struct
-     updated model structure with the fitted parameters.
-
- Examples
- --------
-     [parameters, fitnessScore, exitFlag, newModel]=fitParameters(model,...
-         xRxns,xValues,rxnsToFit,valuesToFit,parameterPositions,fitToRatio,...
-         initialGuess,plotFitting);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [parameters, fitnessScore, exitFlag, newModel]=fitParameters(model,xRxns,xValues,rxnsToFit,valuesToFit,parameterPositions,varargin)
-0002 % fitParameters  Fit stoichiometric parameters such as maintenance ATP to measured data.
-0003 %
-0004 % Searches for the parameter values that minimise the residual sum of squares
-0005 % between the fluxes the model predicts and the measured ones, using
-0006 % fminsearch (Nelder-Mead). Each candidate parameter set is written into the
-0007 % S matrix at the positions given by parameterPositions, and the model is
-0008 % solved once per data point.
-0009 %
-0010 % Use this to *derive* a value from measurements; use setGAM to *apply* a
-0011 % known growth-associated maintenance value to a biomass pseudoreaction.
-0012 %
-0013 % Parameters
-0014 % ----------
-0015 % model : struct
-0016 %     a model structure.
-0017 % xRxns : cell
-0018 %     cell array with the IDs of the reactions that will be fixed for each
-0019 %     data point.
-0020 % xValues : double
-0021 %     matrix with the corresponding values for each xRxns (columns are
-0022 %     reactions).
-0023 % rxnsToFit : cell
-0024 %     cell array with the IDs of reactions that will be fitted to.
-0025 % valuesToFit : double
-0026 %     matrix with the corresponding values for each rxnsToFit (columns are
-0027 %     reactions).
-0028 % parameterPositions : struct
-0029 %     structure that determines where the parameters are in the
-0030 %     stoichiometric matrix, with fields:
-0031 %
-0032 %     - position : cell array of vectors where each element contains the
-0033 %       positions in the S-matrix for that parameter
-0034 %     - isNegative : cell array of vectors where the elements are true if
-0035 %       that position should be the negative of the fitted value (to
-0036 %       differentiate between production/consumption)
-0037 %
-0038 % Name-Value Arguments
-0039 % --------------------
-0040 % fitToRatio : logical
-0041 %     if the ratio of simulated to measured values should be fitted
-0042 %     instead of the absolute value. Used to prevent large fluxes from
-0043 %     having too large an impact (default true).
-0044 % initialGuess : double
-0045 %     initial guess of the parameters (default ones).
-0046 % plotFitting : logical
-0047 %     true if the resulting fitting should be plotted (default false).
-0048 %
-0049 % Returns
-0050 % -------
-0051 % parameters : double
-0052 %     fitted parameters in the same order as in parameterPositions.
-0053 % fitnessScore : double
-0054 %     the corresponding residual sum of squares.
-0055 % exitFlag : double
-0056 %     exit status returned by fminsearch.
-0057 % newModel : struct
-0058 %     updated model structure with the fitted parameters.
-0059 %
-0060 % Examples
-0061 % --------
-0062 %     [parameters, fitnessScore, exitFlag, newModel]=fitParameters(model,...
-0063 %         xRxns,xValues,rxnsToFit,valuesToFit,parameterPositions,fitToRatio,...
-0064 %         initialGuess,plotFitting);
-0065 
-0066 p=parseRAVENargs(varargin, {'fitToRatio',true; 'initialGuess',[]; 'plotFitting',false});
-0067 fitToRatio=p.fitToRatio;
-0068 initialGuess=p.initialGuess;
-0069 if isempty(initialGuess)
-0070     initialGuess=ones(numel(parameterPositions.position),1);
-0071 end
-0072 plotFitting=p.plotFitting;
-0073 
-0074 xRxns=convertCharArray(xRxns);
-0075 rxnsToFit=convertCharArray(rxnsToFit);
-0076 
-0077 %Find the indexes of reactions that will be fitted
-0078 [I, rxnsToFitIndexes]=ismember(rxnsToFit,model.rxns);
-0079 
-0080 if ~all(I)
-0081     EM='Could not find all reactions in rxnsToFit';
-0082     error('RAVEN:badInput', '%s', EM);
-0083 end
-0084 
-0085 %Find the indexes of reactions that will be used for constraints.
-0086 [I, xRxnsIndexes]=ismember(xRxns,model.rxns);
-0087 
-0088 if ~all(I)
-0089     EM='Could not find all reactions in xRxns';
-0090     error('RAVEN:badInput', '%s', EM);
-0091 end
-0092 
-0093 [parameters, fitnessScore, exitFlag]=fminsearch(@(parameters) getRSS(parameters,model,xRxnsIndexes,xValues,rxnsToFitIndexes,valuesToFit,parameterPositions,fitToRatio),initialGuess);
-0094 
-0095 parameters=abs(parameters);
-0096 
-0097 if plotFitting==true
-0098     %Set the resulting parameters
-0099     [~, resultingFluxes, newModel]=getRSS(parameters,model,xRxnsIndexes,xValues,rxnsToFitIndexes,valuesToFit,parameterPositions,true);
-0100     plot(xValues,valuesToFit,'o',xValues,resultingFluxes,'-*');
-0101 end
-0102 end
-0103 
-0104 function [rss, resultingFluxes, newModel]=getRSS(parameters,model,xRxnsIndexes,xValues,rxnsToFitIndexes,valuesToFit,parameterPositions,fitToRatio)
-0105 parameters=abs(parameters);
-0106 
-0107 %Set the parameters at the positions specified in parameterPositions
-0108 for i=1:numel(parameterPositions.position)
-0109     %Set positive
-0110     model.S(parameterPositions.position{i}(parameterPositions.isNegative{i}==false))=parameters(i);
-0111     
-0112     %Set negative
-0113     model.S(parameterPositions.position{i}(parameterPositions.isNegative{i}==true))=parameters(i)*-1;
-0114 end
-0115 
-0116 %Also return an updated model
-0117 newModel=model;
-0118 
-0119 %Loop through each data point, set xRxns to xValues and calculate the sum
-0120 %of squares for the rxnsToFit
-0121 rss=0;
-0122 resultingFluxes=[];
-0123 for i=1:size(xValues,1)
-0124     model.lb(xRxnsIndexes)=xValues(i,:);
-0125     model.ub(xRxnsIndexes)=xValues(i,:);
-0126     
-0127     sol=solveLP(model);
-0128     
-0129     %Calculate the rss
-0130     if fitToRatio==false
-0131         rs=sol.x(rxnsToFitIndexes)'-valuesToFit(i,:);
-0132     else
-0133         rs=sol.x(rxnsToFitIndexes)'./valuesToFit(i,:)-ones(1,size(valuesToFit,2));
-0134     end
-0135     rss=rss+rs*rs';
-0136     resultingFluxes=[resultingFluxes sol.x(rxnsToFitIndexes)];
-0137 end
-0138 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/biomass/getBiomassFractions.html b/doc/biomass/getBiomassFractions.html deleted file mode 100644 index 41926941..00000000 --- a/doc/biomass/getBiomassFractions.html +++ /dev/null @@ -1,224 +0,0 @@ - - - - Description of getBiomassFractions - - - - - - - - - -
Home > biomass > getBiomassFractions.m
- - - -

getBiomassFractions -

- -

PURPOSE ^

-
getBiomassFractions Compute mass fraction per biomass component.
- -

SYNOPSIS ^

-
function fractions = getBiomassFractions(model, biomassConfig)
- -

DESCRIPTION ^

-
 getBiomassFractions  Compute mass fraction per biomass component.
-
- Compute the mass fraction (g/gDW) per biomass component plus the total.
- The MATLAB counterpart of yeast-GEM's legacy sumBioMass.
-
- The biomassConfig struct describes the per-organism biomass layout — see
- Parameters below. Components whose pseudoreaction is missing from the
- model contribute 0.
-
- Parameters
- ----------
- model : struct
-     RAVEN model struct.
- biomassConfig : struct
-     Struct describing the biomass layout, with fields:
-
-     - biomass_rxn : rxn id of the top-level biomass pseudoreaction.
-     - proton_met : met id of cytosolic H+ (used only by
-       rescalePseudoreaction; may be unused here).
-     - components : cell array of component structs, each with fields:
-
-       - name : component name (e.g. 'protein').
-       - pseudoreaction_name : model.rxnNames entry to identify the
-         pseudoreaction.
-       - mass_strategy : 'mw' | 'mw_minus_2h' | 'mw_minus_water' |
-         'grams' — see Notes below.
-
- Returns
- -------
- fractions : struct
-     Struct keyed by component name plus 'total': fractions.protein,
-     fractions.RNA, ... etc. All values are in g/gDW.
-
- Examples
- --------
-     fractions = getBiomassFractions(model, biomassConfig);
-
- Notes
- -----
- mass_strategy values:
-
-     'mw'              MW from chemical formula
-     'mw_minus_2h'     MW − 2.016 g/mol (two protons released per charged
-                       tRNA — protein-pseudoreaction substrates)
-     'mw_minus_water'  MW − 18.015 g/mol (water released per
-                       polymerisation step — RNA / DNA)
-     'grams'           stoichiometry already in g/gDW (lipid backbone)
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function fractions = getBiomassFractions(model, biomassConfig)
-0002 % getBiomassFractions  Compute mass fraction per biomass component.
-0003 %
-0004 % Compute the mass fraction (g/gDW) per biomass component plus the total.
-0005 % The MATLAB counterpart of yeast-GEM's legacy sumBioMass.
-0006 %
-0007 % The biomassConfig struct describes the per-organism biomass layout — see
-0008 % Parameters below. Components whose pseudoreaction is missing from the
-0009 % model contribute 0.
-0010 %
-0011 % Parameters
-0012 % ----------
-0013 % model : struct
-0014 %     RAVEN model struct.
-0015 % biomassConfig : struct
-0016 %     Struct describing the biomass layout, with fields:
-0017 %
-0018 %     - biomass_rxn : rxn id of the top-level biomass pseudoreaction.
-0019 %     - proton_met : met id of cytosolic H+ (used only by
-0020 %       rescalePseudoreaction; may be unused here).
-0021 %     - components : cell array of component structs, each with fields:
-0022 %
-0023 %       - name : component name (e.g. 'protein').
-0024 %       - pseudoreaction_name : model.rxnNames entry to identify the
-0025 %         pseudoreaction.
-0026 %       - mass_strategy : 'mw' | 'mw_minus_2h' | 'mw_minus_water' |
-0027 %         'grams' — see Notes below.
-0028 %
-0029 % Returns
-0030 % -------
-0031 % fractions : struct
-0032 %     Struct keyed by component name plus 'total': fractions.protein,
-0033 %     fractions.RNA, ... etc. All values are in g/gDW.
-0034 %
-0035 % Examples
-0036 % --------
-0037 %     fractions = getBiomassFractions(model, biomassConfig);
-0038 %
-0039 % Notes
-0040 % -----
-0041 % mass_strategy values:
-0042 %
-0043 %     'mw'              MW from chemical formula
-0044 %     'mw_minus_2h'     MW − 2.016 g/mol (two protons released per charged
-0045 %                       tRNA — protein-pseudoreaction substrates)
-0046 %     'mw_minus_water'  MW − 18.015 g/mol (water released per
-0047 %                       polymerisation step — RNA / DNA)
-0048 %     'grams'           stoichiometry already in g/gDW (lipid backbone)
-0049 
-0050 fractions = struct();
-0051 total = 0;
-0052 for i = 1:numel(biomassConfig.components)
-0053     comp = biomassConfig.components{i};
-0054     f = computeComponentFraction(model, comp);
-0055     fractions.(comp.name) = f;
-0056     total = total + f;
-0057 end
-0058 fractions.total = total;
-0059 end
-0060 
-0061 function f = computeComponentFraction(model, comp)
-0062 rxnPos = strcmp(model.rxnNames, comp.pseudoreaction_name);
-0063 if ~any(rxnPos)
-0064     f = 0;
-0065     return;
-0066 end
-0067 S_col = model.S(:, rxnPos);
-0068 isSub = find(S_col < 0);
-0069 if isempty(isSub)
-0070     f = 0;
-0071     return;
-0072 end
-0073 if strcmp(comp.mass_strategy, 'grams')
-0074     f = full(-sum(S_col(isSub)));
-0075     return;
-0076 end
-0077 offset = mwOffset(comp.mass_strategy);
-0078 formulas = model.metFormulas(isSub);
-0079 MWs = zeros(numel(formulas), 1);
-0080 for i = 1:numel(formulas)
-0081     MWs(i) = computeFormulaMW(formulas{i});
-0082 end
-0083 zeroMW = MWs == 0;
-0084 if any(zeroMW)
-0085     error('getBiomassFractions:emptyFormula', ...
-0086         'Biomass metabolite %s has an empty metFormula field.', ...
-0087         model.mets{isSub(find(zeroMW, 1))});
-0088 end
-0089 MWs = MWs + offset;
-0090 f = full(-sum(S_col(isSub) .* MWs) / 1000);
-0091 end
-0092 
-0093 function offset = mwOffset(strategy)
-0094 switch strategy
-0095     case 'mw'
-0096         offset = 0;
-0097     case 'mw_minus_2h'
-0098         offset = -2.016;
-0099     case 'mw_minus_water'
-0100         offset = -18.015;
-0101     otherwise
-0102         error('getBiomassFractions:unknownStrategy', ...
-0103             'Unknown mass_strategy: %s', strategy);
-0104 end
-0105 end
-0106 
-0107 function mw = computeFormulaMW(formula)
-0108 % Molecular weight in g/mol from a Hill-style chemical formula. Same
-0109 % element table as the legacy yeast-GEM sumBioMass.
-0110 tokens = regexp(formula, '([A-Z][a-z]*)(\d*)', 'tokens');
-0111 if isempty(tokens)
-0112     mw = 0;
-0113     return;
-0114 end
-0115 tokensMatrix = vertcat(tokens{:});
-0116 tokensMatrix(cellfun(@isempty, tokensMatrix(:,2)), 2) = {'1'};
-0117 elements = tokensMatrix(:, 1);
-0118 counts = str2double(tokensMatrix(:, 2));
-0119 elem = {'C', 12.01; 'H', 1.008; 'N', 14.007; 'O', 15.999; ...
-0120         'P', 30.974; 'S', 32.06; 'R', 0; ...
-0121         'Fe', 55.845; 'K', 39.098; 'Na', 22.99; 'Cl', 35.45; ...
-0122         'Mn', 54.938; 'Zn', 65.38; 'Ca', 40.078; 'Mg', 24.305; 'Cu', 63.546};
-0123 [~, elemMatch] = ismember(elements, elem(:,1));
-0124 if any(elemMatch == 0)
-0125     error('getBiomassFractions:unknownElement', ...
-0126         'Unknown element in formula %s', formula);
-0127 end
-0128 mw = sum(counts .* transpose([elem{elemMatch, 2}]), 'all');
-0129 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/biomass/scaleBiomassFraction.html b/doc/biomass/scaleBiomassFraction.html deleted file mode 100644 index b5caf640..00000000 --- a/doc/biomass/scaleBiomassFraction.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - Description of scaleBiomassFraction - - - - - - - - - -
Home > biomass > scaleBiomassFraction.m
- - - -

scaleBiomassFraction -

- -

PURPOSE ^

-
scaleBiomassFraction Rescale a biomass component to a target value.
- -

SYNOPSIS ^

-
function model = scaleBiomassFraction(model, biomassConfig, componentName, newValue, varargin)
- -

DESCRIPTION ^

-
 scaleBiomassFraction  Rescale a biomass component to a target value.
-
- Rescale a biomass component to a target g/gDW value, optionally
- balancing a second component so the total biomass mass stays at 1 g/gDW.
- The MATLAB counterpart of yeast-GEM's legacy scaleBioMass.
-
- Parameters
- ----------
- model : struct
-     RAVEN model struct.
- biomassConfig : struct
-     Struct (see getBiomassFractions).
- componentName : char
-     Component to rescale.
- newValue : double
-     Target fraction in g/gDW.
-
- Name-Value Arguments
- --------------------
- balanceOut : char
-     Second component name to adjust so the biomass total remains 1
-     g/gDW. Empty / omit to skip balancing.
-
- Returns
- -------
- model : struct
-     Modified model.
-
- Examples
- --------
-     model = scaleBiomassFraction(model, biomassConfig, 'protein', 0.5, 'carbohydrate');
-
- See also
- --------
- getBiomassFractions
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function model = scaleBiomassFraction(model, biomassConfig, componentName, newValue, varargin)
-0002 % scaleBiomassFraction  Rescale a biomass component to a target value.
-0003 %
-0004 % Rescale a biomass component to a target g/gDW value, optionally
-0005 % balancing a second component so the total biomass mass stays at 1 g/gDW.
-0006 % The MATLAB counterpart of yeast-GEM's legacy scaleBioMass.
-0007 %
-0008 % Parameters
-0009 % ----------
-0010 % model : struct
-0011 %     RAVEN model struct.
-0012 % biomassConfig : struct
-0013 %     Struct (see getBiomassFractions).
-0014 % componentName : char
-0015 %     Component to rescale.
-0016 % newValue : double
-0017 %     Target fraction in g/gDW.
-0018 %
-0019 % Name-Value Arguments
-0020 % --------------------
-0021 % balanceOut : char
-0022 %     Second component name to adjust so the biomass total remains 1
-0023 %     g/gDW. Empty / omit to skip balancing.
-0024 %
-0025 % Returns
-0026 % -------
-0027 % model : struct
-0028 %     Modified model.
-0029 %
-0030 % Examples
-0031 % --------
-0032 %     model = scaleBiomassFraction(model, biomassConfig, 'protein', 0.5, 'carbohydrate');
-0033 %
-0034 % See also
-0035 % --------
-0036 % getBiomassFractions
-0037 
-0038 p=parseRAVENargs(varargin, {'balanceOut',''});
-0039 balanceOut=p.balanceOut;
-0040 
-0041 fractions = getBiomassFractions(model, biomassConfig);
-0042 if ~isfield(fractions, componentName)
-0043     error('scaleBiomassFraction:unknownComponent', ...
-0044         'biomassConfig has no component named %s', componentName);
-0045 end
-0046 current = fractions.(componentName);
-0047 if current == 0
-0048     error('scaleBiomassFraction:zeroCurrent', ...
-0049         ['Cannot scale %s to %g: current fraction is 0 ' ...
-0050          '(pseudoreaction missing or empty).'], componentName, newValue);
-0051 end
-0052 factor = newValue / current;
-0053 model = scaleBiomassPseudoreaction(model, biomassConfig, componentName, factor);
-0054 
-0055 if ~isempty(balanceOut)
-0056     fractions = getBiomassFractions(model, biomassConfig);
-0057     total = fractions.total;
-0058     balanceCurrent = fractions.(balanceOut);
-0059     if balanceCurrent == 0
-0060         return;
-0061     end
-0062     balanceFactor = (balanceCurrent + (1 - total)) / balanceCurrent;
-0063     model = scaleBiomassPseudoreaction(model, biomassConfig, balanceOut, balanceFactor);
-0064 end
-0065 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/biomass/setGAM.html b/doc/biomass/setGAM.html deleted file mode 100644 index eb60e2a9..00000000 --- a/doc/biomass/setGAM.html +++ /dev/null @@ -1,167 +0,0 @@ - - - - Description of setGAM - - - - - - - - - -
Home > biomass > setGAM.m
- - - -

setGAM -

- -

PURPOSE ^

-
setGAM Set the growth-associated maintenance (GAM) coefficient.
- -

SYNOPSIS ^

-
function model = setGAM(model, value, biomassRxn, cofactorMetNames, varargin)
- -

DESCRIPTION ^

-
 setGAM  Set the growth-associated maintenance (GAM) coefficient.
-
- Set the growth-associated maintenance (GAM) coefficient in the biomass
- pseudoreaction, and optionally fix the non-growth maintenance (NGAM)
- reaction's bounds. The MATLAB counterpart of yeast-GEM's legacy
- changeGAM.
-
- For every metabolite in the biomass pseudoreaction whose model.metNames
- entry is in cofactorMetNames, the stoichiometric coefficient is set to
- ±value preserving the sign of the current coefficient. Yeast-GEM scales
- ATP, ADP, H2O, H+ and phosphate (with ATP and H2O on the substrate side,
- ADP / H+ / phosphate on the product side).
-
- Parameters
- ----------
- model : struct
-     RAVEN model struct.
- value : double
-     New GAM value (mmol ATP / gDW per growth unit).
- biomassRxn : char
-     Reaction id of the biomass pseudoreaction.
- cofactorMetNames : cell
-     Cell array of metabolite NAMES (not IDs) to rescale, e.g.
-     {'ATP','ADP','H2O','H+','phosphate'}.
-
- Name-Value Arguments
- --------------------
- ngamRxn : char
-     NGAM reaction id. Required when ngamValue is supplied.
- ngamValue : double
-     NGAM flux to fix. Sets the NGAM reaction's bounds to (ngamValue,
-     ngamValue).
-
- Returns
- -------
- model : struct
-     Modified model.
-
- Examples
- --------
-     model = setGAM(model, 80, 'r_4041', {'ATP','ADP','H2O','H+','phosphate'});
-
- See also
- --------
- fitParameters : estimates such a value from measured fluxes instead of
-     applying a known one. The two are complementary - fit first, then set.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function model = setGAM(model, value, biomassRxn, cofactorMetNames, varargin)
-0002 % setGAM  Set the growth-associated maintenance (GAM) coefficient.
-0003 %
-0004 % Set the growth-associated maintenance (GAM) coefficient in the biomass
-0005 % pseudoreaction, and optionally fix the non-growth maintenance (NGAM)
-0006 % reaction's bounds. The MATLAB counterpart of yeast-GEM's legacy
-0007 % changeGAM.
-0008 %
-0009 % For every metabolite in the biomass pseudoreaction whose model.metNames
-0010 % entry is in cofactorMetNames, the stoichiometric coefficient is set to
-0011 % ±value preserving the sign of the current coefficient. Yeast-GEM scales
-0012 % ATP, ADP, H2O, H+ and phosphate (with ATP and H2O on the substrate side,
-0013 % ADP / H+ / phosphate on the product side).
-0014 %
-0015 % Parameters
-0016 % ----------
-0017 % model : struct
-0018 %     RAVEN model struct.
-0019 % value : double
-0020 %     New GAM value (mmol ATP / gDW per growth unit).
-0021 % biomassRxn : char
-0022 %     Reaction id of the biomass pseudoreaction.
-0023 % cofactorMetNames : cell
-0024 %     Cell array of metabolite NAMES (not IDs) to rescale, e.g.
-0025 %     {'ATP','ADP','H2O','H+','phosphate'}.
-0026 %
-0027 % Name-Value Arguments
-0028 % --------------------
-0029 % ngamRxn : char
-0030 %     NGAM reaction id. Required when ngamValue is supplied.
-0031 % ngamValue : double
-0032 %     NGAM flux to fix. Sets the NGAM reaction's bounds to (ngamValue,
-0033 %     ngamValue).
-0034 %
-0035 % Returns
-0036 % -------
-0037 % model : struct
-0038 %     Modified model.
-0039 %
-0040 % Examples
-0041 % --------
-0042 %     model = setGAM(model, 80, 'r_4041', {'ATP','ADP','H2O','H+','phosphate'});
-0043 %
-0044 % See also
-0045 % --------
-0046 % fitParameters : estimates such a value from measured fluxes instead of
-0047 %     applying a known one. The two are complementary - fit first, then set.
-0048 
-0049 if nargin < 4
-0050     error('setGAM:missingArgs', ...
-0051         'biomassRxn and cofactorMetNames are required.');
-0052 end
-0053 
-0054 p=parseRAVENargs(varargin, {'ngamRxn',[]; 'ngamValue',[]});
-0055 ngamRxn=p.ngamRxn;
-0056 ngamValue=p.ngamValue;
-0057 
-0058 bioPos = strcmp(model.rxns, biomassRxn);
-0059 if ~any(bioPos)
-0060     error('setGAM:missingBiomassRxn', ...
-0061         'Reaction %s not found in model.', biomassRxn);
-0062 end
-0063 
-0064 for i = 1:length(model.mets)
-0065     S_ix = model.S(i, bioPos);
-0066     isCofactor = any(strcmp(cofactorMetNames, model.metNames{i}));
-0067     if S_ix ~= 0 && isCofactor
-0068         model.S(i, bioPos) = sign(S_ix) * value;
-0069     end
-0070 end
-0071 
-0072 if ~isempty(ngamRxn) && ~isempty(ngamValue)
-0073     model = setParam(model, 'eq', ngamRxn, ngamValue);
-0074 end
-0075 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/checkInstallation.html b/doc/checkInstallation.html deleted file mode 100644 index 0bff4cd6..00000000 --- a/doc/checkInstallation.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - Description of checkInstallation - - - - - - - - - -
Home > . > checkInstallation.m
- - - -

checkInstallation -

- -

PURPOSE ^

-
checkInstallation DEPRECATED. Use checkRaven instead.
- -

SYNOPSIS ^

-
function [currVer, installType] = checkInstallation(developMode, checkBinaries)
- -

DESCRIPTION ^

-
 checkInstallation  DEPRECATED. Use checkRaven instead.
-
-   Forwards all arguments and outputs to checkRaven.
-
-   NOTE: this function is run before RAVEN has been added to the MATLAB
-   path, so it must not call any other RAVEN functions until checkRaven has
-   added RAVEN to the path itself. It cannot use the shared
-   utils/deprecationWarning for the same reason.
-
- Usage: [currVer, installType] = checkInstallation(developMode, checkBinaries)
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function [currVer, installType] = checkInstallation(developMode, checkBinaries)
-0002 % checkInstallation  DEPRECATED. Use checkRaven instead.
-0003 %
-0004 %   Forwards all arguments and outputs to checkRaven.
-0005 %
-0006 %   NOTE: this function is run before RAVEN has been added to the MATLAB
-0007 %   path, so it must not call any other RAVEN functions until checkRaven has
-0008 %   added RAVEN to the path itself. It cannot use the shared
-0009 %   utils/deprecationWarning for the same reason.
-0010 %
-0011 % Usage: [currVer, installType] = checkInstallation(developMode, checkBinaries)
-0012 
-0013 warning('RAVEN:deprecated', ...
-0014     'checkInstallation is deprecated and will be removed in the next major release. Use checkRaven instead.')
-0015 if nargin < 1
-0016     developMode = false;
-0017 end
-0018 if nargin < 2
-0019     checkBinaries = true;
-0020 end
-0021 if nargout > 0
-0022     [currVer, installType] = checkRaven(developMode, checkBinaries);
-0023 else
-0024     checkRaven(developMode, checkBinaries);
-0025 end
-0026 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/checkRaven.html b/doc/checkRaven.html deleted file mode 100644 index dd37d42b..00000000 --- a/doc/checkRaven.html +++ /dev/null @@ -1,456 +0,0 @@ - - - - Description of checkRaven - - - - - - - - - -
Home > . > checkRaven.m
- - - -

checkRaven -

- -

PURPOSE ^

-
checkRaven
- -

SYNOPSIS ^

-
function [currVer, installType] = checkRaven(developMode, checkBinaries)
- -

DESCRIPTION ^

-
 checkRaven
-   Checks that all necessary functions are installed and working. It also
-   checks whether there are any functions
-   with overlapping names between RAVEN and other toolboxes or
-   user-defined functions, which are accessible from MATLAB pathlist
-
-   NOTE: this function is run before RAVEN has been added to the MATLAB
-   path, so it must not call any other RAVEN functions until it has added
-   RAVEN to the path itself. Its arguments are therefore parsed directly
-   rather than via parseRAVENargs.
-
- Parameters
- ----------
-   developMode     logical indicating development mode, which includes
-                   testing of binaries that are required to update KEGG
-                   HMMs (default false). If 'versionOnly' is
-                   specified, only the version is reported as currVer, no
-                   further installation or tests are performed.
-   checkBinaries   logical whether non-developMode binaries should be
-                   checked for functionality. If false, it overwrites
-                   developMode. Default true.
-
- Output:
-   currVer         current RAVEN version
-   installType     how RAVEN is installed
-                   0:  via git (as .git folder is found)
-                   1:  as MATLAB Add-On
-                   2:  neither of the above, direct download of ZIP file
-                   This matches the installations mentioned in the wiki:
-                   https://github.com/SysBioChalmers/RAVEN/wiki/Installation
-                   0 = advanced / 1 = easy / 2 = medium
-
- Usage: [currVer, installType] = checkRaven(developMode)
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [currVer, installType] = checkRaven(developMode, checkBinaries)
-0002 % checkRaven
-0003 %   Checks that all necessary functions are installed and working. It also
-0004 %   checks whether there are any functions
-0005 %   with overlapping names between RAVEN and other toolboxes or
-0006 %   user-defined functions, which are accessible from MATLAB pathlist
-0007 %
-0008 %   NOTE: this function is run before RAVEN has been added to the MATLAB
-0009 %   path, so it must not call any other RAVEN functions until it has added
-0010 %   RAVEN to the path itself. Its arguments are therefore parsed directly
-0011 %   rather than via parseRAVENargs.
-0012 %
-0013 % Parameters
-0014 % ----------
-0015 %   developMode     logical indicating development mode, which includes
-0016 %                   testing of binaries that are required to update KEGG
-0017 %                   HMMs (default false). If 'versionOnly' is
-0018 %                   specified, only the version is reported as currVer, no
-0019 %                   further installation or tests are performed.
-0020 %   checkBinaries   logical whether non-developMode binaries should be
-0021 %                   checked for functionality. If false, it overwrites
-0022 %                   developMode. Default true.
-0023 %
-0024 % Output:
-0025 %   currVer         current RAVEN version
-0026 %   installType     how RAVEN is installed
-0027 %                   0:  via git (as .git folder is found)
-0028 %                   1:  as MATLAB Add-On
-0029 %                   2:  neither of the above, direct download of ZIP file
-0030 %                   This matches the installations mentioned in the wiki:
-0031 %                   https://github.com/SysBioChalmers/RAVEN/wiki/Installation
-0032 %                   0 = advanced / 1 = easy / 2 = medium
-0033 %
-0034 % Usage: [currVer, installType] = checkRaven(developMode)
-0035 
-0036 if nargin < 1 || isempty(developMode)
-0037     developMode = false;
-0038 end
-0039 if nargin < 2 || isempty(checkBinaries)
-0040     checkBinaries = true;
-0041 end
-0042 
-0043 if ischar(developMode) && strcmp(developMode,'versionOnly')
-0044     versionOnly = true;
-0045 else
-0046     versionOnly = false;
-0047 end
-0048 
-0049 %Get the RAVEN path. checkRaven.m sits in the RAVEN root.
-0050 [ST, I]=dbstack('-completenames');
-0051 [ravenDir,~,~]=fileparts(ST(I).file);
-0052 
-0053 installType = 2; % If neither git nor add-on, then ZIP was downloaded
-0054 addList = matlab.addons.installedAddons;
-0055 if isfolder(fullfile(ravenDir,'.git'))
-0056     installType = 0;
-0057 elseif any(strcmp(addList.Name,'RAVEN Toolbox'))
-0058     installType = 1;
-0059 end
-0060 
-0061 % Do not print first few lines if only version should be reported
-0062 if ~versionOnly
-0063     fprintf('\n*** THE RAVEN TOOLBOX ***\n\n');
-0064     %Print the RAVEN version if it is not the development version
-0065     fprintf(myStr(' > Installation type',40));
-0066     switch installType
-0067         case 0
-0068             fprintf('Advanced (via git)\n');
-0069         case 1
-0070             fprintf('Easy (as MATLAB Add-On)\n');
-0071         case 2
-0072             fprintf('Medium (as downloaded ZIP file)\n');
-0073     end
-0074     fprintf(myStr(' > Installing from location',40));
-0075     fprintf('%s\n',ravenDir)
-0076     fprintf(myStr(' > Checking RAVEN release',40));
-0077 end
-0078                        
-0079 if exist(fullfile(ravenDir,'version.txt'), 'file') == 2
-0080     currVer = fgetl(fopen(fullfile(ravenDir,'version.txt')));
-0081     fclose('all');
-0082     if ~versionOnly
-0083         fprintf([currVer '\n']);
-0084         try
-0085             newVer=strtrim(webread('https://raw.githubusercontent.com/SysBioChalmers/RAVEN/main/version.txt'));
-0086             newVerNum=str2double(strsplit(newVer,'.'));
-0087             currVerNum=str2double(strsplit(currVer,'.'));
-0088             for i=1:3
-0089                 if currVerNum(i)<newVerNum(i)
-0090                     fprintf(myStr('   > Latest RAVEN release available',40))
-0091                     printOrange([newVer,'\n'])
-0092                     switch installType
-0093                         case 0
-0094                             printOrange('     Run git pull in your favourite git client\n')
-0095                             printOrange('     to get the latest RAVEN release\n');
-0096                         case 1
-0097                             printOrange(myStr('     Instructions on how to upgrade',40))
-0098                             fprintf('<a href="https://github.com/SysBioChalmers/RAVEN/wiki/Installation#upgrade-raven-after-easy-installation">here</a>\n');
-0099                         case 2
-0100                             printOrange(myStr('     Instructions on how to upgrade',40))
-0101                             fprintf('<a href="https://github.com/SysBioChalmers/RAVEN/wiki/Installation#upgrade-raven-after-medium-installation">here</a>\n');                            
-0102                     end
-0103                     break
-0104                 elseif i==3
-0105                     fprintf('   > You are running the latest RAVEN release\n');
-0106                 end
-0107             end
-0108         catch
-0109             fprintf(myStr('   > Checking for latest RAVEN release',40))
-0110             printOrange('Fail\n');
-0111             printOrange('     Cannot reach GitHub for release info\n');
-0112         end
-0113     end
-0114 else
-0115     currVer = 'develop';
-0116     if ~versionOnly; fprintf('DEVELOPMENT\n'); end
-0117 end
-0118 if strcmp(developMode,'versionOnly')
-0119     return;
-0120 end
-0121 
-0122 fprintf(myStr(' > Checking MATLAB release',40))
-0123 fprintf([version('-release') '\n'])
-0124 fprintf(myStr(' > Checking system architecture',40))
-0125 fprintf([computer('arch'),'\n'])
-0126 
-0127 fprintf(myStr(' > Set RAVEN in MATLAB path',40))
-0128 subpath=regexp(genpath(ravenDir),pathsep,'split'); %List all subdirectories
-0129 pathsToKeep=cellfun(@(x) ~contains(x,'.git'),subpath) & cellfun(@(x) ~contains(x,'doc'),subpath);
-0130 try
-0131     addpath(strjoin(subpath(pathsToKeep),pathsep));
-0132     fprintf('Pass\n');
-0133     fprintf(myStr(' > Save MATLAB path',40))
-0134     try
-0135         savepath
-0136         fprintf('Pass\n')   
-0137     catch
-0138         printOrange('Fail\n')
-0139         fprintf(['   You might have to rerun checkRaven again\n'...
-0140                  '   next time you start up MATLAB\n'])        
-0141     end
-0142 catch
-0143     printOrange('Fail\n')
-0144 end
-0145 fprintf(myStr(' > Store RAVEN path as MATLAB pref',40))
-0146 try
-0147     setpref('RAVEN','ravenPath',ravenDir);
-0148     clear('findRAVENroot'); % drop its cached path, in case one is already set for this session
-0149     fprintf('Pass\n');
-0150 catch
-0151     printOrange('Fail\n')
-0152 end
-0153 
-0154 if isunix
-0155     fprintf(myStr('   > Make binaries executable',40))
-0156     status = makeBinaryExecutable(ravenDir);
-0157     if status == 0
-0158         fprintf('Pass\n')
-0159     else
-0160         printOrange('Fail\n')
-0161     end
-0162 end
-0163 
-0164 %Check the model import and export formats
-0165 fprintf('\n=== Model import and export ===\n');
-0166 fprintf(myStr(' > Checking libSBML version',40))
-0167 model = [];
-0168 try
-0169     evalc('model = importModel(fullfile(ravenDir,''tutorial'',''empty.xml''));');
-0170     try
-0171         libSBMLver=OutputSBML_RAVEN; % Only works in libSBML 5.17.0+
-0172         fprintf([libSBMLver.libSBML_version_string '\n']);
-0173     catch
-0174         printOrange('Fail\n')
-0175         fprintf('   An older libSBML version was found, update to version 5.17.0 or higher for a significant improvement of model import\n');
-0176     end
-0177 catch
-0178     printOrange('Fail\n')
-0179     fprintf('   Download libSBML from http://sbml.org/Software/libSBML/Downloading_libSBML and add to MATLAB path\n');
-0180 end
-0181 
-0182 % Import and export the small "empty" model directly, which is much faster
-0183 % than running the full importExportTests test case. A temporary folder
-0184 % holds the exported files and is removed afterwards.
-0185 fprintf(' > Checking model import and export\n')
-0186 tmpDir = tempname; mkdir(tmpDir);
-0187 
-0188 fprintf(myStr('   > Import SBML format',40))
-0189 if ~isempty(model)
-0190     fprintf('Pass\n')
-0191 else
-0192     printOrange('Fail\n')
-0193 end
-0194 
-0195 reportCheck('   > Export SBML format', @() exportModel(model, fullfile(tmpDir,'model.xml')));
-0196 reportCheck('   > Import YAML format', @() readYAMLmodel(fullfile(ravenDir,'tutorial','empty.yml')));
-0197 reportCheck('   > Export YAML format', @() writeYAMLmodel(model, fullfile(tmpDir,'model.yml')));
-0198 
-0199 reportCheck('   > Export Excel format', @() exportToExcelFormat(model, fullfile(tmpDir,'model.xlsx')));
-0200 
-0201 rmdir(tmpDir,'s');
-0202 
-0203 fprintf('\n=== Model solvers ===\n');
-0204 fprintf(' > Checking for LP solvers\n')
-0205 
-0206 % Solve a minimal feasible LP with each solver to verify it is functional.
-0207 % This is much faster than running the full solverTests test case.
-0208 testModel.rxns = {'r1';'r2'};
-0209 testModel.mets = {'a'};
-0210 testModel.S    = sparse([1 -1]);
-0211 testModel.lb   = [0; 0];
-0212 testModel.ub   = [1; 1000];
-0213 testModel.rev  = [0; 0];
-0214 testModel.c    = [0; 1];
-0215 testModel.b    = 0;
-0216 
-0217 solvers  = {'glpk','gurobi','scip','cobra'};
-0218 solverOK = false(1,numel(solvers));
-0219 for i = 1:numel(solvers)
-0220     solverOK(i) = reportCheck(['   > ' solvers{i}], @() testSolver(solvers{i}, testModel));
-0221 end
-0222 
-0223 % Keep the current solver if it is still functional, otherwise pick the best
-0224 % available one. Order of preference: gurobi > glpk > scip > cobra
-0225 fprintf(myStr(' > Set RAVEN solver',40))
-0226 oldSolver = '';
-0227 try oldSolver = getpref('RAVEN','solver'); catch; end
-0228 solverIdx = find(strcmp(oldSolver, solvers));
-0229 if ~isempty(solverIdx) && solverOK(solverIdx)
-0230     fprintf([oldSolver '\n'])
-0231     setRavenSolver(oldSolver);
-0232 elseif solverOK(2)
-0233     fprintf('gurobi\n')
-0234     setRavenSolver('gurobi');
-0235 elseif solverOK(1)
-0236     fprintf('glpk\n')
-0237     setRavenSolver('glpk');
-0238 elseif solverOK(3)
-0239     fprintf('scip\n')
-0240     setRavenSolver('scip');
-0241 elseif solverOK(4)
-0242     fprintf('cobra\n')
-0243     setRavenSolver('cobra');
-0244 else
-0245     fprintf('None, no functional solvers\n')
-0246     fprintf('    The glpk should always be working, check your RAVEN installation to make sure all files are present\n')
-0247 end
-0248 
-0249 fprintf('\n=== Essential binary executables ===\n');
-0250 if ~checkBinaries
-0251     printOrange('    Skipping check of binary executables\n')
-0252 else
-0253     % The BLAST+/DIAMOND/HMMER binaries are downloaded on demand from
-0254     % raven-data; fetch any that are missing for this platform before
-0255     % checking. This doubles as the up-front "prefetch" for users who want
-0256     % everything ready after installation.
-0257     try
-0258         downloadRavenBinaries({'blast+','diamond','hmmer'});
-0259     catch ME
-0260         printOrange(['    Could not download binaries from raven-data: ' ME.message '\n' ...
-0261             '    (offline? fetch the "*-binaries" RAVEN release for an offline install)\n']);
-0262     end
-0263     % Run each bundled binary with a version/help flag to confirm it
-0264     % executes on this system, rather than running the full test cases.
-0265     if ~reportCheck(' > Checking BLAST+', @() testBinary(ravenDir,'blast+','blastp','-version'))
-0266         fprintf('   This is essential to run getBlast()\n')
-0267     end
-0268     if ~reportCheck(' > Checking DIAMOND', @() testBinary(ravenDir,'diamond','diamond','version'))
-0269         fprintf('   This is essential to run getDiamond()\n')
-0270     end
-0271     if ~reportCheck(' > Checking HMMER', @() testBinary(ravenDir,'hmmer','hmmsearch','-h'))
-0272         fprintf(['   This is essential to run getKEGGModelFromHomology()\n'...
-0273             '   when using a FASTA file as input\n'])
-0274     end
-0275 end
-0276 
-0277 fprintf('\n=== Compatibility ===\n');
-0278 fprintf(myStr(' > Checking function uniqueness',40))
-0279 checkFunctionUniqueness();
-0280 
-0281 fprintf('\n*** checkRaven complete ***\n');
-0282 end
-0283 
-0284 function testSolver(solver, model)
-0285 % Set the RAVEN solver and solve a trivial LP; error if it does not return
-0286 % an optimal solution.
-0287 setRavenSolver(solver);
-0288 sol = solveLP(model, 0);
-0289 if ~(isfield(sol,'stat') && sol.stat == 1)
-0290     error('Solver %s did not return an optimal solution', solver);
-0291 end
-0292 end
-0293 
-0294 function testBinary(ravenDir, tool, binName, versionArg)
-0295 % Run a bundled binary with versionArg to confirm it executes; error if not.
-0296 if ispc
-0297     binEnd = '.exe'; % native Windows builds (raven-data windows-x86_64 ZIPs)
-0298 elseif ismac
-0299     binEnd = '.mac';
-0300 else
-0301     binEnd = '';
-0302 end
-0303 cmd = ['"' fullfile(ravenDir,'software',tool,[binName binEnd]) '" ' versionArg];
-0304 [status,~] = system(cmd);
-0305 if status ~= 0
-0306     error('%s did not execute (exit status %d)', binName, status);
-0307 end
-0308 end
-0309 
-0310 function ok = reportCheck(label, fcn)
-0311 % Print label, run fcn (a function handle) with its output suppressed, and
-0312 % report Pass/Fail. Returns true on success.
-0313 assert(isa(fcn,'function_handle'));
-0314 fprintf(myStr(label,40));
-0315 try
-0316     evalc('fcn()');
-0317     ok = true;
-0318     fprintf('Pass\n');
-0319 catch
-0320     ok = false;
-0321     printOrange('Fail\n');
-0322 end
-0323 end
-0324 
-0325 function str = myStr(InputStr,len)
-0326 str=InputStr;
-0327 lenDiff = len - length(str);
-0328 if lenDiff < 0
-0329     warning('String too long');
-0330 else
-0331     str = [str blanks(lenDiff)];
-0332 end
-0333 end
-0334 
-0335 function status = makeBinaryExecutable(ravenDir)
-0336 % This function is required to run when RAVEN is downloaded as MATLAB
-0337 % Add-On, in which case the file permissions are not correctly set
-0338 if ispc
-0339     status = 0; % No need to run on Windows
-0340     return;
-0341 end
-0342 binDir = fullfile(ravenDir,'software');
-0343 
-0344 % blast+/diamond/hmmer may be present from an on-demand download
-0345 % (downloadRavenBinaries already chmods those) or from the offline bundle;
-0346 % chmod whichever unix binaries are present. Missing entries are skipped below.
-0347 binList = {fullfile(binDir,'blast+','blastp');                      fullfile(binDir,'blast+','blastp.mac');
-0348            fullfile(binDir,'blast+','makeblastdb');                 fullfile(binDir,'blast+','makeblastdb.mac');
-0349            fullfile(binDir,'diamond','diamond');                    fullfile(binDir,'diamond','diamond.mac');
-0350            fullfile(binDir,'hmmer','hmmsearch');                    fullfile(binDir,'hmmer','hmmsearch.mac');
-0351            fullfile(binDir,'GLPKmex','glpkcc.mexa64');              fullfile(binDir,'GLPKmex','glpkcc.mexmaci64');              fullfile(binDir,'GLPKmex','glpkcc.mexmaca64');
-0352            fullfile(binDir,'libSBML','TranslateSBML_RAVEN.mexa64'); fullfile(binDir,'libSBML','TranslateSBML_RAVEN.mexmaci64'); fullfile(binDir,'libSBML','TranslateSBML_RAVEN.mexmaca64');
-0353            fullfile(binDir,'libSBML','OutputSBML_RAVEN.mexa64');    fullfile(binDir,'libSBML','OutputSBML_RAVEN.mexmaci64');    fullfile(binDir,'libSBML','OutputSBML_RAVEN.mexmaca64');};
-0354 
-0355 status = 0;
-0356 for i=1:numel(binList)
-0357     if ~isfile(binList{i}); continue; end % skip binaries not present on this platform
-0358     [status,cmdout] = system(['chmod +x "' binList{i} '"']);
-0359     if status ~= 0
-0360         warning('Failed to make %s executable: %s ',binList{i},strip(cmdout))
-0361     end
-0362 end
-0363 end
-0364 
-0365 function printOrange(stringToPrint)
-0366 % printOrange
-0367 %   Duplicate of RAVEN/core/printOrange is also kept here, as this function
-0368 %   should be able to run before adding RAVEN to the MATLAB path.
-0369 try useDesktop = usejava('desktop'); catch, useDesktop = false; end
-0370 if useDesktop
-0371     fprintf(['[\b' stringToPrint,']\b'])
-0372 else
-0373     fprintf(stringToPrint)
-0374 end
-0375 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/comparison/compareMultipleModels.html b/doc/comparison/compareMultipleModels.html deleted file mode 100644 index 5189ea92..00000000 --- a/doc/comparison/compareMultipleModels.html +++ /dev/null @@ -1,674 +0,0 @@ - - - - Description of compareMultipleModels - - - - - - - - - -
Home > comparison > compareMultipleModels.m
- - - -

compareMultipleModels -

- -

PURPOSE ^

-
compareMultipleModels Compare two or more condition-specific models.
- -

SYNOPSIS ^

-
function compStruct = compareMultipleModels(models,varargin)
- -

DESCRIPTION ^

-
 compareMultipleModels  Compare two or more condition-specific models.
-
- Compares two or more models on overlap of reactions, metabolites, genes,
- EC-codes, metabolite names and reaction equations (with and without
- compartmentalization), and on high-dimensional comparisons in the
- reaction-space: subsystem utilization, structural similarity, an optional
- low-dimensional structural projection, and an optional functional
- (task-based) comparison.
-
- Parameters
- ----------
- models : cell
-     cell array of two or more models.
-
- Name-Value Arguments
- --------------------
- printResults : logical
-     true if the results should be printed on the screen (default false).
- plotResults : logical
-     true if the results should be plotted (default false).
- groupVector : double or cell
-     numeric vector or cell array for grouping similar models, i.e. by
-     tissue (default all models ungrouped).
- funcCompare : logical
-     should a functional comparison be run (default false).
- taskFile : char
-     string containing the name of the task file to use for the functional
-     comparison (should be an .xls or .xlsx file, required for functional
-     comparison).
-
- Returns
- -------
- compStruct : struct
-     structure that contains the comparison results, with fields:
-
-     - modelIDs : cell array of model ids
-     - rxns, mets, genes, eccodes, metNames, equ, uEqu : the overlap for
-       each field, each with sub-fields comparison (binary matrix where each
-       row indicates which models are included in the comparison) and
-       nElements (vector with the number of elements for each comparison).
-       "equ" are the reaction equations after sorting and "uEqu" are the
-       equations when not taking compartmentalization into account. These
-       are the only fields of the seven that use EC-code, metabolite name or
-       reaction equation to compare models that do not share an id
-       convention; the rest compare by identifier only.
-     - subsystems : substructure containing subsystem information, with
-       fields matrix (matrix with comparison of number of rxns per
-       subsystem) and ID (vector consisting of names of all subsystems)
-     - reactions : substructure containing reaction information, with
-       fields matrix (binary matrix composed of reactions (rows) in each
-       model (column), used as the input for the model comparisons) and IDs
-       (list of the reactions contained in the reaction matrix)
-     - structComp : matrix with pairwise comparisons of model structure
-       based on (1-Hamming distance) between models
-     - structCompMap : matrix with 3D tSNE (or MDS) mapping of model
-       structures based on Hamming distances
-     - funcComp : substructure containing function comparison results, with
-       fields matrix (matrix with PASS / FAIL (1 / 0) values for each task)
-       and tasks (vector containing names of all tasks)
-
- Examples
- --------
-     compStruct = compareMultipleModels(models, printResults, ...
-         plotResults, groupVector, funcCompare, taskFile);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function compStruct = compareMultipleModels(models,varargin)
-0002 % compareMultipleModels  Compare two or more condition-specific models.
-0003 %
-0004 % Compares two or more models on overlap of reactions, metabolites, genes,
-0005 % EC-codes, metabolite names and reaction equations (with and without
-0006 % compartmentalization), and on high-dimensional comparisons in the
-0007 % reaction-space: subsystem utilization, structural similarity, an optional
-0008 % low-dimensional structural projection, and an optional functional
-0009 % (task-based) comparison.
-0010 %
-0011 % Parameters
-0012 % ----------
-0013 % models : cell
-0014 %     cell array of two or more models.
-0015 %
-0016 % Name-Value Arguments
-0017 % --------------------
-0018 % printResults : logical
-0019 %     true if the results should be printed on the screen (default false).
-0020 % plotResults : logical
-0021 %     true if the results should be plotted (default false).
-0022 % groupVector : double or cell
-0023 %     numeric vector or cell array for grouping similar models, i.e. by
-0024 %     tissue (default all models ungrouped).
-0025 % funcCompare : logical
-0026 %     should a functional comparison be run (default false).
-0027 % taskFile : char
-0028 %     string containing the name of the task file to use for the functional
-0029 %     comparison (should be an .xls or .xlsx file, required for functional
-0030 %     comparison).
-0031 %
-0032 % Returns
-0033 % -------
-0034 % compStruct : struct
-0035 %     structure that contains the comparison results, with fields:
-0036 %
-0037 %     - modelIDs : cell array of model ids
-0038 %     - rxns, mets, genes, eccodes, metNames, equ, uEqu : the overlap for
-0039 %       each field, each with sub-fields comparison (binary matrix where each
-0040 %       row indicates which models are included in the comparison) and
-0041 %       nElements (vector with the number of elements for each comparison).
-0042 %       "equ" are the reaction equations after sorting and "uEqu" are the
-0043 %       equations when not taking compartmentalization into account. These
-0044 %       are the only fields of the seven that use EC-code, metabolite name or
-0045 %       reaction equation to compare models that do not share an id
-0046 %       convention; the rest compare by identifier only.
-0047 %     - subsystems : substructure containing subsystem information, with
-0048 %       fields matrix (matrix with comparison of number of rxns per
-0049 %       subsystem) and ID (vector consisting of names of all subsystems)
-0050 %     - reactions : substructure containing reaction information, with
-0051 %       fields matrix (binary matrix composed of reactions (rows) in each
-0052 %       model (column), used as the input for the model comparisons) and IDs
-0053 %       (list of the reactions contained in the reaction matrix)
-0054 %     - structComp : matrix with pairwise comparisons of model structure
-0055 %       based on (1-Hamming distance) between models
-0056 %     - structCompMap : matrix with 3D tSNE (or MDS) mapping of model
-0057 %       structures based on Hamming distances
-0058 %     - funcComp : substructure containing function comparison results, with
-0059 %       fields matrix (matrix with PASS / FAIL (1 / 0) values for each task)
-0060 %       and tasks (vector containing names of all tasks)
-0061 %
-0062 % Examples
-0063 % --------
-0064 %     compStruct = compareMultipleModels(models, printResults, ...
-0065 %         plotResults, groupVector, funcCompare, taskFile);
-0066 
-0067 % The core comparison (structComp) runs in base MATLAB. The low-dimensional
-0068 % projection (structCompMap) and the heatmap clustering additionally use the
-0069 % Statistics and Machine Learning Toolbox (tsne/mdscale, linkage); these
-0070 % degrade gracefully and are skipped when the toolbox is not available.
-0071 %
-0072 % See also
-0073 % --------
-0074 % diffModels : takes exactly two models and reports the differing *values*
-0075 %     (stoichiometry, bounds, grRules), where this reports overlap counts,
-0076 %     similarity and clustering.
-0077 
-0078 %% Set up input defaults
-0079 p=parseRAVENargs(varargin, {'printResults',false; 'plotResults',false; 'groupVector',[]; 'funcCompare',false; 'taskFile',[]});
-0080 printResults=p.printResults;
-0081 plotResults=p.plotResults;
-0082 groupVector=p.groupVector;
-0083 if isempty(groupVector)
-0084     groupVector = [];
-0085 elseif ~isnumeric(groupVector)
-0086     % convert strings to numeric groups
-0087     [groupNames,~,groupVector] = unique(groupVector);
-0088 else
-0089     % generate group names for vector of numbers
-0090     groupNames = arrayfun(@num2str,unique(groupVector),'UniformOutput',false);
-0091 end
-0092 funcCompare=p.funcCompare;
-0093 taskFile=p.taskFile;
-0094 if ~isempty(taskFile)
-0095     taskFile=char(taskFile);
-0096 end
-0097 if numel(models) <= 1
-0098     EM = 'Cannot compare only one model. Use printModelStats if you want a summary of a model';
-0099     error('RAVEN:badInput', '%s', EM);
-0100 end
-0101 if isempty(taskFile) && funcCompare
-0102     EM = 'Cannot perform the functional comparison without a task file. Specify taskFile or set funcCompare to FALSE.';
-0103     error('RAVEN:badInput', '%s', EM);
-0104 end
-0105 
-0106 %% Set up model ID structure
-0107 compStruct.modelIDs = {};
-0108 fprintf('\n Getting model IDs \n')
-0109 for i = 1:numel(models)
-0110     if ~ischar(models{i}.id)  % to deal with non-character IDs (cells, strings, etc)
-0111         compStruct.modelIDs{i,1} = models{i}.id{1};
-0112     else
-0113         compStruct.modelIDs{i,1} = models{i}.id;
-0114     end
-0115     % Equations are computed once here (with and without compartmentalization)
-0116     % so the overlap comparison below can treat them like any other field.
-0117     models{i}.equ=constructEquations(models{i},models{i}.rxns,true,true,true);
-0118     models{i}.uEqu=constructEquations(models{i},models{i}.rxns,false,true,true);
-0119 end
-0120 fprintf('*** Done \n\n')
-0121 
-0122 %% Compare overlap of reactions, metabolites, genes, EC-codes, metabolite
-0123 % names and equations (with and without compartmentalization)
-0124 fprintf('\n Comparing overlap of reactions, genes, metabolites and compartments \n')
-0125 overlapFields={'rxns','mets','genes','eccodes','metNames','equ','uEqu'};
-0126 overlapLabels={'reaction IDs','metabolite IDs','gene IDs','ec-numbers', ...
-0127     'metabolite names','equations with compartment','equations without compartment'};
-0128 for i=1:numel(overlapFields)
-0129     field=overlapFields{i};
-0130     compStruct.(field).comparison=getToCheck(models,field);
-0131     compStruct.(field).nElements=checkStuff(getElements(models,field),compStruct.(field).comparison);
-0132     if printResults==true
-0133         fprintf(['*** Comparison of ' overlapLabels{i} ':\n']);
-0134         printList(models,compStruct.(field).comparison,compStruct.(field).nElements);
-0135         fprintf('\n\n');
-0136     end
-0137 end
-0138 fprintf('*** Done \n\n')
-0139 
-0140 %% Compare models structure & function based on high-dimensional methods
-0141 % Compare number of reactions in each subsystem in each model using a heatmap
-0142 field = 'subSystems';
-0143 fprintf('\n Comparing subsystem utilization \n')
-0144 if any(~cellfun(@(m) isfield(m,field),models))
-0145     fprintf('\nWARNING: At least one model does not contain the field "subSystems". \n')
-0146     fprintf('         Skipping subsystem comparison. \n\n')
-0147 else
-0148     %% Flatten model subSystems field
-0149     % Convert from cell array of cells to cell array of strings
-0150     % NOTE: this function currently only recognizes one subSystem per reaction;
-0151     %       additional subSystems will be ignored!
-0152     for i = 1:numel(models)
-0153         if any(cellfun(@(x) iscell(x), models{i}.subSystems));
-0154             cells = cellfun(@iscell,models{i}.subSystems);
-0155             models{i}.subSystems(cells) = cellfun(@(s) s{1}, models{i}.subSystems(cells), 'UniformOutput', false);
-0156         end
-0157     end
-0158     [id,compMat] = compareModelField(models,field);
-0159     compStruct.subsystems.ID = id;
-0160     compStruct.subsystems.matrix = compMat;
-0161     fprintf('*** Done \n\n')
-0162 
-0163     if printResults
-0164         % This could use some cleaning up
-0165         fprintf('*** Comparison of reaction subsystem populations:\n\n');
-0166 
-0167         nrow = min([15,numel(compStruct.subsystems.ID)]);
-0168         ncol = min([10,numel(compStruct.modelIDs)]);
-0169         summaryArray = [{field}, compStruct.modelIDs(1:ncol)'];
-0170         summaryArray = [summaryArray; [compStruct.subsystems.ID(1:nrow), ...
-0171             arrayfun(@num2str,compStruct.subsystems.matrix(1:nrow,1:ncol),'UniformOutput',false)]];
-0172 
-0173         charArray = [];
-0174         for i = 1:size(summaryArray,2)
-0175             charArray = [charArray, char(strcat(summaryArray(:,i),{'   '}))];
-0176         end
-0177         disp(charArray);
-0178         if numel(compStruct.subsystems.ID) > 15
-0179             fprintf('...\n');
-0180         end
-0181         fprintf('\n\n');
-0182     end
-0183 
-0184     if plotResults==true
-0185         % Plot all subsystems
-0186         figure;
-0187         plottingData = (compStruct.subsystems.matrix - mean(compStruct.subsystems.matrix,2))./mean(compStruct.subsystems.matrix,2);
-0188         color_map = redblue(length(0:.01:2));
-0189         h = genHeatMap(plottingData',compStruct.subsystems.ID,compStruct.modelIDs,'both','euclidean',color_map,[-1,1]);
-0190         title('Subsystem Coverage - all subsystems','FontSize',18,'FontWeight','bold')
-0191 
-0192         % Plot only subsystems with deviation from mean
-0193         keepSubs = (sum(plottingData~=0,2) ~= 0);
-0194         if sum(keepSubs) > 1
-0195             figure;
-0196             h_small = genHeatMap(plottingData(keepSubs,:)',compStruct.subsystems.ID(keepSubs),...
-0197                 compStruct.modelIDs,'both','euclidean',color_map,[-1,1]);
-0198             title('Subsystem Coverage','FontSize',18,'FontWeight','bold')
-0199 
-0200             % Plot enrichment in subsystems with deviation from mean
-0201             figure;
-0202             color_map_bw = [1 1 1;0 0 0];
-0203             h_enriched = genHeatMap(plottingData(keepSubs,:)',compStruct.subsystems.ID(keepSubs),...
-0204                 compStruct.modelIDs,'both','euclidean',color_map_bw,[-10^-4,10^-4]);
-0205             title('Subsystem Enrichment','FontSize',18,'FontWeight','bold')
-0206         end
-0207     end
-0208 
-0209 end
-0210 
-0211 % Compare overall reaction structure across all models using a heatmap
-0212 field = 'rxns';
-0213 fprintf('\n Comparing model reaction correlations \n')
-0214 
-0215 % Create binary matrix of reactions
-0216 [id,binary_matrix] = compareModelField(models,field);
-0217 compStruct.reactions.IDs = id;
-0218 compStruct.reactions.matrix = binary_matrix;
-0219 
-0220 % calculate hamming similarity (1 - Hamming distance) between models. This
-0221 % is done in base MATLAB so the Statistics Toolbox is not required: for
-0222 % binary columns the agreement fraction equals 1 minus the Hamming distance.
-0223 B = double(binary_matrix);
-0224 compStruct.structComp = (B'*B + (1-B)'*(1-B)) / size(B,1);
-0225 fprintf('*** Done \n\n')
-0226 if plotResults == true
-0227     color_map = [ones(100,1) linspace(1,0,100)' linspace(1,0,100)'];
-0228     figure;
-0229     h = genHeatMap(compStruct.structComp,compStruct.modelIDs,compStruct.modelIDs,'both','euclidean',color_map);
-0230     title('Structural Similarity','FontSize',18,'FontWeight','bold')
-0231 end
-0232 
-0233 % Compare overall reaction structure across all models using a 3D tSNE (or
-0234 % MDS) projection. Both require the Statistics and Machine Learning Toolbox;
-0235 % if neither is available the projection is skipped and structCompMap is left
-0236 % empty.
-0237 rng(42) % For consistency
-0238 fprintf('\n Comparing model reaction structures \n')
-0239 proj_coords = [];
-0240 axis_labels = {};
-0241 if exist('tsne','file') > 0
-0242     proj_coords = tsne(double(binary_matrix'),'Distance','hamming','NumDimensions',3); % 3D
-0243     axis_labels = {'tSNE 1';'tSNE 2';'tSNE 3'};
-0244 elseif exist('mdscale','file') > 0
-0245     proj_coords = mdscale(pdist(double(binary_matrix'),'hamming'),3);
-0246     axis_labels = {'MDS 1';'MDS 2';'MDS 3'};
-0247 else
-0248     fprintf(['   Skipping low-dimensional projection: requires tsne or ' ...
-0249         'mdscale from the Statistics and Machine Learning Toolbox\n']);
-0250 end
-0251 compStruct.structCompMap = proj_coords;
-0252 fprintf('*** Done \n\n')
-0253 
-0254 % plot structure comparison results
-0255 if plotResults == true && ~isempty(proj_coords)
-0256     figure; hold on;
-0257     if ~isempty(groupVector)
-0258         color_data = groupVector;
-0259         if length(groupNames) <= 7
-0260             % "lines" colormap only has 7 unique colors
-0261             color_palette = lines(length(groupNames));
-0262         else
-0263             color_palette = parula(length(groupNames));
-0264         end
-0265         colormap(color_palette);
-0266     else
-0267         color_data = 'k';
-0268     end
-0269     scatter3(proj_coords(:,1),proj_coords(:,2),proj_coords(:,3),35,color_data,'filled');
-0270     view(135,25);  % to make it obvious that it is a 3D plot
-0271     xlabel(axis_labels{1}); ylabel(axis_labels{2}); zlabel(axis_labels{3});
-0272     set(gca,'FontSize',14,'LineWidth',1.25);
-0273     title('Structural Comparison','FontSize',18,'FontWeight','bold')
-0274 
-0275     % add legend
-0276     if ~isempty(groupVector)
-0277         for i = 1:length(groupNames)
-0278             h(i) = scatter3([],[],[],35,color_palette(i,:),'filled');
-0279         end
-0280         legend(h,groupNames);
-0281     end
-0282 end
-0283 
-0284 % Compare model functions by assessing their capacity to perform tasks
-0285 if funcCompare == true && ~isempty(taskFile)
-0286     fprintf('\n Checking model performance on specified tasks. \n')
-0287     taskStructure=parseTaskList(taskFile);
-0288     for i = 1:numel(models)
-0289         fprintf('\n Checking model # %.0f \n',i)
-0290         taskReport{i} = checkTasks(models{i},[],false,false,false,taskStructure);
-0291     end
-0292 
-0293     % Save results
-0294     taskMatrix = zeros(length(taskReport{1}.ok),numel(taskReport));
-0295         for i = 1:numel(taskReport)
-0296             taskMatrix(:,i) = taskReport{i}.ok;
-0297         end
-0298     compStruct.funcComp.matrix = taskMatrix;
-0299     compStruct.funcComp.tasks = taskReport{1}.description;
-0300     fprintf('*** Done \n\n')
-0301 
-0302     % Plot results
-0303     if plotResults == true
-0304         figure;
-0305         color_map_bw = [1 1 1;0 0 0];
-0306         h_enriched = genHeatMap(taskMatrix,compStruct.modelIDs,...
-0307             taskReport{1}.description,'both','euclidean',color_map_bw,[0,1]);
-0308         title('Functional Comparison - All Tasks','FontSize',18,'FontWeight','bold')
-0309 
-0310         figure;
-0311         color_map_bw = [1 1 1;0 0 0];
-0312         h_enriched = genHeatMap(taskMatrix(intersect(find(sum(taskMatrix,2)~=numel(models)),find(sum(taskMatrix,2)~=0)),:),...
-0313             compStruct.modelIDs,taskReport{1}.description(intersect(find(sum(taskMatrix,2)~=numel(models)),find(sum(taskMatrix,2)~=0))),...
-0314             'both','euclidean',color_map_bw,[0,1]);
-0315         title('Functional Similarity','FontSize',18,'FontWeight','bold')
-0316     end
-0317 end
-0318 
-0319 end
-0320 
-0321 %% Additional Functions
-0322 
-0323 function [id,compMat] = compareModelField(models,field)
-0324     % Generates a list of unique field entries and a matrix quantifying the
-0325     % number of appearances of each field entry in each model
-0326 
-0327     % get unique list of field entries
-0328     hasfield = cellfun(@(m) isfield(m,field),models);
-0329     id = cellfun(@(m) m.(field),models(hasfield),'UniformOutput',false);
-0330     id = unique(vertcat(id{:}));
-0331 
-0332     % assemble matrix comparing frequency of each entry in each model
-0333     compMat = zeros(numel(id),numel(models));
-0334     for i = 1:numel(models)
-0335         [~,entryIndex] = ismember(models{i}.(field),id);  % get index of each field entry in the unique id list
-0336         compMat(:,i) = histcounts(entryIndex, 0.5:1:(numel(id)+0.5));  % determine the frequency at which each index appears
-0337     end
-0338 end
-0339 
-0340 function A=getElements(models,field)
-0341 %Gathers the raw field content (e.g. rxns, eccodes, equ) for each model that
-0342 %has that field, for use as the universe of elements in checkStuff.
-0343 A={};
-0344 for i=1:numel(models)
-0345     if isfield(models{i},field)
-0346         A=[A;{models{i}.(field)}];
-0347     end
-0348 end
-0349 end
-0350 
-0351 function toCheck=getToCheck(models,field)
-0352 %Get all the combinations that should be checked for overlap (including the
-0353 %single ones)
-0354 toCheckA=[];
-0355 I=find(cellfun(@checkField,models));
-0356 nI=numel(I);
-0357 for i=nI:-1:1
-0358     combs=nchoosek(1:nI,i);
-0359     toAdd=false(size(combs,1),nI);
-0360     for j=1:size(combs,1)
-0361         toAdd(j,combs(j,:))=true;
-0362     end
-0363     toCheckA=[toCheckA;toAdd];
-0364 end
-0365 
-0366 %If not all of the models have the required field
-0367 toCheck=false(size(toCheckA,1),numel(models));
-0368 toCheck(:,I)=toCheckA;
-0369 
-0370 %Ugly thing to get around parameters
-0371     function I=checkField(A)
-0372         I=isfield(A,field);
-0373     end
-0374 end
-0375 
-0376 function printList(models,toCheck,nElements)
-0377 %To guess how many spaces that are needed to align
-0378 firstLen=[];
-0379 for i=1:size(toCheck,1)
-0380     label=[];
-0381     I=find(toCheck(i,:));
-0382     for j=1:numel(I)
-0383         label=[label models{I(j)}.id '/'];
-0384     end
-0385     if i==1
-0386         firstLen=numel(label);
-0387     end
-0388     nSpaces=firstLen-numel(label);
-0389     fprintf([label(1:end-1) '  ' repmat(sprintf(' '),1,nSpaces) num2str(nElements(i)) '\n']);
-0390 end
-0391 end
-0392 
-0393 function nElements=checkStuff(A,toCheck)
-0394 %Now loop through the toCheck matrix, starting with the combination with
-0395 %the most models. Only elements that were not in iteration n are considered
-0396 %in iteration n+1.
-0397 nElements=zeros(size(toCheck,1),1);
-0398 alreadyChecked=[];
-0399 for i=1:size(toCheck,1)
-0400     I=find(toCheck(i,:));
-0401     inCommon=setdiff(A{I(1)},alreadyChecked);
-0402     for j=2:numel(I)
-0403         inCommon=intersect(inCommon,A{I(j)});
-0404     end
-0405     alreadyChecked=union(alreadyChecked,inCommon);
-0406     nElements(i)=numel(inCommon);
-0407 end
-0408 end
-0409 
-0410 function h = genHeatMap(data,colnames,rownames,clust_dim,clust_dist,col_map,col_bounds,grid_color)
-0411 %genHeatMap  Generate a heatmap for a given matrix of data.
-0412 %
-0413 % Usage:
-0414 %
-0415 %   genHeatMap(data,colnames,rownames,clust_dim,clust_dist,col_map,col_bounds,grid_color);
-0416 %
-0417 % Inputs:
-0418 %
-0419 % data        Numerical matrix.
-0420 %
-0421 % colnames    Cell array of data column names.
-0422 %
-0423 % rownames    Cell array of data row names.
-0424 %
-0425 % clust_dim   "none" - the data will be plotted as provided (DEFAULT)
-0426 %             "rows" - cluster/rearrange the rows based on distance
-0427 %             "cols" - cluster/rearrange the columns based on distance
-0428 %             "both" - cluster/rearrange rows and columns based on distance
-0429 %
-0430 % clust_dist  Distance metric to be used for clustering, ignored if
-0431 %             clust_dim is "none". Options are the same as those for
-0432 %             distance in, e.g., PDIST ("euclidean", "hamming", etc.).
-0433 %             (DEFAULT = "euclidean")
-0434 %
-0435 % col_map     Colormap, provided as string (e.g., "parula", "hot", "etc.")
-0436 %             or an Nx3 RGB matrix of N colors.
-0437 %             (DEFAULT = "hot")
-0438 %
-0439 % col_bounds  A 2-element vector with min and max values, to manually set
-0440 %             the bounds of the colormap.
-0441 %             (DEFAULT = min/max of data).
-0442 %
-0443 % grid_color  Color of the grid surrounding the heatmap cells.
-0444 %             (DEFAULT = "none")
-0445 %
-0446 %
-0447 
-0448 % handle input arguments
-0449 if nargin < 4 || isempty(clust_dim)
-0450     clust_dim = 'none';
-0451 elseif ~ismember(clust_dim,{'none','rows','cols','both'})
-0452     error('%s is not a valid CLUST_DIM option. Choose "none", "rows", "cols", or "both".',clust_dim);
-0453 end
-0454 if nargin < 5 || isempty(clust_dist)
-0455     clust_dist = 'euclidean';
-0456 end
-0457 if nargin < 6 || isempty(col_map)
-0458     col_map = 'hot';
-0459 end
-0460 if nargin < 7 || isempty(col_bounds)
-0461     col_bounds = [min(data(:)),max(data(:))];
-0462 end
-0463 if nargin < 8
-0464     grid_color = 'none';
-0465 end
-0466 
-0467 % perform hierarchical clustering to sort rows/columns (if specified).
-0468 % Clustering needs the Statistics and Machine Learning Toolbox (linkage,
-0469 % optimalleaforder, pdist); without it the heatmap is drawn unordered.
-0470 linkage_method = 'average';
-0471 if ~strcmp(clust_dim,'none') && ~(exist('linkage','file') && ...
-0472         exist('optimalleaforder','file') && exist('pdist','file'))
-0473     fprintf(['   Skipping heatmap clustering: requires the Statistics and ' ...
-0474         'Machine Learning Toolbox\n']);
-0475     clust_dim = 'none';
-0476 end
-0477 if ismember(clust_dim,{'rows','both'})
-0478     L = linkage(data,linkage_method,clust_dist);
-0479     row_ind = optimalleaforder(L,pdist(data,clust_dist));
-0480 else
-0481     row_ind = 1:size(data,1);
-0482 end
-0483 % perform hierarchical clustering to sort columns (if specified)
-0484 if ismember(clust_dim,{'cols','both'})
-0485     L = linkage(data',linkage_method,clust_dist);
-0486     col_ind = optimalleaforder(L,pdist(data',clust_dist));
-0487 else
-0488     col_ind = 1:size(data,2);
-0489 end
-0490 
-0491 % reorder data matrix according to clustering results
-0492 sortdata = data(row_ind,col_ind);
-0493 sortrows = rownames(row_ind);
-0494 sortcols = colnames(col_ind);
-0495 
-0496 % check if data is square matrix with identical row and column names
-0497 if (length(colnames) == length(rownames)) && all(strcmp(colnames,rownames))
-0498     % flip data so the diagonal is from upper left to lower right
-0499     sortdata = fliplr(sortdata);
-0500     sortcols = flipud(sortcols);
-0501 end
-0502 
-0503 % pad data matrix with zeros (pcolor cuts off last row and column)
-0504 sortdata(end+1,end+1) = 0;
-0505 
-0506 % generate pcolor plot
-0507 a = axes;
-0508 set(a,'YAxisLocation','Right','XTick',[],'YTick', (1:size(sortdata,1))+0.5,'YTickLabels',sortrows);
-0509 set(a,'TickLength',[0 0],'XLim',[1 size(sortdata,2)],'YLim',[1 size(sortdata,1)]);
-0510 hold on
-0511 
-0512 h = pcolor(sortdata);
-0513 set(h,'EdgeColor',grid_color);
-0514 set(gca,'XTick', (1:size(sortdata,2))+0.5);
-0515 set(gca,'YTick', (1:size(sortdata,1))+0.5);
-0516 set(gca,'XTickLabels',sortcols,'YTickLabels',sortrows);
-0517 set(gca,'XTickLabelRotation',90);
-0518 colormap(col_map);
-0519 
-0520 if ~isempty(col_bounds)
-0521     caxis(col_bounds);
-0522 end
-0523 
-0524 end
-0525 
-0526 
-0527 function c = redblue(m)
-0528 %REDBLUE    Shades of red and blue color map
-0529 %   REDBLUE(M), is an M-by-3 matrix that defines a colormap.
-0530 %   The colors begin with bright blue, range through shades of
-0531 %   blue to white, and then through shades of red to bright red.
-0532 %   REDBLUE, by itself, is the same length as the current figure
-0533 %   colormap. If no figure exists, MATLAB creates one.
-0534 %
-0535 %   For example, to reset the colormap of the current figure:
-0536 %
-0537 %             colormap(redblue)
-0538 %
-0539 %   See also HSV, GRAY, HOT, BONE, COPPER, PINK, FLAG,
-0540 %   COLORMAP, RGBPLOT.
-0541 if nargin < 1, m = size(get(gcf,'colormap'),1); end
-0542 
-0543 if (mod(m,2) == 0)
-0544     % From [0 0 1] to [1 1 1], then [1 1 1] to [1 0 0];
-0545     m1 = m*0.5;
-0546     r = (0:m1-1)'/max(m1-1,1);
-0547     g = r;
-0548     r = [r; ones(m1,1)];
-0549     g = [g; flipud(g)];
-0550     b = flipud(r);
-0551 else
-0552     % From [0 0 1] to [1 1 1] to [1 0 0];
-0553     m1 = floor(m*0.5);
-0554     r = (0:m1-1)'/max(m1,1);
-0555     g = r;
-0556     r = [r; ones(m1+1,1)];
-0557     g = [g; 1; flipud(g)];
-0558     b = flipud(r);
-0559 end
-0560 
-0561 c = [r g b];
-0562 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/comparison/diffModels.html b/doc/comparison/diffModels.html deleted file mode 100644 index 74c441fd..00000000 --- a/doc/comparison/diffModels.html +++ /dev/null @@ -1,368 +0,0 @@ - - - - Description of diffModels - - - - - - - - - -
Home > comparison > diffModels.m
- - - -

diffModels -

- -

PURPOSE ^

-
diffModels Report the semantic differences between two models.
- -

SYNOPSIS ^

-
function report = diffModels(modelA, modelB, varargin)
- -

DESCRIPTION ^

-
 diffModels  Report the semantic differences between two models.
-
- Compares two models entry by entry, keyed by identifier, and returns a report listing
- every difference found. Reactions, metabolites and genes are matched by
- id; for the ids present in both models the reaction stoichiometry, bounds,
- objective coefficient, grRule, EC codes, and the metabolite formula,
- charge and compartment are compared. This is the structured
- added/removed/changed diff that compareMultipleModels (which reports
- overlap counts, similarity and clustering across N models) does not
- provide.
-
- grRules are compared for logical equality rather than as strings: each
- rule is expanded to disjunctive normal form and the genes within each
- isozyme, and the isozymes themselves, are sorted before comparison, so
- "a and b" equals "b and a" and "(a or b)" equals "(b or a)". This is
- stronger than a case/whitespace string match.
-
- Parameters
- ----------
- modelA : struct
-     the first model.
- modelB : struct
-     the second model.
-
- Name-Value Arguments
- --------------------
- stoichTol : double
-     absolute tolerance for stoichiometric coefficient differences
-     (default 1e-9).
- maxPerCategory : double
-     the maximum number of differences reported per category before the
-     rest are truncated with a single summary line (default 50).
- printResults : logical
-     print the report to the command window (default false).
-
- Returns
- -------
- report : struct
-     difference report with fields:
-
-     - equal : true if no differences were found.
-     - differences : cell array of human-readable difference strings, each
-       naming the category and the entity affected.
-     - rxnsOnlyInA / rxnsOnlyInB : reaction ids present in only one model.
-     - metsOnlyInA / metsOnlyInB : metabolite ids present in only one model.
-     - genesOnlyInA / genesOnlyInB : gene ids present in only one model.
-
- Usage: report = diffModels(modelA, modelB, ...)
-
- See also
- --------
- compareMultipleModels : N models, set membership across seven identity
-     notions (ids, EC codes, metabolite names, equations), similarity
-     matrix, clustering and an optional task-based functional comparison --
-     no values.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function report = diffModels(modelA, modelB, varargin)
-0002 % diffModels  Report the semantic differences between two models.
-0003 %
-0004 % Compares two models entry by entry, keyed by identifier, and returns a report listing
-0005 % every difference found. Reactions, metabolites and genes are matched by
-0006 % id; for the ids present in both models the reaction stoichiometry, bounds,
-0007 % objective coefficient, grRule, EC codes, and the metabolite formula,
-0008 % charge and compartment are compared. This is the structured
-0009 % added/removed/changed diff that compareMultipleModels (which reports
-0010 % overlap counts, similarity and clustering across N models) does not
-0011 % provide.
-0012 %
-0013 % grRules are compared for logical equality rather than as strings: each
-0014 % rule is expanded to disjunctive normal form and the genes within each
-0015 % isozyme, and the isozymes themselves, are sorted before comparison, so
-0016 % "a and b" equals "b and a" and "(a or b)" equals "(b or a)". This is
-0017 % stronger than a case/whitespace string match.
-0018 %
-0019 % Parameters
-0020 % ----------
-0021 % modelA : struct
-0022 %     the first model.
-0023 % modelB : struct
-0024 %     the second model.
-0025 %
-0026 % Name-Value Arguments
-0027 % --------------------
-0028 % stoichTol : double
-0029 %     absolute tolerance for stoichiometric coefficient differences
-0030 %     (default 1e-9).
-0031 % maxPerCategory : double
-0032 %     the maximum number of differences reported per category before the
-0033 %     rest are truncated with a single summary line (default 50).
-0034 % printResults : logical
-0035 %     print the report to the command window (default false).
-0036 %
-0037 % Returns
-0038 % -------
-0039 % report : struct
-0040 %     difference report with fields:
-0041 %
-0042 %     - equal : true if no differences were found.
-0043 %     - differences : cell array of human-readable difference strings, each
-0044 %       naming the category and the entity affected.
-0045 %     - rxnsOnlyInA / rxnsOnlyInB : reaction ids present in only one model.
-0046 %     - metsOnlyInA / metsOnlyInB : metabolite ids present in only one model.
-0047 %     - genesOnlyInA / genesOnlyInB : gene ids present in only one model.
-0048 %
-0049 % Usage: report = diffModels(modelA, modelB, ...)
-0050 %
-0051 % See also
-0052 % --------
-0053 % compareMultipleModels : N models, set membership across seven identity
-0054 %     notions (ids, EC codes, metabolite names, equations), similarity
-0055 %     matrix, clustering and an optional task-based functional comparison --
-0056 %     no values.
-0057 
-0058 p = parseRAVENargs(varargin, {'stoichTol', 1e-9; 'maxPerCategory', 50; ...
-0059     'printResults', false});
-0060 stoichTol = p.stoichTol;
-0061 maxPerCategory = p.maxPerCategory;
-0062 printResults = p.printResults;
-0063 
-0064 diffs = {};                 % collected difference strings
-0065 counts = struct();          % per-category counters, for truncation
-0066 
-0067 report = struct();
-0068 report.equal = true;
-0069 report.differences = {};
-0070 
-0071 % --- Identifier sets --------------------------------------------------
-0072 [report.rxnsOnlyInA, report.rxnsOnlyInB, commonRxns] = idSets(modelA.rxns, modelB.rxns);
-0073 [report.metsOnlyInA, report.metsOnlyInB, commonMets] = idSets(modelA.mets, modelB.mets);
-0074 if isfield(modelA,'genes') && isfield(modelB,'genes')
-0075     [report.genesOnlyInA, report.genesOnlyInB] = idSets(modelA.genes, modelB.genes);
-0076 else
-0077     report.genesOnlyInA = {}; report.genesOnlyInB = {};
-0078 end
-0079 
-0080 reportSet('reactions', report.rxnsOnlyInA, report.rxnsOnlyInB);
-0081 reportSet('metabolites', report.metsOnlyInA, report.metsOnlyInB);
-0082 reportSet('genes', report.genesOnlyInA, report.genesOnlyInB);
-0083 
-0084 % --- Reaction content, on the shared ids ------------------------------
-0085 [~, ia] = ismember(commonRxns, modelA.rxns);
-0086 [~, ib] = ismember(commonRxns, modelB.rxns);
-0087 for k = 1:numel(commonRxns)
-0088     rxn = commonRxns{k};
-0089     ja = ia(k); jb = ib(k);
-0090 
-0091     % Stoichiometry: compare the metabolite id -> coefficient maps.
-0092     [ma, ca] = rxnStoich(modelA, ja);
-0093     [mb, cb] = rxnStoich(modelB, jb);
-0094     if ~isequal(sort(ma), sort(mb))
-0095         push('stoich', sprintf('%s: stoichiometry involves different metabolites', rxn));
-0096     else
-0097         [~, locb] = ismember(ma, mb);
-0098         for m = 1:numel(ma)
-0099             if abs(ca(m) - cb(locb(m))) > stoichTol
-0100                 push('stoich', sprintf('%s: coef[%s] A=%g B=%g', rxn, ma{m}, ca(m), cb(locb(m))));
-0101             end
-0102         end
-0103     end
-0104 
-0105     % Bounds.
-0106     if modelA.lb(ja) ~= modelB.lb(jb) || modelA.ub(ja) ~= modelB.ub(jb)
-0107         push('bounds', sprintf('%s: bounds A=[%g,%g] B=[%g,%g]', rxn, ...
-0108             modelA.lb(ja), modelA.ub(ja), modelB.lb(jb), modelB.ub(jb)));
-0109     end
-0110 
-0111     % Objective coefficient.
-0112     if fieldAt(modelA,'c',ja,0) ~= fieldAt(modelB,'c',jb,0)
-0113         push('objective', sprintf('%s: objective A=%g B=%g', rxn, ...
-0114             fieldAt(modelA,'c',ja,0), fieldAt(modelB,'c',jb,0)));
-0115     end
-0116 
-0117     % grRule, compared as logic rather than text.
-0118     ga = canonicalGpr(cellAt(modelA,'grRules',ja));
-0119     gb = canonicalGpr(cellAt(modelB,'grRules',jb));
-0120     if ~strcmp(ga, gb)
-0121         push('gpr', sprintf('%s: grRule A="%s" B="%s"', rxn, ...
-0122             cellAt(modelA,'grRules',ja), cellAt(modelB,'grRules',jb)));
-0123     end
-0124 
-0125     % EC codes (RAVEN's first-class annotation field).
-0126     ea = cellAt(modelA,'eccodes',ja);
-0127     eb = cellAt(modelB,'eccodes',jb);
-0128     if ~strcmp(ea, eb)
-0129         push('eccodes', sprintf('%s: eccodes A="%s" B="%s"', rxn, ea, eb));
-0130     end
-0131 end
-0132 
-0133 % --- Metabolite content, on the shared ids ----------------------------
-0134 [~, ia] = ismember(commonMets, modelA.mets);
-0135 [~, ib] = ismember(commonMets, modelB.mets);
-0136 for k = 1:numel(commonMets)
-0137     met = commonMets{k};
-0138     ja = ia(k); jb = ib(k);
-0139 
-0140     fa = cellAt(modelA,'metFormulas',ja);
-0141     fb = cellAt(modelB,'metFormulas',jb);
-0142     if ~strcmp(fa, fb)
-0143         push('formula', sprintf('%s: formula A="%s" B="%s"', met, fa, fb));
-0144     end
-0145 
-0146     if ~isequaln(fieldAt(modelA,'metCharges',ja,NaN), fieldAt(modelB,'metCharges',jb,NaN))
-0147         push('charge', sprintf('%s: charge A=%g B=%g', met, ...
-0148             fieldAt(modelA,'metCharges',ja,NaN), fieldAt(modelB,'metCharges',jb,NaN)));
-0149     end
-0150 
-0151     cpA = metComp(modelA, ja);
-0152     cpB = metComp(modelB, jb);
-0153     if ~strcmp(cpA, cpB)
-0154         push('compartment', sprintf('%s: compartment A="%s" B="%s"', met, cpA, cpB));
-0155     end
-0156 end
-0157 
-0158 report.equal = isempty(diffs);
-0159 report.differences = diffs(:);
-0160 
-0161 if printResults
-0162     if report.equal
-0163         fprintf('Models are semantically equal.\n');
-0164     else
-0165         fprintf('Models differ (%d differences):\n', numel(diffs));
-0166         fprintf('  - %s\n', diffs{:});
-0167     end
-0168 end
-0169 
-0170     % --- nested helpers (share diffs / counts / maxPerCategory) --------
-0171     function push(category, message)
-0172         if ~isfield(counts, category)
-0173             counts.(category) = 0;
-0174         end
-0175         counts.(category) = counts.(category) + 1;
-0176         if counts.(category) <= maxPerCategory
-0177             diffs{end+1,1} = message; %#ok<AGROW>
-0178         elseif counts.(category) == maxPerCategory + 1
-0179             diffs{end+1,1} = sprintf('... (%s category truncated at %d entries)', ...
-0180                 category, maxPerCategory); %#ok<AGROW>
-0181         end
-0182     end
-0183 
-0184     function reportSet(label, onlyA, onlyB)
-0185         if ~isempty(onlyA)
-0186             push([label '_only_in_a'], sprintf('%d %s only in A: %s', ...
-0187                 numel(onlyA), label, joinTruncated(onlyA)));
-0188         end
-0189         if ~isempty(onlyB)
-0190             push([label '_only_in_b'], sprintf('%d %s only in B: %s', ...
-0191                 numel(onlyB), label, joinTruncated(onlyB)));
-0192         end
-0193     end
-0194 end
-0195 
-0196 % --- local helpers ----------------------------------------------------
-0197 function [onlyA, onlyB, common] = idSets(a, b)
-0198 onlyA = setdiff(a, b);
-0199 onlyB = setdiff(b, a);
-0200 common = sort(intersect(a, b));
-0201 onlyA = sort(onlyA(:))'; onlyB = sort(onlyB(:))';
-0202 onlyA = onlyA(:); onlyB = onlyB(:);
-0203 end
-0204 
-0205 function [mets, coeffs] = rxnStoich(model, j)
-0206 nz = find(model.S(:, j));
-0207 mets = model.mets(nz);
-0208 mets = mets(:)';
-0209 coeffs = full(model.S(nz, j))';
-0210 end
-0211 
-0212 function v = fieldAt(model, field, j, default)
-0213 if isfield(model, field) && numel(model.(field)) >= j
-0214     v = model.(field)(j);
-0215     if iscell(v), v = v{1}; end
-0216     if isempty(v), v = default; end
-0217 else
-0218     v = default;
-0219 end
-0220 end
-0221 
-0222 function s = cellAt(model, field, j)
-0223 % A char scalar from a cell-array field, '' when absent or empty.
-0224 if isfield(model, field) && numel(model.(field)) >= j
-0225     s = model.(field){j};
-0226     if isempty(s), s = ''; end
-0227 else
-0228     s = '';
-0229 end
-0230 end
-0231 
-0232 function cp = metComp(model, j)
-0233 if isfield(model, 'metComps') && isfield(model, 'comps')
-0234     cp = model.comps{model.metComps(j)};
-0235 else
-0236     cp = '';
-0237 end
-0238 end
-0239 
-0240 function s = canonicalGpr(rule)
-0241 % A canonical string for a grRule under which isozymes and their subunits
-0242 % are order-insensitive. Empty rule -> ''.
-0243 if isempty(rule)
-0244     s = '';
-0245     return;
-0246 end
-0247 clauses = grRuleToDNF(rule);
-0248 parts = cell(1, numel(clauses));
-0249 for i = 1:numel(clauses)
-0250     genes = sort(unique(clauses{i}));
-0251     parts{i} = strjoin(genes(:)', ' and ');
-0252 end
-0253 parts = sort(parts);
-0254 s = strjoin(parts, ' or ');
-0255 end
-0256 
-0257 function s = joinTruncated(ids)
-0258 % Join up to the first 10 ids, appending a count of the remainder.
-0259 ids = ids(:)';
-0260 if numel(ids) > 10
-0261     s = [strjoin(ids(1:10), ', ') sprintf(', ... (%d more)', numel(ids) - 10)];
-0262 else
-0263     s = strjoin(ids, ', ');
-0264 end
-0265 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/conditions/applyCondition.html b/doc/conditions/applyCondition.html deleted file mode 100644 index 0ea72f4d..00000000 --- a/doc/conditions/applyCondition.html +++ /dev/null @@ -1,260 +0,0 @@ - - - - Description of applyCondition - - - - - - - - - -
Home > conditions > applyCondition.m
- - - -

applyCondition -

- -

PURPOSE ^

-
applyCondition Apply a deterministic condition to a model.
- -

SYNOPSIS ^

-
function model = applyCondition(model, condition)
- -

DESCRIPTION ^

-
 applyCondition  Apply a deterministic condition to a model.
-
- Apply a deterministic "condition" to a model: a prelude that resets
- exchange bounds, optional metabolite removals + automatic charge
- rebalancing of a pseudoreaction, optional biomass-stoichiometry delta,
- and a per-reaction bounds diff. The schema is intentionally narrow so a
- condition can be reviewed as data.
-
- The same schema works for any GEM that keeps its condition presets as
- data rather than as code. Project-specific extensions (e.g. yeast-GEM's amino_acid_ratio step that
- rewrites a protein pseudoreaction's stoichiometry from a side-car TSV)
- are handled by the caller before / after this function — kept
- upstream-narrow on purpose.
-
- Parameters
- ----------
- model : struct
-     RAVEN model struct.
- condition : char or struct
-     Either a path to a YAML condition file or a struct already produced
-     by parseYAML. The expected schema (all keys optional):
-
-         prelude:
-           reset_exchanges: out      % truthy -> reset all
-
-         cofactor_pseudoreaction:
-           rxn_id: r_4598
-           remove_mets:
-             - { met: s_3714 }
-           charge_balance_met: s_0794
-
-         biomass_stoichiometry_delta:
-           rxn_id: r_4041
-           add:
-             - { met: s_0689, coef:  0.08 }
-             - { met: s_0687, coef: -0.08 }
-             - { met: s_0794, coef: -0.16 }
-
-         bounds:
-           - { rxn: r_1654, lb: -1000 }
-           - { rxn: r_1992, lb: 0 }
-           - { rxn: r_1663, lb: 0, ub: 0 }
-
-         expected_uptake_count: 15
-
- Returns
- -------
- model : struct
-     Modified model.
-
- Examples
- --------
-     model = applyCondition(model, 'data/conditions/anaerobic.yml');
-     model = applyCondition(model, parseYAML('data/conditions/anaerobic.yml'));
-
- See also
- --------
- parseYAML
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function model = applyCondition(model, condition)
-0002 % applyCondition  Apply a deterministic condition to a model.
-0003 %
-0004 % Apply a deterministic "condition" to a model: a prelude that resets
-0005 % exchange bounds, optional metabolite removals + automatic charge
-0006 % rebalancing of a pseudoreaction, optional biomass-stoichiometry delta,
-0007 % and a per-reaction bounds diff. The schema is intentionally narrow so a
-0008 % condition can be reviewed as data.
-0009 %
-0010 % The same schema works for any GEM that keeps its condition presets as
-0011 % data rather than as code. Project-specific extensions (e.g. yeast-GEM's amino_acid_ratio step that
-0012 % rewrites a protein pseudoreaction's stoichiometry from a side-car TSV)
-0013 % are handled by the caller before / after this function — kept
-0014 % upstream-narrow on purpose.
-0015 %
-0016 % Parameters
-0017 % ----------
-0018 % model : struct
-0019 %     RAVEN model struct.
-0020 % condition : char or struct
-0021 %     Either a path to a YAML condition file or a struct already produced
-0022 %     by parseYAML. The expected schema (all keys optional):
-0023 %
-0024 %         prelude:
-0025 %           reset_exchanges: out      % truthy -> reset all
-0026 %
-0027 %         cofactor_pseudoreaction:
-0028 %           rxn_id: r_4598
-0029 %           remove_mets:
-0030 %             - { met: s_3714 }
-0031 %           charge_balance_met: s_0794
-0032 %
-0033 %         biomass_stoichiometry_delta:
-0034 %           rxn_id: r_4041
-0035 %           add:
-0036 %             - { met: s_0689, coef:  0.08 }
-0037 %             - { met: s_0687, coef: -0.08 }
-0038 %             - { met: s_0794, coef: -0.16 }
-0039 %
-0040 %         bounds:
-0041 %           - { rxn: r_1654, lb: -1000 }
-0042 %           - { rxn: r_1992, lb: 0 }
-0043 %           - { rxn: r_1663, lb: 0, ub: 0 }
-0044 %
-0045 %         expected_uptake_count: 15
-0046 %
-0047 % Returns
-0048 % -------
-0049 % model : struct
-0050 %     Modified model.
-0051 %
-0052 % Examples
-0053 % --------
-0054 %     model = applyCondition(model, 'data/conditions/anaerobic.yml');
-0055 %     model = applyCondition(model, parseYAML('data/conditions/anaerobic.yml'));
-0056 %
-0057 % See also
-0058 % --------
-0059 % parseYAML
-0060 
-0061 if ischar(condition) || isstring(condition)
-0062     cond = parseYAML(char(condition));
-0063 elseif isstruct(condition)
-0064     cond = condition;
-0065 else
-0066     error('applyCondition:invalidCondition', ...
-0067         'condition must be a YAML file path or a struct.');
-0068 end
-0069 
-0070 % --- Step 1: prelude ---------------------------------------------------
-0071 if isfield(cond, 'prelude') && isfield(cond.prelude, 'reset_exchanges')
-0072     [~, exchangeRxns] = getExchangeRxns(model, cond.prelude.reset_exchanges);
-0073     model.lb(exchangeRxns) = 0;
-0074     model.ub(exchangeRxns) = 1000;
-0075 end
-0076 
-0077 % --- Step 2: cofactor pseudoreaction edits ----------------------------
-0078 if isfield(cond, 'cofactor_pseudoreaction')
-0079     cp = cond.cofactor_pseudoreaction;
-0080     cofacIdx = getIndexes(model, cp.rxn_id, 'rxns');
-0081     if isfield(cp, 'remove_mets')
-0082         for i = 1:numel(cp.remove_mets)
-0083             metIdx = getIndexes(model, cp.remove_mets{i}.met, 'mets');
-0084             model.S(metIdx, cofacIdx) = 0;
-0085         end
-0086     end
-0087     if isfield(cp, 'charge_balance_met')
-0088         balanceIdx = find(strcmp(model.mets, cp.charge_balance_met));
-0089         model.S(balanceIdx, cofacIdx) = 0;
-0090         % Only the participating metabolites may be summed: S is sparse and
-0091         % 0*NaN is NaN, while 'omitnan' would treat an unset charge as
-0092         % neutral and silently write the wrong balancing coefficient.
-0093         metIdx = find(model.S(:, cofacIdx));
-0094         if ~isfield(model, 'metCharges')
-0095             error('applyCondition:noCharges', ...
-0096                 ['Cannot charge balance %s: the model has no metCharges ' ...
-0097                  'field.'], cp.rxn_id);
-0098         end
-0099         unknown = metIdx(isnan(model.metCharges(metIdx)));
-0100         if ~isempty(unknown)
-0101             error('applyCondition:unknownCharge', ...
-0102                 ['Cannot charge balance %s: the following metabolites have ' ...
-0103                  'no charge, so the charge balance is unknown rather than ' ...
-0104                  'zero:\n\t%s'], cp.rxn_id, strjoin(model.mets(unknown), ', '));
-0105         end
-0106         model.S(balanceIdx, cofacIdx) = ...
-0107             -sum(full(model.S(metIdx, cofacIdx)) .* model.metCharges(metIdx));
-0108     end
-0109 end
-0110 
-0111 % --- Step 3: biomass stoichiometry delta ------------------------------
-0112 if isfield(cond, 'biomass_stoichiometry_delta')
-0113     delta = cond.biomass_stoichiometry_delta;
-0114     bioIdx = getIndexes(model, delta.rxn_id, 'rxns');
-0115     if isfield(delta, 'add')
-0116         for i = 1:numel(delta.add)
-0117             entry = delta.add{i};
-0118             metIdx = getIndexes(model, entry.met, 'mets');
-0119             model.S(metIdx, bioIdx) = full(model.S(metIdx, bioIdx)) + entry.coef;
-0120         end
-0121     end
-0122 end
-0123 
-0124 % --- Step 4: bounds ---------------------------------------------------
-0125 nUptake = 0;
-0126 if isfield(cond, 'bounds')
-0127     for i = 1:numel(cond.bounds)
-0128         b = cond.bounds{i};
-0129         rxnIdx = find(strcmp(model.rxns, b.rxn));
-0130         if isempty(rxnIdx)
-0131             warning('applyCondition:missingRxn', ...
-0132                 'Reaction %s not found in model; skipping.', b.rxn);
-0133             continue;
-0134         end
-0135         if isfield(b, 'lb')
-0136             model.lb(rxnIdx) = b.lb;
-0137             if b.lb == -1000
-0138                 nUptake = nUptake + 1;
-0139             end
-0140         end
-0141         if isfield(b, 'ub')
-0142             model.ub(rxnIdx) = b.ub;
-0143         end
-0144     end
-0145 end
-0146 
-0147 % --- Step 5: uptake sanity check --------------------------------------
-0148 if isfield(cond, 'expected_uptake_count')
-0149     if nUptake ~= cond.expected_uptake_count
-0150         warning('applyCondition:uptakeCountMismatch', ...
-0151             'Expected %d uptake reactions, applied %d. Some may be missing from the model.', ...
-0152             cond.expected_uptake_count, nUptake);
-0153     end
-0154 end
-0155 
-0156 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/conversion/addIdentifierPrefix.html b/doc/conversion/addIdentifierPrefix.html deleted file mode 100644 index 24266779..00000000 --- a/doc/conversion/addIdentifierPrefix.html +++ /dev/null @@ -1,154 +0,0 @@ - - - - Description of addIdentifierPrefix - - - - - - - - - -
Home > conversion > addIdentifierPrefix.m
- - - -

addIdentifierPrefix -

- -

PURPOSE ^

-
addIdentifierPrefix Add identifier prefixes required by SBML.
- -

SYNOPSIS ^

-
function [model, hasChanged]=addIdentifierPrefix(model,varargin)
- -

DESCRIPTION ^

-
 addIdentifierPrefix  Add identifier prefixes required by SBML.
-
- If reaction, metabolite, compartment, gene or model identifiers do not
- start with a letter or _, which conflicts with SBML specifications,
- prefixes are added for all identifiers in the respective model field.
- The prefixes are:
-
-     "R_" for model.rxns,
-     "M_" for model.mets,
-     "C_" for model.comps;
-     "G_" for model.genes (and also represented in model.grRules)
-
- Parameters
- ----------
- model : struct
-     model whose identifiers should be modified.
-
- Name-Value Arguments
- --------------------
- fields : cell
-     cell array with model field names that should be checked if prefixes
-     should be added, possible values: 'rxns', 'mets', 'comps', 'genes',
-     'id' (default all listed model fields will be checked).
-
- Returns
- -------
- model : struct
-     modified model.
- hasChanged : cell
-     cell array with fields and prefixes that are added.
-
- Examples
- --------
-     [model, hasChanged] = addIdentifierPrefix(model, fields);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function [model, hasChanged]=addIdentifierPrefix(model,varargin)
-0002 % addIdentifierPrefix  Add identifier prefixes required by SBML.
-0003 %
-0004 % If reaction, metabolite, compartment, gene or model identifiers do not
-0005 % start with a letter or _, which conflicts with SBML specifications,
-0006 % prefixes are added for all identifiers in the respective model field.
-0007 % The prefixes are:
-0008 %
-0009 %     "R_" for model.rxns,
-0010 %     "M_" for model.mets,
-0011 %     "C_" for model.comps;
-0012 %     "G_" for model.genes (and also represented in model.grRules)
-0013 %
-0014 % Parameters
-0015 % ----------
-0016 % model : struct
-0017 %     model whose identifiers should be modified.
-0018 %
-0019 % Name-Value Arguments
-0020 % --------------------
-0021 % fields : cell
-0022 %     cell array with model field names that should be checked if prefixes
-0023 %     should be added, possible values: 'rxns', 'mets', 'comps', 'genes',
-0024 %     'id' (default all listed model fields will be checked).
-0025 %
-0026 % Returns
-0027 % -------
-0028 % model : struct
-0029 %     modified model.
-0030 % hasChanged : cell
-0031 %     cell array with fields and prefixes that are added.
-0032 %
-0033 % Examples
-0034 % --------
-0035 %     [model, hasChanged] = addIdentifierPrefix(model, fields);
-0036 
-0037 p=parseRAVENargs(varargin, {'fields',[]});
-0038 fields=p.fields;
-0039 if isempty(fields)
-0040     fields = {'rxns','mets','comps','genes','id'};
-0041 end
-0042 
-0043 modelFields = {'rxns','R_';
-0044                'mets','M_';
-0045                'comps','C_';
-0046                'genes','G_';
-0047                'id','M_'};
-0048 
-0049 toChangeIdx = find(ismember(modelFields(:,1),fields));
-0050 hasChanged  = false(numel(modelFields(:,1)),1);
-0051 for i=1:numel(toChangeIdx)
-0052     currName    = modelFields{toChangeIdx(i),1};
-0053     currPrefix  = modelFields{toChangeIdx(i),2};
-0054     if isfield(model,currName)
-0055         currField   = model.(currName);
-0056     else
-0057         continue;
-0058     end
-0059     if ~all(startsWith(currField,regexpPattern('^[a-zA-Z_]')))
-0060         currField = strcat(currPrefix, currField);
-0061         hasChanged(toChangeIdx(i)) = true;
-0062 
-0063         if strcmp(currName,'genes')
-0064                 model.grRules = regexprep(model.grRules, '(\<[0-9_a-zA-Z])', 'G_$1');
-0065                 model.grRules = regexprep(model.grRules, ' G_or ', ' or ');
-0066                 model.grRules = regexprep(model.grRules, ' G_and ', ' and ');
-0067         end
-0068         model.(currName) = currField;
-0069     end
-0070 end
-0071 
-0072 hasChanged = modelFields(hasChanged,:);
-0073 hasChanged = append('model.', hasChanged(:,1), ' (', hasChanged(:,2), ' prefix)');
-0074 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/conversion/ravenCobraWrapper.html b/doc/conversion/ravenCobraWrapper.html deleted file mode 100644 index 4a2bde36..00000000 --- a/doc/conversion/ravenCobraWrapper.html +++ /dev/null @@ -1,545 +0,0 @@ - - - - Description of ravenCobraWrapper - - - - - - - - - -
Home > conversion > ravenCobraWrapper.m
- - - -

ravenCobraWrapper -

- -

PURPOSE ^

-
ravenCobraWrapper Convert between RAVEN and COBRA structures.
- -

SYNOPSIS ^

-
function newModel=ravenCobraWrapper(model)
- -

DESCRIPTION ^

-
 ravenCobraWrapper  Convert between RAVEN and COBRA structures.
-
- Recognises a COBRA structure by checking for the field 'rules', found
- only in a COBRA Toolbox structure. If the COBRA model also has a grRules
- field, that is used instead of parsing rules.
-
- Parameters
- ----------
- model : struct
-     a RAVEN/COBRA-compatible model structure.
-
- Returns
- -------
- newModel : struct
-     a COBRA/RAVEN-compatible model structure.
-
- Examples
- --------
-     newModel = ravenCobraWrapper(model);
-
- Notes
- -----
- During the RAVEN -> COBRA -> RAVEN conversion cycle the following fields
- are lost: annotation, compOutside, compMiriams, rxnComps, geneComps,
- unconstrained. Boundary metabolites are lost, because the COBRA
- structure does not involve boundary metabolites, so they are removed
- using simplifyModel before RAVEN -> COBRA conversion. The field 'rev' is
- also partially lost, but during COBRA -> RAVEN conversion it is
- reconstructed based on lower bound reaction values.
-
- During the COBRA -> RAVEN -> COBRA conversion cycle the following fields
- are lost: geneEntrezID, modelVersion, proteins.
-
- The information about mandatory RAVEN fields was taken from the
- checkModelStruct function, whereas the corresponding information about
- COBRA fields was fetched from the verifyModel function.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function newModel=ravenCobraWrapper(model)
-0002 % ravenCobraWrapper  Convert between RAVEN and COBRA structures.
-0003 %
-0004 % Recognises a COBRA structure by checking for the field 'rules', found
-0005 % only in a COBRA Toolbox structure. If the COBRA model also has a grRules
-0006 % field, that is used instead of parsing rules.
-0007 %
-0008 % Parameters
-0009 % ----------
-0010 % model : struct
-0011 %     a RAVEN/COBRA-compatible model structure.
-0012 %
-0013 % Returns
-0014 % -------
-0015 % newModel : struct
-0016 %     a COBRA/RAVEN-compatible model structure.
-0017 %
-0018 % Examples
-0019 % --------
-0020 %     newModel = ravenCobraWrapper(model);
-0021 %
-0022 % Notes
-0023 % -----
-0024 % During the RAVEN -> COBRA -> RAVEN conversion cycle the following fields
-0025 % are lost: annotation, compOutside, compMiriams, rxnComps, geneComps,
-0026 % unconstrained. Boundary metabolites are lost, because the COBRA
-0027 % structure does not involve boundary metabolites, so they are removed
-0028 % using simplifyModel before RAVEN -> COBRA conversion. The field 'rev' is
-0029 % also partially lost, but during COBRA -> RAVEN conversion it is
-0030 % reconstructed based on lower bound reaction values.
-0031 %
-0032 % During the COBRA -> RAVEN -> COBRA conversion cycle the following fields
-0033 % are lost: geneEntrezID, modelVersion, proteins.
-0034 %
-0035 % The information about mandatory RAVEN fields was taken from the
-0036 % checkModelStruct function, whereas the corresponding information about
-0037 % COBRA fields was fetched from the verifyModel function.
-0038 
-0039 if isfield(model,'rules')
-0040     isRaven=false;
-0041 else
-0042     isRaven=true;
-0043 end
-0044 
-0045 ravenPath=findRAVENroot();
-0046 
-0047 % Load COBRA field information
-0048 fid             = fopen(fullfile(ravenPath,'conversion','COBRA_structure_fields.csv')); % Taken from https://github.com/opencobra/cobratoolbox/blob/develop/src/base/io/definitions/COBRA_structure_fields.csv
-0049 fieldFile       = textscan(fid,repmat('%s',1,15),'Delimiter','\t','HeaderLines',1);
-0050 dbFields        = ~cellfun(@isempty,fieldFile{5}); % Only keep fields with database annotations that should be translated to xxxMiriams
-0051 dbFields        = dbFields & ~contains(fieldFile{1},{'metInChIString','metKEGGID','metPubChemID','rxnECNumbers'});
-0052 COBRAnamespace  = fieldFile{5}(dbFields);
-0053 COBRAnamespace  = regexprep(COBRAnamespace,';.*',''); % Only keep first suggested namespace
-0054 COBRAfields     = fieldFile{1}(dbFields);
-0055 fclose(fid);
-0056 
-0057 % Load conversion between additional COBRA fields and namespaces:
-0058 fid             = fopen(fullfile(ravenPath,'conversion','cobraNamespaces.csv'));
-0059 fieldFile       = textscan(fid,'%s %s','Delimiter',',','HeaderLines',0);
-0060 COBRAfields     = [COBRAfields; fieldFile{1}];
-0061 COBRAnamespace  = [COBRAnamespace; fieldFile{2}];
-0062 rxnCOBRAfields  = COBRAfields(startsWith(COBRAfields,'rxn'));
-0063 rxnNamespaces   = COBRAnamespace(startsWith(COBRAfields,'rxn'));
-0064 metCOBRAfields  = COBRAfields(startsWith(COBRAfields,'met'));
-0065 metNamespaces   = COBRAnamespace(startsWith(COBRAfields,'met'));
-0066 geneCOBRAfields = COBRAfields(startsWith(COBRAfields,'gene'));
-0067 geneNamespaces  = COBRAnamespace(startsWith(COBRAfields,'gene'));
-0068 fclose(fid);
-0069 
-0070 if isRaven
-0071     %Remove boundary metabolites
-0072     model=simplifyModel(model);
-0073 end
-0074 
-0075 % Keep fields that have identical names and content
-0076 newModel.S=model.S;
-0077 newModel.lb=model.lb;
-0078 newModel.ub=model.ub;
-0079 if isfield(model,'c')
-0080     newModel.c=model.c;
-0081 else
-0082     newModel.c=zeros(numel(model.rxns),1);
-0083 end
-0084 newModel.rxns=model.rxns;
-0085 optFields = {'rxnNames','rxnNotes','rxnConfidenceScores','rxnDeltaG',...
-0086     'rxnGeneMat','rev','subSystems','comps','compNames','metCharges',...
-0087     'metDeltaG','metFormulas','metNotes','metSmiles','genes','proteins'};
-0088 for i=1:length(optFields)
-0089     if isfield(model,optFields{i})
-0090         newModel.(optFields{i})=model.(optFields{i});
-0091     end
-0092 end
-0093     
-0094 % Convert unique fields
-0095 if isRaven
-0096     fprintf('Converting RAVEN structure to COBRA..\n');
-0097     %Convert from RAVEN to COBRA structure
-0098     
-0099     %Mandatory COBRA fields
-0100     newModel.rxns=model.rxns;
-0101     if all(~cellfun(@isempty,regexp(model.mets,'\[[^\]]+\]$')))
-0102         newModel.mets=model.mets;
-0103     else
-0104         %Check if model has compartment info as "met_c" suffix in all metabolites:
-0105         BiGGformat = false(size(model.mets));
-0106         for i=1:numel(model.comps)
-0107             compPos=model.metComps==i;
-0108             BiGGformat(compPos)=~cellfun(@isempty,regexp(model.mets(compPos),['_' model.comps{i} '$']));
-0109         end
-0110         if all(BiGGformat)
-0111             newModel.mets=model.mets;
-0112             for i=1:numel(model.comps)
-0113                 newModel.mets=regexprep(newModel.mets,['_' model.comps{i} '$'],['[' model.comps{i} ']']);
-0114             end
-0115         else
-0116             newModel.mets=strcat(model.mets,'[',model.comps(model.metComps),']');
-0117         end
-0118     end
-0119 
-0120     %b, csense, osenseStr, genes, rules are also mandatory, but defined
-0121     %later to match the order of fields
-0122     
-0123     %Optional COBRA fields
-0124     if isfield(model,'id')
-0125         newModel.modelID=model.id;
-0126     end
-0127     if isfield(model,'name')
-0128         newModel.modelName=model.name;
-0129     end
-0130     if isfield(model,'eccodes')
-0131         newModel.rxnECNumbers=model.eccodes;
-0132     end
-0133     if isfield(model,'rxnMiriams')
-0134         [miriams,extractedMiriamNames]=extractMiriam(model.rxnMiriams);
-0135         for i = 1:length(rxnCOBRAfields)
-0136             j=ismember(extractedMiriamNames,rxnNamespaces{i});
-0137             if any(j)
-0138                 newModel.(rxnCOBRAfields{i}) = miriams(:,j);
-0139             end
-0140         end
-0141     end
-0142     if isfield(model,'rxnReferences') % Concatenate model.rxnReferences to those extracted from model.rxnMiriams
-0143         if isfield(newModel,'rxnReferences')
-0144             newModel.rxnReferences = strcat(newModel.rxnReferences,{'; '},model.rxnReferences);
-0145             newModel.rxnReferences = regexprep(newModel.rxnReferences,'^; $','');
-0146         else
-0147             newModel.rxnReferences = model.rxnReferences;
-0148         end
-0149     end
-0150     if isfield(model,'metNames')
-0151         newModel.metNames=strcat(model.metNames,' [',model.compNames(model.metComps),']');
-0152     end
-0153     if isfield(model,'metMiriams')
-0154         [miriams,extractedMiriamNames]=extractMiriam(model.metMiriams);
-0155         %Shorten miriam names for KEGG and PubChem. These shorter names
-0156         %will be used later to concatenate KEGG COMPOUND/GLYCAN and PubChem
-0157         %Compound/Substance, into corresponding COBRA model fields
-0158         extractedMiriamNames=regexprep(extractedMiriamNames,'^kegg\..+','kegg');
-0159         extractedMiriamNames=regexprep(extractedMiriamNames,'^pubchem\..+','pubchem');
-0160         i=ismember(extractedMiriamNames,'kegg');
-0161         if any(i) % Combine KEGG compounds and glycans
-0162             for j=1:length(i)
-0163                 if i(j) && isfield(newModel,'metKEGGID')~=1
-0164                     newModel.metKEGGID=miriams(:,j);
-0165                 elseif i(j)
-0166                     newModel.metKEGGID=strcat(newModel.metKEGGID,';',miriams(:,j));
-0167                 end
-0168             end
-0169             newModel.metKEGGID=regexprep(newModel.metKEGGID,'^;|;$','');
-0170         end
-0171         i=ismember(extractedMiriamNames,'pubchem');
-0172         if any(i) % Combine Pubchem compounds and substances
-0173             for j=1:length(i)
-0174                 if i(j) && isfield(newModel,'metPubChemID')~=1
-0175                     newModel.metPubChemID=miriams(:,j);
-0176                 elseif i(j)
-0177                     newModel.metPubChemID=strcat(newModel.metPubChemID,';',miriams(:,j));
-0178                 end
-0179             end
-0180             newModel.metPubChemID=regexprep(newModel.metPubChemID,'^;|;$','');
-0181         end
-0182         %All other Miriams can be directly parsed with no modifications:
-0183         for i = 1:length(metCOBRAfields)
-0184             j=ismember(extractedMiriamNames,metNamespaces{i});
-0185             if any(j)
-0186                 newModel.(metCOBRAfields{i}) = miriams(:,j);
-0187             end
-0188         end
-0189     end
-0190     if isfield(model,'inchis')
-0191         newModel.metInChIString=regexprep(strcat('InChI=', model.inchis),'^InChI=$','');
-0192     end
-0193     newModel.b=zeros(numel(model.mets),1);
-0194     newModel.csense=repmat('E',size(model.mets));
-0195     if isfield(model,'geneMiriams')
-0196         [miriams,extractedMiriamNames]=extractMiriam(model.geneMiriams);
-0197         for i = 1:length(geneCOBRAfields)
-0198             j=ismember(extractedMiriamNames,geneNamespaces{i});
-0199             if any(j)
-0200                 newModel.(geneCOBRAfields{i}) = miriams(:,j);
-0201             end
-0202         end
-0203     end
-0204     if isfield(model,'geneShortNames')
-0205         newModel.geneNames=model.geneShortNames;
-0206     end
-0207     if isfield(model,'genes')
-0208         newModel.rules=grrulesToRules(model);
-0209     else
-0210         fprintf('WARNING: no genes detected. The model therefore may not be exportable to SBML file with writeCbModel\n');
-0211     end
-0212     % If needed, normalise subSystems to cell-of-cells format
-0213     if isfield(newModel,'subSystems') && ~isempty(newModel.subSystems)
-0214         if ~iscell(newModel.subSystems{1})
-0215             newModel.subSystems = cellfun(@(x) {x}, newModel.subSystems, 'UniformOutput', false);
-0216         end
-0217     end
-0218 else
-0219     fprintf('Converting COBRA structure to RAVEN..\n');
-0220     %Convert from COBRA to RAVEN structure
-0221 
-0222     %Mandatory RAVEN fields
-0223     newModel.mets=model.mets;
-0224     if ~isfield(model,'comps')
-0225         %Since 'comps' field is not mandatory in COBRA, it may be required
-0226         %to obtain the non-redundant list of comps from metabolite ids, if
-0227         %'comps' field is not available
-0228         newModel.comps = unique(regexprep(model.mets,'.*\[([^\]]+)\]$','$1'));
-0229         newModel.compNames = newModel.comps;
-0230     end
-0231     for i=1:numel(newModel.comps)
-0232         newModel.mets=regexprep(newModel.mets,['\[', newModel.comps{i}, '\]$'],'');
-0233         newModel.mets=regexprep(newModel.mets,['\[', newModel.compNames{i}, '\]$'],'');
-0234     end
-0235     
-0236     %In some cases (e.g. any model that uses BiGG ids as main ids), there
-0237     %may be overlapping mets due to removal of compartment info. To avoid
-0238     %this, we change compartments from e.g. [c] into _c
-0239     if numel(unique(newModel.mets))~=numel(model.mets)
-0240         newModel.mets=model.mets;
-0241         for i=1:numel(newModel.comps)
-0242             newModel.mets=regexprep(newModel.mets,['\[' newModel.comps{i} '\]$'],['_' newModel.comps{i}]);
-0243         end
-0244     end
-0245     %Since COBRA no longer contains rev field it is assumed that rxn is
-0246     %reversible if its lower bound is set below zero
-0247     if ~isfield(model,'rev')
-0248         for i=1:numel(model.rxns)
-0249             if model.lb(i)<0
-0250                 newModel.rev(i,1)=1;
-0251             else
-0252                 newModel.rev(i,1)=0;
-0253             end
-0254         end
-0255     end
-0256     newModel.b=zeros(numel(model.mets),1);
-0257    
-0258     %metComps is also mandatory, but defined later to match the order of
-0259     %fields
-0260     
-0261     %Fields 'name' and 'id' are also considered as mandatory, but
-0262     %these are added to the model during exportModel/exportToExcelFormat
-0263     %anyway, so there is no point to add this information here
-0264     
-0265     %Optional RAVEN fields
-0266     if isfield(model,'modelID')
-0267         newModel.id=model.modelID;
-0268     end
-0269     if isfield(model,'modelName')
-0270         newModel.name=model.modelName;
-0271     end
-0272     if isfield(model,'rules') && ~isfield(model,'grRules')
-0273         model.grRules        = rulesTogrrules(model);
-0274     end
-0275     if isfield(model,'grRules')
-0276         [grRules,rxnGeneMat] = standardizeGrRules(model,true);
-0277         newModel.grRules     = grRules;
-0278         newModel.rxnGeneMat  = rxnGeneMat;
-0279     end
-0280     if isfield(model,'rxnECNumbers')
-0281         newModel.eccodes=regexprep(model.rxnECNumbers,'EC|EC:','');
-0282     end
-0283     if any(isfield(model,rxnCOBRAfields))
-0284         for i=1:numel(model.rxns)
-0285             counter=1;
-0286             newModel.rxnMiriams{i,1}=[];
-0287             if isfield(model,'rxnReferences')
-0288                 if ~isempty(model.rxnReferences{i})
-0289                     pmids = model.rxnReferences{i};
-0290                     pmids = strsplit(pmids,'; ');
-0291                     nonPmids = cellfun(@isempty,regexp(pmids,'^\d+$','match','once'));
-0292                     if any(nonPmids) %Not a pubmed id, keep in rxnReferences instead
-0293                         newModel.rxnReferences{i,1} = strjoin(pmids(nonPmids),', ');
-0294                         pmids(nonPmids)=[];
-0295                     end
-0296                     for j = 1:length(pmids)
-0297                         newModel.rxnMiriams{i,1}.name{counter,1} = 'pubmed';
-0298                         newModel.rxnMiriams{i,1}.value{counter,1} = pmids{j};
-0299                         counter=counter+1;
-0300                     end
-0301                 end
-0302             end
-0303             for j = 2:length(rxnCOBRAfields) %Start from 2, as 1 is rxnReferences
-0304                 if isfield(model,rxnCOBRAfields{j})
-0305                     rxnAnnotation = model.(rxnCOBRAfields{j}){i};
-0306                     if ~isempty(rxnAnnotation)
-0307                         rxnAnnotation = strtrim(strsplit(rxnAnnotation,';'));
-0308                         for a=1:length(rxnAnnotation)
-0309                             newModel.rxnMiriams{i,1}.name{counter,1} = rxnNamespaces{j};
-0310                             newModel.rxnMiriams{i,1}.value{counter,1} = rxnAnnotation{a};
-0311                             counter=counter+1;
-0312                         end
-0313                     end
-0314                 end
-0315             end
-0316         end
-0317     end
-0318     if isfield(newModel,'rxnReferences')
-0319         emptyEntry = cellfun(@isempty,newModel.rxnReferences);
-0320         newModel.rxnReferences(emptyEntry)={''};
-0321         diffNumel = numel(newModel.rxns) - numel(newModel.rxnReferences);
-0322         if diffNumel > 0
-0323             newModel.rxnReferences(end+1:end+diffNumel) = {''};
-0324         end
-0325     end
-0326     if any(isfield(model,geneCOBRAfields))
-0327         for i=1:numel(model.genes)
-0328             counter=1;
-0329             newModel.geneMiriams{i,1}=[];
-0330             for j = 1:length(geneCOBRAfields)
-0331                 if isfield(model,geneCOBRAfields{j})
-0332                     geneAnnotation = model.(geneCOBRAfields{j}){i};
-0333                     if ~isempty(geneAnnotation)
-0334                         geneAnnotation = strtrim(strsplit(geneAnnotation,';'));
-0335                         for a=1:length(geneAnnotation)
-0336                             newModel.geneMiriams{i,1}.name{counter,1} = geneNamespaces{j};
-0337                             newModel.geneMiriams{i,1}.value{counter,1} = geneAnnotation{a};
-0338                             counter=counter+1;
-0339                         end
-0340                     end
-0341                 end
-0342             end
-0343         end
-0344     end
-0345     if isfield(model,'geneNames')
-0346         newModel.geneShortNames=model.geneNames;
-0347     end
-0348     newModel.metNames=model.metNames;
-0349     for i=1:numel(newModel.comps)
-0350         newModel.metNames=regexprep(newModel.metNames,['\[', newModel.comps{i}, '\]$'],'');
-0351         newModel.metNames=regexprep(newModel.metNames,['\[', newModel.compNames{i}, '\]$'],'');
-0352     end
-0353     newModel.metNames=deblank(newModel.metNames);
-0354     newModel.metComps=regexprep(model.mets,'^.+\[','');
-0355     newModel.metComps=regexprep(newModel.metComps,'\]$','');
-0356     [~, newModel.metComps]=ismember(newModel.metComps,newModel.comps);
-0357     if isfield(model,'metInChIString')
-0358         newModel.inchis=regexprep(model.metInChIString,'^InChI=','');
-0359     end
-0360     printWarning=false;
-0361     if any(isfield(model,[metCOBRAfields;'metKEGGID';'metPubChemID']))
-0362         for i=1:numel(model.mets)
-0363             counter=1;
-0364             newModel.metMiriams{i,1}=[];
-0365             if isfield(model,'metKEGGID')
-0366                 if ~isempty(model.metKEGGID{i})
-0367                     if strcmp(model.metKEGGID{i}(1),'C')
-0368                         newModel.metMiriams{i,1}.name{counter,1} = 'kegg.compound';
-0369                         newModel.metMiriams{i,1}.value{counter,1} = model.metKEGGID{i};
-0370                         counter=counter+1;
-0371                     elseif strcmp(model.metKEGGID{i}(1),'G')
-0372                         newModel.metMiriams{i,1}.name{counter,1} = 'kegg.glycan';
-0373                         newModel.metMiriams{i,1}.value{counter,1} = model.metKEGGID{i};
-0374                         counter=counter+1;
-0375                     end
-0376                 end
-0377             end
-0378             if isfield(model,'metPubChemID')
-0379                 if ~isempty(model.metPubChemID{i})
-0380                     if length(model.metPubChemID{i})>3 && strcmp(model.metPubChemID{i}(1:4),'CID:')
-0381                         newModel.metMiriams{i,1}.name{counter,1} = 'pubchem.compound';
-0382                         newModel.metMiriams{i,1}.value{counter,1} = model.metPubChemID{i};
-0383                         counter=counter+1;
-0384                     elseif length(model.metPubChemID{i})>3 && strcmp(model.metPubChemID{i}(1:4),'SID:')
-0385                         newModel.metMiriams{i,1}.name{counter,1} = 'pubchem.substance';
-0386                         newModel.metMiriams{i,1}.value{counter,1} = model.metPubChemID{i};
-0387                         counter=counter+1;
-0388                     else
-0389                         newModel.metMiriams{i,1}.name{counter,1} = 'pubchem.compound';
-0390                         newModel.metMiriams{i,1}.value{counter,1} = model.metPubChemID{i};
-0391                         counter=counter+1;
-0392                         printWarning=true;
-0393                     end
-0394                 end
-0395             end            
-0396             for j = 1:length(metCOBRAfields)
-0397                 if isfield(model,metCOBRAfields{j})
-0398                     metAnnotation = model.(metCOBRAfields{j}){i};
-0399                     if ~isempty(metAnnotation)
-0400                         metAnnotation = strtrim(strsplit(metAnnotation,';'));
-0401                         for a=1:length(metAnnotation)
-0402                             newModel.metMiriams{i,1}.name{counter,1} = metNamespaces{j};
-0403                             newModel.metMiriams{i,1}.value{counter,1} = metAnnotation{a};
-0404                             counter=counter+1;
-0405                         end
-0406                     end
-0407                 end
-0408             end
-0409         end
-0410     end
-0411     % Flatten subSystems if every reaction has exactly one subsystem.
-0412     if isfield(newModel,'subSystems') && ~isempty(newModel.subSystems)
-0413         if all(cellfun(@(x) iscell(x) && isscalar(x), newModel.subSystems))
-0414             newModel.subSystems = transpose([newModel.subSystems{:}]);
-0415         end
-0416     end
-0417     if printWarning
-0418         fprintf('Could not determine whether PubChemIDs are compounds (CID)\n or substances (SID). All annotated PubChemIDs will therefore \n be assigned as compounds (CID).\n');
-0419     end
-0420 end
-0421 
-0422 % Order fields
-0423 newModel=standardizeModelFieldOrder(newModel); % Corrects for both RAVEN and COBRA models
-0424 end
-0425 
-0426 function rules=grrulesToRules(model)
-0427 %Changes all gene names in grRules to 'x(geneNumber)' and changes 'or' and
-0428 %'and' relations to the corresponding symbols
-0429 replacingGenes=cell([size(model.genes,1) 1]);
-0430 for i=1:numel(replacingGenes)
-0431     replacingGenes{i}=strcat('x(',num2str(i),')');
-0432 end
-0433 rules = strcat({' '},model.grRules,{' '});
-0434 for i=1:length(model.genes)
-0435     %Escaped: a gene id is arbitrary model text, not a regex pattern, and
-0436     %commonly contains metacharacters (e.g. "ENSG00000141510.16", where an
-0437     %unescaped "." would match any character instead of a literal dot).
-0438     escapedGene=regexptranslate('escape',model.genes{i});
-0439     rules=regexprep(rules,[' ' escapedGene ' '],[' ' replacingGenes{i} ' ']);
-0440     rules=regexprep(rules,['(' escapedGene ' '],['(' replacingGenes{i} ' ']);
-0441     rules=regexprep(rules,[' ' escapedGene ')'],[' ' replacingGenes{i} ')']);
-0442 end
-0443 rules=regexprep(rules,' and ',' & ');
-0444 rules=regexprep(rules,' or ',' | ');
-0445 rules=strtrim(rules);
-0446 end
-0447 
-0448 function grRules=rulesTogrrules(model)
-0449 %Replaces &/| with and/or, replaces the x(i) format with the actual gene
-0450 %ID, and strips the extra whitespace and redundant parenthesis introduced
-0451 %by COBRA, to create grRules.
-0452 grRules = strrep(model.rules,'&','and');
-0453 grRules = strrep(grRules,'|','or');
-0454 for i = 1:length(model.genes)
-0455     grRules = strrep(grRules,['x(' num2str(i) ')'],model.genes{i});
-0456 end
-0457 grRules = strrep(grRules,'( ','(');
-0458 grRules = strrep(grRules,' )',')');
-0459 grRules = regexprep(grRules,'^(',''); %rules that start with a "("
-0460 grRules = regexprep(grRules,')$',''); %rules that end with a ")"
-0461 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/conversion/standardizeModelFieldOrder.html b/doc/conversion/standardizeModelFieldOrder.html deleted file mode 100644 index 45c3897f..00000000 --- a/doc/conversion/standardizeModelFieldOrder.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - Description of standardizeModelFieldOrder - - - - - - - - - -
Home > conversion > standardizeModelFieldOrder.m
- - - -

standardizeModelFieldOrder -

- -

PURPOSE ^

-
standardizeModelFieldOrder Order RAVEN model structure fields.
- -

SYNOPSIS ^

-
function orderedModel=standardizeModelFieldOrder(model)
- -

DESCRIPTION ^

-
 standardizeModelFieldOrder  Order RAVEN model structure fields.
-
- Orders fields of a RAVEN model structure as specified at
- https://github.com/SysBioChalmers/RAVEN/wiki/RAVEN-Model-Structure
-
- The model fields themselves are not changed, only the order is modified.
- For changing model fields between RAVEN and COBRA format, use
- ravenCobraWrapper().
-
- Parameters
- ----------
- model : struct
-     model structure, either RAVEN or COBRA format.
-
- Returns
- -------
- orderedModel : struct
-     model structure with ordered fields.
-
- Examples
- --------
-     orderedModel = standardizeModelFieldOrder(model);
-
- See also
- --------
- ravenCobraWrapper
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function orderedModel=standardizeModelFieldOrder(model)
-0002 % standardizeModelFieldOrder  Order RAVEN model structure fields.
-0003 %
-0004 % Orders fields of a RAVEN model structure as specified at
-0005 % https://github.com/SysBioChalmers/RAVEN/wiki/RAVEN-Model-Structure
-0006 %
-0007 % The model fields themselves are not changed, only the order is modified.
-0008 % For changing model fields between RAVEN and COBRA format, use
-0009 % ravenCobraWrapper().
-0010 %
-0011 % Parameters
-0012 % ----------
-0013 % model : struct
-0014 %     model structure, either RAVEN or COBRA format.
-0015 %
-0016 % Returns
-0017 % -------
-0018 % orderedModel : struct
-0019 %     model structure with ordered fields.
-0020 %
-0021 % Examples
-0022 % --------
-0023 %     orderedModel = standardizeModelFieldOrder(model);
-0024 %
-0025 % See also
-0026 % --------
-0027 % ravenCobraWrapper
-0028 
-0029 ravenPath=findRAVENroot();
-0030 
-0031 if ~isfield(model,'rules') % Check if model is RAVEN
-0032     fid = fopen(fullfile(ravenPath,'conversion','orderRavenFields.csv'));
-0033     fields = textscan(fid,'%s','Delimiter',',','HeaderLines',0);
-0034     fields = fields{1};
-0035     fclose(fid);
-0036 else % If model is COBRA
-0037     fid = fopen(fullfile(ravenPath,'conversion','COBRA_structure_fields.csv')); % Taken from https://github.com/opencobra/cobratoolbox/blob/develop/src/base/io/definitions/COBRA_structure_fields.csv
-0038     fields = textscan(fid,repmat('%s',1,15),'Delimiter','\t','HeaderLines',1);
-0039     fields = fields{1};
-0040     fclose(fid);
-0041 end
-0042 
-0043 modelfields = fieldnames(model);
-0044 order = fields(ismember(fields(:,1),modelfields));
-0045 remainingOrder = sort(setdiff(modelfields,order));
-0046 overallOrder = [columnVector(order);columnVector(remainingOrder)];
-0047 orderedModel = orderfields(model,overallOrder);
-0048 end
-0049 
-0050 function vecT = columnVector(vec)
-0051 % Converts a vector to a column vector
-0052 %
-0053 % USAGE:
-0054 %
-0055 %   vecT = columnVector(vec)
-0056 %
-0057 % INPUT:
-0058 %   vec:     a vector
-0059 %
-0060 % OUTPUT:
-0061 %   vecT:    a column vector
-0062 %
-0063 % .. Authors:
-0064 %     - Markus Herrgard (COBRA Toolbox)
-0065 
-0066 [n, m] = size(vec);
-0067 
-0068 if (m ~= 1 && n < m) || n == 1
-0069     vecT = vec';
-0070 else
-0071     vecT = vec;
-0072 end
-0073 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/curation/curateModelFromTables.html b/doc/curation/curateModelFromTables.html deleted file mode 100644 index 3e0625fb..00000000 --- a/doc/curation/curateModelFromTables.html +++ /dev/null @@ -1,468 +0,0 @@ - - - - Description of curateModelFromTables - - - - - - - - - -
Home > curation > curateModelFromTables.m
- - - -

curateModelFromTables -

- -

PURPOSE ^

-
curateModelFromTables Curate or add mets, rxns and genes from tables.
- -

SYNOPSIS ^

-
function newModel=curateModelFromTables(model,metsInfo,varargin)
- -

DESCRIPTION ^

-
 curateModelFromTables  Curate or add mets, rxns and genes from tables.
-
- Curate existing and/or add new metabolites, reactions and genes from
- tabular data files.
-
- If the *.tsv files contain metabolites, reactions and/or genes that are
- already present in the model, then information in the model will be
- overwritten. Note that this includes empty annotations in the *.tsv
- files! Metabolites are matched by metaboliteName[comp]; reactions by the
- stoichiometry of its reactants and products; genes by their gene name.
- This function can therefore be used to add new entities in the model, or
- curate those already existing in the model.
-
- Parameters
- ----------
- model : struct
-     RAVEN model structure to be curated.
- metsInfo : char
-     Path to a *.tsv file with metabolite information, or 'none' to skip
-     metabolite curation. Columns: metNames, comps, formula, charge,
-     inchi, metNotes, then any number of MIRIAM-namespace columns.
-
- Name-Value Arguments
- --------------------
- genesInfo : char
-     Path to a *.tsv file with gene information, or 'none'. Columns:
-     genes, geneShortNames, then MIRIAM.
- rxnsCoeffs : char
-     Path to a *.tsv file with reaction stoichiometric coefficients, or
-     'none'. Columns: rxnIdx, rxnNames, metNames, comps, coefficient. One
-     row per (reaction, metabolite) pair.
- rxnsInfo : char
-     Path to a *.tsv file with reaction information, or 'none'. Columns:
-     rxnIdx, rxnNames, grRules, lb, ub, rev, subSystems, eccodes,
-     rxnNotes, rxnReferences, rxnConfidenceScores, then MIRIAM.
- metPrefix : char
-     Prefix used to mint fresh metabolite ids (e.g. 's_' for yeast-GEM,
-     'M_' for the BiGG default) (default 'M_').
- rxnPrefix : char
-     Prefix used to mint fresh reaction ids (default 'R_').
-
- Returns
- -------
- newModel : struct
-     Curated RAVEN model structure.
-
- Examples
- --------
-     newModel = curateModelFromTables(model, metsInfo, genesInfo, ...
-                     rxnsCoeffs, rxnsInfo, metPrefix, rxnPrefix);
-
- Notes
- -----
- The 'everything after the core columns is MIRIAM' convention applies to
- all three info tables: any column whose header is not one of the listed
- core fields is treated as a MIRIAM annotation namespace and stored on
- the matching entity.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function newModel=curateModelFromTables(model,metsInfo,varargin)
-0002 % curateModelFromTables  Curate or add mets, rxns and genes from tables.
-0003 %
-0004 % Curate existing and/or add new metabolites, reactions and genes from
-0005 % tabular data files.
-0006 %
-0007 % If the *.tsv files contain metabolites, reactions and/or genes that are
-0008 % already present in the model, then information in the model will be
-0009 % overwritten. Note that this includes empty annotations in the *.tsv
-0010 % files! Metabolites are matched by metaboliteName[comp]; reactions by the
-0011 % stoichiometry of its reactants and products; genes by their gene name.
-0012 % This function can therefore be used to add new entities in the model, or
-0013 % curate those already existing in the model.
-0014 %
-0015 % Parameters
-0016 % ----------
-0017 % model : struct
-0018 %     RAVEN model structure to be curated.
-0019 % metsInfo : char
-0020 %     Path to a *.tsv file with metabolite information, or 'none' to skip
-0021 %     metabolite curation. Columns: metNames, comps, formula, charge,
-0022 %     inchi, metNotes, then any number of MIRIAM-namespace columns.
-0023 %
-0024 % Name-Value Arguments
-0025 % --------------------
-0026 % genesInfo : char
-0027 %     Path to a *.tsv file with gene information, or 'none'. Columns:
-0028 %     genes, geneShortNames, then MIRIAM.
-0029 % rxnsCoeffs : char
-0030 %     Path to a *.tsv file with reaction stoichiometric coefficients, or
-0031 %     'none'. Columns: rxnIdx, rxnNames, metNames, comps, coefficient. One
-0032 %     row per (reaction, metabolite) pair.
-0033 % rxnsInfo : char
-0034 %     Path to a *.tsv file with reaction information, or 'none'. Columns:
-0035 %     rxnIdx, rxnNames, grRules, lb, ub, rev, subSystems, eccodes,
-0036 %     rxnNotes, rxnReferences, rxnConfidenceScores, then MIRIAM.
-0037 % metPrefix : char
-0038 %     Prefix used to mint fresh metabolite ids (e.g. 's_' for yeast-GEM,
-0039 %     'M_' for the BiGG default) (default 'M_').
-0040 % rxnPrefix : char
-0041 %     Prefix used to mint fresh reaction ids (default 'R_').
-0042 %
-0043 % Returns
-0044 % -------
-0045 % newModel : struct
-0046 %     Curated RAVEN model structure.
-0047 %
-0048 % Examples
-0049 % --------
-0050 %     newModel = curateModelFromTables(model, metsInfo, genesInfo, ...
-0051 %                     rxnsCoeffs, rxnsInfo, metPrefix, rxnPrefix);
-0052 %
-0053 % Notes
-0054 % -----
-0055 % The 'everything after the core columns is MIRIAM' convention applies to
-0056 % all three info tables: any column whose header is not one of the listed
-0057 % core fields is treated as a MIRIAM annotation namespace and stored on
-0058 % the matching entity.
-0059 
-0060 p=parseRAVENargs(varargin, {'genesInfo','none'; 'rxnsCoeffs','none'; 'rxnsInfo','none'; 'metPrefix',[]; 'rxnPrefix',[]});
-0061 genesInfo=p.genesInfo;
-0062 rxnsCoeffs=p.rxnsCoeffs;
-0063 rxnsInfo=p.rxnsInfo;
-0064 metPrefix=p.metPrefix;
-0065 if isempty(metPrefix)
-0066     metPrefix = 'M_';
-0067 end
-0068 rxnPrefix=p.rxnPrefix;
-0069 if isempty(rxnPrefix)
-0070     rxnPrefix = 'R_';
-0071 end
-0072 if xor(strcmp(rxnsCoeffs,'none'), strcmp(rxnsInfo,'none'))
-0073     error('Provide both a ''rxnsInfo'' and a ''rxnsCoeffs'' file')
-0074 end
-0075 newModel=model;
-0076 
-0077 %% Metabolites
-0078 if ~strcmp(metsInfo,'none')
-0079     fid = fopen(metsInfo);
-0080     raw = textscan(fid,['%q' repmat(' %q',1,17)],'Delimiter','\t');
-0081     fclose(fid);
-0082     metsToAdd.metNames      = raw{1}(2:end);
-0083     metsToAdd.compartments  = raw{2}(2:end);
-0084     metsToAdd.metFormulas   = raw{3}(2:end);
-0085     metsToAdd.metCharges    = cellfun(@str2num,raw{4}(2:end),'UniformOutput',false);
-0086     emptyEntry = cellfun(@isempty,metsToAdd.metCharges);
-0087     if all(emptyEntry)
-0088         metsToAdd           = rmfield(metsToAdd,'metCharges');
-0089     elseif any(emptyEntry) % If some charges are given, assume 0 for those without charges specified
-0090         metsToAdd.metCharges(emptyEntry) = {0};
-0091         metsToAdd.metCharges = cell2mat(metsToAdd.metCharges);
-0092     else
-0093         metsToAdd.metCharges = cell2mat(metsToAdd.metCharges);
-0094     end
-0095     metsToAdd.inchis        = raw{5}(2:end);
-0096     metsToAdd.metNotes      = raw{6}(2:end);
-0097     
-0098     % Check if metabolite already exists (check by metName[comp])
-0099     existingMets            = [];
-0100     existingMetsIdx         = [];
-0101     newMets                 = [];
-0102     for i=1:numel(metsToAdd.metNames)
-0103         metIdx              = find(strcmp(newModel.metNames,metsToAdd.metNames{i}));
-0104         existMet            = strcmp(metsToAdd.compartments{i},newModel.comps(newModel.metComps(metIdx)));
-0105         if any(existMet)
-0106             existingMets    = [existingMets, i];
-0107             existingMetsIdx = [existingMetsIdx, metIdx(existMet)];
-0108         else
-0109             newMets    = [newMets, i];
-0110         end
-0111     end
-0112     
-0113     % Overwrite annotation of existing entries
-0114     if any(existingMets)
-0115         if isfield(newModel,'metFormulas')
-0116             newModel.metFormulas(existingMetsIdx)  = metsToAdd.metFormulas(existingMets);
-0117         end
-0118         if isfield(newModel,'metCharges') && isfield(metsToAdd,'metCharges')
-0119             newModel.metCharges(existingMetsIdx)   = metsToAdd.metCharges(existingMets);
-0120         end
-0121         if isfield(newModel,'inchis')
-0122             newModel.inchis(existingMetsIdx)       = metsToAdd.inchis(existingMets);
-0123         end
-0124         newModel = extractAndAddMiriam(newModel,raw(7:end),existingMets,existingMetsIdx,'met');
-0125         warning(['The following metabolites are already present in the model, '...
-0126             'their annotation will be overwritten to match the metsInfo file. '...
-0127             'If you do not particularly want to curate their annotations, it '...
-0128             'would be better to removes these metabolites from metsInfo:\n\t\t%s'],...
-0129             strjoin(strcat(metsToAdd.metNames(existingMets),'[',metsToAdd.compartments(existingMets),']'),'\n\t\t'));
-0130     end
-0131     
-0132     % Continue with new metabolites
-0133     if numel(newMets)>0
-0134         metsToAdd.metNames(existingMets)        = [];
-0135         metsToAdd.compartments(existingMets)    = [];
-0136         metsToAdd.metFormulas(existingMets)     = [];
-0137         if all(cellfun(@isempty,metsToAdd.metFormulas))
-0138             metsToAdd = rmfield(metsToAdd,'metFormulas');
-0139         end
-0140         if isfield(metsToAdd,'metCharges')
-0141             metsToAdd.metCharges(existingMets)    = [];
-0142         end
-0143         metsToAdd.inchis(existingMets)          = [];
-0144         if all(cellfun(@isempty,metsToAdd.inchis))
-0145             metsToAdd = rmfield(metsToAdd,'inchis');
-0146         end
-0147         metsToAdd.metNotes(existingMets)        = [];
-0148         if all(cellfun(@isempty,metsToAdd.metNotes))
-0149             metsToAdd = rmfield(metsToAdd,'metNotes');
-0150         end
-0151         % Add metabolites
-0152         newModel        = addMets(newModel,metsToAdd,true,metPrefix);
-0153         addedIdx        = numel(newModel.mets)-numel(newMets)+1:numel(newModel.mets);
-0154         newModel        = extractAndAddMiriam(newModel,raw(7:end),newMets,addedIdx,'met');
-0155     end
-0156 end
-0157 %% Genes
-0158 if ~strcmp(genesInfo,'none')
-0159     % Gather all data, first about reaction stoichiometries...
-0160     fid = fopen(genesInfo);
-0161     raw = textscan(fid,['%q' repmat(' %q',1,10)],'Delimiter','\t');
-0162     fclose(fid);
-0163     genesToAdd.genes            = raw{1}(2:end);
-0164     genesToAdd.geneShortNames   = raw{2}(2:end);
-0165 
-0166     [~,notNewGene,existingGene] = intersect(genesToAdd.genes,newModel.genes);
-0167     if ~isempty(notNewGene)
-0168         warning(['The following genes are already present in the model, their annotation '...
-0169             'will be overwritten to match the genesInfo file: \n\t\t%s'],...
-0170             strjoin(genesToAdd.genes(notNewGene),'\n\t\t'));
-0171         if isfield(newModel,'geneShortNames')
-0172             newModel.geneShortNames(existingGene) = genesToAdd.geneShortNames(notNewGene);
-0173         end
-0174         newModel = extractAndAddMiriam(newModel,raw(3:end),notNewGene,existingGene,'gene');
-0175     end
-0176     
-0177     % Continue with new genes
-0178     toAdd               = 1:numel(genesToAdd.genes);
-0179     toAdd(notNewGene)   = [];
-0180     if ~isempty(toAdd)
-0181         genesToAdd.genes            = genesToAdd.genes(toAdd);
-0182         genesToAdd.geneShortNames   = genesToAdd.geneShortNames(toAdd);
-0183         if all(cellfun(@isempty,genesToAdd.geneShortNames))
-0184             genesToAdd = rmfield(genesToAdd,'geneShortNames');
-0185         end
-0186         % Add genes
-0187         newModel = addGenesRaven(newModel,genesToAdd);
-0188         addedIdx = numel(newModel.genes)-numel(toAdd)+1:numel(newModel.genes);
-0189         newModel = extractAndAddMiriam(newModel,raw(3:end),toAdd,addedIdx,'gene');
-0190     end
-0191 end
-0192 %% Reactions
-0193 if ~any(strcmp({rxnsCoeffs,rxnsInfo},'none'))
-0194     % Gather all data, first about reaction stoichiometries...
-0195     fid = fopen(rxnsCoeffs);
-0196     raw = textscan(fid,'%q %q %q %q %f','Delimiter','\t','HeaderLines',1);
-0197     fclose(fid);
-0198     rxnCheck.coeffsIdx = str2double(raw{1});
-0199     rxns.rxnNames   = raw{2};
-0200     rxns.metNames   = raw{3};
-0201     rxns.comps      = raw{4};
-0202     rxns.coeffs     = raw{5};
-0203     rxnCheck.coeffs = strcat(raw{1},'***',rxns.rxnNames);
-0204 
-0205     % ... and then additional reaction-specific data.
-0206     fid = fopen(rxnsInfo);
-0207     raw = textscan(fid,['%q' repmat(' %q',1,20)],'Delimiter','\t');
-0208     fclose(fid);
-0209     rxnCheck.rxnsIdx = str2double(raw{1}(2:end));
-0210     rxnCheck.rxns = strcat(raw{1}(2:end),'***',raw{2}(2:end));
-0211     
-0212     notMatching = setxor(rxnCheck.coeffs,rxnCheck.rxns);
-0213     if numel(notMatching)>1
-0214         error(['The following reactions ánd/or their indices are not matched '...
-0215             'between the rxnsInfo and rxnsCoeffs files:\n\t\t%s'],...
-0216             strjoin(regexprep(notMatching,'^\d+***',''),'\n\t\t'))
-0217     end
-0218     
-0219     rxnsToAdd.rxnNames              = raw{2}(2:end);
-0220     rxnsToAdd.grRules               = raw{3}(2:end);
-0221     rxnsToAdd.lb                    = cellfun(@str2num,raw{4}(2:end));
-0222     rxnsToAdd.ub                    = cellfun(@str2num,raw{5}(2:end));
-0223     rxnsToAdd.rev                   = cellfun(@str2num,raw{6}(2:end));
-0224     rxnsToAdd.subSystems            = raw{7}(2:end);
-0225     rxnsToAdd.eccodes               = raw{8}(2:end);
-0226     rxnsToAdd.rxnNotes              = raw{9}(2:end);
-0227     rxnsToAdd.rxnReferences         = raw{10}(2:end);
-0228     rxnsToAdd.rxnConfidenceScores   = cellfun(@str2num,raw{11}(2:end),'UniformOutput',false);
-0229     emptyEntry = cellfun(@isempty,rxnsToAdd.rxnConfidenceScores);
-0230     if all(emptyEntry)
-0231         rxnsToAdd           = rmfield(rxnsToAdd,'rxnConfidenceScores');
-0232     elseif any(emptyEntry) % If some rxnConfidenceScores are given, assume 0 for those without rxnConfidenceScores specified
-0233         rxnsToAdd.rxnConfidenceScores(emptyEntry) = {0};
-0234         rxnsToAdd.rxnConfidenceScores = cell2mat(rxnsToAdd.rxnConfidenceScores);
-0235     else
-0236         rxnsToAdd.rxnConfidenceScores = cell2mat(rxnsToAdd.rxnConfidenceScores);
-0237     end
-0238     
-0239     existingRxn=[];
-0240     notNewRxn=[];
-0241     for i=1:numel(rxnCheck.rxnsIdx)
-0242         rxnRows                     = find(rxnCheck.rxnsIdx(i)==rxnCheck.coeffsIdx);
-0243         rxnsToAdd.mets{i,1}         = cell(1,1);
-0244         rxnsToAdd.stoichCoeffs{i,1} = cell(1,1);
-0245         for j=1:numel(rxnRows)
-0246             newMetComp = [rxns.metNames{rxnRows(j)}, '[', rxns.comps{rxnRows(j)}, ']'];
-0247             try
-0248                 newMetComp = getIndexes(newModel,newMetComp,'metcomps');
-0249             catch
-0250                 error(['Not all metabolites in reaction "%s" are present in the '...
-0251                     'model or in the provided table with new metabolites.'],rxnsToAdd.rxnNames{i})
-0252             end
-0253             rxnsToAdd.mets{i}(j) = newModel.mets(newMetComp);
-0254             rxnsToAdd.stoichCoeffs{i}{j} = rxns.coeffs(rxnRows(j));
-0255         end
-0256         rxnsToAdd.stoichCoeffs{i}=cell2mat(rxnsToAdd.stoichCoeffs{i});
-0257         %Check if the reaction not already exists, by stoichiometry of its
-0258         %products and reactants
-0259         metIdx              = getIndexes(newModel,rxnsToAdd.mets{i},'mets');
-0260         modelCoeffs         = transpose(newModel.S(metIdx,:));
-0261         modelCoeffs2        = find(~all(modelCoeffs==0,2));
-0262         % Only reactions built from exactly these metabolites can be
-0263         % duplicates. Any model reaction that also involves other
-0264         % metabolites projects onto the same row of coefficients, but is a
-0265         % different reaction and must not be overwritten.
-0266         sameSupport         = full(sum(newModel.S(:,modelCoeffs2)~=0,1))' == numel(metIdx);
-0267         modelCoeffs2        = modelCoeffs2(sameSupport);
-0268         modelCoeffs         = modelCoeffs(modelCoeffs2,:);
-0269         [~, duplicateRxn]   = intersect(modelCoeffs,rxnsToAdd.stoichCoeffs{i},'rows');
-0270         if ~isempty(duplicateRxn)
-0271             existingRxn     = [existingRxn,modelCoeffs2(duplicateRxn)];
-0272             notNewRxn       = [notNewRxn,i];
-0273         end
-0274     end
-0275     
-0276     % If some reactions already existed, then replace its information
-0277     if ~isempty(existingRxn)
-0278         warning(['The following reactions are the same as existing model '...
-0279             'reactions, their annotation will be overwritten to match the '...
-0280             'rxnCoeffs file: \n\t\t(Existing reaction ID): New reaction name\n\t\t%s'],...
-0281             strjoin(strcat('(',newModel.rxns(existingRxn),{'): '},rxnsToAdd.rxnNames(notNewRxn)),'\n\t\t'));
-0282         newModel.rxnNames(existingRxn) = rxnsToAdd.rxnNames(notNewRxn);
-0283         newModel.lb(existingRxn)       = rxnsToAdd.lb(notNewRxn);
-0284         newModel.ub(existingRxn)       = rxnsToAdd.ub(notNewRxn);
-0285         newModel.rev(existingRxn)      = rxnsToAdd.rev(notNewRxn);
-0286         if isfield(newModel,'subSystems')
-0287             newModel.subSystems(existingRxn)    = rxnsToAdd.subSystems(notNewRxn);
-0288         end
-0289         if isfield(newModel,'eccodes')
-0290             newModel.eccodes(existingRxn)       = rxnsToAdd.eccodes(notNewRxn);
-0291         end
-0292         if isfield(newModel,'rxnNotes')
-0293             newModel.rxnNotes(existingRxn)      = rxnsToAdd.rxnNotes(notNewRxn);
-0294         end
-0295         if isfield(newModel,'rxnConfidenceScores') && isfield(rxnsToAdd,'rxnConfidenceScores')
-0296             newModel.rxnConfidenceScores(existingRxn) = rxnsToAdd.rxnConfidenceScores(notNewRxn);
-0297         end
-0298         emptyEntries = cellfun(@isempty,rxnsToAdd.grRules);
-0299         if ~all(emptyEntries)
-0300             newModel    = changeGrRules(newModel,newModel.rxns(existingRxn(~emptyEntries)),rxnsToAdd.grRules(notNewRxn(~emptyEntries)),true);
-0301         end        
-0302         newModel    = extractAndAddMiriam(newModel,raw(11:end),notNewRxn,existingRxn,'rxn');
-0303     end
-0304     
-0305     % Continue with new reactions
-0306     toAdd               = 1:numel(rxnsToAdd.rxnNames);
-0307     toAdd(notNewRxn)    = [];
-0308     if ~isempty(toAdd)
-0309         rxnsToAdd.rxnNames      = rxnsToAdd.rxnNames(toAdd);
-0310         rxnsToAdd.lb            = rxnsToAdd.lb(toAdd);
-0311         rxnsToAdd.ub            = rxnsToAdd.ub(toAdd);
-0312         rxnsToAdd.rev           = rxnsToAdd.rev(toAdd);
-0313         rxnsToAdd.subSystems    = rxnsToAdd.subSystems(toAdd);
-0314         if all(cellfun(@isempty,rxnsToAdd.subSystems))
-0315             rxnsToAdd = rmfield(rxnsToAdd,'subSystems');
-0316         end
-0317         rxnsToAdd.eccodes       = rxnsToAdd.eccodes(toAdd);
-0318         if all(cellfun(@isempty,rxnsToAdd.eccodes))
-0319             rxnsToAdd = rmfield(rxnsToAdd,'eccodes');
-0320         end
-0321         rxnsToAdd.rxnNotes      = rxnsToAdd.rxnNotes(toAdd);
-0322         if all(cellfun(@isempty,rxnsToAdd.rxnNotes))
-0323             rxnsToAdd = rmfield(rxnsToAdd,'rxnNotes');
-0324         end
-0325         if isfield(rxnsToAdd,'rxnConfidenceScores')
-0326             rxnsToAdd.rxnConfidenceScores = rxnsToAdd.rxnConfidenceScores(toAdd);
-0327         end
-0328         rxnsToAdd.rxnReferences = rxnsToAdd.rxnReferences(toAdd);
-0329         if all(cellfun(@isempty,rxnsToAdd.rxnReferences))
-0330             rxnsToAdd = rmfield(rxnsToAdd,'rxnReferences');
-0331         end
-0332         rxnsToAdd.grRules       = rxnsToAdd.grRules(toAdd);
-0333         rxnsToAdd.mets          = rxnsToAdd.mets(toAdd);
-0334         rxnsToAdd.stoichCoeffs  = rxnsToAdd.stoichCoeffs(toAdd);
-0335         rxnsToAdd.rxns          = generateNewIds(model,'rxns',rxnPrefix,numel(rxnsToAdd.rxnNames));
-0336         
-0337         newModel        = addRxns(newModel,rxnsToAdd,1,[],false,false);
-0338         rxnsModelIdx    = numel(newModel.rxns)-numel(toAdd)+1:numel(newModel.rxns);
-0339         newModel        = extractAndAddMiriam(newModel,raw(11:end),toAdd,rxnsModelIdx,'rxn');
-0340     end
-0341 end
-0342 end
-0343 
-0344 function newModel = extractAndAddMiriam(model,raw,inputIndex,modelIndex,type)
-0345 newModel=model;
-0346 miriamName              = cell(numel(inputIndex),1);
-0347 miriamValues            = cell(1,numel(inputIndex));
-0348 for i=1:numel(raw)
-0349     miriamName{i}                       = raw{i}{1};
-0350     miriamValues(1:numel(inputIndex),i) = raw{i}(inputIndex+1);
-0351 end
-0352 emptyMiriam     =   all(cellfun(@isempty,miriamValues),1);
-0353 miriamName(emptyMiriam)     = [];
-0354 miriamValues(:,emptyMiriam) = [];
-0355 if ~isfield(newModel,[type 'Miriams']);
-0356     newModel.([type 'Miriams'])=cell(numel(newModel.([type 's'])),1);
-0357 end
-0358 if ~isempty(miriamName)
-0359     for i=1:numel(miriamName)
-0360         newModel = editMiriam(newModel,type,modelIndex,miriamName{i},miriamValues(:,i),'replace');
-0361     end
-0362 end
-0363 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/curation/downloadGenomeData.html b/doc/curation/downloadGenomeData.html deleted file mode 100644 index 62aea358..00000000 --- a/doc/curation/downloadGenomeData.html +++ /dev/null @@ -1,205 +0,0 @@ - - - - Description of downloadGenomeData - - - - - - - - - -
Home > curation > downloadGenomeData.m
- - - -

downloadGenomeData -

- -

PURPOSE ^

-
downloadGenomeData Download genome annotation files from NCBI.
- -

SYNOPSIS ^

-
function [gffFile, faaFile] = downloadGenomeData(accession, varargin)
- -

DESCRIPTION ^

-
 downloadGenomeData  Download genome annotation files from NCBI.
-
- Retrieves the GFF3 annotation and protein FASTA (.faa) files for a given
- NCBI genome assembly accession using the NCBI Datasets v2 API, which
- returns both files in a single archive. The files are saved locally and
- can be used directly by getGeneData to build a gene-mapping table.
-
- Parameters
- ----------
- accession : char
-     NCBI genome assembly accession, e.g. 'GCF_000002595.2'.
-     Both RefSeq (GCF_) and GenBank (GCA_) prefixes are accepted.
-
- Name-Value Arguments
- --------------------
- outputDir : char
-     Directory where downloaded files are saved (default: current working
-     directory).
- verbose : logical
-     Print download progress to the command window (default: true).
-
- Returns
- -------
- gffFile : char
-     Full path to the downloaded GFF3 annotation file.
- faaFile : char
-     Full path to the downloaded protein FASTA file.
-
- Examples
- --------
-     % Download to current directory
-     [gff, faa] = downloadGenomeData('GCF_000002595.2');
-
-     % Save to a specific directory
-     [gff, faa] = downloadGenomeData('GCF_000002595.2', 'data/');
-
-     % Suppress progress messages
-     [gff, faa] = downloadGenomeData('GCF_000002595.2', 'data/', false);
-
- Notes
- -----
- Requires an active internet connection. Files already present in
- outputDir are not re-fetched; delete them manually to force a refresh.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • getGeneData getGeneData Build a gene-mapping table from NCBI genome annotation files.
- - - - -

SOURCE CODE ^

-
0001 function [gffFile, faaFile] = downloadGenomeData(accession, varargin)
-0002 % downloadGenomeData  Download genome annotation files from NCBI.
-0003 %
-0004 % Retrieves the GFF3 annotation and protein FASTA (.faa) files for a given
-0005 % NCBI genome assembly accession using the NCBI Datasets v2 API, which
-0006 % returns both files in a single archive. The files are saved locally and
-0007 % can be used directly by getGeneData to build a gene-mapping table.
-0008 %
-0009 % Parameters
-0010 % ----------
-0011 % accession : char
-0012 %     NCBI genome assembly accession, e.g. 'GCF_000002595.2'.
-0013 %     Both RefSeq (GCF_) and GenBank (GCA_) prefixes are accepted.
-0014 %
-0015 % Name-Value Arguments
-0016 % --------------------
-0017 % outputDir : char
-0018 %     Directory where downloaded files are saved (default: current working
-0019 %     directory).
-0020 % verbose : logical
-0021 %     Print download progress to the command window (default: true).
-0022 %
-0023 % Returns
-0024 % -------
-0025 % gffFile : char
-0026 %     Full path to the downloaded GFF3 annotation file.
-0027 % faaFile : char
-0028 %     Full path to the downloaded protein FASTA file.
-0029 %
-0030 % Examples
-0031 % --------
-0032 %     % Download to current directory
-0033 %     [gff, faa] = downloadGenomeData('GCF_000002595.2');
-0034 %
-0035 %     % Save to a specific directory
-0036 %     [gff, faa] = downloadGenomeData('GCF_000002595.2', 'data/');
-0037 %
-0038 %     % Suppress progress messages
-0039 %     [gff, faa] = downloadGenomeData('GCF_000002595.2', 'data/', false);
-0040 %
-0041 % Notes
-0042 % -----
-0043 % Requires an active internet connection. Files already present in
-0044 % outputDir are not re-fetched; delete them manually to force a refresh.
-0045 
-0046     if nargin < 1 || isempty(accession)
-0047     error('downloadGenomeData:missingInput', ...
-0048         ['accession is required. ', ...
-0049          'Provide an NCBI assembly accession, e.g. ''GCF_000002595.2''.']);
-0050     end
-0051     accession = char(accession);
-0052     
-0053     if ~any(strncmpi(accession, {'GCF_', 'GCA_'}, 4))
-0054         error('downloadGenomeData:invalidAccession', ...
-0055         ['Accession must start with ''GCF_'' (RefSeq) or ''GCA_'' (GenBank). ', ...
-0056          'Received: ''%s''.'], accession);
-0057     end
-0058 
-0059     p = parseRAVENargs(varargin, {'outputDir', pwd; 'verbose', true});
-0060     downloadDir = char(p.outputDir);
-0061     verbose = p.verbose;
-0062 
-0063     if ~exist(downloadDir, 'dir')
-0064         mkdir(downloadDir);
-0065     end
-0066 
-0067     gffFile = fullfile(downloadDir, [accession '_genomic.gff']);
-0068     faaFile = fullfile(downloadDir, [accession '_protein.faa']);
-0069 
-0070     % Skip the download if both files are already present
-0071     if isfile(gffFile) && isfile(faaFile)
-0072         if verbose; fprintf('Genome data for %s already present, skipping download.\n', accession); end
-0073         return
-0074     end
-0075 
-0076     % --- NCBI Datasets v2 API: download GFF3 and protein FASTA as one zip ---
-0077     if verbose; fprintf('Downloading genome data for %s from NCBI Datasets...\n', accession); end
-0078     url = ['https://api.ncbi.nlm.nih.gov/datasets/v2alpha/genome/accession/', ...
-0079            accession, '/download?include_annotation_type=GENOME_GFF,PROT_FASTA'];
-0080     zipFile    = [tempname '.zip'];
-0081     extractDir = tempname;
-0082     try
-0083         websave(zipFile, url);
-0084     catch ME
-0085         error('downloadGenomeData:download', 'Download failed for %s: %s', accession, ME.message);
-0086     end
-0087     try
-0088         unzip(zipFile, extractDir);
-0089     catch ME
-0090         delete(zipFile);
-0091         error('downloadGenomeData:extract', 'Extraction failed for %s: %s', accession, ME.message);
-0092     end
-0093     delete(zipFile);
-0094 
-0095     % The archive stores the files under ncbi_dataset/data/<assembly>/
-0096     dataParent = fullfile(extractDir, 'ncbi_dataset', 'data');
-0097     sub = dir(dataParent);
-0098     sub = sub([sub.isdir] & ~startsWith({sub.name}, '.'));
-0099     if ~isempty(sub)
-0100         assemblyDir = fullfile(dataParent, sub(1).name);
-0101         srcGff = fullfile(assemblyDir, 'genomic.gff');
-0102         srcFaa = fullfile(assemblyDir, 'protein.faa');
-0103     else
-0104         srcGff = '';
-0105         srcFaa = '';
-0106     end
-0107     if ~isfile(srcGff) || ~isfile(srcFaa)
-0108         rmdir(extractDir, 's');
-0109         error('downloadGenomeData:noAnnotation', ...
-0110             ['NCBI Datasets did not return both a GFF3 annotation and a protein ', ...
-0111              'FASTA for %s. The assembly may lack annotation.'], accession);
-0112     end
-0113     movefile(srcGff, gffFile, 'f');
-0114     movefile(srcFaa, faaFile, 'f');
-0115     rmdir(extractDir, 's');
-0116 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/curation/getGeneData.html b/doc/curation/getGeneData.html deleted file mode 100644 index f9c134b0..00000000 --- a/doc/curation/getGeneData.html +++ /dev/null @@ -1,346 +0,0 @@ - - - - Description of getGeneData - - - - - - - - - -
Home > curation > getGeneData.m
- - - -

getGeneData -

- -

PURPOSE ^

-
getGeneData Build a gene-mapping table from NCBI genome annotation files.
- -

SYNOPSIS ^

-
function geneTable = getGeneData(accession, varargin)
- -

DESCRIPTION ^

-
 getGeneData  Build a gene-mapping table from NCBI genome annotation files.
-
- Parses the GFF3 annotation and protein FASTA (.faa) files for a given
- NCBI genome assembly and produces a table mapping locus tags to gene
- symbols, protein IDs, and other identifiers. The resulting table can be
- passed directly to renameModelGenes to update gene identifiers in a
- RAVEN model. If no local files are provided, downloadGenomeData is
- called automatically to fetch them.
-
- Parameters
- ----------
- accession : char
-     NCBI genome assembly accession, e.g. 'GCF_000002595.2', or a path to a
-     local GFF3 annotation file.
-
- Name-Value Arguments
- --------------------
- outputFile : char
-     Path to save the resulting table as a tab-delimited .tsv file. If
-     omitted or empty, no file is written and the table is only returned.
- downloadDir : char
-     Directory where genome files are downloaded if not already present
-     (default: current working directory).
-
- Returns
- -------
- geneTable : table
-     MATLAB table with one row per gene containing:
-       locus_tag       — stable locus identifier (e.g. 'Cre01.g000001')
-       old_locus_tag   — previous locus identifier when available
-       GeneID          — NCBI Gene ID (e.g. '5723799')
-       gene_name       — common gene symbol when available (e.g. 'rbcL')
-       GenBank_protein — protein accession (e.g. 'XP_001698190.2'), matching
-                         the protein FASTA headers
-       UniProt         — UniProt accession from Dbxref when available
-
- Examples
- --------
-     % Fetch and parse gene data for Chlamydomonas reinhardtii
-     geneTable = getGeneData('GCF_000002595.2');
-
-     % Save the mapping table to a specific file
-     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv');
-
-     % Save to a file and download to a specific directory
-     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv', 'data/');
-
- Notes
- -----
- When outputFile is provided the table is written as UTF-8 encoded
- tab-delimited text with a header row, compatible with readtable and
- renameModelGenes. Rows without a locus_tag are silently discarded.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function geneTable = getGeneData(accession, varargin)
-0002 % getGeneData  Build a gene-mapping table from NCBI genome annotation files.
-0003 %
-0004 % Parses the GFF3 annotation and protein FASTA (.faa) files for a given
-0005 % NCBI genome assembly and produces a table mapping locus tags to gene
-0006 % symbols, protein IDs, and other identifiers. The resulting table can be
-0007 % passed directly to renameModelGenes to update gene identifiers in a
-0008 % RAVEN model. If no local files are provided, downloadGenomeData is
-0009 % called automatically to fetch them.
-0010 %
-0011 % Parameters
-0012 % ----------
-0013 % accession : char
-0014 %     NCBI genome assembly accession, e.g. 'GCF_000002595.2', or a path to a
-0015 %     local GFF3 annotation file.
-0016 %
-0017 % Name-Value Arguments
-0018 % --------------------
-0019 % outputFile : char
-0020 %     Path to save the resulting table as a tab-delimited .tsv file. If
-0021 %     omitted or empty, no file is written and the table is only returned.
-0022 % downloadDir : char
-0023 %     Directory where genome files are downloaded if not already present
-0024 %     (default: current working directory).
-0025 %
-0026 % Returns
-0027 % -------
-0028 % geneTable : table
-0029 %     MATLAB table with one row per gene containing:
-0030 %       locus_tag       — stable locus identifier (e.g. 'Cre01.g000001')
-0031 %       old_locus_tag   — previous locus identifier when available
-0032 %       GeneID          — NCBI Gene ID (e.g. '5723799')
-0033 %       gene_name       — common gene symbol when available (e.g. 'rbcL')
-0034 %       GenBank_protein — protein accession (e.g. 'XP_001698190.2'), matching
-0035 %                         the protein FASTA headers
-0036 %       UniProt         — UniProt accession from Dbxref when available
-0037 %
-0038 % Examples
-0039 % --------
-0040 %     % Fetch and parse gene data for Chlamydomonas reinhardtii
-0041 %     geneTable = getGeneData('GCF_000002595.2');
-0042 %
-0043 %     % Save the mapping table to a specific file
-0044 %     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv');
-0045 %
-0046 %     % Save to a file and download to a specific directory
-0047 %     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv', 'data/');
-0048 %
-0049 % Notes
-0050 % -----
-0051 % When outputFile is provided the table is written as UTF-8 encoded
-0052 % tab-delimited text with a header row, compatible with readtable and
-0053 % renameModelGenes. Rows without a locus_tag are silently discarded.
-0054 
-0055     if nargin < 1 || isempty(accession)
-0056         error('getGeneData:missingInput', ...
-0057             'accession is required. Provide an NCBI assembly accession, e.g. ''GCF_000002595.2''.');
-0058     end
-0059 
-0060     p = parseRAVENargs(varargin, {'outputFile', ''; 'downloadDir', pwd});
-0061     outputFile = char(p.outputFile);
-0062     downloadDir = char(p.downloadDir);
-0063 
-0064     % Resolve input → local GFF3 path. Decide whether input is a file path or an NCBI accession.
-0065     if ischar(accession) || isstring(accession)
-0066         inp = char(accession);
-0067     else
-0068         error('getGeneData:badInputType', 'Input must be a character vector or string.');
-0069     end
-0070     
-0071     gffPath = '';
-0072     % If input is an existing file, use it.
-0073     if isfile(inp)
-0074         gffPath = inp;
-0075     else
-0076         % Check if it looks like an NCBI assembly accession (GCF_... or GCA_...)
-0077         if ~isempty(regexp(inp, '^GC[FA]_\d+(\.\d+)?$', 'once'))
-0078             gffPath = downloadGenomeData(inp, downloadDir);
-0079         else
-0080             error('getGeneData:badInput', ...
-0081                 'Input "%s" is neither an existing file nor a recognised NCBI assembly accession.', inp);
-0082         end
-0083     end
-0084 
-0085     % Parse the GFF3 annotation into a gene-mapping table
-0086     geneTable = parseGFF(gffPath);
-0087 
-0088     % Save result only if an output file was requested
-0089     if ~isempty(outputFile)
-0090         writetable(geneTable, outputFile, 'Delimiter', '\t', 'FileType', 'text');
-0091         fprintf('Gene table saved to: %s  (%d rows)\n', outputFile, height(geneTable));
-0092     end
-0093 end
-0094 
-0095 %--------------------------------------------------------------------------
-0096 % Helper functions
-0097 
-0098 function T = parseGFF(gffPath)
-0099 % Parse a GFF3 file extracting gene + CDS pairs. The protein accession is
-0100 % taken from each CDS protein_id, and the owning gene is resolved through
-0101 % the Parent chain (CDS → mRNA → gene for eukaryotes, CDS → gene for
-0102 % prokaryotes), so the GenBank_protein column matches the protein FASTA.
-0103 %
-0104 % Returns a table with columns:
-0105 %   locus_tag | old_locus_tag | GeneID | gene_name | GenBank_protein | UniProt
-0106 
-0107     batchSize = 10000;
-0108     rows  = cell(batchSize, 6);
-0109     nRows = 0;
-0110     % Use a containers.Map to hold the last gene record keyed by gene ID,
-0111     % and an mRNA → gene map to resolve eukaryote CDS parents.
-0112     geneMap    = containers.Map('KeyType', 'char', 'ValueType', 'any');
-0113     mrnaToGene = containers.Map('KeyType', 'char', 'ValueType', 'char');
-0114     emptyGene = struct('locus_tag','','old_locus_tag','','geneID','','name','','uniProt','');
-0115 
-0116     fid = fopen(gffPath, 'r');
-0117     while ~feof(fid)
-0118         line = fgetl(fid);
-0119         if ~ischar(line) || isempty(line) || line(1) == '#', continue, end
-0120         fields = strsplit(line, '\t');
-0121         if numel(fields) < 9, continue, end
-0122 
-0123         featureType = fields{3};
-0124         attrs       = fields{9};
-0125 
-0126         if strcmp(featureType, 'gene')
-0127             g = parseGeneAttrs(attrs);
-0128             if ~isempty(g.id)
-0129                 geneMap(g.id) = g;
-0130             end
-0131 
-0132         elseif strcmp(featureType, 'mRNA')
-0133             id     = extractAttr(attrs, 'ID');
-0134             parent = extractAttr(attrs, 'Parent');
-0135             if ~isempty(id) && ~isempty(parent)
-0136                 mrnaToGene(id) = parent;
-0137             end
-0138 
-0139         elseif strcmp(featureType, 'CDS')
-0140             child     = parseCDSAttrs(attrs);
-0141             proteinID = child.protein_id;
-0142             if isempty(child.parent) || isempty(proteinID), continue, end
-0143 
-0144             % Resolve the owning gene through the Parent chain
-0145             if isKey(mrnaToGene, child.parent)
-0146                 geneKey = mrnaToGene(child.parent);
-0147             else
-0148                 geneKey = child.parent;  % prokaryote: CDS Parent is the gene
-0149             end
-0150             if isKey(geneMap, geneKey)
-0151                 g = geneMap(geneKey);
-0152             else
-0153                 g = emptyGene;
-0154             end
-0155 
-0156             nRows = nRows + 1;
-0157             if nRows > size(rows, 1)
-0158                 rows = [rows; cell(batchSize, 6)];
-0159             end
-0160             rows(nRows, :) = {g.locus_tag, g.old_locus_tag, ...
-0161                                g.geneID,    g.name,          proteinID, g.uniProt};
-0162         end
-0163     end
-0164     fclose(fid);
-0165 
-0166     rows = rows(1:nRows, :);
-0167 
-0168     % Deduplicate rows (CDS repeats per exon).
-0169     rowKeys = cellfun(@(a,b,c,d,e) strjoin({a,b,c,d,e}, '|'), ...
-0170         rows(:,1), rows(:,2), rows(:,3), rows(:,4), rows(:,5), ...
-0171         'UniformOutput', false);
-0172     [~, ia] = unique(rowKeys, 'stable');
-0173     rows = rows(ia, :);
-0174 
-0175     T = cell2table(rows, 'VariableNames', ...
-0176         {'locus_tag','old_locus_tag','GeneID','gene_name','GenBank_protein','UniProt'});
-0177 end
-0178 
-0179 % Attibute parsing utilities
-0180 function value = extractAttr(attrStr, key)
-0181 % Extract the value of a GFF3 attribute by key.
-0182 % Handles URL-encoded characters (%2C etc.) for robustness.
-0183 % Returns '' if key is not present.
-0184     pattern = sprintf('(?:^|;)%s=([^;]+)', regexptranslate('escape', key));
-0185     tok = regexp(attrStr, pattern, 'tokens', 'once');
-0186     if isempty(tok)
-0187         value = '';
-0188     else
-0189         value = gff3Decode(tok{1});
-0190     end
-0191 end
-0192 
-0193 function out = gff3Decode(str)
-0194 % Decode GFF3 percent-encoded characters (%XX). Unlike urldecode, '+' is
-0195 % left untouched, as GFF3 does not use it to encode spaces.
-0196     out = regexprep(str, '%([0-9A-Fa-f]{2})', '${char(hex2dec($1))}');
-0197 end
-0198 
-0199 function value = extractDbxrefField(dbxref, prefix)
-0200 % From a comma-separated Dbxref value like
-0201 %   "Phytozome:Cre16.g651050,GeneID:5723799,GenBank:XM_001698190.2"
-0202 % extract the part after "prefix:".
-0203 % Returns '' if not found.
-0204     if isempty(dbxref)
-0205         value = '';
-0206         return
-0207     end
-0208     parts   = strsplit(dbxref, ',');
-0209     pattern = sprintf('^%s:(.+)$', regexptranslate('escape', prefix));
-0210     value   = '';
-0211     for i = 1:numel(parts)
-0212         tok = regexp(strtrim(parts{i}), pattern, 'tokens', 'once');
-0213         if ~isempty(tok)
-0214             value = tok{1};
-0215             return
-0216         end
-0217     end
-0218 end
-0219 
-0220 function g = parseGeneAttrs(attrs)
-0221     g.id            = extractAttr(attrs, 'ID');
-0222     g.name          = extractAttr(attrs, 'Name');
-0223     g.locus_tag     = extractAttr(attrs, 'locus_tag');
-0224     g.old_locus_tag = extractAttr(attrs, 'old_locus_tag');
-0225 
-0226     % Dbxref is a comma-separated list like:
-0227     %   GeneID:5723799,GenBank:XM_001698190.2
-0228     dbxref          = extractAttr(attrs, 'Dbxref');
-0229     g.geneID        = extractDbxrefField(dbxref, 'GeneID');
-0230 
-0231     % UniProt accession appears as UniProtKB/Swiss-Prot:P12345 or UniProtKB:P12345
-0232     g.uniProt       = extractDbxrefField(dbxref, 'UniProtKB/Swiss-Prot');
-0233     if isempty(g.uniProt)
-0234         g.uniProt   = extractDbxrefField(dbxref, 'UniProtKB');
-0235     end
-0236 end
-0237 
-0238 function c = parseCDSAttrs(attrs)
-0239     c.parent     = extractAttr(attrs, 'Parent');
-0240     c.protein_id = extractAttr(attrs, 'protein_id');
-0241     if isempty(c.protein_id)
-0242         % Some files put it in Dbxref as GenBank:WP_...
-0243         dbxref       = extractAttr(attrs, 'Dbxref');
-0244         c.protein_id = extractDbxrefField(dbxref, 'GenBank');
-0245     end
-0246 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/curation/processProteinFastaFile.html b/doc/curation/processProteinFastaFile.html deleted file mode 100644 index 4984f47d..00000000 --- a/doc/curation/processProteinFastaFile.html +++ /dev/null @@ -1,237 +0,0 @@ - - - - Description of processProteinFastaFile - - - - - - - - - -
Home > curation > processProteinFastaFile.m
- - - -

processProteinFastaFile -

- -

PURPOSE ^

-
processProteinFastaFile Rename protein FASTA headers using a gene mapping table.
- -

SYNOPSIS ^

-
function processedFaaFile = processProteinFastaFile(faaFile, geneTable, headerCol, varargin)
- -

DESCRIPTION ^

-
 processProteinFastaFile  Rename protein FASTA headers using a gene mapping table.
-
- Reads a protein FASTA file (as downloaded by downloadGenomeData) and
- replaces each sequence header with the value from the specified geneTable
- column, matched via the GenBank_protein accession present in the original
- FASTA header.  Sequences whose accession is not found in geneTable are
- kept with their original header unchanged.
-
- Parameters
- ----------
- faaFile : char
-     Path to the protein FASTA file (.faa) to process.
- geneTable : char | table
-     Either a path to a tab-delimited .tsv file produced by getGeneData,
-     or a MATLAB table variable.  Must contain at least the columns
-     'GenBank_protein' and the column named by headerCol.
- headerCol : char
-     Name of the geneTable column whose values will replace each FASTA
-     header (e.g. 'locus_tag', 'gene_name', 'GenBank_protein').
-
- Name-Value Arguments
- --------------------
- outputDir : char
-     Directory where the processed FASTA file is saved.  The output file
-     name is the original base name with '_processed' appended before the
-     extension (default: current working directory).
-
- Returns
- -------
- processedFaaFile : char
-     Full path to the written processed FASTA file.
-
- Examples
- --------
-     % Rename headers using locus_tag
-     [~, faa]  = downloadGenomeData('GCF_000002595.2');
-     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv');
-     outFile   = processProteinFastaFile(faa, geneTable, 'locus_tag');
-
-     % Load both inputs from saved files, use gene_name as header
-     outFile = processProteinFastaFile('GCF_000002595.2_protein.faa', 'chlamy.tsv', 'gene_name');
-
-     % Save the processed file to a specific directory
-     outFile = processProteinFastaFile(faa, 'chlamy.tsv', 'locus_tag', 'results/');
-
- Notes
- -----
- GenBank_protein must contain accessions matching the first token of each
- FASTA header (after '>').  For prokaryotes this is the protein_id
- (e.g. WP_012345678.1); for eukaryotes it is the GenBank accession stored
- in the GFF3 annotation by getGeneData.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function processedFaaFile = processProteinFastaFile(faaFile, geneTable, headerCol, varargin)
-0002 % processProteinFastaFile  Rename protein FASTA headers using a gene mapping table.
-0003 %
-0004 % Reads a protein FASTA file (as downloaded by downloadGenomeData) and
-0005 % replaces each sequence header with the value from the specified geneTable
-0006 % column, matched via the GenBank_protein accession present in the original
-0007 % FASTA header.  Sequences whose accession is not found in geneTable are
-0008 % kept with their original header unchanged.
-0009 %
-0010 % Parameters
-0011 % ----------
-0012 % faaFile : char
-0013 %     Path to the protein FASTA file (.faa) to process.
-0014 % geneTable : char | table
-0015 %     Either a path to a tab-delimited .tsv file produced by getGeneData,
-0016 %     or a MATLAB table variable.  Must contain at least the columns
-0017 %     'GenBank_protein' and the column named by headerCol.
-0018 % headerCol : char
-0019 %     Name of the geneTable column whose values will replace each FASTA
-0020 %     header (e.g. 'locus_tag', 'gene_name', 'GenBank_protein').
-0021 %
-0022 % Name-Value Arguments
-0023 % --------------------
-0024 % outputDir : char
-0025 %     Directory where the processed FASTA file is saved.  The output file
-0026 %     name is the original base name with '_processed' appended before the
-0027 %     extension (default: current working directory).
-0028 %
-0029 % Returns
-0030 % -------
-0031 % processedFaaFile : char
-0032 %     Full path to the written processed FASTA file.
-0033 %
-0034 % Examples
-0035 % --------
-0036 %     % Rename headers using locus_tag
-0037 %     [~, faa]  = downloadGenomeData('GCF_000002595.2');
-0038 %     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv');
-0039 %     outFile   = processProteinFastaFile(faa, geneTable, 'locus_tag');
-0040 %
-0041 %     % Load both inputs from saved files, use gene_name as header
-0042 %     outFile = processProteinFastaFile('GCF_000002595.2_protein.faa', 'chlamy.tsv', 'gene_name');
-0043 %
-0044 %     % Save the processed file to a specific directory
-0045 %     outFile = processProteinFastaFile(faa, 'chlamy.tsv', 'locus_tag', 'results/');
-0046 %
-0047 % Notes
-0048 % -----
-0049 % GenBank_protein must contain accessions matching the first token of each
-0050 % FASTA header (after '>').  For prokaryotes this is the protein_id
-0051 % (e.g. WP_012345678.1); for eukaryotes it is the GenBank accession stored
-0052 % in the GFF3 annotation by getGeneData.
-0053     
-0054     if nargin < 1 || isempty(faaFile)
-0055         error('processProteinFastaFile:missingInput', ...
-0056             'faaFile is required. Provide a path to a protein FASTA (.faa) file.');
-0057     end
-0058     faaFile = char(faaFile);
-0059     if ~isfile(faaFile)
-0060         error('processProteinFastaFile:fileNotFound', ...
-0061             'FASTA file not found: ''%s''.', faaFile);
-0062     end
-0063 
-0064     if nargin < 2 || isempty(geneTable)
-0065         error('processProteinFastaFile:missingInput', ...
-0066             'geneTable is required. Provide a table variable or a path to a .tsv file.');
-0067     end
-0068 
-0069     if nargin < 3 || isempty(headerCol)
-0070         error('processProteinFastaFile:missingInput', ...
-0071             'headerCol is required. Specify the geneTable column to use as the new FASTA header (e.g. ''locus_tag'').');
-0072     end
-0073     headerCol = char(headerCol);
-0074 
-0075     p = parseRAVENargs(varargin, {'outputDir', pwd});
-0076     outputDir = char(p.outputDir);
-0077 
-0078     % Load gene table
-0079     if ischar(geneTable) || isstring(geneTable)
-0080         tsvPath = char(geneTable);
-0081         if ~isfile(tsvPath)
-0082             error('processProteinFastaFile:fileNotFound', ...
-0083                 'Gene table file not found: ''%s''.', tsvPath);
-0084         end
-0085         geneTable = readtable(tsvPath, 'FileType', 'text', 'Delimiter', '\t', 'TextType', 'char');
-0086     end
-0087 
-0088     if ~istable(geneTable)
-0089         error('processProteinFastaFile:badInput', ...
-0090             'geneTable must be a MATLAB table or a path to a .tsv file.');
-0091     end
-0092 
-0093     for col = {'GenBank_protein', headerCol}
-0094         if ~ismember(col{1}, geneTable.Properties.VariableNames)
-0095             error('processProteinFastaFile:missingColumn', ...
-0096                 'geneTable is missing required column: ''%s''.', col{1});
-0097         end
-0098     end
-0099 
-0100     % Build protein-accession → header-value lookup map
-0101     proteinIDs = geneTable.GenBank_protein;
-0102     if ~iscell(proteinIDs), proteinIDs = cellstr(proteinIDs); end
-0103 
-0104     headerValues = geneTable.(headerCol);
-0105     if ~iscell(headerValues), headerValues = cellstr(headerValues); end
-0106 
-0107     % Keep only rows with a non-empty protein accession
-0108     validMask  = ~cellfun(@isempty, proteinIDs);
-0109     proteinMap = containers.Map(proteinIDs(validMask), num2cell(find(validMask)));
-0110 
-0111 
-0112     % Prepare output path
-0113     if ~exist(outputDir, 'dir')
-0114         mkdir(outputDir);
-0115     end
-0116     [~, baseName, ext] = fileparts(faaFile);
-0117     processedFaaFile   = fullfile(outputDir, [baseName '_processed' ext]);
-0118 
-0119 
-0120     % Read the FASTA, rename headers, write the result
-0121     fastaStruct = readFasta(faaFile);
-0122     nMatched    = 0;
-0123     for i = 1:numel(fastaStruct)
-0124         % The protein accession is the first whitespace-delimited token of
-0125         % the header (readFasta strips the leading '>')
-0126         tokens     = strsplit(fastaStruct(i).Header);
-0127         proteinAcc = tokens{1};
-0128         if isKey(proteinMap, proteinAcc)
-0129             fastaStruct(i).Header = headerValues{proteinMap(proteinAcc)};
-0130             nMatched = nMatched + 1;
-0131         end
-0132         % Sequences with no match keep their original header
-0133     end
-0134     writeFasta(processedFaaFile, fastaStruct);
-0135 
-0136     fprintf('Processed FASTA written to: %s\n', processedFaaFile);
-0137     fprintf('  Total sequences  : %d\n', numel(fastaStruct));
-0138     fprintf('  Renamed          : %d\n', nMatched);
-0139     fprintf('  Kept (no match)  : %d\n', numel(fastaStruct) - nMatched);
-0140 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/curation/renameModelGenes.html b/doc/curation/renameModelGenes.html deleted file mode 100644 index 54a6c461..00000000 --- a/doc/curation/renameModelGenes.html +++ /dev/null @@ -1,332 +0,0 @@ - - - - Description of renameModelGenes - - - - - - - - - -
Home > curation > renameModelGenes.m
- - - -

renameModelGenes -

- -

PURPOSE ^

-
renameModelGenes Replace gene identifiers in a RAVEN model.
- -

SYNOPSIS ^

-
function model = renameModelGenes(model, geneTable, fromCol, toCol)
- -

DESCRIPTION ^

-
 renameModelGenes  Replace gene identifiers in a RAVEN model.
-
- Updates model.genes and model.grRules by substituting the identifiers
- currently used in the model (fromCol) with new identifiers (toCol) from
- a mapping table produced by getGeneData or supplied manually. After
- renaming, model.rxnGeneMat is rebuilt automatically.
-
- Parameters
- ----------
- model : struct
-     RAVEN model struct. Must contain model.genes and model.grRules;
-     model.rxnGeneMat is rebuilt automatically.
- geneTable : table | char
-     Gene-mapping table. Can be supplied as:
-       (a) A MATLAB table variable already loaded in the workspace, or
-       (b) A path to a tab-delimited .tsv file — loaded automatically.
- fromCol : char
-     Column in geneTable whose values match the identifiers currently in
-     model.genes (e.g. 'locus_tag').
- toCol : char
-     Column in geneTable whose values will replace the current
-     identifiers (e.g. 'gene_name').
-
- Returns
- -------
- model : struct
-     Updated model struct with:
-       model.genes      — renamed gene list.
-       model.grRules    — gene-reaction rules using new identifiers.
-       model.rxnGeneMat — rebuilt sparse matrix matching the new gene list.
-
- Examples
- --------
-     model     = readYAMLmodel('iCre1355.yml');
-     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv');
-     model     = renameModelGenes(model, geneTable, 'locus_tag', 'gene_name');
-
-     % Supply a TSV file path directly
-     model = renameModelGenes(model, 'chlamy.tsv', 'locus_tag', 'gene_name');
-
- Notes
- -----
- * Genes with no entry in fromCol, or whose toCol value is empty, are left
-   unchanged; a warning lists them for investigation.
- * Word-boundary matching prevents partial substitution (e.g. 'gene1'
-   is not replaced inside 'gene10').
- * grRules are standardized via standardizeGrRules before and after
-   renaming to ensure consistent formatting.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function model = renameModelGenes(model, geneTable, fromCol, toCol)
-0002 % renameModelGenes  Replace gene identifiers in a RAVEN model.
-0003 %
-0004 % Updates model.genes and model.grRules by substituting the identifiers
-0005 % currently used in the model (fromCol) with new identifiers (toCol) from
-0006 % a mapping table produced by getGeneData or supplied manually. After
-0007 % renaming, model.rxnGeneMat is rebuilt automatically.
-0008 %
-0009 % Parameters
-0010 % ----------
-0011 % model : struct
-0012 %     RAVEN model struct. Must contain model.genes and model.grRules;
-0013 %     model.rxnGeneMat is rebuilt automatically.
-0014 % geneTable : table | char
-0015 %     Gene-mapping table. Can be supplied as:
-0016 %       (a) A MATLAB table variable already loaded in the workspace, or
-0017 %       (b) A path to a tab-delimited .tsv file — loaded automatically.
-0018 % fromCol : char
-0019 %     Column in geneTable whose values match the identifiers currently in
-0020 %     model.genes (e.g. 'locus_tag').
-0021 % toCol : char
-0022 %     Column in geneTable whose values will replace the current
-0023 %     identifiers (e.g. 'gene_name').
-0024 %
-0025 % Returns
-0026 % -------
-0027 % model : struct
-0028 %     Updated model struct with:
-0029 %       model.genes      — renamed gene list.
-0030 %       model.grRules    — gene-reaction rules using new identifiers.
-0031 %       model.rxnGeneMat — rebuilt sparse matrix matching the new gene list.
-0032 %
-0033 % Examples
-0034 % --------
-0035 %     model     = readYAMLmodel('iCre1355.yml');
-0036 %     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv');
-0037 %     model     = renameModelGenes(model, geneTable, 'locus_tag', 'gene_name');
-0038 %
-0039 %     % Supply a TSV file path directly
-0040 %     model = renameModelGenes(model, 'chlamy.tsv', 'locus_tag', 'gene_name');
-0041 %
-0042 % Notes
-0043 % -----
-0044 % * Genes with no entry in fromCol, or whose toCol value is empty, are left
-0045 %   unchanged; a warning lists them for investigation.
-0046 % * Word-boundary matching prevents partial substitution (e.g. 'gene1'
-0047 %   is not replaced inside 'gene10').
-0048 % * grRules are standardized via standardizeGrRules before and after
-0049 %   renaming to ensure consistent formatting.
-0050 
-0051     if nargin < 1 || isempty(model)
-0052         error('renameModelGenes:missingInput', ...
-0053             'model is required. Provide a RAVEN model struct.');
-0054     end
-0055 
-0056     if nargin < 2 || isempty(geneTable)
-0057         error('renameModelGenes:missingInput', ...
-0058             'geneTable is required. Provide a table variable or a path to a .tsv file.');
-0059     end
-0060 
-0061     if nargin < 3 || isempty(fromCol)
-0062         error('renameModelGenes:missingInput', ...
-0063             'fromCol is required. Specify the column in geneTable whose values match model.genes.');
-0064     end
-0065 
-0066     if nargin < 4 || isempty(toCol)
-0067         error('renameModelGenes:missingInput', ...
-0068             'toCol is required. Specify the column in geneTable with the replacement identifiers.');
-0069     end
-0070 
-0071     fromCol = char(fromCol);
-0072     toCol   = char(toCol);
-0073 
-0074     % Load table if a file path was supplied
-0075     if ischar(geneTable) || isstring(geneTable)
-0076         if ~isfile(geneTable)
-0077             error('renameModelGenes:fileNotFound', ...
-0078                   'Table file not found: %s', geneTable);
-0079         end
-0080         geneTable = readtable(geneTable, 'Delimiter', '\t', 'FileType', 'text', ...
-0081                               'TextType', 'char');
-0082         fprintf('Loaded gene table from file: %s  (%d rows)\n', ...
-0083                 geneTable, height(geneTable));
-0084     end
-0085 
-0086     % Validate column names
-0087     if ~ismember(fromCol, geneTable.Properties.VariableNames)
-0088         error('renameModelGenes:badColumn', ...
-0089               'Column "%s" not found in geneTable.\nAvailable columns: %s', ...
-0090               fromCol, strjoin(geneTable.Properties.VariableNames, ', '));
-0091     end
-0092     if ~ismember(toCol, geneTable.Properties.VariableNames)
-0093         error('renameModelGenes:badColumn', ...
-0094               'Column "%s" not found in geneTable.\nAvailable columns: %s', ...
-0095               toCol, strjoin(geneTable.Properties.VariableNames, ', '));
-0096     end
-0097 
-0098     % Build mapping: oldName → newName
-0099     % Use only rows where both fromCol and toCol are non-empty. If a fromCol value maps to
-0100     % multiple toCol values (isoforms), take the first non-empty one — gene names are
-0101     % per-gene, not per-isoform.
-0102 
-0103     fromVals = geneTable.(fromCol);
-0104     toVals   = geneTable.(toCol);
-0105 
-0106     % Convert to char cell if needed
-0107     if ~iscell(fromVals), fromVals = cellstr(fromVals); end
-0108     if ~iscell(toVals),   toVals   = cellstr(toVals);   end
-0109 
-0110     % Build map (first occurrence wins for duplicates). Only genes with a
-0111     % non-empty toCol value are mapped; the rest are left unchanged.
-0112     nameMap = containers.Map('KeyType', 'char', 'ValueType', 'char');
-0113     for i = 1:numel(fromVals)
-0114         k = strtrim(fromVals{i});
-0115         v = strtrim(toVals{i});
-0116         if ~isempty(k) && ~isempty(v) && ~isKey(nameMap, k)
-0117             nameMap(k) = v;
-0118         end
-0119     end
-0120 
-0121     % Standardize grRules before renaming to ensure consistent formatting for accurate replacement.
-0122     fprintf('Removing unnecessary outer parentheses ...\n');
-0123     model.grRules = removeUnnecessaryParentheses(model.grRules);
-0124     
-0125     fprintf('Standardizing grRules ...\n');
-0126     [model.grRules, ~, indexes2check] = standardizeGrRules(model, true);
-0127     if ~isempty(indexes2check)
-0128         warning('renameModelGenes:potentialErrors', ...
-0129             '%d grRules have potential issues. Review them before proceeding.', ...
-0130             numel(indexes2check));
-0131     end
-0132 
-0133     % Rename model.genes; genes without a mapping are left unchanged
-0134     nGenes   = numel(model.genes);
-0135     notFound = {};
-0136     for i = 1:nGenes
-0137         oldName = model.genes{i};
-0138         if isKey(nameMap, oldName)
-0139             model.genes{i} = nameMap(oldName);
-0140         else
-0141             notFound{end+1} = oldName; %#ok<AGROW>
-0142         end
-0143     end
-0144 
-0145     % Report genes that had no mapping and were left unchanged
-0146     if ~isempty(notFound)
-0147         warning('renameModelGenes:noMapping', ...
-0148             '%d gene(s) in the model had no mapping in column "%s" and were left unchanged:\n  %s', ...
-0149             numel(notFound), fromCol, strjoin(unique(notFound), ', '));
-0150     end
-0151 
-0152     % Rename genes inside grRules Sort keys longest-first to avoid partial substitutions
-0153     % (e.g. replace 'gene10' before 'gene1').
-0154     allKeys    = keys(nameMap);
-0155     allVals    = values(nameMap);
-0156     [~, order] = sort(cellfun(@numel, allKeys), 'descend');
-0157     allKeys    = allKeys(order);
-0158     allVals    = allVals(order);
-0159     
-0160     nRxns = numel(model.grRules);
-0161     for i = 1:nRxns
-0162         rule = model.grRules{i};
-0163         if isempty(rule)
-0164             continue
-0165         end
-0166         for j = 1:numel(allKeys)
-0167             oldG = allKeys{j};
-0168             newG = allVals{j};
-0169             % Use word-boundary replacement: only match oldG when surrounded
-0170             % by non-word characters (spaces, parentheses, start/end of string)
-0171             % to avoid replacing 'gene1' inside 'gene10'.
-0172             rule = regexprep(rule, ['(?<![^\s()])' regexptranslate('escape', oldG) ...
-0173                                     '(?![^\s()])'], newG);
-0174         end
-0175         model.grRules{i} = rule;
-0176     end
-0177 
-0178     % Rebuild rxnGeneMat to match the updated gene list
-0179     fprintf('Rebuilding rxnGeneMat ...\n');
-0180     [model.grRules, model.rxnGeneMat, ~] = standardizeGrRules(model, true);
-0181     
-0182     fprintf('Done. %d genes renamed, %d left unchanged.\n', ...
-0183             nGenes - numel(notFound), numel(notFound));
-0184 end
-0185 
-0186 %--------------------------------------------------------------------------
-0187 %  Helper functions
-0188 
-0189 function grRules = removeUnnecessaryParentheses(grRules)
-0190 % Remove unnecessary outer parentheses from grRules.
-0191 % e.g., '(A and (B or C))' → 'A and (B or C)'
-0192 %       '(A or B)' → 'A or B'
-0193 % but preserves '(A and B) or C' (outer parens are needed here)
-0194 %
-0195 % Checks if the opening parenthesis at position 1 closes before the end
-0196 % of the string. If yes, outer parens are necessary. If no, they can be
-0197 % removed recursively.
-0198 
-0199     nRxns = numel(grRules);
-0200     for i = 1:nRxns
-0201         rule = grRules{i};
-0202         if isempty(rule)
-0203             continue
-0204         end
-0205     
-0206         % Recursively remove outer parentheses while they are unnecessary
-0207         while length(rule) >= 2 && rule(1) == '(' && rule(end) == ')'
-0208             % Count brackets to see when the opening paren closes
-0209             parenCount = 0;
-0210             closedEarly = false;
-0211     
-0212             for j = 1:length(rule)
-0213                 if rule(j) == '('
-0214                     parenCount = parenCount + 1;
-0215                 elseif rule(j) == ')'
-0216                     parenCount = parenCount - 1;
-0217                     % If it reaches 0 before the end, outer parens are necessary
-0218                     if parenCount == 0 && j < length(rule)
-0219                         closedEarly = true;
-0220                         break
-0221                     end
-0222                 end
-0223             end
-0224     
-0225             if closedEarly
-0226                 % Opening paren closes early → outer parens are needed
-0227                 break
-0228             else
-0229                 % Opening paren only closes at the end → they're unnecessary
-0230                 rule = rule(2:end-1);
-0231             end
-0232         end
-0233     
-0234         grRules{i} = rule;
-0235     end
-0236 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/gapfilling/checkProduction.html b/doc/gapfilling/checkProduction.html deleted file mode 100644 index 6cdd7b37..00000000 --- a/doc/gapfilling/checkProduction.html +++ /dev/null @@ -1,259 +0,0 @@ - - - - Description of checkProduction - - - - - - - - - -
Home > gapfilling > checkProduction.m
- - - -

checkProduction -

- -

PURPOSE ^

-
checkProduction Check which metabolites can be produced from a model.
- -

SYNOPSIS ^

-
function [notProduced, notProducedNames, neededForProductionMat,minToConnect,model]=checkProduction(model,varargin)
- -

DESCRIPTION ^

-
 checkProduction  Check which metabolites can be produced from a model.
-
- Checks which metabolites that can be produced from a model using the
- specified constraints.
-
- The function is intended to be used to identify which metabolites must be
- connected in order to have a fully connected network. It does so by first
- identifying which metabolites could have a net production in the network.
- Then it calculates which other metabolites must be able to have net
- production in order to have production of all metabolites in the network.
- So, if a network contains the equations A[external]->B, C->D, and D->E it
- will identify that production of C will connect the metabolites D and E.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- checkNeededForProduction : logical
-     for each of the metabolites that could not be produced, include an
-     artificial production reaction and calculate which new metabolites that
-     could be produced as an effect of this (default false).
- excretionFromCompartments : cell
-     cell array with compartment ids from which metabolites can be excreted
-     (default model.comps).
- printDetails : logical
-     print details to the screen (default true).
-
- Returns
- -------
- notProduced : double
-     indices of metabolites that could not be produced.
- notProducedNames : cell
-     cell array with names and compartments for metabolites that could not
-     be produced.
- neededForProductionMat : logical
-     matrix where n x m is true if metabolite n allows for production of
-     metabolite m.
- minToConnect : cell
-     the minimal number of metabolites that need to be connected in order to
-     be able to produce all other metabolites, and which metabolites each of
-     them connects.
- model : struct
-     updated model structure with excretion reactions added.
-
- Examples
- --------
-     [notProduced, notProducedNames, neededForProductionMat, minToConnect, model] = ...
-         checkProduction(model, checkNeededForProduction, excretionFromCompartments, printDetails);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • gapReport gapReport Perform a gap analysis and summarize the results.
- - - - -

SOURCE CODE ^

-
0001 function [notProduced, notProducedNames, neededForProductionMat,minToConnect,model]=checkProduction(model,varargin)
-0002 % checkProduction  Check which metabolites can be produced from a model.
-0003 %
-0004 % Checks which metabolites that can be produced from a model using the
-0005 % specified constraints.
-0006 %
-0007 % The function is intended to be used to identify which metabolites must be
-0008 % connected in order to have a fully connected network. It does so by first
-0009 % identifying which metabolites could have a net production in the network.
-0010 % Then it calculates which other metabolites must be able to have net
-0011 % production in order to have production of all metabolites in the network.
-0012 % So, if a network contains the equations A[external]->B, C->D, and D->E it
-0013 % will identify that production of C will connect the metabolites D and E.
-0014 %
-0015 % Parameters
-0016 % ----------
-0017 % model : struct
-0018 %     a model structure.
-0019 %
-0020 % Name-Value Arguments
-0021 % --------------------
-0022 % checkNeededForProduction : logical
-0023 %     for each of the metabolites that could not be produced, include an
-0024 %     artificial production reaction and calculate which new metabolites that
-0025 %     could be produced as an effect of this (default false).
-0026 % excretionFromCompartments : cell
-0027 %     cell array with compartment ids from which metabolites can be excreted
-0028 %     (default model.comps).
-0029 % printDetails : logical
-0030 %     print details to the screen (default true).
-0031 %
-0032 % Returns
-0033 % -------
-0034 % notProduced : double
-0035 %     indices of metabolites that could not be produced.
-0036 % notProducedNames : cell
-0037 %     cell array with names and compartments for metabolites that could not
-0038 %     be produced.
-0039 % neededForProductionMat : logical
-0040 %     matrix where n x m is true if metabolite n allows for production of
-0041 %     metabolite m.
-0042 % minToConnect : cell
-0043 %     the minimal number of metabolites that need to be connected in order to
-0044 %     be able to produce all other metabolites, and which metabolites each of
-0045 %     them connects.
-0046 % model : struct
-0047 %     updated model structure with excretion reactions added.
-0048 %
-0049 % Examples
-0050 % --------
-0051 %     [notProduced, notProducedNames, neededForProductionMat, minToConnect, model] = ...
-0052 %         checkProduction(model, checkNeededForProduction, excretionFromCompartments, printDetails);
-0053 
-0054 p=parseRAVENargs(varargin, {'checkNeededForProduction',false; ...
-0055     'excretionFromCompartments',[]; ...
-0056     'printDetails',true});
-0057 checkNeededForProduction=p.checkNeededForProduction;
-0058 excretionFromCompartments=p.excretionFromCompartments;
-0059 printDetails=p.printDetails;
-0060 
-0061 if isempty(excretionFromCompartments)
-0062     excretionFromCompartments=model.comps;
-0063 else
-0064     excretionFromCompartments=convertCharArray(excretionFromCompartments);
-0065 end
-0066 
-0067 %Add an exchange reaction for each metabolite in the allowed compartments
-0068 %and see if it can carry a flux
-0069 allowedMetIds=ismember(model.comps(model.metComps),excretionFromCompartments);
-0070 allowedMetIndexes=find(allowedMetIds);
-0071 [model, addedRxns]=addExchangeRxns(model,'out',allowedMetIndexes);
-0072 
-0073 canProduce=haveFlux(model,10^-5,addedRxns);
-0074 
-0075 notProduced=find(~canProduce);
-0076 minToConnect={};
-0077 if checkNeededForProduction==true
-0078     %For each of the metabolites that couldn't be produced allow uptake and
-0079     %check which of the other metabolites that couldn't be produced that
-0080     %can be produced
-0081     neededForProductionMat=false(numel(notProduced));
-0082     for i=1:numel(notProduced)
-0083         %Add uptake for this metabolite
-0084         if i>1
-0085             %Reset last iteration
-0086             model.S(:,numel(model.rxns)-numel(addedRxns)+notProduced(i-1))=model.S(:,numel(model.rxns)-numel(addedRxns)+notProduced(i-1))*-1;
-0087         end
-0088         %Change the production reaction to an uptake reaction
-0089         model.S(:,numel(model.rxns)-numel(addedRxns)+notProduced(i))=model.S(:,numel(model.rxns)-numel(addedRxns)+notProduced(i))*-1;
-0090         
-0091         %Test which of the metabolites that couldn't be produced that can
-0092         %be produced now
-0093         neededForProductionMat(i,:)=haveFlux(model,10^-5,addedRxns(notProduced));
-0094     end
-0095     %Calculate the smallest number of metabolites that must be connected to
-0096     %make everything connected and return their names
-0097     
-0098     %The algorithm is relatively straight forward. It finds the metabolite
-0099     %that connects the most unconnected metabolites (iteratively), adds it
-0100     %and removes the now connected metabolites until all are connected.
-0101     %This is not guaranteed to find the global minimum
-0102     neededForProdTemp=neededForProductionMat;
-0103     while 1==1
-0104         %Get all metabolites a metabolite makes connected
-0105         totalConnected=false(size(neededForProdTemp));
-0106         for i=1:numel(notProduced)
-0107             totalConnected(i,:)=neededForProdTemp(i,:);
-0108             
-0109             lastIter=0;
-0110             while 1==1
-0111                 [~, a]=find(neededForProdTemp(totalConnected(i,:),:));
-0112                 totalConnected(i,a)=true;
-0113                 if numel(a)==lastIter
-0114                     break; %No more connections were possible
-0115                 else
-0116                     lastIter=numel(a);
-0117                 end
-0118             end
-0119         end
-0120         [connections, mostConnected]=max(sum(totalConnected,2));
-0121         
-0122         if connections>0
-0123             %Add the most connected metabolite to the list and remove all
-0124             %metabolites that it's connected to
-0125             metID=allowedMetIndexes(notProduced(mostConnected));
-0126             entry=[model.metNames{metID},'[',model.comps{model.metComps(metID)},'] (connects ' num2str(connections) ' metabolites)'];
-0127             minToConnect=[minToConnect;entry];
-0128             neededForProdTemp(totalConnected(mostConnected,:),:)=false;
-0129             neededForProdTemp(:,totalConnected(mostConnected,:))=false;
-0130         else
-0131             break;
-0132         end
-0133     end
-0134 else
-0135     neededForProductionMat=[];
-0136 end
-0137 
-0138 notProducedNames=strcat(model.metNames(allowedMetIndexes(notProduced)),'[',model.comps(model.metComps(allowedMetIndexes(notProduced))),']');
-0139 
-0140 if printDetails==true
-0141     fprintf('The following metabolites could not be produced:\n');
-0142     [notProducedNamesTemp,perm]=sort(notProducedNames);
-0143     
-0144     if checkNeededForProduction==true
-0145         neededForProdTemp=neededForProductionMat(:,perm);
-0146         neededForProdTemp=neededForProdTemp(perm,:);
-0147         fprintf('\tIf the production of a metabolite is dependent on some other metabolites then those are printed under the name\n\n');
-0148     end
-0149     for i=1:numel(notProducedNamesTemp)
-0150         fprintf([notProducedNamesTemp{i} '\n']);
-0151         neededForProdTemp(i,i)=false; %Not neat to do this here. Prevent printing itself
-0152         if checkNeededForProduction==true
-0153             enablesProduction=find(neededForProdTemp(:,i));
-0154             if any(enablesProduction)
-0155                 for j=1:numel(enablesProduction)
-0156                     fprintf(['\t' notProducedNamesTemp{enablesProduction(j)} '\n']);
-0157                 end
-0158             end
-0159         end
-0160     end
-0161 end
-0162 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/gapfilling/fillGaps.html b/doc/gapfilling/fillGaps.html deleted file mode 100644 index 597442ad..00000000 --- a/doc/gapfilling/fillGaps.html +++ /dev/null @@ -1,443 +0,0 @@ - - - - Description of fillGaps - - - - - - - - - -
Home > gapfilling > fillGaps.m
- - - -

fillGaps -

- -

PURPOSE ^

-
fillGaps Use template model(s) to fill gaps in a model.
- -

SYNOPSIS ^

-
function [newConnected, cannotConnect, addedRxns, newModel, exitFlag]=fillGaps(model,models,varargin)
- -

DESCRIPTION ^

-
 fillGaps  Use template model(s) to fill gaps in a model.
-
- This method works by merging the model with the reference model(s) and
- checking which reactions can carry flux. All reactions that cannot carry
- flux are removed (cannotConnect). If useModelConstraints is false it
- then solves the MILP problem of minimizing the number of active
- reactions from the reference models that are required to have flux in
- all the reactions in model. This requires that the input model has
- exchange reactions present for the nutrients that are needed for its
- metabolism. If useModelConstraints is true then the problem is to
- include as few reactions as possible from the reference models in order
- to satisfy the model constraints.
-
- The intended use is that the user can attempt a general gap-filling
- using useModelConstraints=false, or a more targeted gap-filling by
- setting constraints in the model structure and then using
- useModelConstraints=true. For example, to include reactions so that all
- biomass components can be synthesized, the user could define a biomass
- equation and set its lower bound to >0. Running this function with
- useModelConstraints=true would then give the smallest set of reactions
- that have to be included in order for the model to produce biomass.
-
- Parameters
- ----------
- model : struct
-     a model structure that may contain gaps to be filled.
- models : cell or struct
-     a cell array of reference models or a model structure. The gaps will
-     be filled using reactions from these models.
-
- Name-Value Arguments
- --------------------
- allowNetProduction : logical
-     true if net production of all metabolites is allowed. A reaction can
-     be unable to carry flux because one of the reactants is unavailable
-     or because one of the products cannot be further processed. If true,
-     only the first type of unconnectivity is considered (default false).
- useModelConstraints : logical
-     true if the constraints specified in the model structure should be
-     used. If false then reactions are included from the template
-     model(s) so that as many reactions as possible in model can carry
-     flux (default false).
- supressWarnings : logical
-     false if warnings should be displayed (default false).
- rxnScores : double or cell
-     array with scores for each of the reactions in the reference
-     model(s). If more than one model is supplied, then rxnScores should
-     be a cell array of vectors. The solver will try to maximize the sum
-     of the scores for the included reactions (default is -1 for all
-     reactions).
- algorithm : char
-     gap-filling algorithm to use (default 'reference'):
-
-     - 'reference'   : MILP-based connectivity maximisation
-     - 'fastLP'      : L1-norm LP relaxation via FASTCORE (gapFillFastLP)
-     - 'swiftLP'     : SWIFTCORE single-LP variant (gapFillFastLP)
-     - 'gapfillMILP' : growth-floor MILP with directionality repair
-                       (gapFillMILP). Uses only models{1} as the database.
-     - 'topological' : BFS metabolite producibility pre-screening
-                       (gapFillTopological); no model modification.
- epsilon : double
-     minimum flux threshold used by fastLP and swiftLP (default 1e-4).
- minGrowth : double
-     minimum growth rate for gapfillMILP (default [] = 10% of max FBA).
- verbose : logical
-     print progress messages for new algorithms (default true).
-
- Returns
- -------
- newConnected : cell
-     cell array with the reactions that could be connected. This is not
-     calculated if useModelConstraints is true.
- cannotConnect : cell
-     cell array with reactions that could not be connected. This is not
-     calculated if useModelConstraints is true.
- addedRxns : cell
-     cell array with the reactions that were added from "models".
- newModel : struct
-     the model with reactions added to fill gaps.
- exitFlag : double
-     exit status:
-
-     - 1 : optimal solution found
-     - -1 : no feasible solution found
-     - -2 : optimization time out
-
- Examples
- --------
-     [newConnected, cannotConnect, addedRxns, newModel, exitFlag]=...
-         fillGaps(model,models,allowNetProduction,useModelConstraints,...
-         supressWarnings,rxnScores,params);
-
- See Also
- --------
- ftINITFillGaps : the ftINIT-side counterpart, used when the reference
-     model already contains the model's own reactions, so no per-task
-     merge is needed.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • gapFillFastLP gapFillFastLP LP-based gap-filling (fastGapFill / swiftGapFill).
  • gapFillMILP gapFillMILP Objective-based gap-filling via a global growth-floor MILP.
  • gapFillTopological gapFillTopological BFS metabolite-producibility pre-screening.
-This function is called by: -
    -
  • fitTasks fitTasks Fill gaps in a model so it can perform a list of tasks.
  • gapReport gapReport Perform a gap analysis and summarize the results.
- - - - -

SOURCE CODE ^

-
0001 function [newConnected, cannotConnect, addedRxns, newModel, exitFlag]=fillGaps(model,models,varargin)
-0002 % fillGaps  Use template model(s) to fill gaps in a model.
-0003 %
-0004 % This method works by merging the model with the reference model(s) and
-0005 % checking which reactions can carry flux. All reactions that cannot carry
-0006 % flux are removed (cannotConnect). If useModelConstraints is false it
-0007 % then solves the MILP problem of minimizing the number of active
-0008 % reactions from the reference models that are required to have flux in
-0009 % all the reactions in model. This requires that the input model has
-0010 % exchange reactions present for the nutrients that are needed for its
-0011 % metabolism. If useModelConstraints is true then the problem is to
-0012 % include as few reactions as possible from the reference models in order
-0013 % to satisfy the model constraints.
-0014 %
-0015 % The intended use is that the user can attempt a general gap-filling
-0016 % using useModelConstraints=false, or a more targeted gap-filling by
-0017 % setting constraints in the model structure and then using
-0018 % useModelConstraints=true. For example, to include reactions so that all
-0019 % biomass components can be synthesized, the user could define a biomass
-0020 % equation and set its lower bound to >0. Running this function with
-0021 % useModelConstraints=true would then give the smallest set of reactions
-0022 % that have to be included in order for the model to produce biomass.
-0023 %
-0024 % Parameters
-0025 % ----------
-0026 % model : struct
-0027 %     a model structure that may contain gaps to be filled.
-0028 % models : cell or struct
-0029 %     a cell array of reference models or a model structure. The gaps will
-0030 %     be filled using reactions from these models.
-0031 %
-0032 % Name-Value Arguments
-0033 % --------------------
-0034 % allowNetProduction : logical
-0035 %     true if net production of all metabolites is allowed. A reaction can
-0036 %     be unable to carry flux because one of the reactants is unavailable
-0037 %     or because one of the products cannot be further processed. If true,
-0038 %     only the first type of unconnectivity is considered (default false).
-0039 % useModelConstraints : logical
-0040 %     true if the constraints specified in the model structure should be
-0041 %     used. If false then reactions are included from the template
-0042 %     model(s) so that as many reactions as possible in model can carry
-0043 %     flux (default false).
-0044 % supressWarnings : logical
-0045 %     false if warnings should be displayed (default false).
-0046 % rxnScores : double or cell
-0047 %     array with scores for each of the reactions in the reference
-0048 %     model(s). If more than one model is supplied, then rxnScores should
-0049 %     be a cell array of vectors. The solver will try to maximize the sum
-0050 %     of the scores for the included reactions (default is -1 for all
-0051 %     reactions).
-0052 % algorithm : char
-0053 %     gap-filling algorithm to use (default 'reference'):
-0054 %
-0055 %     - 'reference'   : MILP-based connectivity maximisation
-0056 %     - 'fastLP'      : L1-norm LP relaxation via FASTCORE (gapFillFastLP)
-0057 %     - 'swiftLP'     : SWIFTCORE single-LP variant (gapFillFastLP)
-0058 %     - 'gapfillMILP' : growth-floor MILP with directionality repair
-0059 %                       (gapFillMILP). Uses only models{1} as the database.
-0060 %     - 'topological' : BFS metabolite producibility pre-screening
-0061 %                       (gapFillTopological); no model modification.
-0062 % epsilon : double
-0063 %     minimum flux threshold used by fastLP and swiftLP (default 1e-4).
-0064 % minGrowth : double
-0065 %     minimum growth rate for gapfillMILP (default [] = 10% of max FBA).
-0066 % verbose : logical
-0067 %     print progress messages for new algorithms (default true).
-0068 %
-0069 % Returns
-0070 % -------
-0071 % newConnected : cell
-0072 %     cell array with the reactions that could be connected. This is not
-0073 %     calculated if useModelConstraints is true.
-0074 % cannotConnect : cell
-0075 %     cell array with reactions that could not be connected. This is not
-0076 %     calculated if useModelConstraints is true.
-0077 % addedRxns : cell
-0078 %     cell array with the reactions that were added from "models".
-0079 % newModel : struct
-0080 %     the model with reactions added to fill gaps.
-0081 % exitFlag : double
-0082 %     exit status:
-0083 %
-0084 %     - 1 : optimal solution found
-0085 %     - -1 : no feasible solution found
-0086 %     - -2 : optimization time out
-0087 %
-0088 % Examples
-0089 % --------
-0090 %     [newConnected, cannotConnect, addedRxns, newModel, exitFlag]=...
-0091 %         fillGaps(model,models,allowNetProduction,useModelConstraints,...
-0092 %         supressWarnings,rxnScores,params);
-0093 %
-0094 % See Also
-0095 % --------
-0096 % ftINITFillGaps : the ftINIT-side counterpart, used when the reference
-0097 %     model already contains the model's own reactions, so no per-task
-0098 %     merge is needed.
-0099 
-0100 %If the user only supplied a single template model
-0101 if ~iscell(models)
-0102     models={models};
-0103 end
-0104 
-0105 p=parseRAVENargs(varargin, {'allowNetProduction',false; ...
-0106     'useModelConstraints',false; ...
-0107     'supressWarnings',false; ...
-0108     'rxnScores',[]; ...
-0109     'params',[]; ...
-0110     'algorithm','reference'; ...
-0111     'epsilon',1e-4; ...
-0112     'minGrowth',[]; ...
-0113     'verbose',true});
-0114 allowNetProduction=p.allowNetProduction;
-0115 useModelConstraints=p.useModelConstraints;
-0116 supressWarnings=p.supressWarnings;
-0117 rxnScores=p.rxnScores;
-0118 params=p.params;
-0119 
-0120 % ---- Dispatch to alternative gap-filling algorithms ----
-0121 switch lower(p.algorithm)
-0122     case {'fastlp', 'swiftlp'}
-0123         if numel(models) < 1
-0124             error('RAVEN:badInput', 'fillGaps: fastLP/swiftLP requires at least one reference model.');
-0125         end
-0126         variant = strrep(lower(p.algorithm), 'lp', '');
-0127         [addedRxns, newModel, cannotConnect] = gapFillFastLP(model, models{1}, ...
-0128             'epsilon', p.epsilon, 'verbose', p.verbose, 'variant', variant);
-0129         newConnected = {};
-0130         exitFlag = 1;
-0131         return;
-0132     case 'gapfillmilp'
-0133         if numel(models) < 1
-0134             error('RAVEN:badInput', 'fillGaps: gapfillMILP requires at least one reference model.');
-0135         end
-0136         [addedRxns, ~, newModel, exitFlag] = gapFillMILP(model, models{1}, ...
-0137             'minGrowth', p.minGrowth, 'verbose', p.verbose, 'params', params);
-0138         newConnected = {};
-0139         cannotConnect = {};
-0140         return;
-0141     case 'topological'
-0142         if numel(models) < 1
-0143             error('RAVEN:badInput', 'fillGaps: topological requires at least one reference model.');
-0144         end
-0145         result = gapFillTopological(model, models{1}, 'verbose', p.verbose);
-0146         addedRxns    = {};
-0147         newModel     = model;
-0148         cannotConnect = result.blockedMets;
-0149         newConnected = {};
-0150         exitFlag = 1;
-0151         return;
-0152 end
-0153 % Falls through to the 'reference' algorithm for all other values.
-0154 
-0155 if isempty(rxnScores)
-0156     rxnScores=cell(numel(models),1);
-0157     for i=1:numel(models)
-0158         rxnScores{i}=ones(numel(models{i}.rxns),1)*-1;
-0159     end
-0160 end
-0161 if isempty(params)
-0162     params=struct();
-0163 end
-0164 
-0165 if ~iscell(rxnScores)
-0166     rxnScores={rxnScores};
-0167 end
-0168 
-0169 models=models(:);
-0170 rxnScores=rxnScores(:);
-0171 
-0172 %Check if the original model has an unconstrained field. If so, give a
-0173 %warning
-0174 if supressWarnings==false
-0175     if isfield(model,'unconstrained')
-0176         EM='This algorithm is meant to function on a model with exchange reactions for uptake and excretion of metabolites. The current model still has the "unconstrained" field';
-0177         warning('RAVEN:warning', '%s', EM);
-0178     else
-0179         if isempty(getExchangeRxns(model,'both'))
-0180             fprintf('NOTE: This algorithm is meant to function on a model with exchange reactions for uptake and excretion of metabolites. The current model does not seem to contain any such reactions.\n');
-0181         end
-0182     end
-0183 end
-0184 
-0185 %Simplify the template models to remove constrained rxns. At the same time,
-0186 %check that the id of the template models is not the same as the model. That
-0187 %would cause an error further down
-0188 for i=1:numel(models)
-0189     models{i}.rxnScores=rxnScores{i};
-0190     models{i}=simplifyModel(models{i},false,false,true);
-0191     if strcmpi(models{i}.id,model.id)
-0192         EM='The reference model(s) cannot have the same id as the model';
-0193         error('RAVEN:badInput', '%s', EM);
-0194     end
-0195 end
-0196 
-0197 %This is a rather ugly solution to the issue that it is a bit tricky to keep
-0198 %track of which scores belong to which reactions. This requires that
-0199 %removeReactions and mergeModels are modified to check for the new field.
-0200 model.rxnScores=zeros(numel(model.rxns),1);
-0201 
-0202 %First merge all models into one big one
-0203 allModels=mergeModels([{model};models],'metNames',true);
-0204 
-0205 %Add that net production is ok
-0206 if allowNetProduction==true
-0207     %A second column in model.b means that the b field is lower and upper
-0208     %bound on the RHS.
-0209     model.b=[model.b(:,1) inf(numel(model.mets),1)];
-0210     allModels.b=[allModels.b(:,1) inf(numel(allModels.mets),1)];
-0211 end
-0212 
-0213 if useModelConstraints==true
-0214     newConnected={};
-0215     cannotConnect={};
-0216     
-0217     %Check that the input model is not solveable without any input
-0218     sol=solveLP(model);
-0219     if ~isempty(sol.f)
-0220         addedRxns={};
-0221         newModel=model;
-0222         exitFlag=1;
-0223         return;
-0224     end
-0225     
-0226     %Then check that the merged model is solveable
-0227     sol=solveLP(allModels);
-0228     if isempty(sol.f)
-0229         EM='There are no reactions in the template model(s) that can make the model constraints satisfied';
-0230         error('RAVEN:badInput', '%s', EM);
-0231     end
-0232     
-0233     %Remove dead ends for speed reasons. This has to be done here and
-0234     %duplicate below because there is otherwise a risk that a reaction
-0235     %which is constrained to something relevant is removed
-0236     allModels=simplifyModel(allModels,false,false,false,true,false,false,false,[],true);
-0237     allModels.c(:)=0;
-0238 else
-0239     %Remove dead ends for speed reasons
-0240     allModels=simplifyModel(allModels,false,false,false,true,false,false,false,[],true);
-0241     allModels.c(:)=0;
-0242     
-0243     %If model constraints should not be used, then determine which reactions
-0244     %to force to have flux
-0245     
-0246     %Get the reactions that can carry flux in the original model
-0247     originalFlux=haveFlux(model,1);
-0248     
-0249     %For the ones that cannot carry flux, see if they can do so in the
-0250     %merged model. Reactions from model keep their id unchanged in
-0251     %allModels (mergeModels renames colliding ids in the other input
-0252     %models, never in model itself), so intersecting with allModels.rxns
-0253     %both identifies them and drops any that simplifyModel has since
-0254     %removed as dead ends. The previous strcmp(allModels.rxnFrom,model.id)
-0255     %failed whenever model already carried a rxnFrom field of its own, as
-0256     %getModelFromHomology output does, tagging each reaction with its
-0257     %template rather than model.id, so mergeModels leaves it untouched and
-0258     %it never matches model.id here.
-0259     toCheck=intersect(model.rxns(~originalFlux),allModels.rxns);
-0260     
-0261     %Get the ones that still cannot carry flux. Guard against an empty
-0262     %toCheck, since haveFlux interprets an empty reaction list as "all
-0263     %reactions" rather than "none"
-0264     if isempty(toCheck)
-0265         K=toCheck;
-0266     else
-0267         I=haveFlux(allModels,1,toCheck);
-0268         %Get the reactions that cannot carry flux in the original model, but can
-0269         %in the merged one
-0270         K=toCheck(I);
-0271     end
-0272     
-0273     %This is a temporary thing to only look at the non-reversible rxns.
-0274     %This is because all reversible rxns can have a flux in the
-0275     %irreversible model format that is used by getMinNrFluxes
-0276     [~, I]=ismember(K,model.rxns);
-0277     K(model.rev(I)~=0)=[];
-0278     
-0279     %Constrain all reactions in the original model to have a flux
-0280     allModels.lb(ismember(allModels.rxns,K))=0.1;
-0281     
-0282     newConnected=K;
-0283     cannotConnect=setdiff(model.rxns(~originalFlux),newConnected);
-0284 end
-0285 
-0286 %Then minimize for the number of fluxes used. The fixed rxns (model's own)
-0287 %do not need to participate; see the comment above on why this is
-0288 %identified by rxn ID rather than allModels.rxnFrom.
-0289 templateRxns=find(~ismember(allModels.rxns,model.rxns));
-0290 [~, J, exitFlag]=getMinNrFluxes(allModels,templateRxns,params,allModels.rxnScores(templateRxns));
-0291 
-0292 %Remove everything except for the added ones
-0293 I=true(numel(allModels.rxns),1);
-0294 I(templateRxns(J))=false;
-0295 addedModel=removeReactions(allModels,I,true,true,true);
-0296 
-0297 newModel=mergeModels({model;addedModel},'metNames',true);
-0298 addedRxns=setdiff(newModel.rxns,model.rxns);
-0299 newModel=rmfield(newModel,'rxnScores');
-0300 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/gapfilling/ftINITFillGaps.html b/doc/gapfilling/ftINITFillGaps.html deleted file mode 100644 index f3e141d0..00000000 --- a/doc/gapfilling/ftINITFillGaps.html +++ /dev/null @@ -1,215 +0,0 @@ - - - - Description of ftINITFillGaps - - - - - - - - - -
Home > gapfilling > ftINITFillGaps.m
- - - -

ftINITFillGaps -

- -

PURPOSE ^

-
ftINITFillGaps
- -

SYNOPSIS ^

-
function [addedRxns, newModel, exitFlag]=ftINITFillGaps(tModel, origModel, tRefModel,allowNetProduction,supressWarnings,rxnScores,params,verbose,resolveTies)
- -

DESCRIPTION ^

-
 ftINITFillGaps
-   Variant of fillGaps specially adapted to speed up generation of ftINIT models.
-
-   tModel              model that contains the task-specific rxns.
-   origModel           model without task-specific rxns.         
-   tRefModel           reference tModel - the full tModel, containing all rxns
-                       used for gap-filling of tModel + the task-specific rxns
-   allowNetProduction  true if net production of all metabolites is
-                       allowed. A reaction can be unable to carry flux because one of
-                       the reactants is unavailable or because one of the
-                       products can't be further processed. If this
-                       parameter is true, only the first type of
-                       unconnectivity is considered (optional, default false)
-   supressWarnings     false if warnings should be displayed (optional, default
-                       false)
-   rxnScores           scores for each of the reactions in the
-                       reference tModel. 
-                       The solver will try to maximize the sum of the
-                       scores for the included reactions
-   params              *obsolete option*
-   verbose             if true, the MILP progression will be shown.
-   resolveTies         if true, pin the gap-fill MILP's degenerate optimum to a
-                       canonical answer instead of relying on the solver Seed
-                       alone -- see getMinNrFluxes' resolveTies (optional,
-                       default false)
-
-   addedRxns           the rxns added
-   newModel            the tModel with reactions added to fill gaps
-   exitFlag            1: optimal solution found
-                      -1: no feasible solution found
-                      -2: a solution was found but is not proven optimal,
-                          because the solver stopped before reaching
-                          optimality
-
-   This method works by merging the tModel to the reference model and
-   checking which reactions can carry flux. All reactions that can't
-   carry flux are removed. It then solves the MILP problem of including
-   as few additional reactions as possible from the reference model so
-   that the constraints already set on tModel (e.g. a required biomass
-   flux, or exchange bounds) can be satisfied.
-
-   See also: fillGaps, the tINIT-side counterpart, which merges a
-   reference model with the model being gap-filled on every call instead
-   of expecting them pre-merged.
-
- Usage: [addedRxns, newModel, exitFlag]=...
-           ftINITFillGaps(tModel,origModel,tRefModel,allowNetProduction,...
-           supressWarnings,rxnScores,params,verbose,resolveTies)
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • fitTasks fitTasks Fill gaps in a model so it can perform a list of tasks.
- - - - -

SOURCE CODE ^

-
0001 function [addedRxns, newModel, exitFlag]=ftINITFillGaps(tModel, origModel, tRefModel,allowNetProduction,supressWarnings,rxnScores,params,verbose,resolveTies)
-0002 % ftINITFillGaps
-0003 %   Variant of fillGaps specially adapted to speed up generation of ftINIT models.
-0004 %
-0005 %   tModel              model that contains the task-specific rxns.
-0006 %   origModel           model without task-specific rxns.
-0007 %   tRefModel           reference tModel - the full tModel, containing all rxns
-0008 %                       used for gap-filling of tModel + the task-specific rxns
-0009 %   allowNetProduction  true if net production of all metabolites is
-0010 %                       allowed. A reaction can be unable to carry flux because one of
-0011 %                       the reactants is unavailable or because one of the
-0012 %                       products can't be further processed. If this
-0013 %                       parameter is true, only the first type of
-0014 %                       unconnectivity is considered (optional, default false)
-0015 %   supressWarnings     false if warnings should be displayed (optional, default
-0016 %                       false)
-0017 %   rxnScores           scores for each of the reactions in the
-0018 %                       reference tModel.
-0019 %                       The solver will try to maximize the sum of the
-0020 %                       scores for the included reactions
-0021 %   params              *obsolete option*
-0022 %   verbose             if true, the MILP progression will be shown.
-0023 %   resolveTies         if true, pin the gap-fill MILP's degenerate optimum to a
-0024 %                       canonical answer instead of relying on the solver Seed
-0025 %                       alone -- see getMinNrFluxes' resolveTies (optional,
-0026 %                       default false)
-0027 %
-0028 %   addedRxns           the rxns added
-0029 %   newModel            the tModel with reactions added to fill gaps
-0030 %   exitFlag            1: optimal solution found
-0031 %                      -1: no feasible solution found
-0032 %                      -2: a solution was found but is not proven optimal,
-0033 %                          because the solver stopped before reaching
-0034 %                          optimality
-0035 %
-0036 %   This method works by merging the tModel to the reference model and
-0037 %   checking which reactions can carry flux. All reactions that can't
-0038 %   carry flux are removed. It then solves the MILP problem of including
-0039 %   as few additional reactions as possible from the reference model so
-0040 %   that the constraints already set on tModel (e.g. a required biomass
-0041 %   flux, or exchange bounds) can be satisfied.
-0042 %
-0043 %   See also: fillGaps, the tINIT-side counterpart, which merges a
-0044 %   reference model with the model being gap-filled on every call instead
-0045 %   of expecting them pre-merged.
-0046 %
-0047 % Usage: [addedRxns, newModel, exitFlag]=...
-0048 %           ftINITFillGaps(tModel,origModel,tRefModel,allowNetProduction,...
-0049 %           supressWarnings,rxnScores,params,verbose,resolveTies)
-0050 
-0051 if nargin<9 || isempty(resolveTies)
-0052     resolveTies=false;
-0053 end
-0054 if isempty(rxnScores)
-0055     rxnScores=ones(numel(tRefModel.rxns),1)*-1;
-0056 end
-0057 
-0058 %Simplify the template models to remove constrained rxns. At the same time,
-0059 %check that the id of the template models isn't the same as the tModel. That
-0060 %would cause an error further down
-0061 tRefModel.rxnScores=rxnScores;
-0062 tRefModel=simplifyModel(tRefModel,false,false,true);
-0063 
-0064 %This is a rather ugly solution to the issue that it's a bit tricky to keep
-0065 %track of which scores belong to which reactions. This requires that
-0066 %removeReactions and mergeModels are modified to check for the new field.
-0067 tModel.rxnScores=zeros(numel(tModel.rxns),1);
-0068 
-0069 %First merge all models into one big one
-0070 fullModel = tRefModel;%mergeModels([{tModel};models],'metNames',true);
-0071 
-0072 %Add that net production is ok
-0073 if allowNetProduction==true
-0074     %A second column in tModel.b means that the b field is lower and upper
-0075     %bound on the RHS.
-0076     tModel.b=[tModel.b(:,1) inf(numel(tModel.mets),1)];
-0077     fullModel.b=[fullModel.b(:,1) inf(numel(fullModel.mets),1)];
-0078 end
-0079 
-0080 
-0081 fullModel.c(:)=0;
-0082 
-0083 %Then minimize for the number of fluxes used. The fixed rxns doesn't need
-0084 %to participate
-0085 templateRxns = find(~ismember(fullModel.rxns, tModel.rxns)); %Check if this is slow, in that case keep track of this in fitTasksOpt and send it in
-0086 
-0087 if isempty(templateRxns)
-0088     %The reference model holds no reaction that the model does not already
-0089     %have, so no set of additions can satisfy the constraints. This has to
-0090     %be caught first: an empty toMinimize means "all reactions" to
-0091     %getMinNrFluxes, and its solution would then be indexed against an
-0092     %empty templateRxns.
-0093     addedRxns = {};
-0094     newModel = origModel;
-0095     exitFlag = -1;
-0096     return;
-0097 end
-0098 
-0099 %The reversible formulation uses one binary per reaction rather than one
-0100 %per irreversible reaction, which is what makes this fast enough to run
-0101 %once per task on a genome-scale reference model
-0102 [~, J, exitFlag]=getMinNrFluxes(fullModel,templateRxns,params,fullModel.rxnScores(templateRxns),'formulation','reversible','verbose',verbose,'resolveTies',resolveTies);%only the scores from the template rxns are used, so the others doesn't matter
-0103 
-0104 %Remove everything except for the added ones
-0105 addedRxns = fullModel.rxns(templateRxns(J));
-0106 if isempty(addedRxns)
-0107     %No reactions were selected, either because the MILP found no solution
-0108     %or because none were needed. Handing an empty set to addRxns below
-0109     %errors out, which would reach the caller as a thrown exception rather
-0110     %than as the exitFlag it keys on.
-0111     addedRxns = {};
-0112     newModel = origModel;
-0113     return;
-0114 end
-0115 rxnsToAdd.rxns = addedRxns;
-0116 rxnsToAdd.equations = constructEquations(fullModel, addedRxns);
-0117 rxnsToAdd.ub = fullModel.ub(templateRxns(J));
-0118 rxnsToAdd.lb = fullModel.lb(templateRxns(J));
-0119 newModel = addRxns(origModel,rxnsToAdd, 3, [], true); %we ignore gene rules etc here, they are not needed - we regenerate the model in the end by subtracting rxns from the full model.
-0120 
-0121 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/gapfilling/gapFillFastCore.html b/doc/gapfilling/gapFillFastCore.html deleted file mode 100644 index d7e6d2e4..00000000 --- a/doc/gapfilling/gapFillFastCore.html +++ /dev/null @@ -1,199 +0,0 @@ - - - - Description of gapFillFastCore - - - - - - - - - -
Home > gapfilling > gapFillFastCore.m
- - - -

gapFillFastCore -

- -

PURPOSE ^

-
gapFillFastCore L1-norm LP subroutine for fastGapFill.
- -

SYNOPSIS ^

-
function activeRxns = gapFillFastCore(model, coreRxns, epsilon)
- -

DESCRIPTION ^

-
 gapFillFastCore  L1-norm LP subroutine for fastGapFill.
-
- Finds the minimum-L1-flux extension of the model that activates all
- reactions in the core set. Any reaction with non-zero flux in the
- solution is "consistent" with the core. Used internally by gapFillFastLP.
-
- Based on FASTCORE (Vlassis et al. 2014, PLoS Comput Biol 10:e1003424).
-
- Parameters
- ----------
- model : struct
-     a RAVEN model structure.
- coreRxns : cell or logical or double
-     core reaction set: cell array of reaction IDs, a logical vector, or a
-     vector of reaction indices into model.rxns. All core reactions are
-     forced to carry flux >= epsilon in the LP solution.
- epsilon : double
-     minimum flux threshold for a reaction to be considered active.
-
- Returns
- -------
- activeRxns : logical
-     logical vector (length numel(model.rxns)) where true indicates the
-     reaction carries flux in the L1-minimal solution. Empty if the LP is
-     infeasible (core reactions cannot all carry flux simultaneously).
-
- See also
- --------
- gapFillSwiftCore, gapFillFastLP
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • gapFillFastLP gapFillFastLP LP-based gap-filling (fastGapFill / swiftGapFill).
- - - - -

SOURCE CODE ^

-
0001 function activeRxns = gapFillFastCore(model, coreRxns, epsilon)
-0002 % gapFillFastCore  L1-norm LP subroutine for fastGapFill.
-0003 %
-0004 % Finds the minimum-L1-flux extension of the model that activates all
-0005 % reactions in the core set. Any reaction with non-zero flux in the
-0006 % solution is "consistent" with the core. Used internally by gapFillFastLP.
-0007 %
-0008 % Based on FASTCORE (Vlassis et al. 2014, PLoS Comput Biol 10:e1003424).
-0009 %
-0010 % Parameters
-0011 % ----------
-0012 % model : struct
-0013 %     a RAVEN model structure.
-0014 % coreRxns : cell or logical or double
-0015 %     core reaction set: cell array of reaction IDs, a logical vector, or a
-0016 %     vector of reaction indices into model.rxns. All core reactions are
-0017 %     forced to carry flux >= epsilon in the LP solution.
-0018 % epsilon : double
-0019 %     minimum flux threshold for a reaction to be considered active.
-0020 %
-0021 % Returns
-0022 % -------
-0023 % activeRxns : logical
-0024 %     logical vector (length numel(model.rxns)) where true indicates the
-0025 %     reaction carries flux in the L1-minimal solution. Empty if the LP is
-0026 %     infeasible (core reactions cannot all carry flux simultaneously).
-0027 %
-0028 % See also
-0029 % --------
-0030 % gapFillSwiftCore, gapFillFastLP
-0031 
-0032 if nargin < 3 || isempty(epsilon)
-0033     epsilon = 1e-4;
-0034 end
-0035 
-0036 % ---- Resolve core reaction indices into original model ----
-0037 % coreOrigIdx: numeric indices into model.rxns
-0038 if iscell(coreRxns)
-0039     coreOrigIdx = find(getIndexes(model, coreRxns, 'rxns', true));
-0040 elseif islogical(coreRxns)
-0041     coreOrigIdx = find(coreRxns);
-0042 else
-0043     coreOrigIdx = coreRxns(:);
-0044 end
-0045 
-0046 % ---- Convert to irreversible form ----
-0047 % All fluxes become non-negative → L1 norm = sum of fluxes.
-0048 [irrevModel, matchRev, ~, irrev2rev] = convertToIrrev(model);
-0049 nIrrev = numel(irrevModel.rxns);
-0050 nMets  = numel(irrevModel.mets);
-0051 
-0052 % Map core reactions to their irreversible equivalents. convertToIrrev
-0053 % keeps the forward copy at the reaction's original index, so coreOrigIdx
-0054 % already are that copy's indices.
-0055 coreIdxIrrev = false(nIrrev, 1);
-0056 coreIdxIrrev(coreOrigIdx) = true;
-0057 
-0058 % A reversible core reaction also has a reverse copy elsewhere in
-0059 % irrevModel, with identical but oppositely-signed stoichiometry. Forcing
-0060 % both copies to >= epsilon would let them cancel out in S_irrev*v=0
-0061 % (v_fwd=v_bwd=epsilon nets to zero for every metabolite the reaction
-0062 % touches), satisfying the "core must carry flux" requirement with a
-0063 % self-contained loop that needs no support from the rest of the network,
-0064 % regardless of whether the reaction has any real connectivity. Instead,
-0065 % require flux on one copy at a time: try forward first, and if that is
-0066 % infeasible, retry with reverse forced instead, so a reaction whose only
-0067 % genuine support is in the reverse direction is still recognised.
-0068 isRevCore   = matchRev(coreOrigIdx) > 0;
-0069 fwdCoreCopy = coreOrigIdx(isRevCore);          % forward copy of reversible core rxns
-0070 revCoreCopy = matchRev(coreOrigIdx(isRevCore)); % their reverse copy
-0071 irrCoreCopy = coreOrigIdx(~isRevCore);          % irreversible core rxns: single copy
-0072 
-0073 c = ones(nIrrev, 1);
-0074 c(coreIdxIrrev) = 0;   % core reactions are not in the objective
-0075 
-0076 prob.A      = irrevModel.S;
-0077 prob.a      = prob.A;
-0078 prob.b      = zeros(nMets, 1);
-0079 prob.csense = repmat('E', 1, nMets);
-0080 prob.c      = c;
-0081 prob.osense = 1;          % minimise
-0082 prob.vartype = repmat('C', 1, nIrrev);
-0083 
-0084 % ---- Formulate LP ----
-0085 % Minimize sum of non-core fluxes (all fluxes >= 0 in irreversible form,
-0086 % so this is the L1 norm over non-core reactions).
-0087 %
-0088 %   min   sum_{j not in C} v_j
-0089 %   s.t.  S_irrev * v = 0
-0090 %         v_j >= epsilon   for j in C
-0091 %         lb <= v <= ub
-0092 %
-0093 forceCopy   = {fwdCoreCopy, revCoreCopy};
-0094 disableCopy = {revCoreCopy, fwdCoreCopy};
-0095 nPasses     = 1 + ~isempty(fwdCoreCopy);   % a second pass only differs when a reversible core rxn exists
-0096 for pass = 1:nPasses
-0097     lb = irrevModel.lb;
-0098     lb(irrCoreCopy)         = max(lb(irrCoreCopy), epsilon);
-0099     lb(forceCopy{pass})     = max(lb(forceCopy{pass}), epsilon);
-0100     ub = irrevModel.ub;
-0101     ub(disableCopy{pass})   = 0;
-0102     prob.lb = lb;
-0103     prob.ub = ub;
-0104 
-0105     sol = optimizeProb(prob, [], false);
-0106     if checkSolution(sol)
-0107         break;
-0108     end
-0109 end
-0110 
-0111 if ~checkSolution(sol)
-0112     activeRxns = false(numel(model.rxns), 1);
-0113     return;
-0114 end
-0115 
-0116 % ---- Map solution back to original model ----
-0117 irrevActive = sol.full(1:nIrrev) >= epsilon / 2;
-0118 activeRxns  = false(numel(model.rxns), 1);
-0119 for k = 1:nIrrev
-0120     if irrevActive(k)
-0121         activeRxns(irrev2rev(k)) = true;
-0122     end
-0123 end
-0124 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/gapfilling/gapFillFastLP.html b/doc/gapfilling/gapFillFastLP.html deleted file mode 100644 index 16cf6d5a..00000000 --- a/doc/gapfilling/gapFillFastLP.html +++ /dev/null @@ -1,255 +0,0 @@ - - - - Description of gapFillFastLP - - - - - - - - - -
Home > gapfilling > gapFillFastLP.m
- - - -

gapFillFastLP -

- -

PURPOSE ^

-
gapFillFastLP LP-based gap-filling (fastGapFill / swiftGapFill).
- -

SYNOPSIS ^

-
function [addedRxns, newModel, cannotConnect] = gapFillFastLP(model, universalModel, varargin)
- -

DESCRIPTION ^

-
 gapFillFastLP  LP-based gap-filling (fastGapFill / swiftGapFill).
-
- Merges the draft model with a universal reaction database, then for each
- blocked draft reaction calls an LP subroutine (FASTCORE or SWIFTCORE) to
- find the minimum set of database reactions that makes the blocked reaction
- carry flux. The union of all activated database reactions across all blocked
- reactions is returned as addedRxns.
-
- Based on fastGapFill (Thiele et al. 2014, PLoS Comput Biol 10:e1003515)
- and swiftGapFill (Tefagh & Boyd 2020, BMC Bioinformatics 21:23).
-
- Parameters
- ----------
- model : struct
-     draft RAVEN model to be gap-filled.
- universalModel : struct
-     universal reaction database (RAVEN model). The caller is responsible
-     for building this, e.g. via getKEGGModelForOrganism with broad scope.
-
- Optional name-value parameters
- --------------------------------
- 'epsilon' (default 1e-4)
-     minimum flux threshold for a reaction to be considered active.
- 'variant' (default 'fast')
-     'fast'  — use FASTCORE L1-norm LP (gapFillFastCore)
-     'swift' — use SWIFTCORE single-LP (gapFillSwiftCore); faster but
-               stochastic (results may vary between runs/solvers).
- 'verbose' (default true)
-     print progress messages.
-
- Returns
- -------
- addedRxns : cell
-     reaction IDs from universalModel that should be added to rescue
-     blocked reactions.
- newModel : struct
-     copy of model with addedRxns incorporated.
- cannotConnect : cell
-     blocked draft reaction IDs that remain blocked even after augmenting
-     with the full universalModel (cannot be rescued by the database).
-
- See also
- --------
- gapFillFastCore, gapFillSwiftCore, gapFillMILP, fillGaps
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
  • fillGaps fillGaps Use template model(s) to fill gaps in a model.
- - - - -

SOURCE CODE ^

-
0001 function [addedRxns, newModel, cannotConnect] = gapFillFastLP(model, universalModel, varargin)
-0002 % gapFillFastLP  LP-based gap-filling (fastGapFill / swiftGapFill).
-0003 %
-0004 % Merges the draft model with a universal reaction database, then for each
-0005 % blocked draft reaction calls an LP subroutine (FASTCORE or SWIFTCORE) to
-0006 % find the minimum set of database reactions that makes the blocked reaction
-0007 % carry flux. The union of all activated database reactions across all blocked
-0008 % reactions is returned as addedRxns.
-0009 %
-0010 % Based on fastGapFill (Thiele et al. 2014, PLoS Comput Biol 10:e1003515)
-0011 % and swiftGapFill (Tefagh & Boyd 2020, BMC Bioinformatics 21:23).
-0012 %
-0013 % Parameters
-0014 % ----------
-0015 % model : struct
-0016 %     draft RAVEN model to be gap-filled.
-0017 % universalModel : struct
-0018 %     universal reaction database (RAVEN model). The caller is responsible
-0019 %     for building this, e.g. via getKEGGModelForOrganism with broad scope.
-0020 %
-0021 % Optional name-value parameters
-0022 % --------------------------------
-0023 % 'epsilon' (default 1e-4)
-0024 %     minimum flux threshold for a reaction to be considered active.
-0025 % 'variant' (default 'fast')
-0026 %     'fast'  — use FASTCORE L1-norm LP (gapFillFastCore)
-0027 %     'swift' — use SWIFTCORE single-LP (gapFillSwiftCore); faster but
-0028 %               stochastic (results may vary between runs/solvers).
-0029 % 'verbose' (default true)
-0030 %     print progress messages.
-0031 %
-0032 % Returns
-0033 % -------
-0034 % addedRxns : cell
-0035 %     reaction IDs from universalModel that should be added to rescue
-0036 %     blocked reactions.
-0037 % newModel : struct
-0038 %     copy of model with addedRxns incorporated.
-0039 % cannotConnect : cell
-0040 %     blocked draft reaction IDs that remain blocked even after augmenting
-0041 %     with the full universalModel (cannot be rescued by the database).
-0042 %
-0043 % See also
-0044 % --------
-0045 % gapFillFastCore, gapFillSwiftCore, gapFillMILP, fillGaps
-0046 
-0047 % ---- Parse optional arguments ----
-0048 p = inputParser();
-0049 addParameter(p, 'epsilon', 1e-4,   @isnumeric);
-0050 addParameter(p, 'variant', 'fast', @ischar);
-0051 addParameter(p, 'verbose', true,   @islogical);
-0052 parse(p, varargin{:});
-0053 epsilon = p.Results.epsilon;
-0054 variant = lower(p.Results.variant);
-0055 verbose = p.Results.verbose;
-0056 
-0057 % ---- Initialise outputs ----
-0058 addedRxns    = {};
-0059 cannotConnect = {};
-0060 newModel     = model;
-0061 
-0062 % ---- Identify blocked reactions in the draft model ----
-0063 blocked = find(~haveFlux(model, epsilon));
-0064 if isempty(blocked)
-0065     if verbose
-0066         fprintf('gapFillFastLP: all reactions carry flux; nothing to repair.\n');
-0067     end
-0068     return;
-0069 end
-0070 nBlocked = numel(blocked);
-0071 if verbose
-0072     fprintf('gapFillFastLP: %d blocked reaction(s) found in draft model.\n', nBlocked);
-0073 end
-0074 
-0075 % ---- Merge draft + universal database ----
-0076 mergedModel = mergeModels({model; universalModel});
-0077 
-0078 % For each draft reaction, find its position in the merged model.
-0079 [inMerged, draftPosInMerged] = ismember(model.rxns, mergedModel.rxns);
-0080 if any(~inMerged)
-0081     warning('gapFillFastLP: %d draft reaction(s) not found in merged model — skipped.', sum(~inMerged));
-0082 end
-0083 
-0084 % ---- Determine which blocked reactions can carry flux in the merged model ----
-0085 mergedHasFlux = haveFlux(mergedModel, epsilon);
-0086 draftCanCarry = mergedHasFlux(draftPosInMerged);   % indexed by draft position
-0087 
-0088 % Blocked reactions that still cannot carry flux even with the full DB.
-0089 blockedCanCarry = draftCanCarry(blocked);
-0090 cannotConnectIdx = blocked(~blockedCanCarry);
-0091 cannotConnect    = model.rxns(cannotConnectIdx);
-0092 rescuable        = blocked(blockedCanCarry);
-0093 
-0094 if verbose && ~isempty(cannotConnect)
-0095     fprintf('gapFillFastLP: %d reaction(s) cannot be rescued by the universal database.\n', ...
-0096         numel(cannotConnect));
-0097 end
-0098 
-0099 if isempty(rescuable)
-0100     if verbose
-0101         fprintf('gapFillFastLP: no rescuable blocked reactions; nothing to add.\n');
-0102     end
-0103     return;
-0104 end
-0105 if verbose
-0106     fprintf('gapFillFastLP: %d/%d blocked reaction(s) are rescuable; running %s...\n', ...
-0107         numel(rescuable), nBlocked, variant);
-0108 end
-0109 
-0110 % ---- For each rescuable blocked reaction, run the LP subroutine ----
-0111 % Accumulate which merged-model reactions are activated.
-0112 isUniversal   = ~ismember(mergedModel.rxns, model.rxns);
-0113 candidateSet  = false(numel(mergedModel.rxns), 1);
-0114 
-0115 nSkipped = 0;
-0116 for i = 1:numel(rescuable)
-0117     coreIdxInMerged = draftPosInMerged(rescuable(i));
-0118     if coreIdxInMerged == 0
-0119         nSkipped = nSkipped + 1;
-0120         continue;
-0121     end
-0122 
-0123     if strcmp(variant, 'swift')
-0124         active = gapFillSwiftCore(mergedModel, coreIdxInMerged, epsilon);
-0125     else
-0126         active = gapFillFastCore(mergedModel, coreIdxInMerged, epsilon);
-0127     end
-0128 
-0129     if any(active)
-0130         candidateSet = candidateSet | active;
-0131     else
-0132         nSkipped = nSkipped + 1;
-0133         if verbose
-0134             fprintf('gapFillFastLP: LP infeasible for %s; skipping.\n', model.rxns{rescuable(i)});
-0135         end
-0136     end
-0137 end
-0138 
-0139 if verbose && nSkipped > 0
-0140     fprintf('gapFillFastLP: %d reaction(s) skipped (infeasible LP).\n', nSkipped);
-0141 end
-0142 
-0143 % ---- Extract universal reactions that appear in any solution ----
-0144 addedMask = candidateSet & isUniversal;
-0145 addedRxns = mergedModel.rxns(addedMask);
-0146 
-0147 if isempty(addedRxns)
-0148     if verbose
-0149         fprintf('gapFillFastLP: no universal reactions needed (all gaps already resolved).\n');
-0150     end
-0151     return;
-0152 end
-0153 
-0154 % ---- Build new model: draft + selected universal reactions ----
-0155 toRemove = find(isUniversal & ~addedMask);
-0156 if ~isempty(toRemove)
-0157     newModel = removeReactions(mergedModel, mergedModel.rxns(toRemove));
-0158 else
-0159     newModel = mergedModel;
-0160 end
-0161 
-0162 if verbose
-0163     fprintf('gapFillFastLP: added %d reaction(s) from universal database.\n', numel(addedRxns));
-0164 end
-0165 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/gapfilling/gapFillMILP.html b/doc/gapfilling/gapFillMILP.html deleted file mode 100644 index f6f468e4..00000000 --- a/doc/gapfilling/gapFillMILP.html +++ /dev/null @@ -1,397 +0,0 @@ - - - - Description of gapFillMILP - - - - - - - - - -
Home > gapfilling > gapFillMILP.m
- - - -

gapFillMILP -

- -

PURPOSE ^

-
gapFillMILP Objective-based gap-filling via a global growth-floor MILP.
- -

SYNOPSIS ^

-
function [addedRxns, reversedRxns, newModel, exitFlag] = gapFillMILP(model, universalModel, varargin)
- -

DESCRIPTION ^

-
 gapFillMILP  Objective-based gap-filling via a global growth-floor MILP.
-
- Finds the minimum-cost set of modifications to make the model's objective
- (biomass) >= minGrowth. Supports two repair mechanisms from Kumar et al.
- 2007 (Bioinformatics 23:1626-1635):
-   1. Directionality reversal of existing draft reactions (binary y_rev)
-   2. Addition of reactions from a universal database (binary y_db)
-
- The universal model should contain all candidate reactions, including
- exchange and transport reactions if those repair classes are desired.
-
- This is a SINGLE-level MILP (not bilevel). It is computationally more
- expensive than gapFillFastLP but supports the directionality-reversal
- repair mechanism and directly optimises for objective feasibility.
-
- Parameters
- ----------
- model : struct
-     draft RAVEN model to be gap-filled.
- universalModel : struct
-     universal reaction database (RAVEN model).
-
- Optional name-value parameters
- --------------------------------
- 'minGrowth' (default [])
-     minimum objective value to achieve. If empty, runs FBA on the merged
-     model and uses 10% of the maximum as the floor.
- 'weights' (default [1 2 3 4])
-     cost weights [w_rev w_db w_exch w_trans]. w_rev is the cost per
-     reversed draft reaction; w_db is the cost per added universal
-     reaction. w_exch and w_trans are reserved for future use when
-     exchange and transport reactions are tracked separately.
- 'bigM' (default 1000)
-     Big-M constant for coupling constraints. Must be larger than any
-     expected flux magnitude.
- 'params' (default [])
-     solver parameter struct passed to optimizeProb. Fields such as
-     params.TimeLimit and params.intTol can be used to tune the MILP.
- 'verbose' (default true)
-     print progress messages.
-
- Returns
- -------
- addedRxns : cell
-     reaction IDs from universalModel that are selected by the MILP.
- reversedRxns : cell
-     draft reaction IDs whose directionality is reversed by the MILP.
- newModel : struct
-     model with addedRxns incorporated and reversedRxns bounds updated
-     (lb set to -bigM for reversed reactions).
- exitFlag : double
-     1 = optimal, -1 = infeasible, -2 = timeout or other failure.
-
- See also
- --------
- gapFillFastLP, gapFillTopological, fillGaps
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • fillGaps fillGaps Use template model(s) to fill gaps in a model.
- - - - -

SOURCE CODE ^

-
0001 function [addedRxns, reversedRxns, newModel, exitFlag] = gapFillMILP(model, universalModel, varargin)
-0002 % gapFillMILP  Objective-based gap-filling via a global growth-floor MILP.
-0003 %
-0004 % Finds the minimum-cost set of modifications to make the model's objective
-0005 % (biomass) >= minGrowth. Supports two repair mechanisms from Kumar et al.
-0006 % 2007 (Bioinformatics 23:1626-1635):
-0007 %   1. Directionality reversal of existing draft reactions (binary y_rev)
-0008 %   2. Addition of reactions from a universal database (binary y_db)
-0009 %
-0010 % The universal model should contain all candidate reactions, including
-0011 % exchange and transport reactions if those repair classes are desired.
-0012 %
-0013 % This is a SINGLE-level MILP (not bilevel). It is computationally more
-0014 % expensive than gapFillFastLP but supports the directionality-reversal
-0015 % repair mechanism and directly optimises for objective feasibility.
-0016 %
-0017 % Parameters
-0018 % ----------
-0019 % model : struct
-0020 %     draft RAVEN model to be gap-filled.
-0021 % universalModel : struct
-0022 %     universal reaction database (RAVEN model).
-0023 %
-0024 % Optional name-value parameters
-0025 % --------------------------------
-0026 % 'minGrowth' (default [])
-0027 %     minimum objective value to achieve. If empty, runs FBA on the merged
-0028 %     model and uses 10% of the maximum as the floor.
-0029 % 'weights' (default [1 2 3 4])
-0030 %     cost weights [w_rev w_db w_exch w_trans]. w_rev is the cost per
-0031 %     reversed draft reaction; w_db is the cost per added universal
-0032 %     reaction. w_exch and w_trans are reserved for future use when
-0033 %     exchange and transport reactions are tracked separately.
-0034 % 'bigM' (default 1000)
-0035 %     Big-M constant for coupling constraints. Must be larger than any
-0036 %     expected flux magnitude.
-0037 % 'params' (default [])
-0038 %     solver parameter struct passed to optimizeProb. Fields such as
-0039 %     params.TimeLimit and params.intTol can be used to tune the MILP.
-0040 % 'verbose' (default true)
-0041 %     print progress messages.
-0042 %
-0043 % Returns
-0044 % -------
-0045 % addedRxns : cell
-0046 %     reaction IDs from universalModel that are selected by the MILP.
-0047 % reversedRxns : cell
-0048 %     draft reaction IDs whose directionality is reversed by the MILP.
-0049 % newModel : struct
-0050 %     model with addedRxns incorporated and reversedRxns bounds updated
-0051 %     (lb set to -bigM for reversed reactions).
-0052 % exitFlag : double
-0053 %     1 = optimal, -1 = infeasible, -2 = timeout or other failure.
-0054 %
-0055 % See also
-0056 % --------
-0057 % gapFillFastLP, gapFillTopological, fillGaps
-0058 
-0059 % ---- Parse optional arguments ----
-0060 p = inputParser();
-0061 addParameter(p, 'minGrowth', [],  @(x) isempty(x) || isnumeric(x));
-0062 addParameter(p, 'weights',   [1 2 3 4], @isnumeric);
-0063 addParameter(p, 'bigM',      1000,      @isnumeric);
-0064 addParameter(p, 'params',    [],        @(x) isempty(x) || isstruct(x));
-0065 addParameter(p, 'verbose',   true,      @islogical);
-0066 parse(p, varargin{:});
-0067 minGrowth = p.Results.minGrowth;
-0068 weights   = p.Results.weights;
-0069 M         = p.Results.bigM;
-0070 params    = p.Results.params;
-0071 verbose   = p.Results.verbose;
-0072 
-0073 w_rev = weights(1);
-0074 w_db  = weights(2);
-0075 
-0076 % ---- Initialise outputs ----
-0077 addedRxns    = {};
-0078 reversedRxns = {};
-0079 newModel     = model;
-0080 exitFlag     = -1;
-0081 
-0082 % Check that the model has an objective
-0083 if all(model.c == 0)
-0084     error('gapFillMILP: model has no objective function. Set model.c.');
-0085 end
-0086 
-0087 % ---- Merge draft + universal database ----
-0088 mergedModel = mergeModels({model; universalModel});
-0089 nMergedMets = numel(mergedModel.mets);
-0090 nMergedRxns = numel(mergedModel.rxns);
-0091 
-0092 % Identify draft reactions in the merged model
-0093 isDraft = ismember(mergedModel.rxns, model.rxns);
-0094 isUniv  = ~isDraft;
-0095 
-0096 % Universal reactions that can carry flux (non-trivially blocked)
-0097 canCarry = (mergedModel.ub > 0 | mergedModel.lb < 0);
-0098 isUniv   = isUniv & canCarry;
-0099 univIdx  = find(isUniv);     % positions in merged model
-0100 nUniv    = numel(univIdx);
-0101 
-0102 % Reversal candidates: draft reactions that are currently irreversible
-0103 % (lb >= 0). Reversing their directionality means allowing negative flux.
-0104 revCandMask = isDraft & mergedModel.lb >= 0;
-0105 revCandIdx  = find(revCandMask);   % positions in merged model
-0106 nRev        = numel(revCandIdx);
-0107 
-0108 if verbose
-0109     fprintf('gapFillMILP: merged model has %d mets, %d rxns (%d draft, %d universal).\n', ...
-0110         nMergedMets, nMergedRxns, sum(isDraft), nUniv);
-0111     fprintf('gapFillMILP: %d reversal candidates, %d database candidates.\n', nRev, nUniv);
-0112 end
-0113 
-0114 % ---- Determine minGrowth ----
-0115 if isempty(minGrowth)
-0116     % Run FBA on merged model (unconstrained) to get max growth.
-0117     probFBA.A      = mergedModel.S;
-0118     probFBA.a      = probFBA.A;
-0119     probFBA.b      = zeros(nMergedMets, 1);
-0120     probFBA.csense = repmat('E', 1, nMergedMets);
-0121     probFBA.c      = -mergedModel.c;   % negate: osense=1 minimises
-0122     probFBA.osense = 1;
-0123     probFBA.lb     = mergedModel.lb;
-0124     probFBA.ub     = mergedModel.ub;
-0125     probFBA.vartype = repmat('C', 1, nMergedRxns);
-0126     solFBA = optimizeProb(probFBA, params, false);
-0127     if ~checkSolution(solFBA)
-0128         if verbose
-0129             fprintf('gapFillMILP: FBA on merged model is infeasible — cannot gap-fill.\n');
-0130         end
-0131         return;
-0132     end
-0133     maxGrowth = mergedModel.c' * solFBA.full(1:nMergedRxns);
-0134     if maxGrowth <= 0
-0135         if verbose
-0136             fprintf('gapFillMILP: merged model objective is non-positive (%.4g). Cannot gap-fill.\n', ...
-0137                 maxGrowth);
-0138         end
-0139         return;
-0140     end
-0141     minGrowth = 0.1 * maxGrowth;
-0142     if verbose
-0143         fprintf('gapFillMILP: setting minGrowth = %.4g (10%% of max %.4g).\n', minGrowth, maxGrowth);
-0144     end
-0145 end
-0146 
-0147 % ---- Build MILP ----
-0148 % Variable ordering:
-0149 %   cols 1 : nMergedRxns          → v (all fluxes, continuous)
-0150 %   cols nMergedRxns+1 : +nRev    → y_rev (binary, directionality reversal)
-0151 %   cols nMergedRxns+nRev+1 : end → y_db  (binary, universal reactions)
-0152 nVar = nMergedRxns + nRev + nUniv;
-0153 
-0154 % --- Variable bounds ---
-0155 lb = mergedModel.lb;
-0156 ub = mergedModel.ub;
-0157 
-0158 % Relax reversal candidates to allow negative flux (Big-M on lower bound)
-0159 lb(revCandIdx) = -M;
-0160 
-0161 % Universal reactions: allow them to be forced to 0 (extend lb toward 0)
-0162 lb(univIdx) = min(mergedModel.lb(univIdx), 0);
-0163 
-0164 % Binary variable bounds
-0165 lb = [lb; zeros(nRev + nUniv, 1)];
-0166 ub = [ub; ones(nRev + nUniv, 1)];
-0167 
-0168 % --- Objective ---
-0169 c = zeros(nVar, 1);
-0170 c(nMergedRxns + 1 : nMergedRxns + nRev)  = w_rev;  % y_rev cost
-0171 c(nMergedRxns + nRev + 1 : end)           = w_db;   % y_db cost
-0172 
-0173 % --- Constraint blocks ---
-0174 % (1) Steady state: S * v = 0   [nMergedMets rows, 'E']
-0175 A_SS = [mergedModel.S, sparse(nMergedMets, nRev + nUniv)];
-0176 
-0177 % (2) Growth floor: c' * v >= minGrowth   [1 row, 'G']
-0178 A_growth = [sparse(mergedModel.c'), sparse(1, nRev + nUniv)];
-0179 
-0180 % (3) Universal upper coupling: v_k - ub_k * y_db_k <= 0   [nUniv rows, 'L']
-0181 %     (forces v_k <= 0 when y_db_k = 0)
-0182 % Coupling coefficients are capped at the Big-M constant: an unbounded
-0183 % universal reaction (ub = Inf, RAVEN's own default when no upper-bound
-0184 % annotation is set) would otherwise put an Inf coefficient into the
-0185 % constraint matrix, and Inf*0 (y_db_k = 0) is NaN, not 0.
-0186 yDbCols = nMergedRxns + nRev + (1:nUniv);
-0187 uniUb = min(mergedModel.ub(univIdx), M);
-0188 uniLb = max(mergedModel.lb(univIdx), -M);
-0189 A_uniUp = sparse(...
-0190     [1:nUniv, 1:nUniv], ...
-0191     [univIdx(:)', yDbCols], ...
-0192     [ones(1, nUniv), -uniUb'], ...
-0193     nUniv, nVar);
-0194 
-0195 % (4) Universal lower coupling: v_k - lb_k * y_db_k >= 0   [nUniv rows, 'G']
-0196 %     (forces v_k >= 0 when y_db_k = 0; together with (3): v_k = 0 when y_db_k = 0)
-0197 A_uniLo = sparse(...
-0198     [1:nUniv, 1:nUniv], ...
-0199     [univIdx(:)', yDbCols], ...
-0200     [ones(1, nUniv), -uniLb'], ...
-0201     nUniv, nVar);
-0202 
-0203 % (5) Reversal coupling: v_j + M * y_rev_j >= 0   [nRev rows, 'G']
-0204 %     (forces v_j >= 0 when y_rev_j = 0, i.e., preserves original irreversibility)
-0205 yRevCols = nMergedRxns + (1:nRev);
-0206 A_rev = sparse(...
-0207     [1:nRev, 1:nRev], ...
-0208     [revCandIdx(:)', yRevCols], ...
-0209     [ones(1, nRev), M * ones(1, nRev)], ...
-0210     nRev, nVar);
-0211 
-0212 % --- Assemble ---
-0213 prob.A = [A_SS; A_growth; A_uniUp; A_uniLo; A_rev];
-0214 prob.a = prob.A;
-0215 prob.b = [zeros(nMergedMets, 1); minGrowth; ...
-0216           zeros(nUniv, 1); zeros(nUniv, 1); zeros(nRev, 1)];
-0217 prob.csense = [repmat('E', 1, nMergedMets), 'G', ...
-0218                repmat('L', 1, nUniv), ...
-0219                repmat('G', 1, nUniv + nRev)];
-0220 prob.c      = c;
-0221 prob.osense = 1;   % minimise weighted sum of binary variables
-0222 prob.lb     = lb;
-0223 prob.ub     = ub;
-0224 prob.vartype = repmat('C', 1, nVar);
-0225 prob.vartype(nMergedRxns + 1 : end) = 'B';   % y_rev and y_db are binary
-0226 
-0227 if isempty(params)
-0228     params.intTol   = 1e-9;
-0229     params.TimeLimit = 600;
-0230 end
-0231 
-0232 % ---- Solve ----
-0233 if verbose
-0234     fprintf('gapFillMILP: solving MILP (%d vars, %d binary, %d constraints)...\n', ...
-0235         nVar, nRev + nUniv, size(prob.A, 1));
-0236 end
-0237 sol = optimizeProb(prob, params, verbose);
-0238 
-0239 if ~checkSolution(sol)
-0240     if verbose
-0241         fprintf('gapFillMILP: MILP infeasible or timed out.\n');
-0242     end
-0243     exitFlag = -1;
-0244     return;
-0245 end
-0246 
-0247 % ---- Extract solution ----
-0248 binThreshold = 1e-3;
-0249 yRev = sol.full(nMergedRxns + 1 : nMergedRxns + nRev);
-0250 yDb  = sol.full(nMergedRxns + nRev + 1 : end);
-0251 
-0252 revSelected = revCandIdx(yRev > binThreshold);
-0253 dbSelected  = univIdx(yDb > binThreshold);
-0254 
-0255 % Map back to original IDs. dbSelected is already restricted to universal
-0256 % reactions via univIdx/isDraft above (an id-membership test against
-0257 % model.rxns, not a name it could have been renamed away from), so no
-0258 % further filtering against universalModel.rxns is needed here -- doing so
-0259 % would silently drop a universal reaction that mergeModels renamed
-0260 % because its id collided with one already in the draft model.
-0261 reversedRxns = mergedModel.rxns(revSelected);
-0262 addedRxns    = mergedModel.rxns(dbSelected);
-0263 
-0264 if verbose
-0265     fprintf('gapFillMILP: reversed %d draft reaction(s), added %d universal reaction(s).\n', ...
-0266         numel(reversedRxns), numel(addedRxns));
-0267 end
-0268 
-0269 % ---- Build new model ----
-0270 newModel = model;
-0271 
-0272 % Apply directionality reversals: allow negative flux
-0273 for k = 1:numel(reversedRxns)
-0274     rxnPos = find(getIndexes(newModel, reversedRxns(k), 'rxns', true));
-0275     if ~isempty(rxnPos)
-0276         newModel.lb(rxnPos) = -M;
-0277         newModel.rev(rxnPos) = 1;
-0278     end
-0279 end
-0280 
-0281 % Add universal reactions. Extracted from mergedModel by the dbSelected
-0282 % indices directly, not by looking addedRxns' names back up in
-0283 % universalModel: that lookup fails whenever mergeModels renamed one of
-0284 % them because its id collided with one already in the draft model, in
-0285 % which case setdiff would keep the (unrenamed) original in universalModel
-0286 % marked for removal, silently dropping the very reaction just selected.
-0287 if ~isempty(dbSelected)
-0288     toRemove = true(nMergedRxns, 1);
-0289     toRemove(dbSelected) = false;
-0290     trimmedUniv = removeReactions(mergedModel, toRemove);
-0291     newModel = mergeModels({newModel; trimmedUniv});
-0292 end
-0293 
-0294 exitFlag = 1;
-0295 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/gapfilling/gapFillTopological.html b/doc/gapfilling/gapFillTopological.html deleted file mode 100644 index 1efacfb2..00000000 --- a/doc/gapfilling/gapFillTopological.html +++ /dev/null @@ -1,375 +0,0 @@ - - - - Description of gapFillTopological - - - - - - - - - -
Home > gapfilling > gapFillTopological.m
- - - -

gapFillTopological -

- -

PURPOSE ^

-
gapFillTopological BFS metabolite-producibility pre-screening.
- -

SYNOPSIS ^

-
function result = gapFillTopological(model, universalModel, varargin)
- -

DESCRIPTION ^

-
 gapFillTopological  BFS metabolite-producibility pre-screening.
-
- Identifies which metabolites in the draft model cannot be produced from a
- given set of seed metabolites (medium), using a graph-based fixed-point
- "scope" computation (no solver required). For each unreachable metabolite,
- finds candidate reactions in the universal database that produce it
- directly.
-
- This is a computationally cheap pre-screening step that prunes the
- universal database before a more expensive gap-filling solve. It is
- inspired by the topological analysis used in Meneco (Prigent et al. 2017,
- PLoS Comput Biol).
-
- Algorithm
- ---------
- Starting from seed metabolites (available in the medium), iteratively
- mark reactions as "fireable" when all their substrates are reachable,
- then mark their products as newly reachable. Continue until no new
- metabolite is reached (fixed point). Reversible reactions (lb < 0) are
- also considered in their reverse direction.
-
- Parameters
- ----------
- model : struct
-     draft RAVEN model.
- universalModel : struct
-     universal reaction database (RAVEN model).
-
- Optional name-value parameters
- --------------------------------
- 'seeds' (default [])
-     Metabolite IDs (cell array) available from the medium. Default:
-     metabolites of every exchange reaction whose bounds let it supply the
-     metabolite, i.e. getExchangeRxns 'uptake' plus 'reverse'.
- 'targets' (default [])
-     Metabolite IDs (cell array) that should be produced. Default:
-     substrates of the objective (biomass) reaction.
- 'verbose' (default true)
-     print a summary of the results.
-
- Returns
- -------
- result : struct
-     .reachableMets   — logical vector (length nMets) marking producible mets
-     .blockedMets     — cell array of metabolite IDs that cannot be reached
-     .candidateRxns   — n-by-1 cell array of universal rxn ID lists, one
-                        cell per blocked metabolite (same order as blockedMets)
-     .pruningFraction — fraction of universal reactions pruned (not needed
-                        for any blocked metabolite)
-
- See also
- --------
- gapFillFastLP, gapFillMILP, fillGaps
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • fillGaps fillGaps Use template model(s) to fill gaps in a model.
- - - - -

SOURCE CODE ^

-
0001 function result = gapFillTopological(model, universalModel, varargin)
-0002 % gapFillTopological  BFS metabolite-producibility pre-screening.
-0003 %
-0004 % Identifies which metabolites in the draft model cannot be produced from a
-0005 % given set of seed metabolites (medium), using a graph-based fixed-point
-0006 % "scope" computation (no solver required). For each unreachable metabolite,
-0007 % finds candidate reactions in the universal database that produce it
-0008 % directly.
-0009 %
-0010 % This is a computationally cheap pre-screening step that prunes the
-0011 % universal database before a more expensive gap-filling solve. It is
-0012 % inspired by the topological analysis used in Meneco (Prigent et al. 2017,
-0013 % PLoS Comput Biol).
-0014 %
-0015 % Algorithm
-0016 % ---------
-0017 % Starting from seed metabolites (available in the medium), iteratively
-0018 % mark reactions as "fireable" when all their substrates are reachable,
-0019 % then mark their products as newly reachable. Continue until no new
-0020 % metabolite is reached (fixed point). Reversible reactions (lb < 0) are
-0021 % also considered in their reverse direction.
-0022 %
-0023 % Parameters
-0024 % ----------
-0025 % model : struct
-0026 %     draft RAVEN model.
-0027 % universalModel : struct
-0028 %     universal reaction database (RAVEN model).
-0029 %
-0030 % Optional name-value parameters
-0031 % --------------------------------
-0032 % 'seeds' (default [])
-0033 %     Metabolite IDs (cell array) available from the medium. Default:
-0034 %     metabolites of every exchange reaction whose bounds let it supply the
-0035 %     metabolite, i.e. getExchangeRxns 'uptake' plus 'reverse'.
-0036 % 'targets' (default [])
-0037 %     Metabolite IDs (cell array) that should be produced. Default:
-0038 %     substrates of the objective (biomass) reaction.
-0039 % 'verbose' (default true)
-0040 %     print a summary of the results.
-0041 %
-0042 % Returns
-0043 % -------
-0044 % result : struct
-0045 %     .reachableMets   — logical vector (length nMets) marking producible mets
-0046 %     .blockedMets     — cell array of metabolite IDs that cannot be reached
-0047 %     .candidateRxns   — n-by-1 cell array of universal rxn ID lists, one
-0048 %                        cell per blocked metabolite (same order as blockedMets)
-0049 %     .pruningFraction — fraction of universal reactions pruned (not needed
-0050 %                        for any blocked metabolite)
-0051 %
-0052 % See also
-0053 % --------
-0054 % gapFillFastLP, gapFillMILP, fillGaps
-0055 
-0056 % ---- Parse optional arguments ----
-0057 p = inputParser();
-0058 addParameter(p, 'seeds',   [], @(x) isempty(x) || iscell(x));
-0059 addParameter(p, 'targets', [], @(x) isempty(x) || iscell(x));
-0060 addParameter(p, 'verbose', true, @islogical);
-0061 parse(p, varargin{:});
-0062 seeds   = p.Results.seeds;
-0063 targets = p.Results.targets;
-0064 verbose = p.Results.verbose;
-0065 
-0066 nMets = numel(model.mets);
-0067 nRxns = numel(model.rxns);
-0068 
-0069 % ---- Identify seed metabolites ----
-0070 if isempty(seeds)
-0071     % Default: metabolites that an exchange reaction can supply. Ask
-0072     % getExchangeRxns rather than testing lb < 0 here: RAVEN writes an uptake
-0073     % exchange either as "=> met" (addExchangeRxns 'in', so lb = 0 and a
-0074     % positive flux supplies the metabolite) or as "met =>" with lb < 0. Only
-0075     % the second has lb < 0, so a hand-rolled lb < 0 test silently misses
-0076     % every 'in' exchange.
-0077     %
-0078     % 'uptake' alone is not enough either: it means the bounds allow *only*
-0079     % uptake, so a reversible exchange ("met <=>") is classified 'reverse'
-0080     % even though it supplies the metabolite just as well. The union of the
-0081     % two is exactly "forward flux produces it, or reverse flux does", which
-0082     % is what analyse_topology's lower_bound < 0 test means under cobra's
-0083     % convention, where every exchange is written "met <=>".
-0084     [~, onlyUptakeIdx] = getExchangeRxns(model, 'uptake');
-0085     [~, reversibleIdx] = getExchangeRxns(model, 'reverse');
-0086     uptakeIdx = union(onlyUptakeIdx, reversibleIdx);
-0087     if isempty(uptakeIdx)
-0088         warning(['gapFillTopological: no uptake exchange reactions found. ' ...
-0089             'Provide seeds manually via the ''seeds'' option.']);
-0090         seedMetIdx = [];
-0091     else
-0092         [seedRows, ~] = find(model.S(:, uptakeIdx) ~= 0);
-0093         seedMetIdx = unique(seedRows);
-0094     end
-0095 else
-0096     [~, seedMetIdx] = ismember(seeds, model.mets);
-0097     seedMetIdx = seedMetIdx(seedMetIdx > 0);
-0098 end
-0099 
-0100 % ---- Identify target metabolites ----
-0101 if isempty(targets)
-0102     % Default: substrates of the objective (biomass) reaction
-0103     objRxns = find(model.c ~= 0);
-0104     if isempty(objRxns)
-0105         error(['gapFillTopological: no objective function defined. ' ...
-0106             'Provide targets manually via the ''targets'' option.']);
-0107     end
-0108     biomassIdx = objRxns(1);
-0109     targetMetIdx = find(model.S(:, biomassIdx) ~= 0);
-0110 else
-0111     [~, targetMetIdx] = ismember(targets, model.mets);
-0112     targetMetIdx = targetMetIdx(targetMetIdx > 0);
-0113 end
-0114 
-0115 % ---- Build adjacency lists ----
-0116 % subOf{m}  = reaction indices where metabolite m is a substrate (S < 0)
-0117 % prodOf{m} = reaction indices where metabolite m is a product  (S > 0)
-0118 % rxnSubs{j} = substrate metabolite indices of reaction j
-0119 % rxnProds{j} = product metabolite indices of reaction j
-0120 %
-0121 % Use find() on the sparse S directly to avoid materialising a dense logical
-0122 % matrix: extract all nonzeros then partition by sign.
-0123 [allRows, allCols, allVals] = find(model.S);
-0124 subMask    = allVals < 0;
-0125 proMask    = allVals > 0;
-0126 subMetRows = allRows(subMask);
-0127 subRxnCols = allCols(subMask);
-0128 proMetRows = allRows(proMask);
-0129 proRxnCols = allCols(proMask);
-0130 
-0131 subOf  = cell(nMets, 1);
-0132 prodOf = cell(nMets, 1);
-0133 for k = 1:numel(subMetRows)
-0134     subOf{subMetRows(k)}(end+1) = subRxnCols(k);
-0135 end
-0136 for k = 1:numel(proMetRows)
-0137     prodOf{proMetRows(k)}(end+1) = proRxnCols(k);
-0138 end
-0139 
-0140 rxnSubs  = cell(nRxns, 1);
-0141 rxnProds = cell(nRxns, 1);
-0142 for k = 1:numel(subMetRows)
-0143     rxnSubs{subRxnCols(k)}(end+1) = subMetRows(k);
-0144 end
-0145 for k = 1:numel(proMetRows)
-0146     rxnProds{proRxnCols(k)}(end+1) = proMetRows(k);
-0147 end
-0148 
-0149 isRev = model.lb < 0;
-0150 
-0151 % ---- BFS scope computation ----
-0152 % Uses a countdown approach: a reaction fires when all its substrates are
-0153 % reachable. Each time a metabolite becomes reachable, decrement the
-0154 % substrate count of all reactions it participates in.
-0155 subCountFwd = cellfun(@numel, rxnSubs);   % remaining unreachable substrates (fwd)
-0156 subCountRev = cellfun(@numel, rxnProds);  % remaining unreachable products  (rev)
-0157 
-0158 reachable = false(nMets, 1);
-0159 firedFwd  = false(nRxns, 1);
-0160 firedRev  = false(nRxns, 1);
-0161 
-0162 % Initialize queue with seed metabolites
-0163 queue  = seedMetIdx(:)';
-0164 qHead  = 1;
-0165 reachable(seedMetIdx) = true;
-0166 
-0167 % Reactions with no substrates have nothing to wait for and fire straight
-0168 % away. They are in no subOf list, so the countdown below never reaches them
-0169 % and their products would otherwise stay unreachable forever. This is where
-0170 % "=> met" uptake exchanges enter the scope.
-0171 for j = find(subCountFwd == 0)'
-0172     firedFwd(j) = true;
-0173     for p = rxnProds{j}
-0174         if ~reachable(p)
-0175             reachable(p) = true;
-0176             queue(end+1) = p; %#ok<AGROW>
-0177         end
-0178     end
-0179 end
-0180 
-0181 while qHead <= numel(queue)
-0182     m = queue(qHead);
-0183     qHead = qHead + 1;
-0184 
-0185     % --- Forward direction ---
-0186     % m is a substrate of rxn j (S(m,j) < 0)
-0187     for j = subOf{m}
-0188         subCountFwd(j) = subCountFwd(j) - 1;
-0189         if subCountFwd(j) == 0 && ~firedFwd(j)
-0190             firedFwd(j) = true;
-0191             for p = rxnProds{j}
-0192                 if ~reachable(p)
-0193                     reachable(p) = true;
-0194                     queue(end+1) = p;
-0195                 end
-0196             end
-0197         end
-0198     end
-0199 
-0200     % --- Reverse direction (reversible reactions only) ---
-0201     % m is a product of rxn j (S(m,j) > 0), which in reverse means m is a
-0202     % substrate; if all products are reachable, the reaction can fire in
-0203     % reverse and produce the substrates.
-0204     for j = prodOf{m}
-0205         if isRev(j)
-0206             subCountRev(j) = subCountRev(j) - 1;
-0207             if subCountRev(j) == 0 && ~firedRev(j)
-0208                 firedRev(j) = true;
-0209                 for s = rxnSubs{j}
-0210                     if ~reachable(s)
-0211                         reachable(s) = true;
-0212                         queue(end+1) = s;
-0213                     end
-0214                 end
-0215             end
-0216         end
-0217     end
-0218 end
-0219 
-0220 % ---- Report blocked target metabolites ----
-0221 targetReachable = reachable(targetMetIdx);
-0222 blockedMetIdx   = targetMetIdx(~targetReachable);
-0223 blockedMets     = model.mets(blockedMetIdx);
-0224 
-0225 if verbose
-0226     fprintf('gapFillTopological: %d/%d target metabolites are reachable.\n', ...
-0227         sum(targetReachable), numel(targetMetIdx));
-0228     if ~isempty(blockedMets)
-0229         fprintf('gapFillTopological: %d blocked target metabolite(s).\n', numel(blockedMets));
-0230     end
-0231 end
-0232 
-0233 % ---- Find candidate universal reactions for each blocked metabolite ----
-0234 candidateRxns  = cell(numel(blockedMets), 1);
-0235 allCandidates  = {};
-0236 
-0237 for k = 1:numel(blockedMetIdx)
-0238     m     = blockedMetIdx(k);
-0239     metId = model.mets{m};
-0240 
-0241     % Find this metabolite in the universal model
-0242     univMetIdx = find(strcmp(universalModel.mets, metId));
-0243     if isempty(univMetIdx)
-0244         candidateRxns{k} = {};
-0245         continue;
-0246     end
-0247     univMetIdx = univMetIdx(1);   % take first match; IDs should be unique
-0248 
-0249     univSRow = full(universalModel.S(univMetIdx, :));
-0250 
-0251     % Forward production: rxns with S(m,j) > 0
-0252     fwdProdMask = univSRow > 0;
-0253     % Reverse production: rxns with S(m,j) < 0 that can carry negative flux
-0254     revProdMask = univSRow < 0 & (universalModel.lb' < 0);
-0255 
-0256     candidateMask         = fwdProdMask | revProdMask;
-0257     candidateRxns{k}      = universalModel.rxns(candidateMask);
-0258     allCandidates         = [allCandidates; candidateRxns{k}];
-0259 end
-0260 
-0261 % ---- Pruning fraction ----
-0262 uniqueCandidates   = unique(allCandidates);
-0263 pruningFraction    = 1 - numel(uniqueCandidates) / numel(universalModel.rxns);
-0264 
-0265 if verbose
-0266     fprintf('gapFillTopological: %d/%d universal reactions pruned (%.0f%%).\n', ...
-0267         numel(universalModel.rxns) - numel(uniqueCandidates), ...
-0268         numel(universalModel.rxns), pruningFraction * 100);
-0269 end
-0270 
-0271 % ---- Pack output ----
-0272 result.reachableMets     = reachable;
-0273 result.blockedMets       = blockedMets;
-0274 result.candidateRxns     = candidateRxns;
-0275 result.pruningFraction   = pruningFraction;
-0276 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/index.html b/doc/index.html deleted file mode 100644 index 8f1b761a..00000000 --- a/doc/index.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - Index for Directory . - - - - - - - - - - -
< Master indexIndex for . >
- -

Index for .

- -

Matlab files in this directory:

- -
 checkInstallationcheckInstallation DEPRECATED. Use checkRaven instead.
 checkRavencheckRaven
- - -

Subsequent directories:

-
    -
  • .git
  • .github
  • INIT
  • analysis
  • annotation
  • biomass
  • comparison
  • conditions
  • conversion
  • curation
  • deprecated
  • doc
  • docs
  • gapfilling
  • installation
  • io
  • localization
  • manipulation
  • omics
  • queries
  • readme
  • reconstruction
  • software
  • solver
  • tasks
  • testing
  • tutorial
  • utils
- -
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/installation/checkFunctionUniqueness.html b/doc/installation/checkFunctionUniqueness.html deleted file mode 100644 index 2c7e755b..00000000 --- a/doc/installation/checkFunctionUniqueness.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - Description of checkFunctionUniqueness - - - - - - - - - -
Home > installation > checkFunctionUniqueness.m
- - - -

checkFunctionUniqueness -

- -

PURPOSE ^

-
checkFunctionUniqueness
- -

SYNOPSIS ^

-
function status = checkFunctionUniqueness(varargin)
- -

DESCRIPTION ^

-
 checkFunctionUniqueness
-   Checks whether RAVEN toolbox functions are unique across other
-   toolboxes or user-created functions accessible in Matlab pathlist.
-
- Name-Value Arguments
- --------------------
-   altDirs     cell array of alternative directories with functions,
-               should only be specified if this function is used for other
-               purposes, NOT when looking for RAVEN toolbox functions.
-
- Output:
-   status      true if all functions are unique, otherwise false
-
- Usage: checkFunctionUniqueness()
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function status = checkFunctionUniqueness(varargin)
-0002 % checkFunctionUniqueness
-0003 %   Checks whether RAVEN toolbox functions are unique across other
-0004 %   toolboxes or user-created functions accessible in Matlab pathlist.
-0005 %
-0006 % Name-Value Arguments
-0007 % --------------------
-0008 %   altDirs     cell array of alternative directories with functions,
-0009 %               should only be specified if this function is used for other
-0010 %               purposes, NOT when looking for RAVEN toolbox functions.
-0011 %
-0012 % Output:
-0013 %   status      true if all functions are unique, otherwise false
-0014 %
-0015 % Usage: checkFunctionUniqueness()
-0016 
-0017 p=parseRAVENargs(varargin, {'altDirs',[]});
-0018 altDirs=p.altDirs;
-0019 
-0020 if isempty(altDirs)
-0021     %Get the RAVEN path
-0022     ravenDir=findRAVENroot();
-0023     
-0024     %Now getting all RAVEN functions recursively;
-0025     temp_res1=dir([ravenDir '/*/*.m']);
-0026     temp_res2=dir([ravenDir '/*/*/*.m']);
-0027     
-0028     ravenFunctions={temp_res1.name,temp_res2.name}';
-0029 else
-0030     if ispc
-0031         splitChar = ';';
-0032     else
-0033         splitChar = ':';
-0034     end
-0035     altDirs = strsplit(altDirs,splitChar)';
-0036     ravenFunctions={};
-0037     for i=1:numel(altDirs)
-0038         temp_res=dir(fullfile(char(altDirs(i)),'*.m'));
-0039         ravenFunctions = [ravenFunctions, temp_res.name];
-0040     end
-0041     ravenFunctions=ravenFunctions';
-0042     ravenDir=altDirs;
-0043 end
-0044 
-0045 %startup.m is not a normal function, any startup.m in the path should run
-0046 %during startup, so duplicate use of this name is fine
-0047 ravenFunctions=ravenFunctions(~ismember(ravenFunctions,'startup.m'));
-0048 
-0049 %Getting all the paths added to Matlab
-0050 if ispc
-0051     matlabPaths=regexp(path, ';', 'split')';
-0052 elseif isunix
-0053     matlabPaths=regexp(path, ':', 'split')';
-0054 end
-0055 
-0056 overlapPath={};
-0057 overlapFunctions={};
-0058 multiRaven=false;
-0059 multiFunction=false;
-0060 
-0061 for i=1:numel(matlabPaths)
-0062     if ~startsWith(matlabPaths{i},ravenDir)
-0063         temp_res=dir([matlabPaths{i} '/*.m']);
-0064         if ~isempty(temp_res)
-0065             pathFunctions={temp_res.name}';
-0066         else
-0067             pathFunctions='';
-0068         end
-0069         if ~isempty(pathFunctions) && ~any(ismember('Contents.m',pathFunctions))
-0070             if any(ismember(ravenFunctions,pathFunctions))
-0071                 if sum(ismember(ravenFunctions,pathFunctions))>(numel(ravenFunctions)/4)
-0072                     multiRaven=true;
-0073                 else
-0074                     multiFunction=true;
-0075                     overlapPath = [overlapPath, matlabPaths(i)];
-0076                     overlapFunctions = [overlapFunctions, {ravenFunctions(ismember(ravenFunctions,pathFunctions))}];
-0077                 end
-0078             end
-0079         end
-0080     end
-0081 end
-0082 
-0083 if multiRaven==true || multiFunction == true
-0084         if nargout > 0
-0085             status = false;
-0086         else
-0087             fprintf('Fail\n')
-0088         end
-0089     if multiRaven==true && isempty(altDirs)
-0090         error(['Multiple RAVEN versions detected in MATLAB path. Remove all ',...
-0091                'RAVEN directories from the MATLAB path with removeRavenFromPath(), ',...
-0092                'or manually remove them. Afterwards, re-run checkRaven']);
-0093     elseif multiFunction == true
-0094         for i=1:numel(overlapPath)
-0095             fprintf(['   Duplicate functions in ',regexprep(overlapPath{i},'(\\)','\\$1'),'\n']);
-0096             fprintf(['     ' strjoin(overlapFunctions{i},'\n     ') '\n']);
-0097         end
-0098     fprintf('   Resolve conflicting functions to avoid unexpected behaviour\n');
-0099     end
-0100 else
-0101     if nargout > 0
-0102         status = true;
-0103     else
-0104         fprintf('Pass\n')
-0105     end
-0106 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/installation/downloadRavenBinaries.html b/doc/installation/downloadRavenBinaries.html deleted file mode 100644 index 9e3ae378..00000000 --- a/doc/installation/downloadRavenBinaries.html +++ /dev/null @@ -1,176 +0,0 @@ - - - - Description of downloadRavenBinaries - - - - - - - - - -
Home > installation > downloadRavenBinaries.m
- - - -

downloadRavenBinaries -

- -

PURPOSE ^

-
downloadRavenBinaries
- -

SYNOPSIS ^

-
function downloadRavenBinaries(tools)
- -

DESCRIPTION ^

-
 downloadRavenBinaries
-   Download RAVEN's external command-line binaries on demand from the
-   raven-data repository into RAVEN's software/ directory. Only the binary
-   matching the current platform is fetched, and only when needed.
-
-   Input:
-   tools   cell array, subset of {'blast+','diamond','hmmer'} to
-           download. Defaults to all of them. Tools whose target executable is
-           already present are skipped.
-
-   This requires an internet connection. For an offline / air-gapped install,
-   download the "*-binaries" RAVEN release (which bundles all binaries) and
-   unzip it into the RAVEN software/ directory instead.
-
-   The binaries are hosted as release assets in
-   https://github.com/SysBioChalmers/raven-data.
-
-   Usage: downloadRavenBinaries({'blast+','diamond'})
-
- NOTE: lazily invoked by getBlast/getDiamond/getKEGGModelForOrganism and
- offered by checkRaven when a binary is missing.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function downloadRavenBinaries(tools)
-0002 % downloadRavenBinaries
-0003 %   Download RAVEN's external command-line binaries on demand from the
-0004 %   raven-data repository into RAVEN's software/ directory. Only the binary
-0005 %   matching the current platform is fetched, and only when needed.
-0006 %
-0007 %   Input:
-0008 %   tools   cell array, subset of {'blast+','diamond','hmmer'} to
-0009 %           download. Defaults to all of them. Tools whose target executable is
-0010 %           already present are skipped.
-0011 %
-0012 %   This requires an internet connection. For an offline / air-gapped install,
-0013 %   download the "*-binaries" RAVEN release (which bundles all binaries) and
-0014 %   unzip it into the RAVEN software/ directory instead.
-0015 %
-0016 %   The binaries are hosted as release assets in
-0017 %   https://github.com/SysBioChalmers/raven-data.
-0018 %
-0019 %   Usage: downloadRavenBinaries({'blast+','diamond'})
-0020 %
-0021 % NOTE: lazily invoked by getBlast/getDiamond/getKEGGModelForOrganism and
-0022 % offered by checkRaven when a binary is missing.
-0023 
-0024 if nargin < 1 || isempty(tools)
-0025     tools = {'blast+','diamond','hmmer'};
-0026 elseif ischar(tools)
-0027     tools = {tools};
-0028 end
-0029 
-0030 ravenDir = findRAVENroot();
-0031 
-0032 % raven-data platform key for the per-platform binary ZIPs.
-0033 if ispc
-0034     plat = 'windows-x86_64';
-0035 elseif ismac
-0036     plat = 'macos-arm64';
-0037 else
-0038     plat = 'linux-x86_64';
-0039 end
-0040 
-0041 for i = 1:numel(tools)
-0042     tool = tools{i};
-0043     switch tool
-0044         case 'blast+'
-0045             tag = 'blast-2.17.0';
-0046             asset = ['blast-2.17.0-' plat '.zip'];
-0047             destDir = fullfile(ravenDir,'software','blast+');
-0048             sentinel = fullfile(destDir,'blastp');           % bare name on every OS (.exe on Windows)
-0049             execs = {'blastp','makeblastdb'};
-0050         case 'diamond'
-0051             tag = 'diamond-2.1.17';
-0052             asset = ['diamond-2.1.17-' plat '.zip'];
-0053             destDir = fullfile(ravenDir,'software','diamond');
-0054             sentinel = fullfile(destDir,'diamond');
-0055             execs = {'diamond'};
-0056         case 'hmmer'
-0057             if ispc
-0058                 tag = 'hmmer-3.3.2';                          % native Windows build (Cygwin); reads HMMER3/f
-0059                 asset = ['hmmer-3.3.2-' plat '.zip'];
-0060             else
-0061                 tag = 'hmmer-3.4.0';
-0062                 asset = ['hmmer-3.4.0-' plat '.zip'];
-0063             end
-0064             destDir = fullfile(ravenDir,'software','hmmer');
-0065             sentinel = fullfile(destDir,'hmmsearch');
-0066             execs = {'hmmsearch'};
-0067         otherwise
-0068             error('downloadRavenBinaries: unknown tool ''%s''.',tool);
-0069     end
-0070 
-0071     % macOS uses a .mac suffix and Windows a .exe suffix (Linux: bare name).
-0072     if ispc
-0073         sentinel = [sentinel '.exe']; %#ok<AGROW>
-0074     elseif ismac
-0075         sentinel = [sentinel '.mac']; %#ok<AGROW>
-0076     end
-0077     if exist(sentinel,'file')
-0078         continue;   % already provisioned
-0079     end
-0080 
-0081     zipPath = fetchRavenDataAsset(fullfile(ravenDir,'software'),tag,asset);
-0082     unzip(zipPath,destDir);
-0083     delete(zipPath);
-0084 
-0085     % macOS uses the .mac suffix throughout RAVEN, but the raven-data macOS ZIP
-0086     % ships bare names, so rename the extracted executables to match.
-0087     if ismac
-0088         for k = 1:numel(execs)
-0089             bare = fullfile(destDir,execs{k});
-0090             if isfile(bare)
-0091                 movefile(bare, fullfile(destDir,[execs{k} '.mac']));
-0092             end
-0093         end
-0094     end
-0095 
-0096     % MATLAB's unzip does not preserve Unix execute permissions; restore them.
-0097     if isunix
-0098         if ismac; sfx = '.mac'; else; sfx = ''; end
-0099         for k = 1:numel(execs)
-0100             system(['chmod +x "' fullfile(destDir,[execs{k} sfx]) '"']);
-0101         end
-0102         % Downloaded binaries are quarantined by macOS Gatekeeper and will not
-0103         % execute until the com.apple.quarantine attribute is cleared.
-0104         if ismac
-0105             system(['xattr -dr com.apple.quarantine "' destDir '"']);
-0106         end
-0107     end
-0108 end
-0109 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/installation/fetchRavenDataAsset.html b/doc/installation/fetchRavenDataAsset.html deleted file mode 100644 index 69c9c767..00000000 --- a/doc/installation/fetchRavenDataAsset.html +++ /dev/null @@ -1,111 +0,0 @@ - - - - Description of fetchRavenDataAsset - - - - - - - - - -
Home > installation > fetchRavenDataAsset.m
- - - -

fetchRavenDataAsset -

- -

PURPOSE ^

-
fetchRavenDataAsset Download (if needed) a raven-data release asset.
- -

SYNOPSIS ^

-
function localPath=fetchRavenDataAsset(destDir,releaseTag,assetName)
- -

DESCRIPTION ^

-
 fetchRavenDataAsset  Download (if needed) a raven-data release asset.
-
- Downloads assetName from the given raven-data release tag into destDir
- and returns its local path. An already-downloaded copy is reused as-is.
- Generic across raven-data's release families (KEGG artefacts, HMM
- libraries, binaries, ...): nothing here is specific to any one of
- them, so callers supply the release tag and asset name rather than
- this function hardcoding either.
-
- Parameters
- ----------
- destDir : char
-     directory to download into (created if it does not exist yet).
- releaseTag : char
-     the raven-data release tag, e.g. 'kegg118'.
- assetName : char
-     the release asset file name, e.g. 'kegg118_core.tar.gz'.
-
- Returns
- -------
- localPath : char
-     path to the downloaded file.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: - - - - - -

SOURCE CODE ^

-
0001 function localPath=fetchRavenDataAsset(destDir,releaseTag,assetName)
-0002 % fetchRavenDataAsset  Download (if needed) a raven-data release asset.
-0003 %
-0004 % Downloads assetName from the given raven-data release tag into destDir
-0005 % and returns its local path. An already-downloaded copy is reused as-is.
-0006 % Generic across raven-data's release families (KEGG artefacts, HMM
-0007 % libraries, binaries, ...): nothing here is specific to any one of
-0008 % them, so callers supply the release tag and asset name rather than
-0009 % this function hardcoding either.
-0010 %
-0011 % Parameters
-0012 % ----------
-0013 % destDir : char
-0014 %     directory to download into (created if it does not exist yet).
-0015 % releaseTag : char
-0016 %     the raven-data release tag, e.g. 'kegg118'.
-0017 % assetName : char
-0018 %     the release asset file name, e.g. 'kegg118_core.tar.gz'.
-0019 %
-0020 % Returns
-0021 % -------
-0022 % localPath : char
-0023 %     path to the downloaded file.
-0024 
-0025 if ~isfolder(destDir)
-0026     mkdir(destDir);
-0027 end
-0028 localPath=fullfile(destDir,assetName);
-0029 if isfile(localPath)
-0030     return;
-0031 end
-0032 fprintf(['Downloading ' assetName '... ']);
-0033 try
-0034     websave(localPath,['https://github.com/SysBioChalmers/raven-data/releases/download/' releaseTag '/' assetName]);
-0035 catch ME
-0036     if strcmp(ME.identifier,'MATLAB:webservices:HTTP404StatusCodeError')
-0037         error('Failed to download %s, the server returned a 404 error, try again later. If the problem persists please report it on the RAVEN GitHub Issues page: https://github.com/SysBioChalmers/RAVEN/issues',assetName)
-0038     else
-0039         rethrow(ME);
-0040     end
-0041 end
-0042 fprintf('COMPLETE\n');
-0043 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/installation/findRAVENroot.html b/doc/installation/findRAVENroot.html deleted file mode 100644 index db39bea4..00000000 --- a/doc/installation/findRAVENroot.html +++ /dev/null @@ -1,112 +0,0 @@ - - - - Description of findRAVENroot - - - - - - - - - -
Home > installation > findRAVENroot.m
- - - -

findRAVENroot -

- -

PURPOSE ^

-
findRAVENroot
- -

SYNOPSIS ^

-
function [ravenPath, prevDir] = findRAVENroot()
- -

DESCRIPTION ^

-
 findRAVENroot
-   Finds the root of the RAVEN directory by searching for the path to
-   RAVEN.png. Can also record the current directory, in case a function will
-   use the ravenPath to navigate to a precise folder, and it should return to
-   the previous directory afterwards. See e.g. optimizeProb calling glpk.
-
-   The resolved path is cached for the rest of the MATLAB session: this is
-   called on every solver invocation, and re-reading the RAVEN.ravenPath
-   preference from disk every time is both wasteful and, under the sustained
-   call volume of a full test run, an intermittent source of failures from
-   MATLAB's own preference-file I/O. Run `clear findRAVENroot` after
-   changing that preference (checkRaven does this already) to pick
-   up the change without restarting MATLAB.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: - - - - - -

SOURCE CODE ^

-
0001 function [ravenPath, prevDir] = findRAVENroot()
-0002 % findRAVENroot
-0003 %   Finds the root of the RAVEN directory by searching for the path to
-0004 %   RAVEN.png. Can also record the current directory, in case a function will
-0005 %   use the ravenPath to navigate to a precise folder, and it should return to
-0006 %   the previous directory afterwards. See e.g. optimizeProb calling glpk.
-0007 %
-0008 %   The resolved path is cached for the rest of the MATLAB session: this is
-0009 %   called on every solver invocation, and re-reading the RAVEN.ravenPath
-0010 %   preference from disk every time is both wasteful and, under the sustained
-0011 %   call volume of a full test run, an intermittent source of failures from
-0012 %   MATLAB's own preference-file I/O. Run `clear findRAVENroot` after
-0013 %   changing that preference (checkRaven does this already) to pick
-0014 %   up the change without restarting MATLAB.
-0015 
-0016 persistent cachedPath
-0017 prevDir = pwd();
-0018 if ~isempty(cachedPath)
-0019     ravenPath = cachedPath;
-0020     return;
-0021 end
-0022 
-0023 % A stored preference is only trusted if it still points at a real RAVEN
-0024 % install; otherwise fall through to walking up from the currently
-0025 % executing copy of this file. Without this check, a stale preference
-0026 % left over from a different RAVEN checkout on the same machine silently
-0027 % resolves to that other copy's data, not the one actually running.
-0028 ravenPath = '';
-0029 if ispref('RAVEN','ravenPath')
-0030     prefPath = getpref('RAVEN','ravenPath');
-0031     if isfile(fullfile(prefPath,'RAVEN.png'))
-0032         ravenPath = prefPath;
-0033     end
-0034 end
-0035 if isempty(ravenPath)
-0036     ST=dbstack('-completenames');
-0037     ravenPath = ST(strcmp({ST.name},'findRAVENroot')).file;
-0038     rootFound = 0;
-0039     while rootFound == 0
-0040         isRoot = isfile(fullfile(ravenPath,'RAVEN.png'));
-0041         if isRoot
-0042             rootFound = 1;
-0043         else
-0044             ravenPathOld = ravenPath;
-0045             ravenPath = fileparts(ravenPath);
-0046             if strcmp(ravenPathOld,ravenPath)
-0047                 error('Cannot find the RAVEN root directory. Make sure you have not removed the RAVEN.png file from your RAVEN installation.')
-0048             end
-0049         end
-0050     end
-0051 end
-0052 cachedPath = ravenPath;
-0053 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/io/cleanSheet.html b/doc/io/cleanSheet.html deleted file mode 100644 index ca088754..00000000 --- a/doc/io/cleanSheet.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - Description of cleanSheet - - - - - - - - - -
Home > io > cleanSheet.m
- - - -

cleanSheet -

- -

PURPOSE ^

-
cleanSheet Clean up an Excel sheet.
- -

SYNOPSIS ^

-
function [raw,keptRows,keptCols]=cleanSheet(raw,varargin)
- -

DESCRIPTION ^

-
 cleanSheet  Clean up an Excel sheet.
-
- Removes empty rows/columns (and performs some other checks).
-
- Parameters
- ----------
- raw : cell
-     cell array with the data in the sheet.
-
- Name-Value Arguments
- --------------------
- removeComments : logical
-     true if commented lines (non-empty first cell in each row) should be
-     removed (default true).
- removeOnlyCap : logical
-     remove columns with captions but no other values (default false).
- removeNoCap : logical
-     remove columns without captions (default true).
- removeEmptyRows : logical
-     remove rows with no non-empty cells (default true).
-
- Returns
- -------
- raw : cell
-     cleaned version.
- keptRows : double
-     indices of the kept rows in the original structure.
- keptCols : double
-     indices of the kept columns in the original structure.
-
- Examples
- --------
-     [raw, keptRows, keptCols] = cleanSheet(raw, removeComments, ...
-         removeOnlyCap, removeNoCap, removeEmptyRows);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 % cleanSheet  Clean up an Excel sheet.
-0002 %
-0003 % Removes empty rows/columns (and performs some other checks).
-0004 %
-0005 % Parameters
-0006 % ----------
-0007 % raw : cell
-0008 %     cell array with the data in the sheet.
-0009 %
-0010 % Name-Value Arguments
-0011 % --------------------
-0012 % removeComments : logical
-0013 %     true if commented lines (non-empty first cell in each row) should be
-0014 %     removed (default true).
-0015 % removeOnlyCap : logical
-0016 %     remove columns with captions but no other values (default false).
-0017 % removeNoCap : logical
-0018 %     remove columns without captions (default true).
-0019 % removeEmptyRows : logical
-0020 %     remove rows with no non-empty cells (default true).
-0021 %
-0022 % Returns
-0023 % -------
-0024 % raw : cell
-0025 %     cleaned version.
-0026 % keptRows : double
-0027 %     indices of the kept rows in the original structure.
-0028 % keptCols : double
-0029 %     indices of the kept columns in the original structure.
-0030 %
-0031 % Examples
-0032 % --------
-0033 %     [raw, keptRows, keptCols] = cleanSheet(raw, removeComments, ...
-0034 %         removeOnlyCap, removeNoCap, removeEmptyRows);
-0035 function [raw,keptRows,keptCols]=cleanSheet(raw,varargin)
-0036 p=parseRAVENargs(varargin, {'removeComments',true; 'removeOnlyCap',false; 'removeNoCap',true; 'removeEmptyRows',true});
-0037 removeComments=p.removeComments; removeOnlyCap=p.removeOnlyCap; removeNoCap=p.removeNoCap; removeEmptyRows=p.removeEmptyRows;
-0038 
-0039 keptRows=1:size(raw,1);
-0040 keptRows=keptRows(:);
-0041 keptCols=1:size(raw,2);
-0042 keptCols=keptCols(:);
-0043 
-0044 %First check that it's a cell array. If a sheet is completely empty, then
-0045 %raw=NaN
-0046 if iscell(raw)
-0047     %Clear cells which contain only white spaces or NaN. This could happen
-0048     %if you accidentally inserted a space, or if a blank/NaN cell was read
-0049     %from the source file.
-0050     whites=cellfun(@wrapperWS,raw);
-0051     raw(whites)={[]};
-0052     
-0053     %Find the rows that are not commented. This corresponds to the first
-0054     %row and the ones which are empty in the first column
-0055     if removeComments==true
-0056         keepers=cellfun(@isempty,raw(:,1));
-0057         keepers(1)=true;
-0058         raw=raw(keepers,:);
-0059         keptRows=keptRows(keepers);
-0060     end
-0061     
-0062     %Remove columns that don't have string headers. If you cut and paste a
-0063     %lot in the sheet there tends to be columns that are empty
-0064     if removeNoCap==true
-0065         I=cellfun(@isstr,raw(1,:));
-0066         raw=raw(:,I);
-0067         keptCols=keptCols(I);
-0068     end
-0069     
-0070     %Remove columns which are empty except for header
-0071     if removeOnlyCap==true
-0072         I=~all(cellfun(@isempty,raw(2:end,:)),1);
-0073         raw=raw(:,I);
-0074         keptCols=keptCols(I);
-0075     end
-0076     
-0077     %Check if there are any rows that are all empty. This could happen if
-0078     %it reads too far or if the user has inserted them for layout reasons.
-0079     %Remove any such rows
-0080     if removeEmptyRows==true
-0081         I=~all(cellfun(@isempty,raw),2);
-0082         raw=raw(I,:);
-0083         keptRows=keptRows(I);
-0084     end
-0085 else
-0086     raw={[]};
-0087 end
-0088 
-0089 %Checks if something is all white spaces or NaN
-0090     function I=wrapperWS(A)
-0091         if isnan(A)
-0092             I=true;
-0093         else
-0094             %isstrprop gives an error if boolean
-0095             if islogical(A)
-0096                 I=false;
-0097             else
-0098                 %I have to make this check, since otherwise it will pick up
-0099                 %on doubles for which the ASCII representation is a white
-0100                 %space character
-0101                 if isnumeric(A)
-0102                     I=false;
-0103                 else
-0104                     I=all(isstrprop(A,'wspace'));
-0105                 end
-0106             end
-0107         end
-0108     end
-0109 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/io/exportForGit.html b/doc/io/exportForGit.html deleted file mode 100644 index a97b3e93..00000000 --- a/doc/io/exportForGit.html +++ /dev/null @@ -1,326 +0,0 @@ - - - - Description of exportForGit - - - - - - - - - -
Home > io > exportForGit.m
- - - -

exportForGit -

- -

PURPOSE ^

-
exportForGit Export a model for a Git-maintained model repository.
- -

SYNOPSIS ^

-
function out=exportForGit(model,varargin)
- -

DESCRIPTION ^

-
 exportForGit  Export a model for a Git-maintained model repository.
-
- Generates a directory structure and populates it with model files, ready
- to be committed to a Git(Hub) maintained model repository. Writes the
- model as SBML L3V1 FBCv2 (both XML and YAML), COBRA text, Matlab MAT-file
- and Microsoft Excel formats.
-
- Parameters
- ----------
- model : struct
-     model structure in RAVEN format that should be exported.
-
- Name-Value Arguments
- --------------------
- prefix : char
-     prefix for all filenames (default 'model').
- path : char
-     path where the directory structure should be generated and populated
-     with all files (default current working directory).
- formats : cell
-     cell array of strings specifying in what file formats the model
-     should be exported (default all formats as {'mat', 'txt', 'xlsx',
-     'xml', 'yml'}).
- mainBranchFlag : logical
-     if true, function will error if RAVEN (and GECKO for enzyme-
-     constrained models) is/are not on the main branch (default false).
- subDirs : logical
-     whether model files for each file format should be written in their
-     own subdirectory, with 'model' as parent directory, in accordance to
-     the standard-GEM repository format. If false, all files are stored in
-     the same folder (default true).
- COBRAtext : logical
-     whether the txt file should be in COBRA Toolbox format using
-     metabolite IDs, instead of metabolite names and compartments
-     (default false).
- neverPrefixIDs : logical
-     true if prefixes are never added to identifiers, even if they start
-     with e.g. digits. This might result in invalid SBML files (default
-     false).
-
- Examples
- --------
-     exportForGit(model, prefix, path, formats, mainBranchFlag, subDirs, ...
-         COBRAtext, neverPrefixIDs);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • exportModel exportModel Export a constraint-based model to an SBML file (L3V1 FBCv2).
  • exportToExcelFormat exportToExcelFormat Export a model to the Microsoft Excel model format.
  • writeYAMLmodel writeYAMLmodel Write a model to a yaml file in the standard cobra-format
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function out=exportForGit(model,varargin)
-0002 % exportForGit  Export a model for a Git-maintained model repository.
-0003 %
-0004 % Generates a directory structure and populates it with model files, ready
-0005 % to be committed to a Git(Hub) maintained model repository. Writes the
-0006 % model as SBML L3V1 FBCv2 (both XML and YAML), COBRA text, Matlab MAT-file
-0007 % and Microsoft Excel formats.
-0008 %
-0009 % Parameters
-0010 % ----------
-0011 % model : struct
-0012 %     model structure in RAVEN format that should be exported.
-0013 %
-0014 % Name-Value Arguments
-0015 % --------------------
-0016 % prefix : char
-0017 %     prefix for all filenames (default 'model').
-0018 % path : char
-0019 %     path where the directory structure should be generated and populated
-0020 %     with all files (default current working directory).
-0021 % formats : cell
-0022 %     cell array of strings specifying in what file formats the model
-0023 %     should be exported (default all formats as {'mat', 'txt', 'xlsx',
-0024 %     'xml', 'yml'}).
-0025 % mainBranchFlag : logical
-0026 %     if true, function will error if RAVEN (and GECKO for enzyme-
-0027 %     constrained models) is/are not on the main branch (default false).
-0028 % subDirs : logical
-0029 %     whether model files for each file format should be written in their
-0030 %     own subdirectory, with 'model' as parent directory, in accordance to
-0031 %     the standard-GEM repository format. If false, all files are stored in
-0032 %     the same folder (default true).
-0033 % COBRAtext : logical
-0034 %     whether the txt file should be in COBRA Toolbox format using
-0035 %     metabolite IDs, instead of metabolite names and compartments
-0036 %     (default false).
-0037 % neverPrefixIDs : logical
-0038 %     true if prefixes are never added to identifiers, even if they start
-0039 %     with e.g. digits. This might result in invalid SBML files (default
-0040 %     false).
-0041 %
-0042 % Examples
-0043 % --------
-0044 %     exportForGit(model, prefix, path, formats, mainBranchFlag, subDirs, ...
-0045 %         COBRAtext, neverPrefixIDs);
-0046 p=parseRAVENargs(varargin, {'prefix',[]; 'path',[]; 'formats',[]; 'mainBranchFlag',[]; 'subDirs',[]; 'COBRAtext',[]; 'neverPrefixIDs',false});
-0047 prefix=p.prefix; path=p.path; formats=p.formats; mainBranchFlag=p.mainBranchFlag; subDirs=p.subDirs; COBRAtext=p.COBRAtext; neverPrefixIDs=p.neverPrefixIDs;
-0048 if isempty(COBRAtext)
-0049     COBRAtext=false;
-0050 end
-0051 if isempty(subDirs)
-0052     subDirs=true;
-0053 end
-0054 if isempty(mainBranchFlag)
-0055     mainBranchFlag=false;
-0056 end
-0057 if isempty(formats)
-0058     formats={'mat', 'txt', 'xlsx', 'xml', 'yml'};
-0059 else
-0060     formats=convertCharArray(formats);
-0061 end
-0062 if any(~ismember(formats, {'mat', 'txt', 'xlsx', 'xml', 'yml'}))
-0063     EM='Unknown file format defined. Only mat, txt, xlsx, xml and yml are allowed file formats.';
-0064     error(EM)
-0065 end
-0066 if isempty(path)
-0067     path='.';
-0068 else
-0069     path=char(path);
-0070 end
-0071 if isempty(prefix)
-0072     prefix='model';
-0073 else
-0074     prefix=char(prefix);
-0075 end
-0076 
-0077 %Sort reactions, metabolites and genes alphabetically
-0078 model=sortIdentifiers(model);
-0079 
-0080 %Get versions or commits of toolboxes. GECKO is only relevant for
-0081 %enzyme-constrained models (those with an "ec" field).
-0082 RAVENver = getToolboxVersion('RAVEN','ravenCobraWrapper.m',mainBranchFlag);
-0083 if isfield(model,'ec')
-0084     GECKOver = getToolboxVersion('GECKO','GECKOInstaller.m',mainBranchFlag);
-0085 else
-0086     GECKOver = [];
-0087 end
-0088 %Retrieve libSBML version:
-0089 [ravenDir,prevDir]=findRAVENroot();
-0090 try % 5.17.0 and newer
-0091     libSBMLver=OutputSBML_RAVEN;
-0092     libSBMLver=libSBMLver.libSBML_version_string;
-0093 catch % before 5.17.0
-0094     fid = fopen('tempModelForLibSBMLversion.xml','w+');
-0095     fclose(fid);
-0096     evalc('[~,~,libSBMLver]=TranslateSBML_RAVEN(''tempModelForLibSBMLversion.xml'',0,0)');
-0097     libSBMLver=libSBMLver.libSBML_version_string;
-0098     delete('tempModelForLibSBMLversion.xml');
-0099 end
-0100 
-0101 % Make models folder, no warnings if folder already exists
-0102 if subDirs
-0103     path=fullfile(path,'model');
-0104     filePath=strcat(path,filesep,{'txt','yml','mat','xlsx','xml'});
-0105     [~,~,~]=mkdir(path);
-0106     for i = 1:length(formats)
-0107         [~,~,~]=mkdir(fullfile(path,formats{i}));
-0108     end
-0109 else
-0110     filePath=cell(1,5); filePath(:)={path};
-0111 end
-0112 
-0113 
-0114 % Write TXT format
-0115 if ismember('txt', formats)
-0116     fid=fopen(fullfile(filePath{1},strcat(prefix,'.txt')),'w');
-0117     if COBRAtext==true
-0118         eqns=constructEquations(model,model.rxns,false,false,false);
-0119         eqns=strrep(eqns,' => ','  -> ');
-0120         eqns=strrep(eqns,' <=> ','  <=> ');
-0121         eqns=regexprep(eqns,'> $','>');
-0122         grRules=regexprep(model.grRules,'\((?!\()','( ');
-0123         grRules=regexprep(grRules,'(?<!\))\)',' )');
-0124     else
-0125         eqns=constructEquations(model,model.rxns);
-0126         grRules=model.grRules;
-0127     end
-0128     fprintf(fid, 'Rxn name\tFormula\tGene-reaction association\tLB\tUB\tObjective\n');
-0129     for i = 1:numel(model.rxns)
-0130         fprintf(fid, '%s\t', model.rxns{i});
-0131         fprintf(fid, '%s \t', eqns{i});
-0132         fprintf(fid, '%s\t', grRules{i});
-0133         fprintf(fid, '%6.2f\t%6.2f\t%6.2f\n', model.lb(i), model.ub(i), model.c(i));
-0134     end
-0135     fclose(fid);
-0136 end
-0137 
-0138 % Write YML format
-0139 if ismember('yml', formats)
-0140     writeYAMLmodel(model,fullfile(filePath{2},strcat(prefix,'.yml')));
-0141 end
-0142 
-0143 % Write MAT format
-0144 if ismember('mat', formats)
-0145     save(fullfile(filePath{3},strcat(prefix,'.mat')),'model');
-0146 end
-0147 
-0148 % Write XLSX format
-0149 if ismember('xlsx', formats)
-0150     exportToExcelFormat(model,fullfile(filePath{4},strcat(prefix,'.xlsx')));
-0151 end
-0152 
-0153 % Write XML format
-0154 if ismember('xml', formats)
-0155         exportModel(model,fullfile(filePath{5},strcat(prefix,'.xml')),neverPrefixIDs);
-0156 end
-0157 
-0158 %Save file with versions:
-0159 fid = fopen(fullfile(path,'dependencies.txt'),'wt');
-0160 fprintf(fid,['MATLAB\t' version '\n']);
-0161 fprintf(fid,['libSBML\t' libSBMLver '\n']);
-0162 fprintf(fid,['RAVEN_toolbox\t' RAVENver '\n']);
-0163 if ~isempty(GECKOver)
-0164     fprintf(fid,['GECKO_toolbox\t' GECKOver '\n']);
-0165 end
-0166 if isfield(model,'modelVersion')
-0167     fields = fieldnames(model.modelVersion);
-0168     for i = 1:length(fields)
-0169         value = model.modelVersion.(fields{i});
-0170         fprintf(fid,[fields{i} '\t' num2str(value) '\n']);
-0171     end
-0172 end
-0173 fclose(fid);
-0174 end
-0175 
-0176 function version = getToolboxVersion(toolbox,fileID,mainBranchFlag)
-0177 % getToolboxVersion  Return the version of RAVEN or GECKO.
-0178 %
-0179 % Returns the version of the toolbox as stated in its version.txt, or
-0180 % 'unknown' if that file cannot be found.
-0181 %
-0182 % toolbox        name of the toolbox (e.g. 'RAVEN' or 'GECKO').
-0183 % fileID         a file only found in that toolbox, used to locate its root
-0184 %                (e.g. 'ravenCobraWrapper.m' or 'GECKOInstaller.m').
-0185 % mainBranchFlag if true, error if the toolbox is not on the main branch.
-0186 currentPath = pwd;
-0187 version     = '';
-0188 
-0189 %Try to find root of toolbox:
-0190 try
-0191     toolboxPath = which(fileID);                %full file path
-0192     slashPos    = getSlashPos(toolboxPath);
-0193     toolboxPath = toolboxPath(1:slashPos(end)); %folder path
-0194     %Go up until the root is found:
-0195     D = dir(toolboxPath);
-0196     while ~ismember({'.git'},{D.name})
-0197         slashPos    = getSlashPos(toolboxPath);
-0198         toolboxPath = toolboxPath(1:slashPos(end-1));
-0199         D           = dir(toolboxPath);
-0200     end
-0201     cd(toolboxPath);
-0202 catch
-0203     disp([toolbox ' toolbox cannot be found'])
-0204     version = 'unknown';
-0205 end
-0206 %Check if in main:
-0207 if mainBranchFlag
-0208     [~,currentBranch] = system('git rev-parse --abbrev-ref HEAD');
-0209     currentBranch = strtrim(currentBranch);
-0210     if ~any([strcmp(currentBranch, "main"), strcmp(currentBranch, "master")])
-0211         cd(currentPath);
-0212         error(['ERROR: ' toolbox ' not in main (or master) branch. Check-out this branch of ' toolbox ' before submitting model for Git.'])
-0213     end
-0214 end
-0215 %Read the version from the toolbox version.txt; report 'unknown' otherwise
-0216 if isempty(version)
-0217     fid = fopen([toolboxPath 'version.txt'],'r');
-0218     if fid ~= -1
-0219         version = fscanf(fid,'%s');
-0220         fclose(fid);
-0221     end
-0222     if isempty(version)
-0223         version = 'unknown';
-0224     end
-0225 end
-0226 cd(currentPath);
-0227 end
-0228 
-0229 function slashPos = getSlashPos(path)
-0230 slashPos = strfind(path,'\');       %Windows
-0231 if isempty(slashPos)
-0232     slashPos = strfind(path,'/');   %MAC/Linux
-0233 end
-0234 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/io/importModel.html b/doc/io/importModel.html deleted file mode 100644 index 8eec851a..00000000 --- a/doc/io/importModel.html +++ /dev/null @@ -1,1095 +0,0 @@ - - - - Description of importModel - - - - - - - - - -
Home > io > importModel.m
- - - -

importModel -

- -

PURPOSE ^

-
importModel Import a constraint-based model from an SBML file.
- -

SYNOPSIS ^

-
function model=importModel(varargin)
- -

DESCRIPTION ^

-
 importModel  Import a constraint-based model from an SBML file.
-
- Name-Value Arguments
- --------------------
- fileName : char
-     a SBML file to import. A dialog window will open if no file name is
-     specified.
- removeExcMets : logical
-     if true, exchange metabolites are removed from the imported model
-     (default true).
- removePrefix : logical
-     true if identifier prefixes should be removed when loading the model:
-     G_ for genes, R_ for reactions, M_ for metabolites, and C_ for
-     compartments. These are only removed if all identifiers of a certain
-     type contain the prefix (default true).
- supressWarnings : logical
-     true if warnings regarding the model structure should be supressed
-     (default false).
-
- Returns
- -------
- model : struct
-     imported model structure with fields:
-
-     - id : model ID
-     - name : name of model contents
-     - annotation : additional information about model
-     - rxns : reaction ids
-     - mets : metabolite ids
-     - S : stoichiometric matrix
-     - lb : lower bounds
-     - ub : upper bounds
-     - rev : reversibility vector
-     - c : objective coefficients
-     - b : equality constraints for the metabolite equations
-     - comps : compartment ids
-     - compNames : compartment names
-     - compOutside : the id (as in comps) for the compartment surrounding
-       each of the compartments
-     - compMiriams : structure with MIRIAM information about the
-       compartments
-     - rxnNames : reaction description
-     - rxnComps : compartments for reactions
-     - grRules : reaction to gene rules in text form
-     - rxnGeneMat : reaction-to-gene mapping in sparse matrix form
-     - subSystems : subsystem name for each reaction
-     - eccodes : EC-codes for the reactions
-     - rxnMiriams : structure with MIRIAM information about the reactions
-     - rxnNotes : reaction notes
-     - rxnReferences : reaction references
-     - rxnConfidenceScores : reaction confidence scores
-     - rxnDeltaG : reaction Gibbs free energy, from a `deltaG` notes key
-       (same convention exportModel writes and raven-toolbox reads/writes;
-       omitted entirely if no reaction has one)
-     - genes : list of all genes
-     - geneComps : compartments for genes
-     - geneMiriams : structure with MIRIAM information about the genes
-     - geneShortNames : gene alternative names (e.g. ERG10)
-     - proteins : protein associated to each gene
-     - metNames : metabolite description
-     - metComps : compartments for metabolites
-     - inchis : InChI-codes for metabolites
-     - metFormulas : metabolite chemical formula
-     - metMiriams : structure with MIRIAM information about the metabolites
-     - metCharges : metabolite charge
-     - metDeltaG : metabolite Gibbs free energy, from a `deltaG` notes key
-       (same convention exportModel writes and raven-toolbox reads/writes;
-       omitted entirely if no metabolite has one)
-     - unconstrained : true if the metabolite is an exchange metabolite
-
- Examples
- --------
-     model = importModel(fileName, removeExcMets, removePrefix, supressWarnings);
-
- Notes
- -----
- A number of consistency checks are performed to ensure that the model is
- valid. Take these warnings seriously and modify the model structure to
- solve them.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function model=importModel(varargin)
-0002 % importModel  Import a constraint-based model from an SBML file.
-0003 %
-0004 % Name-Value Arguments
-0005 % --------------------
-0006 % fileName : char
-0007 %     a SBML file to import. A dialog window will open if no file name is
-0008 %     specified.
-0009 % removeExcMets : logical
-0010 %     if true, exchange metabolites are removed from the imported model
-0011 %     (default true).
-0012 % removePrefix : logical
-0013 %     true if identifier prefixes should be removed when loading the model:
-0014 %     G_ for genes, R_ for reactions, M_ for metabolites, and C_ for
-0015 %     compartments. These are only removed if all identifiers of a certain
-0016 %     type contain the prefix (default true).
-0017 % supressWarnings : logical
-0018 %     true if warnings regarding the model structure should be supressed
-0019 %     (default false).
-0020 %
-0021 % Returns
-0022 % -------
-0023 % model : struct
-0024 %     imported model structure with fields:
-0025 %
-0026 %     - id : model ID
-0027 %     - name : name of model contents
-0028 %     - annotation : additional information about model
-0029 %     - rxns : reaction ids
-0030 %     - mets : metabolite ids
-0031 %     - S : stoichiometric matrix
-0032 %     - lb : lower bounds
-0033 %     - ub : upper bounds
-0034 %     - rev : reversibility vector
-0035 %     - c : objective coefficients
-0036 %     - b : equality constraints for the metabolite equations
-0037 %     - comps : compartment ids
-0038 %     - compNames : compartment names
-0039 %     - compOutside : the id (as in comps) for the compartment surrounding
-0040 %       each of the compartments
-0041 %     - compMiriams : structure with MIRIAM information about the
-0042 %       compartments
-0043 %     - rxnNames : reaction description
-0044 %     - rxnComps : compartments for reactions
-0045 %     - grRules : reaction to gene rules in text form
-0046 %     - rxnGeneMat : reaction-to-gene mapping in sparse matrix form
-0047 %     - subSystems : subsystem name for each reaction
-0048 %     - eccodes : EC-codes for the reactions
-0049 %     - rxnMiriams : structure with MIRIAM information about the reactions
-0050 %     - rxnNotes : reaction notes
-0051 %     - rxnReferences : reaction references
-0052 %     - rxnConfidenceScores : reaction confidence scores
-0053 %     - rxnDeltaG : reaction Gibbs free energy, from a `deltaG` notes key
-0054 %       (same convention exportModel writes and raven-toolbox reads/writes;
-0055 %       omitted entirely if no reaction has one)
-0056 %     - genes : list of all genes
-0057 %     - geneComps : compartments for genes
-0058 %     - geneMiriams : structure with MIRIAM information about the genes
-0059 %     - geneShortNames : gene alternative names (e.g. ERG10)
-0060 %     - proteins : protein associated to each gene
-0061 %     - metNames : metabolite description
-0062 %     - metComps : compartments for metabolites
-0063 %     - inchis : InChI-codes for metabolites
-0064 %     - metFormulas : metabolite chemical formula
-0065 %     - metMiriams : structure with MIRIAM information about the metabolites
-0066 %     - metCharges : metabolite charge
-0067 %     - metDeltaG : metabolite Gibbs free energy, from a `deltaG` notes key
-0068 %       (same convention exportModel writes and raven-toolbox reads/writes;
-0069 %       omitted entirely if no metabolite has one)
-0070 %     - unconstrained : true if the metabolite is an exchange metabolite
-0071 %
-0072 % Examples
-0073 % --------
-0074 %     model = importModel(fileName, removeExcMets, removePrefix, supressWarnings);
-0075 %
-0076 % Notes
-0077 % -----
-0078 % A number of consistency checks are performed to ensure that the model is
-0079 % valid. Take these warnings seriously and modify the model structure to
-0080 % solve them.
-0081 
-0082 p=parseRAVENargs(varargin, {'fileName',[]; 'removeExcMets',[]; 'removePrefix',[]; 'supressWarnings',false});
-0083 fileName=p.fileName; removeExcMets=p.removeExcMets; removePrefix=p.removePrefix; supressWarnings=p.supressWarnings;
-0084 if isempty(fileName)
-0085     [fileName, pathName] = uigetfile({'*.xml;*.sbml'}, 'Please select the model file');
-0086     if fileName == 0
-0087         error('You should select a model file')
-0088     else
-0089         fileName = fullfile(pathName,fileName);
-0090     end
-0091 end
-0092 fileName=char(fileName);
-0093 if isempty(removeExcMets)
-0094     removeExcMets=true;
-0095 end
-0096 
-0097 if isempty(removePrefix)
-0098     removePrefix=true;
-0099 end
-0100 
-0101 fileName=checkFileExistence(fileName,1);
-0102 % If path contains non-ASCII characters, copy file to tempdir first, as
-0103 % libSBML is known to have problems with this on Windows:
-0104 % https://sbml.org/software/libsbml/libsbml-docs/known-pitfalls/#matlab-on-windows-has-issues-with-unicode-filenames
-0105 if ispc && any(double(fileName)>128)
-0106     [~,originalFile,ext] = fileparts(fileName);
-0107     tempFile = fullfile(tempdir,[originalFile ext]);
-0108     copyfile(fileName,tempFile);
-0109     fileName = tempFile;
-0110 end
-0111 
-0112 %Set a canonical order of the model fields
-0113 model=[];
-0114 model.id=[];
-0115 model.name=[];
-0116 model.annotation=[];
-0117 model.rxns={};
-0118 model.mets={};
-0119 model.S=[];
-0120 model.lb=[];
-0121 model.ub=[];
-0122 model.rev=[];
-0123 model.c=[];
-0124 model.b=[];
-0125 model.comps={};
-0126 model.compNames={};
-0127 model.compOutside={};
-0128 model.compMiriams={};
-0129 model.rxnNames={};
-0130 model.rxnComps=[];
-0131 model.grRules={};
-0132 model.rxnGeneMat=[];
-0133 model.subSystems={};
-0134 model.eccodes={};
-0135 model.rxnMiriams={};
-0136 model.rxnNotes={};
-0137 model.rxnReferences={};
-0138 model.rxnConfidenceScores=[];
-0139 model.genes={};
-0140 model.geneComps=[];
-0141 model.geneMiriams={};
-0142 model.geneShortNames={};
-0143 model.proteins={};
-0144 model.metNames={};
-0145 model.metComps=[];
-0146 model.inchis={};
-0147 model.metFormulas={};
-0148 model.metMiriams={};
-0149 model.metCharges=[];
-0150 model.unconstrained=[];
-0151 
-0152 %Load the model using libSBML
-0153 [modelSBML,errorMsg] = TranslateSBML_RAVEN(fileName,0,0,[1 1]);
-0154 if exist('tempFile','var')
-0155     delete(tempFile)
-0156 end
-0157 
-0158 if isempty(modelSBML)
-0159     EM=['There is a problem with the SBML file. Try using the SBML Validator at http://sbml.org/Facilities/Validator.\nlibSBML reports: ', errorMsg.message];
-0160     error('RAVEN:badInput', '%s', EM);
-0161 end
-0162 
-0163 %RAVEN only supports SBML Level 3 Version 1 with the FBC version 2 package.
-0164 if ~(isfield(modelSBML,'SBML_level') && isequal(modelSBML.SBML_level,3) && ...
-0165         isfield(modelSBML,'SBML_version') && isequal(modelSBML.SBML_version,1) && ...
-0166         isfield(modelSBML,'fbc_version') && isequal(modelSBML.fbc_version,2))
-0167     EM=['importModel only supports SBML Level 3 Version 1 with the FBC '...
-0168         'version 2 package. Convert the model to this format first (for '...
-0169         'example with another tool), or use an older RAVEN version to '...
-0170         'import it.'];
-0171     error('RAVEN:badInput', '%s', EM);
-0172 end
-0173 
-0174 %Retrieve compartment names and IDs
-0175 compartmentNames=cell(numel(modelSBML.compartment),1);
-0176 compartmentIDs=cell(numel(modelSBML.compartment),1);
-0177 compartmentOutside=cell(numel(modelSBML.compartment),1);
-0178 compartmentMiriams=cell(numel(modelSBML.compartment),1);
-0179 
-0180 if isfield(modelSBML.compartment,'sboTerm') && isscalar(unique([modelSBML.compartment.sboTerm]))
-0181     %If all the SBO terms are identical, do not add them to compMiriams
-0182     modelSBML.compartment = rmfield(modelSBML.compartment,'sboTerm');
-0183 end
-0184 
-0185 for i=1:numel(modelSBML.compartment)
-0186     compartmentNames{i}=modelSBML.compartment(i).name;
-0187     compartmentIDs{i}=modelSBML.compartment(i).id;
-0188     if isfield(modelSBML.compartment(i),'outside')
-0189         if ~isempty(modelSBML.compartment(i).outside)
-0190             compartmentOutside{i}=modelSBML.compartment(i).outside;
-0191         else
-0192             compartmentOutside{i}='';
-0193         end
-0194     else
-0195         compartmentOutside{i}=[];
-0196     end
-0197 
-0198     if isfield(modelSBML.compartment(i),'annotation')
-0199         compartmentMiriams{i}=parseMiriam(modelSBML.compartment(i).annotation);
-0200     else
-0201         compartmentMiriams{i}=[];
-0202     end
-0203 
-0204     if isfield(modelSBML.compartment(i),'sboTerm') && ~(modelSBML.compartment(i).sboTerm==-1)
-0205         compartmentMiriams{i} = addSBOtoMiriam(compartmentMiriams{i},modelSBML.compartment(i).sboTerm);
-0206     end
-0207 end
-0208 
-0209 %If there are no compartment names then use compartment id as name
-0210 if all(cellfun(@isempty,compartmentNames))
-0211     compartmentNames=compartmentIDs;
-0212 end
-0213 
-0214 %Retrieve info on metabolites
-0215 metaboliteNames={};
-0216 metaboliteIDs={};
-0217 metaboliteCompartments={};
-0218 metaboliteUnconstrained=[];
-0219 metaboliteFormula={};
-0220 metaboliteInChI={};
-0221 metaboliteMiriams={};
-0222 metaboliteCharges=[];
-0223 metaboliteDeltaG=[];
-0224 
-0225 %Gene information is collected from fbc_geneProduct further below
-0226 geneMiriams={};
-0227 geneShortNames={};
-0228 proteins={};
-0229 
-0230 %In FBC v2 all species are metabolites; genes are stored as fbc_geneProduct
-0231 %and are handled separately below.
-0232 metSBOs = [];
-0233 %Regex of compartment names, later to be used to remove from metabolite
-0234 %names if present as suffix.
-0235 escapedCompNames = regexptranslate('escape',{modelSBML.compartment.name});
-0236 regexCompNames = ['\s?\[((' strjoin(escapedCompNames,')|(') '))\]$'];
-0237 for i=1:numel(modelSBML.species)
-0238     metaboliteNames{numel(metaboliteNames)+1,1}=modelSBML.species(i).name;
-0239     metaboliteIDs{numel(metaboliteIDs)+1,1}=modelSBML.species(i).id;
-0240     metaboliteCompartments{numel(metaboliteCompartments)+1,1}=modelSBML.species(i).compartment;
-0241     metaboliteUnconstrained(numel(metaboliteUnconstrained)+1,1)=modelSBML.species(i).boundaryCondition;
-0242 
-0243     %For each metabolite retrieve the formula and the InChI code if
-0244     %available First add the InChI code and the formula from the
-0245     %InChI. This allows for overwriting the formula by setting the
-0246     %actual formula field
-0247     if ~isempty(modelSBML.species(i).annotation)
-0248         %Get the formula if available
-0249         startString='>InChI=';
-0250         endString='</in:inchi>';
-0251         formStart=strfind(modelSBML.species(i).annotation,startString);
-0252         if isempty(formStart)
-0253             startString='InChI=';
-0254             endString='"/>';
-0255         end
-0256         formStart=strfind(modelSBML.species(i).annotation,startString);
-0257         if ~isempty(formStart)
-0258             formEnd=strfind(modelSBML.species(i).annotation,endString);
-0259             formEndIndex=find(formEnd>formStart, 1 );
-0260             formula=modelSBML.species(i).annotation(formStart+numel(startString):formEnd(formEndIndex)-1);
-0261             metaboliteInChI{numel(metaboliteInChI)+1,1}=formula;
-0262 
-0263             %The composition is most often present between the
-0264             %first and second "/" in the model. In some simple
-0265             %molecules, such as salts, there is no second "/". The
-0266             %formula is then assumed to be to the end of the string
-0267             compositionIndexes=strfind(formula,'/');
-0268             if numel(compositionIndexes)>1
-0269                 metaboliteFormula{numel(metaboliteFormula)+1,1}=...
-0270                     formula(compositionIndexes(1)+1:compositionIndexes(2)-1);
-0271             else
-0272                 if isscalar(compositionIndexes)
-0273                     %Probably a simple molecule which can have only
-0274                     %one conformation
-0275                     metaboliteFormula{numel(metaboliteFormula)+1,1}=...
-0276                         formula(compositionIndexes(1)+1:numel(formula));
-0277                 else
-0278                     metaboliteFormula{numel(metaboliteFormula)+1,1}='';
-0279                 end
-0280             end
-0281         elseif isfield(modelSBML.species(i),'fbc_chemicalFormula')
-0282             metaboliteInChI{numel(metaboliteInChI)+1,1}='';
-0283             if ~isempty(modelSBML.species(i).fbc_chemicalFormula)
-0284                 %Cannot extract InChi from formula, so remains
-0285                 %empty
-0286                 metaboliteFormula{numel(metaboliteFormula)+1,1}=modelSBML.species(i).fbc_chemicalFormula;
-0287             else
-0288                 metaboliteFormula{numel(metaboliteFormula)+1,1}='';
-0289             end
-0290         else
-0291             metaboliteInChI{numel(metaboliteInChI)+1,1}='';
-0292             metaboliteFormula{numel(metaboliteFormula)+1,1}='';
-0293         end
-0294 
-0295         %Get Miriam info
-0296         metMiriam=parseMiriam(modelSBML.species(i).annotation);
-0297         metaboliteMiriams{numel(metaboliteMiriams)+1,1}=metMiriam;
-0298     else
-0299         metaboliteInChI{numel(metaboliteInChI)+1,1}='';
-0300         metaboliteFormula{numel(metaboliteFormula)+1,1}='';
-0301         metaboliteMiriams{numel(metaboliteMiriams)+1,1}=[];
-0302     end
-0303     %Get SBO term. One entry per species, always, so metSBOs stays aligned
-0304     %with metaboliteNames below even when some species have no SBO term;
-0305     %-1 (SBML's own "unset" value) marks those.
-0306     if isfield(modelSBML.species(i),'sboTerm') && ~(modelSBML.species(i).sboTerm==-1)
-0307         metSBOs(end+1,1) = modelSBML.species(i).sboTerm;
-0308     else
-0309         metSBOs(end+1,1) = -1;
-0310     end
-0311 
-0312     %Remove trailing [compartment] from metabolite name if present
-0313     metaboliteNames{end,1}=regexprep(metaboliteNames{end,1},regexCompNames,'');
-0314     if isfield(modelSBML.species(i),'fbc_charge')
-0315         if ~isempty(modelSBML.species(i).fbc_charge) && modelSBML.species(i).isSetfbc_charge
-0316             metaboliteCharges(numel(metaboliteCharges)+1,1)=double(modelSBML.species(i).fbc_charge);
-0317         else
-0318             metaboliteCharges(numel(metaboliteCharges)+1,1)=NaN;
-0319         end
-0320     else
-0321         metaboliteCharges(numel(metaboliteCharges)+1,1)=NaN;
-0322     end
-0323     %deltaG: same <p>deltaG: VALUE</p> notes convention exportModel writes
-0324     %and raven-toolbox reads/writes -- see RAVEN#724.
-0325     %str2double('')==NaN, matching metaboliteDeltaG's own NaN default.
-0326     if isfield(modelSBML.species(i),'notes')
-0327         metaboliteDeltaG(numel(metaboliteDeltaG)+1,1)=str2double(parseNote(modelSBML.species(i).notes,'deltaG'));
-0328     else
-0329         metaboliteDeltaG(numel(metaboliteDeltaG)+1,1)=NaN;
-0330     end
-0331     %Additional information from FBC format Chemical formula
-0332     if isfield(modelSBML.species(i),'fbc_chemicalFormula')
-0333         if ~isempty(modelSBML.species(i).fbc_chemicalFormula)
-0334             metaboliteFormula{numel(metaboliteFormula),1}=modelSBML.species(i).fbc_chemicalFormula;
-0335         end
-0336     end
-0337 end
-0338 
-0339 %Add SBO terms to metabolite miriam fields
-0340 if numel(unique(metSBOs)) > 1
-0341     for i = 1:numel(metaboliteNames)
-0342         if metSBOs(i) ~= -1
-0343             metaboliteMiriams{i} = addSBOtoMiriam(metaboliteMiriams{i},metSBOs(i));
-0344         end
-0345     end
-0346 end
-0347 
-0348 %Retrieve info on reactions
-0349 reactionNames=cell(numel(modelSBML.reaction),1);
-0350 reactionIDs=cell(numel(modelSBML.reaction),1);
-0351 subsystems=cell(numel(modelSBML.reaction),1);
-0352 eccodes=cell(numel(modelSBML.reaction),1);
-0353 eccodes(:,:)=cellstr('');
-0354 rxnconfidencescores=NaN(numel(modelSBML.reaction),1);
-0355 rxndeltag=NaN(numel(modelSBML.reaction),1);
-0356 rxnreferences=cell(numel(modelSBML.reaction),1);
-0357 rxnreferences(:,:)=cellstr('');
-0358 rxnnotes=cell(numel(modelSBML.reaction),1);
-0359 rxnnotes(:,:)=cellstr('');
-0360 grRules=cell(numel(modelSBML.reaction),1);
-0361 grRules(:,:)=cellstr('');
-0362 rxnComps=zeros(numel(modelSBML.reaction),1);
-0363 rxnMiriams=cell(numel(modelSBML.reaction),1);
-0364 reactionReversibility=zeros(numel(modelSBML.reaction),1);
-0365 reactionUB=zeros(numel(modelSBML.reaction),1);
-0366 reactionLB=zeros(numel(modelSBML.reaction),1);
-0367 reactionObjective=zeros(numel(modelSBML.reaction),1);
-0368 
-0369 %Construct the stoichiometric matrix while the reaction info is read.
-0370 %Accumulated as (row,col,value) triples and assembled into a sparse
-0371 %matrix in one pass at the end (sparse() sums duplicate (row,col)
-0372 %triples, matching the +'d accumulation below), rather than allocating a
-0373 %dense (mets x rxns) matrix, which for a genome-scale model can be
-0374 %hundreds of MB even though S is typically >95% sparse.
-0375 nStoichEntries=sum(arrayfun(@(r) numel(r.reactant)+numel(r.product),modelSBML.reaction));
-0376 sRow=zeros(nStoichEntries,1);
-0377 sCol=zeros(nStoichEntries,1);
-0378 sVal=zeros(nStoichEntries,1);
-0379 sN=0;
-0380 
-0381 counter=0;
-0382 %If FBC, then bounds have parameter ids defined for the whole model
-0383 if isfield(modelSBML,'parameter')
-0384     parameter.name=cell(numel(modelSBML.parameter),1);
-0385     parameter.name={modelSBML.parameter(:).id}';
-0386     parameter.value={modelSBML.parameter(:).value}';
-0387 end
-0388 
-0389 if isfield(modelSBML.reaction,'sboTerm') && isscalar(unique([modelSBML.reaction.sboTerm]))
-0390     %If all the SBO terms are identical, do not add them to rxnMiriams
-0391     modelSBML.reaction = rmfield(modelSBML.reaction,'sboTerm');
-0392 end
-0393 
-0394 for i=1:numel(modelSBML.reaction)
-0395 
-0396     counter=counter+1;
-0397 
-0398     reactionNames{counter}=modelSBML.reaction(i).name;
-0399 
-0400     reactionIDs{counter}=modelSBML.reaction(i).id;
-0401     reactionReversibility(counter)=modelSBML.reaction(i).reversible;
-0402 
-0403     %If model is FBC, first get parameter of bound and then replace it with
-0404     %the correct value. Probably faster with replace(), but this was only
-0405     %introduced in Matlab R2016b
-0406     if isfield(modelSBML.reaction(i),'fbc_lowerFluxBound')
-0407         lb=modelSBML.reaction(i).fbc_lowerFluxBound;
-0408         ub=modelSBML.reaction(i).fbc_upperFluxBound;
-0409         [~,fluxBoundIdx] = ismember({lb,ub},parameter.name);
-0410         reactionLB(counter) = parameter.value{fluxBoundIdx(1)};
-0411         reactionUB(counter) = parameter.value{fluxBoundIdx(2)};
-0412     else
-0413         if reactionReversibility(counter)==true
-0414             reactionLB(counter)=-inf;
-0415         else
-0416             reactionLB(counter)=0;
-0417         end
-0418         reactionUB(counter)=inf;
-0419         reactionObjective(counter)=0;
-0420     end
-0421 
-0422     %Find the associated gene if available. In FBC v2 gene association data
-0423     %is stored in the fbc_geneProductAssociation field.
-0424     if isfield(modelSBML.reaction(i),'fbc_geneProductAssociation')
-0425         if ~isempty(modelSBML.reaction(i).fbc_geneProductAssociation) && ~isempty(modelSBML.reaction(i).fbc_geneProductAssociation.fbc_association)
-0426             grRules{counter}=modelSBML.reaction(i).fbc_geneProductAssociation.fbc_association.fbc_association;
-0427         end
-0428     end
-0429 
-0430     %Add reaction compartment
-0431     rxnComp='';
-0432     if isfield(modelSBML.reaction(i),'compartment')
-0433         if ~isempty(modelSBML.reaction(i).compartment)
-0434             rxnComp=modelSBML.reaction(i).compartment;
-0435         end
-0436     end
-0437     if ~isempty(rxnComp)
-0438         %Find it in the compartment list
-0439         [~, J]=ismember(rxnComp,compartmentIDs);
-0440         rxnComps(counter)=J;
-0441     end
-0442 
-0443 
-0444     miriamStruct=parseMiriam(modelSBML.reaction(i).annotation);
-0445     rxnMiriams{counter}=miriamStruct;
-0446     if isfield(modelSBML.reaction(i),'notes')
-0447         %parseNote joins several SUBSYSTEM notes on a reaction with ';',
-0448         %so split on it, or a reaction with more than one subsystem ends
-0449         %up with a single subsystem literally named "a;b"
-0450         subsystems{counter,1}=strtrim(strsplit(parseNote(modelSBML.reaction(i).notes,'SUBSYSTEM'),';'));
-0451         subsystems{counter,1}(cellfun('isempty',subsystems{counter,1})) = [];
-0452         if strfind(modelSBML.reaction(i).notes,'Confidence Level')
-0453             confScore = parseNote(modelSBML.reaction(i).notes,'Confidence Level');
-0454             if isempty(confScore)
-0455                 confScore = 0;
-0456             end
-0457             rxnconfidencescores(counter)=str2double(confScore);
-0458         end
-0459         rxnreferences{counter,1}=parseNote(modelSBML.reaction(i).notes,'AUTHORS');
-0460         rxnnotes{counter,1}=parseNote(modelSBML.reaction(i).notes,'NOTES');
-0461         %deltaG: same <p>deltaG: VALUE</p> notes convention exportModel
-0462         %writes and raven-toolbox reads/writes -- see RAVEN#724.
-0463         %str2double('')==NaN, matching rxndeltag's own NaN default.
-0464         rxndeltag(counter)=str2double(parseNote(modelSBML.reaction(i).notes,'deltaG'));
-0465     end
-0466 
-0467     %Get SBO terms
-0468     if isfield(modelSBML.reaction(i),'sboTerm') && ~(modelSBML.reaction(i).sboTerm==-1)
-0469         rxnMiriams{counter} = addSBOtoMiriam(rxnMiriams{counter}, modelSBML.reaction(i).sboTerm);
-0470     end
-0471 
-0472     %Get ec-codes
-0473     eccode='';
-0474     if ~isempty(modelSBML.reaction(i).annotation)
-0475         if strfind(modelSBML.reaction(i).annotation,'http://identifiers.org/ec-code')
-0476             eccode=parseAnnotation(modelSBML.reaction(i).annotation,'http://identifiers.org/','/','ec-code');
-0477         elseif strfind(modelSBML.reaction(i).annotation,'https://identifiers.org/ec-code')
-0478             eccode=parseAnnotation(modelSBML.reaction(i).annotation,'https://identifiers.org/','/','ec-code');
-0479         end
-0480     end
-0481     eccodes{counter}=eccode;
-0482 
-0483     %Add all reactants
-0484     for j=1:numel(modelSBML.reaction(i).reactant)
-0485         %Get the index of the metabolite in metaboliteIDs. External
-0486         %metabolites will be removed at a later stage
-0487         metIndex=find(strcmp(modelSBML.reaction(i).reactant(j).species,metaboliteIDs),1);
-0488         if isempty(metIndex)
-0489             EM=['Could not find metabolite ' modelSBML.reaction(i).reactant(j).species ' in reaction ' reactionIDs{counter}];
-0490             error('RAVEN:badInput', '%s', EM);
-0491         end
-0492         sN=sN+1;
-0493         sRow(sN)=metIndex;
-0494         sCol(sN)=counter;
-0495         sVal(sN)=modelSBML.reaction(i).reactant(j).stoichiometry*-1;
-0496     end
-0497 
-0498     %Add all products
-0499     for j=1:numel(modelSBML.reaction(i).product)
-0500         %Get the index of the metabolite in metaboliteIDs.
-0501         metIndex=find(strcmp(modelSBML.reaction(i).product(j).species,metaboliteIDs),1);
-0502         if isempty(metIndex)
-0503             EM=['Could not find metabolite ' modelSBML.reaction(i).product(j).species ' in reaction ' reactionIDs{counter}];
-0504             error('RAVEN:badInput', '%s', EM);
-0505         end
-0506         sN=sN+1;
-0507         sRow(sN)=metIndex;
-0508         sCol(sN)=counter;
-0509         sVal(sN)=modelSBML.reaction(i).product(j).stoichiometry;
-0510     end
-0511 end
-0512 
-0513 %if FBC, objective function is separately defined. Multiple objective
-0514 %functions can be defined, one is set as active
-0515 if isfield(modelSBML, 'fbc_activeObjective')
-0516     obj=modelSBML.fbc_activeObjective;
-0517     for i=1:numel(modelSBML.fbc_objective)
-0518         if strcmp(obj,modelSBML.fbc_objective(i).fbc_id)
-0519             if ~isempty(modelSBML.fbc_objective(i).fbc_fluxObjective)
-0520                 %fbc_fluxObjective is a struct array when the objective is
-0521                 %a combination of more than one reaction; indexing it
-0522                 %directly would silently keep only the first
-0523                 fluxObjective=modelSBML.fbc_objective(i).fbc_fluxObjective;
-0524                 for k=1:numel(fluxObjective)
-0525                     idx=ismember(reactionIDs,fluxObjective(k).fbc_reaction);
-0526                     reactionObjective(idx)=fluxObjective(k).fbc_coefficient;
-0527                 end
-0528             end
-0529         end
-0530     end
-0531 end
-0532 
-0533 %subSystems can be stored as groups instead of in annotations
-0534 if isfield(modelSBML,'groups_group')
-0535     for i=1:numel(modelSBML.groups_group)
-0536         groupreactions={modelSBML.groups_group(i).groups_member(:).groups_idRef};
-0537         [~, idx] = ismember(groupreactions, reactionIDs);
-0538         if any(idx)
-0539             for j=1:numel(idx)
-0540                 if isempty(subsystems{idx(j)}) % First subsystem
-0541                     subsystems{idx(j)} = {modelSBML.groups_group(i).groups_name};
-0542                 else % Consecutive subsystems: concatenate
-0543                     subsystems{idx(j)} = horzcat(subsystems{idx(j)}, modelSBML.groups_group(i).groups_name);
-0544                 end
-0545             end
-0546         end
-0547     end
-0548 end
-0549 
-0550 %Shrink the structures if complex-forming reactions had to be skipped
-0551 reactionNames=reactionNames(1:counter);
-0552 reactionIDs=reactionIDs(1:counter);
-0553 subsystems=subsystems(1:counter);
-0554 eccodes=eccodes(1:counter);
-0555 rxnconfidencescores=rxnconfidencescores(1:counter);
-0556 rxndeltag=rxndeltag(1:counter);
-0557 rxnreferences=rxnreferences(1:counter);
-0558 rxnnotes=rxnnotes(1:counter);
-0559 grRules=grRules(1:counter);
-0560 rxnMiriams=rxnMiriams(1:counter);
-0561 reactionReversibility=reactionReversibility(1:counter);
-0562 reactionUB=reactionUB(1:counter);
-0563 reactionLB=reactionLB(1:counter);
-0564 reactionObjective=reactionObjective(1:counter);
-0565 
-0566 model.name=modelSBML.name;
-0567 model.id=modelSBML.id;
-0568 model.rxns=reactionIDs;
-0569 model.mets=metaboliteIDs;
-0570 model.S=sparse(sRow(1:sN),sCol(1:sN),sVal(1:sN),numel(metaboliteIDs),counter);
-0571 model.lb=reactionLB;
-0572 model.ub=reactionUB;
-0573 model.rev=reactionReversibility;
-0574 model.c=reactionObjective;
-0575 model.b=zeros(numel(metaboliteIDs),1);
-0576 model.comps=compartmentIDs;
-0577 model.compNames=compartmentNames;
-0578 model.rxnConfidenceScores=rxnconfidencescores;
-0579 model.rxnReferences=rxnreferences;
-0580 model.rxnNotes=rxnnotes;
-0581 model.rxnDeltaG=rxndeltag;
-0582 
-0583 %Load annotation if available. If there are several authors, only the first
-0584 %author credentials are imported
-0585 if isfield(modelSBML,'annotation')
-0586     endString='</';
-0587     I=strfind(modelSBML.annotation,endString);
-0588     J=strfind(modelSBML.annotation,'<vCard:Family>');
-0589     if any(J)
-0590         model.annotation.familyName=modelSBML.annotation(J(1)+14:I(find(I>J(1),1))-1);
-0591     end
-0592     J=strfind(modelSBML.annotation,'<vCard:Given>');
-0593     if any(J)
-0594         model.annotation.givenName=modelSBML.annotation(J(1)+13:I(find(I>J(1),1))-1);
-0595     end
-0596     J=strfind(modelSBML.annotation,'<vCard:EMAIL>');
-0597     if any(J)
-0598         model.annotation.email=modelSBML.annotation(J(1)+13:I(find(I>J(1),1))-1);
-0599     end
-0600     J=strfind(modelSBML.annotation,'<vCard:Orgname>');
-0601     if any(J)
-0602         model.annotation.organization=modelSBML.annotation(J(1)+15:I(find(I>J(1),1))-1);
-0603     end
-0604     endString='"/>';
-0605     I=strfind(modelSBML.annotation,endString);
-0606     %Finding whether the taxonomy is written in the old or the new way
-0607     J=strfind(modelSBML.annotation,'"http://identifiers.org/');
-0608     if any(J)
-0609         I = I(find(I>J,1))-1;
-0610         J = J(find(J<I,1))+24;
-0611         model.annotation.taxonomy=modelSBML.annotation(J:I);
-0612     else
-0613         J=strfind(modelSBML.annotation,'"https://identifiers.org/');
-0614         if any(J)
-0615             I = I(find(I>J,1))-1;
-0616             J = J(find(J<I,1))+25;
-0617             model.annotation.taxonomy=modelSBML.annotation(J:I);
-0618         end
-0619     end
-0620 end
-0621 if isfield(modelSBML,'notes')
-0622     startString=strfind(modelSBML.notes,'xhtml">');
-0623     endString=strfind(modelSBML.notes,'</body>');
-0624     if any(startString) && any(endString)
-0625         model.annotation.note=modelSBML.notes(startString(1)+7:endString-1);
-0626         model.annotation.note=regexprep(model.annotation.note,'<p.*?>|</p.*?>','');
-0627         model.annotation.note=strtrim(model.annotation.note);
-0628         if regexp(model.annotation.note,'This file was generated using the exportModel function in RAVEN Toolbox \d\.\d and OutputSBML in libSBML')
-0629             model.annotation=rmfield(model.annotation,'note'); % Default note added when running exportModel
-0630         end
-0631     end
-0632 end
-0633 
-0634 if any(~cellfun(@isempty,compartmentOutside))
-0635     model.compOutside=compartmentOutside;
-0636 end
-0637 
-0638 model.rxnNames=reactionNames;
-0639 model.metNames=metaboliteNames;
-0640 
-0641 %Match the compartments for metabolites
-0642 [~, J]=ismember(metaboliteCompartments,model.comps);
-0643 model.metComps=J;
-0644 
-0645 %If any gene associations have been loaded, build the gene list
-0646 if ~all(cellfun(@isempty,grRules))
-0647     %If fbc_geneProduct exists, follow the specified gene order, such
-0648     %that matching geneShortNames in function below will work
-0649     if isfield(modelSBML,'fbc_geneProduct')
-0650         genes={modelSBML.fbc_geneProduct.fbc_id};
-0651 
-0652         %Get gene Miriams from fbc_geneProduct
-0653         if isempty(geneMiriams)
-0654             geneMiriams = cell(numel(genes),1);
-0655             if isfield(modelSBML.fbc_geneProduct,'sboTerm') && isscalar(unique([modelSBML.fbc_geneProduct.sboTerm]))
-0656                 %If all the SBO terms are identical, do not add them to geneMiriams
-0657                 modelSBML.fbc_geneProduct = rmfield(modelSBML.fbc_geneProduct,'sboTerm');
-0658             end
-0659             for i = 1:numel(genes)
-0660                 geneMiriams{i}=parseMiriam(modelSBML.fbc_geneProduct(i).annotation);
-0661                 if isfield(modelSBML.fbc_geneProduct(i),'sboTerm') && ~(modelSBML.fbc_geneProduct(i).sboTerm==-1)
-0662                     geneMiriams{i} = addSBOtoMiriam(geneMiriams{i},modelSBML.fbc_geneProduct(i).sboTerm);
-0663                 end
-0664             end
-0665         end
-0666         proteins={modelSBML.fbc_geneProduct.fbc_name};
-0667     else
-0668         genes=getGenesFromGrRules(grRules);
-0669     end
-0670     model.genes=genes;
-0671     model.grRules=grRules;
-0672     [grRules,rxnGeneMat] = standardizeGrRules(model,true);
-0673     model.grRules = grRules;
-0674     model.rxnGeneMat = rxnGeneMat;
-0675 end
-0676 
-0677 if all(cellfun(@isempty,geneShortNames))
-0678     if isfield(modelSBML,'fbc_geneProduct')
-0679         %Iterate fbc_geneProduct directly: genes is only assigned above
-0680         %when grRules is non-empty, but fbc_geneProduct can be present
-0681         %(even as an empty list) regardless of that.
-0682         for i=1:numel(modelSBML.fbc_geneProduct)
-0683             if ~isempty(modelSBML.fbc_geneProduct(i).fbc_label)
-0684                 geneShortNames{i,1}=modelSBML.fbc_geneProduct(i).fbc_label;
-0685             elseif ~isempty(modelSBML.fbc_geneProduct(i).fbc_name)
-0686                 geneShortNames{i,1}=modelSBML.fbc_geneProduct(i).fbc_name;
-0687             else
-0688                 geneShortNames{i,1}='';
-0689             end
-0690         end
-0691     end
-0692 end
-0693 
-0694 %If any InChIs have been loaded
-0695 if any(~cellfun(@isempty,metaboliteInChI))
-0696     model.inchis=metaboliteInChI;
-0697 end
-0698 
-0699 %If any formulas have been loaded
-0700 if any(~cellfun(@isempty,metaboliteFormula))
-0701     model.metFormulas=metaboliteFormula;
-0702 end
-0703 
-0704 %If any charges have been loaded
-0705 if ~isempty(metaboliteCharges)
-0706     model.metCharges=metaboliteCharges;
-0707 end
-0708 
-0709 %If any deltaG values have been loaded
-0710 if ~isempty(metaboliteDeltaG) && ~all(isnan(metaboliteDeltaG))
-0711     model.metDeltaG=metaboliteDeltaG;
-0712 end
-0713 
-0714 %If any gene short names have been loaded
-0715 if any(~cellfun(@isempty,geneShortNames))
-0716     model.geneShortNames=geneShortNames;
-0717 end
-0718 
-0719 %If any Miriam strings for compartments have been loaded
-0720 if any(~cellfun(@isempty,compartmentMiriams))
-0721     model.compMiriams=compartmentMiriams;
-0722 end
-0723 
-0724 %If any Miriam strings for metabolites have been loaded
-0725 if any(~cellfun(@isempty,metaboliteMiriams))
-0726     model.metMiriams=metaboliteMiriams;
-0727 end
-0728 
-0729 %If any subsystems have been loaded
-0730 if any(~cellfun(@isempty,subsystems))
-0731     model.subSystems=subsystems;
-0732 end
-0733 if any(rxnComps)
-0734     if all(rxnComps)
-0735         model.rxnComps=rxnComps;
-0736     else
-0737         if supressWarnings==false
-0738             EM='The compartments for the following reactions could not be matched. Ignoring reaction compartment information';
-0739             warning('RAVEN:warning', '%s', ravenList(EM,model.rxns(rxnComps==0)));
-0740         end
-0741     end
-0742 end
-0743 
-0744 %If any ec-codes have been loaded
-0745 if any(~cellfun(@isempty,eccodes))
-0746     model.eccodes=eccodes;
-0747 end
-0748 
-0749 %If any Miriam strings for reactions have been loaded
-0750 if any(~cellfun(@isempty,rxnMiriams))
-0751     model.rxnMiriams=rxnMiriams;
-0752 end
-0753 
-0754 %If any Miriam strings for genes have been loaded
-0755 if any(~cellfun(@isempty,geneMiriams))
-0756     model.geneMiriams=geneMiriams;
-0757 end
-0758 
-0759 %If any protein strings have been loaded
-0760 if any(~cellfun(@isempty,proteins))
-0761     proteins = reshape(proteins,[],1);
-0762     model.proteins=proteins;
-0763 end
-0764 
-0765 model.unconstrained=metaboliteUnconstrained;
-0766 
-0767 %Convert SBML IDs back into their original strings. Here we are using part
-0768 %from convertSBMLID, originating from the COBRA Toolbox
-0769 model.rxns=regexprep(model.rxns,'__([0-9]+)__','${char(str2num($1))}');
-0770 model.mets=regexprep(model.mets,'__([0-9]+)__','${char(str2num($1))}');
-0771 model.comps=regexprep(model.comps,'__([0-9]+)__','${char(str2num($1))}');
-0772 model.grRules=regexprep(model.grRules,'__([0-9]+)__','${char(str2num($1))}');
-0773 model.genes=regexprep(model.genes,'__([0-9]+)__','${char(str2num($1))}');
-0774 model.id=regexprep(model.id,'__([0-9]+)__','${char(str2num($1))}');
-0775 
-0776 if removePrefix
-0777     [model, hasChanged]=removeIdentifierPrefix(model);
-0778     warning('RAVEN:warning', '%s', ravenList(['The following fields have prefixes removed from all entries. '...
-0779     'If this is undesired, run importModel with removePrefix as false. Example: '...
-0780     'importModel(''filename.xml'',[],false);'],hasChanged))
-0781 end
-0782 
-0783 %Remove unused fields
-0784 if isempty(model.annotation)
-0785     model=rmfield(model,'annotation');
-0786 end
-0787 if isempty(model.compOutside)
-0788     model=rmfield(model,'compOutside');
-0789 end
-0790 if isempty(model.compMiriams)
-0791     model=rmfield(model,'compMiriams');
-0792 end
-0793 if isempty(model.rxnComps)
-0794     model=rmfield(model,'rxnComps');
-0795 end
-0796 if isempty(model.grRules)
-0797     model=rmfield(model,'grRules');
-0798 end
-0799 if isempty(model.rxnGeneMat)
-0800     model=rmfield(model,'rxnGeneMat');
-0801 end
-0802 if isempty(model.subSystems)
-0803     model=rmfield(model,'subSystems');
-0804 else
-0805     model.subSystems(cellfun(@isempty,subsystems))={{''}};
-0806     % If all subSystems have single entries, then unnest them
-0807     if all(cellfun(@(x) iscell(x) && isscalar(x), model.subSystems))
-0808         model.subSystems = transpose([model.subSystems{:}]);
-0809     end
-0810 end
-0811 if isempty(model.eccodes)
-0812     model=rmfield(model,'eccodes');
-0813 end
-0814 if isempty(model.rxnMiriams)
-0815     model=rmfield(model,'rxnMiriams');
-0816 end
-0817 if cellfun(@isempty,model.rxnNotes)
-0818     model=rmfield(model,'rxnNotes');
-0819 end
-0820 if cellfun(@isempty,model.rxnReferences)
-0821     model=rmfield(model,'rxnReferences');
-0822 end
-0823 if isempty(model.rxnConfidenceScores) || all(isnan(model.rxnConfidenceScores))
-0824     model=rmfield(model,'rxnConfidenceScores');
-0825 end
-0826 if isempty(model.rxnDeltaG) || all(isnan(model.rxnDeltaG))
-0827     model=rmfield(model,'rxnDeltaG');
-0828 end
-0829 if isempty(model.genes)
-0830     model=rmfield(model,'genes');
-0831 elseif isrow(model.genes)
-0832     model.genes=transpose(model.genes);
-0833 end
-0834 if isempty(model.geneComps)
-0835     model=rmfield(model,'geneComps');
-0836 end
-0837 if isempty(model.geneMiriams)
-0838     model=rmfield(model,'geneMiriams');
-0839 end
-0840 if isempty(model.geneShortNames)
-0841     model=rmfield(model,'geneShortNames');
-0842 end
-0843 if isempty(model.proteins)
-0844     model=rmfield(model,'proteins');
-0845 end
-0846 if isempty(model.inchis)
-0847     model=rmfield(model,'inchis');
-0848 end
-0849 if isempty(model.metFormulas)
-0850     model=rmfield(model,'metFormulas');
-0851 end
-0852 if isempty(model.metMiriams)
-0853     model=rmfield(model,'metMiriams');
-0854 end
-0855 %Unlike inchis/metFormulas/metMiriams above, metCharges always gets one
-0856 %entry per metabolite (NaN where SBML had no fbc_charge), so it is never
-0857 %literally empty; drop it only when none of those entries are real, not
-0858 %when they are all genuinely zero (any(zeros)==false would remove a
-0859 %field of real, all-neutral charges; any(NaN)==true would keep an
-0860 %all-unset field)
-0861 if all(isnan(model.metCharges))
-0862     model=rmfield(model,'metCharges');
-0863 end
-0864 
-0865 %This just removes the grRules if no genes have been loaded
-0866 if ~isfield(model,'genes') && isfield(model,'grRules')
-0867     model=rmfield(model,'grRules');
-0868 end
-0869 
-0870 %Print warnings about bad structure
-0871 if supressWarnings==false
-0872     checkModelStruct(model,false);
-0873 end
-0874 
-0875 if removeExcMets==true
-0876     model=simplifyModel(model);
-0877 end
-0878 end
-0879 
-0880 function fieldContent=parseNote(searchString,fieldName)
-0881 %The function obtains the particular information from "notes" field, using
-0882 %fieldName as the dummy string
-0883 
-0884 fieldContent='';
-0885 
-0886 if strfind(searchString,fieldName)
-0887     [~,targetString] = regexp(searchString,['<p>' fieldName '.*?</p>'],'tokens','match');
-0888     targetString=regexprep(targetString,'<p>|</p>','');
-0889     targetString=regexprep(targetString,[fieldName, ':'],'');
-0890     for i=1:numel(targetString)
-0891         fieldContent=[fieldContent ';' strtrim(targetString{1,i})];
-0892     end
-0893     fieldContent=regexprep(fieldContent,'^;|;$','');
-0894 else
-0895     fieldContent='';
-0896 end
-0897 end
-0898 
-0899 function fieldContent=parseAnnotation(searchString,startString,midString,fieldName)
-0900 
-0901 fieldContent='';
-0902 
-0903 %Removing whitespace characters from the ending strings, which may occur in
-0904 %several cases
-0905 searchString=regexprep(searchString,'" />','"/>');
-0906 [~,targetString] = regexp(searchString,['<rdf:li rdf:resource="' startString fieldName midString '.*?"/>'],'tokens','match');
-0907 targetString=regexprep(targetString,'<rdf:li rdf:resource="|"/>','');
-0908 targetString=regexprep(targetString,startString,'');
-0909 targetString=regexprep(targetString,[fieldName midString],'');
-0910 
-0911 for i=1:numel(targetString)
-0912     fieldContent=[fieldContent ';' strtrim(targetString{1,i})];
-0913 end
-0914 
-0915 fieldContent=regexprep(fieldContent,'^;|;$','');
-0916 end
-0917 
-0918 function miriamStruct=parseMiriam(searchString)
-0919 %Generates miriam structure from annotation field
-0920 
-0921 %Finding whether miriams are written in the old or the new way
-0922 if strfind(searchString,'http://identifiers.org/')
-0923     startString='http://identifiers.org/';
-0924 elseif strfind(searchString,'https://identifiers.org/')
-0925     startString='https://identifiers.org/';
-0926 else
-0927     miriamStruct=[];
-0928     return;
-0929 end
-0930 
-0931 miriamStruct=[];
-0932 
-0933 searchString=regexprep(searchString,'" />','"/>');
-0934 [~,targetString] = regexp(searchString,'<rdf:li rdf:resource=".*?"/>','tokens','match');
-0935 targetString=regexprep(targetString,'<rdf:li rdf:resource="|"/>','');
-0936 targetString=regexprep(targetString,startString,'');
-0937 
-0938 fwdslash  = contains(targetString,'/');
-0939 midString = cell(numel(targetString),1);
-0940 midString(fwdslash) = {'/'};
-0941 midString(~fwdslash) = {':'};
-0942 
-0943 counter=0;
-0944 for i=1:numel(targetString)
-0945     if isempty(regexp(targetString{1,i},'inchi|ec-code', 'once'))
-0946         counter=counter+1;
-0947         miriamStruct.name{counter,1} = regexprep(targetString{1,i},[midString{i} '.+'],'','once');
-0948         miriamStruct.value{counter,1} = regexprep(targetString{1,i},[miriamStruct.name{counter,1} midString{i}],'','once');
-0949         miriamStruct.name{counter,1} = regexprep(miriamStruct.name{counter,1},'^obo\.','');
-0950     end
-0951 end
-0952 end
-0953 
-0954 function miriam = addSBOtoMiriam(miriam,sboTerm)
-0955 %Appends SBO term to miriam structure
-0956 
-0957 sboTerm = {['SBO:' sprintf('%07u',sboTerm)]};  % convert to proper format
-0958 if isempty(miriam)
-0959     miriam.name = {'sbo'};
-0960     miriam.value = sboTerm;
-0961 elseif any(strcmp('sbo',miriam.name))
-0962     currSbo = strcmp('sbo',miriam.name);
-0963     miriam.value(currSbo) = sboTerm;
-0964 else
-0965     miriam.name(end+1) = {'sbo'};
-0966     miriam.value(end+1) = sboTerm;
-0967 end
-0968 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/io/parseYAML.html b/doc/io/parseYAML.html deleted file mode 100644 index 99c89e87..00000000 --- a/doc/io/parseYAML.html +++ /dev/null @@ -1,367 +0,0 @@ - - - - Description of parseYAML - - - - - - - - - -
Home > io > parseYAML.m
- - - -

parseYAML -

- -

PURPOSE ^

-
parseYAML Read a YAML file into a MATLAB struct/cell tree.
- -

SYNOPSIS ^

-
function out = parseYAML(filename)
- -

DESCRIPTION ^

-
 parseYAML  Read a YAML file into a MATLAB struct/cell tree.
-
- Supports block-style mappings and sequences (indentation-based),
- flow-style mappings and sequences (`{ key: value }`, `[a, b]`), quoted
- and unquoted scalars, and comments. Does not support multi-document
- files, anchors/aliases, tags, or block scalars (`|`, `>`).
-
- Use this for parsing YAML configuration / data files with a nested
- mapping/sequence/scalar shape (e.g. condition files consumed by
- applyCondition). For loading a cobra-format model YAML, use
- readYAMLmodel instead — that function knows the model schema and
- returns a populated RAVEN model struct.
-
- Parameters
- ----------
- filename : char
-     Path to the YAML file.
-
- Returns
- -------
- out : struct or cell or char or double or logical or []
-     MATLAB representation of the document:
-
-     - mapping  -> struct (keys sanitised to valid MATLAB field names)
-     - sequence -> cell column vector
-     - quoted or unquoted string -> char
-     - integer or float -> double
-     - true/false -> logical
-     - null / ~ / empty value -> []
-
- Examples
- --------
-     cfg = parseYAML('data/conditions/anaerobic.yml');
-
- See also
- --------
- readYAMLmodel
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function out = parseYAML(filename)
-0002 % parseYAML  Read a YAML file into a MATLAB struct/cell tree.
-0003 %
-0004 % Supports block-style mappings and sequences (indentation-based),
-0005 % flow-style mappings and sequences (`{ key: value }`, `[a, b]`), quoted
-0006 % and unquoted scalars, and comments. Does not support multi-document
-0007 % files, anchors/aliases, tags, or block scalars (`|`, `>`).
-0008 %
-0009 % Use this for parsing YAML configuration / data files with a nested
-0010 % mapping/sequence/scalar shape (e.g. condition files consumed by
-0011 % applyCondition). For loading a cobra-format model YAML, use
-0012 % readYAMLmodel instead — that function knows the model schema and
-0013 % returns a populated RAVEN model struct.
-0014 %
-0015 % Parameters
-0016 % ----------
-0017 % filename : char
-0018 %     Path to the YAML file.
-0019 %
-0020 % Returns
-0021 % -------
-0022 % out : struct or cell or char or double or logical or []
-0023 %     MATLAB representation of the document:
-0024 %
-0025 %     - mapping  -> struct (keys sanitised to valid MATLAB field names)
-0026 %     - sequence -> cell column vector
-0027 %     - quoted or unquoted string -> char
-0028 %     - integer or float -> double
-0029 %     - true/false -> logical
-0030 %     - null / ~ / empty value -> []
-0031 %
-0032 % Examples
-0033 % --------
-0034 %     cfg = parseYAML('data/conditions/anaerobic.yml');
-0035 %
-0036 % See also
-0037 % --------
-0038 % readYAMLmodel
-0039 
-0040 if ~isfile(filename)
-0041     error('parseYAML:fileNotFound', 'File not found: %s', filename);
-0042 end
-0043 
-0044 [lines, indents] = readYAMLLines(filename);
-0045 if isempty(lines)
-0046     out = struct();
-0047     return
-0048 end
-0049 [out, ~] = parseNode(lines, 1, indents);
-0050 end
-0051 
-0052 
-0053 function [lines, indents] = readYAMLLines(filename)
-0054 % Read filename into non-blank, comment-stripped content lines, paired
-0055 % with each line's leading-space indentation count. Document markers
-0056 % (`---`, `...`) are dropped.
-0057 fid = fopen(filename, 'r');
-0058 cleanup = onCleanup(@() fclose(fid));
-0059 
-0060 lines = {};
-0061 indents = [];
-0062 tline = fgetl(fid);
-0063 while ischar(tline)
-0064     content = stripComment(tline);
-0065     trimmed = strtrim(content);
-0066     if ~isempty(trimmed) && ~strcmp(trimmed, '---') && ~strcmp(trimmed, '...')
-0067         indents(end+1,1) = numel(content) - numel(regexprep(content, '^ +', '')); %#ok<AGROW>
-0068         lines{end+1,1} = trimmed; %#ok<AGROW>
-0069     end
-0070     tline = fgetl(fid);
-0071 end
-0072 end
-0073 
-0074 
-0075 function line = stripComment(line)
-0076 % Strip a trailing `# comment`, ignoring `#` inside a quoted string.
-0077 inSingle = false;
-0078 inDouble = false;
-0079 for i = 1:numel(line)
-0080     c = line(i);
-0081     if c == '''' && ~inDouble
-0082         inSingle = ~inSingle;
-0083     elseif c == '"' && ~inSingle
-0084         inDouble = ~inDouble;
-0085     elseif c == '#' && ~inSingle && ~inDouble
-0086         line = line(1:i-1);
-0087         break
-0088     end
-0089 end
-0090 end
-0091 
-0092 
-0093 function [value, nextIdx] = parseNode(lines, idx, indents)
-0094 % Dispatch to a block sequence or block mapping, based on whether the
-0095 % line at idx starts a sequence item (`-`) or a mapping key.
-0096 if startsWithDash(lines{idx})
-0097     [value, nextIdx] = parseSequence(lines, idx, indents);
-0098 else
-0099     [value, nextIdx] = parseMapping(lines, idx, indents);
-0100 end
-0101 end
-0102 
-0103 
-0104 function tf = startsWithDash(s)
-0105 tf = ~isempty(s) && (strcmp(s, '-') || (numel(s) >= 2 && s(1) == '-' && s(2) == ' '));
-0106 end
-0107 
-0108 
-0109 function [value, nextIdx] = parseMapping(lines, idx, indents)
-0110 baseIndent = indents(idx);
-0111 value = struct();
-0112 while idx <= numel(lines) && indents(idx) == baseIndent && ~startsWithDash(lines{idx})
-0113     [key, rest] = splitKeyValue(lines{idx});
-0114     key = matlabFieldName(key);
-0115     if isempty(rest)
-0116         if idx+1 <= numel(lines) && indents(idx+1) > baseIndent
-0117             [childVal, idx] = parseNode(lines, idx+1, indents);
-0118         else
-0119             childVal = [];
-0120             idx = idx + 1;
-0121         end
-0122     else
-0123         childVal = parseScalarOrFlow(rest);
-0124         idx = idx + 1;
-0125     end
-0126     value.(key) = childVal;
-0127 end
-0128 nextIdx = idx;
-0129 end
-0130 
-0131 
-0132 function [value, nextIdx] = parseSequence(lines, idx, indents)
-0133 baseIndent = indents(idx);
-0134 items = {};
-0135 while idx <= numel(lines) && indents(idx) == baseIndent && startsWithDash(lines{idx})
-0136     rest = strtrim(lines{idx}(2:end));
-0137     if isempty(rest)
-0138         if idx+1 <= numel(lines) && indents(idx+1) > baseIndent
-0139             [itemVal, idx] = parseNode(lines, idx+1, indents);
-0140         else
-0141             itemVal = [];
-0142             idx = idx + 1;
-0143         end
-0144     else
-0145         itemVal = parseScalarOrFlow(rest);
-0146         idx = idx + 1;
-0147     end
-0148     items{end+1,1} = itemVal; %#ok<AGROW>
-0149 end
-0150 value = items;
-0151 nextIdx = idx;
-0152 end
-0153 
-0154 
-0155 function [key, rest] = splitKeyValue(line)
-0156 % Split "key: value" (or "key:") on the first top-level colon.
-0157 inSingle = false;
-0158 inDouble = false;
-0159 colonPos = 0;
-0160 for i = 1:numel(line)
-0161     c = line(i);
-0162     if c == '''' && ~inDouble
-0163         inSingle = ~inSingle;
-0164     elseif c == '"' && ~inSingle
-0165         inDouble = ~inDouble;
-0166     elseif c == ':' && ~inSingle && ~inDouble
-0167         colonPos = i;
-0168         break
-0169     end
-0170 end
-0171 if colonPos == 0
-0172     error('parseYAML:invalidLine', 'Expected "key: value" or "key:", got: %s', line);
-0173 end
-0174 key = strtrim(line(1:colonPos-1));
-0175 key = strrep(strrep(key, '"', ''), '''', '');
-0176 rest = strtrim(line(colonPos+1:end));
-0177 end
-0178 
-0179 
-0180 function value = parseScalarOrFlow(s)
-0181 s = strtrim(s);
-0182 if ~isempty(s) && s(1) == '{'
-0183     value = parseFlowMap(s);
-0184 elseif ~isempty(s) && s(1) == '['
-0185     value = parseFlowSeq(s);
-0186 else
-0187     value = parseScalar(s);
-0188 end
-0189 end
-0190 
-0191 
-0192 function value = parseFlowMap(s)
-0193 inner = strtrim(s(2:end-1));
-0194 value = struct();
-0195 if isempty(inner)
-0196     return
-0197 end
-0198 parts = splitTopLevel(inner, ',');
-0199 for i = 1:numel(parts)
-0200     [key, rest] = splitKeyValue(parts{i});
-0201     value.(matlabFieldName(key)) = parseScalarOrFlow(rest);
-0202 end
-0203 end
-0204 
-0205 
-0206 function value = parseFlowSeq(s)
-0207 inner = strtrim(s(2:end-1));
-0208 if isempty(inner)
-0209     value = cell(0,1);
-0210     return
-0211 end
-0212 parts = splitTopLevel(inner, ',');
-0213 value = cell(numel(parts), 1);
-0214 for i = 1:numel(parts)
-0215     value{i} = parseScalarOrFlow(parts{i});
-0216 end
-0217 end
-0218 
-0219 
-0220 function parts = splitTopLevel(s, delim)
-0221 % Split s on delim, ignoring delim inside quotes or nested {}/[].
-0222 parts = {};
-0223 depth = 0;
-0224 inSingle = false;
-0225 inDouble = false;
-0226 last = 1;
-0227 for i = 1:numel(s)
-0228     c = s(i);
-0229     if c == '''' && ~inDouble
-0230         inSingle = ~inSingle;
-0231     elseif c == '"' && ~inSingle
-0232         inDouble = ~inDouble;
-0233     elseif ~inSingle && ~inDouble
-0234         if c == '{' || c == '['
-0235             depth = depth + 1;
-0236         elseif c == '}' || c == ']'
-0237             depth = depth - 1;
-0238         elseif c == delim && depth == 0
-0239             parts{end+1} = s(last:i-1); %#ok<AGROW>
-0240             last = i + 1;
-0241         end
-0242     end
-0243 end
-0244 parts{end+1} = s(last:end); %#ok<AGROW>
-0245 parts = strtrim(parts);
-0246 end
-0247 
-0248 
-0249 function value = parseScalar(s)
-0250 if isempty(s) || strcmp(s, '~') || strcmpi(s, 'null')
-0251     value = [];
-0252     return
-0253 end
-0254 if numel(s) >= 2 && ((s(1) == '"' && s(end) == '"') || (s(1) == '''' && s(end) == ''''))
-0255     value = s(2:end-1);
-0256     return
-0257 end
-0258 if strcmpi(s, 'true')
-0259     value = true;
-0260     return
-0261 end
-0262 if strcmpi(s, 'false')
-0263     value = false;
-0264     return
-0265 end
-0266 if ~isempty(regexp(s, '^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$', 'once'))
-0267     value = str2double(s);
-0268     return
-0269 end
-0270 value = s;
-0271 end
-0272 
-0273 
-0274 function name = matlabFieldName(key)
-0275 % Sanitise a YAML key into a valid MATLAB field name. Replaces non-
-0276 % alphanumeric characters with underscores; prefixes a digit-starting
-0277 % key with 'f_'.
-0278 name = regexprep(key, '[^A-Za-z0-9_]', '_');
-0279 if isempty(name) || ~isstrprop(name(1), 'alpha')
-0280     name = ['f_' name];
-0281 end
-0282 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/io/readYAMLmodel.html b/doc/io/readYAMLmodel.html deleted file mode 100644 index a858c2cc..00000000 --- a/doc/io/readYAMLmodel.html +++ /dev/null @@ -1,958 +0,0 @@ - - - - Description of readYAMLmodel - - - - - - - - - -
Home > io > readYAMLmodel.m
- - - -

readYAMLmodel -

- -

PURPOSE ^

-
readYAMLmodel Read a model structure from a YAML file.
- -

SYNOPSIS ^

-
function model=readYAMLmodel(varargin)
- -

DESCRIPTION ^

-
 readYAMLmodel  Read a model structure from a YAML file.
-
- Reads a yaml file matching (roughly) the standard cobra-format model
- yaml structure.
-
- Name-Value Arguments
- --------------------
- fileName : char
-     a model file in yaml file format. A dialog window will open if no
-     file name is specified.
- verbose : logical
-     set as true to monitor progress (default false).
-
- Returns
- -------
- model : struct
-     a model structure.
-
- Examples
- --------
-     model = readYAMLmodel(fileName, verbose);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function model=readYAMLmodel(varargin)
-0002 % readYAMLmodel  Read a model structure from a YAML file.
-0003 %
-0004 % Reads a yaml file matching (roughly) the standard cobra-format model
-0005 % yaml structure.
-0006 %
-0007 % Name-Value Arguments
-0008 % --------------------
-0009 % fileName : char
-0010 %     a model file in yaml file format. A dialog window will open if no
-0011 %     file name is specified.
-0012 % verbose : logical
-0013 %     set as true to monitor progress (default false).
-0014 %
-0015 % Returns
-0016 % -------
-0017 % model : struct
-0018 %     a model structure.
-0019 %
-0020 % Examples
-0021 % --------
-0022 %     model = readYAMLmodel(fileName, verbose);
-0023 p=parseRAVENargs(varargin, {'fileName',[]; 'verbose',false});
-0024 fileName=p.fileName; verbose=p.verbose;
-0025 if isempty(fileName)
-0026     [fileName, pathName] = uigetfile({'*.yml;*.yaml'}, 'Please select the model file');
-0027     if fileName == 0
-0028         error('You should select a model file')
-0029     else
-0030         fileName = fullfile(pathName,fileName);
-0031     end
-0032 end
-0033 
-0034 if ~isfile(fileName)
-0035     error('Yaml file %s cannot be found', string(fileName));
-0036 end
-0037 
-0038 if verLessThan('matlab','9.9') %readlines introduced 2020b
-0039     fid=fopen(fileName);
-0040     line_raw=cell(1000000,1);
-0041     i=1;
-0042     while ~feof(fid)
-0043         line_raw{i}=fgetl(fid);
-0044         i=i+1;
-0045     end
-0046     line_raw(i:end)=[];
-0047     line_raw=string(line_raw);
-0048     fclose(fid);
-0049 else
-0050     line_raw=readlines(fileName);
-0051 end
-0052 % If entry is broken over multiple lines (a folded scalar --- some YAML
-0053 % writers wrap a plain or single-quoted scalar that would exceed a
-0054 % configured width), concatenate the continuation onto the previous line.
-0055 % Assumes at
-0056 % least 6 leading spaces to avoid metaData being concatenated.
-0057 %
-0058 % A continuation line is recognised by NOT starting with '- ': every real
-0059 % entry in this format, at every nesting depth (including a nested list
-0060 % under a key, e.g. an annotation sub-entry indented 6+ spaces), is itself
-0061 % introduced with a literal leading dash, while a folded scalar's
-0062 % continuation is raw text with no list-item marker of its own.
-0063 newLine=regexp(line_raw,'^ {6,}([^-\s].*)','tokenExtents');
-0064 brokenLine=find(~cellfun('isempty',newLine));
-0065 for i=flip(1:numel(brokenLine))
-0066     extraLine = char(line_raw(brokenLine(i)));
-0067     extraLine = extraLine(newLine{brokenLine(i)}{1}(1):end);
-0068     % deblank, not strtrim: only the trailing whitespace is spurious (e.g.
-0069     % "- inchis: " with nothing after the colon on this line); the leading
-0070     % indentation must survive untouched for the key-detection regex below.
-0071     line_raw{brokenLine(i)-1} = strjoin({deblank(line_raw{brokenLine(i)-1}),extraLine},' ');
-0072 end
-0073 line_raw(brokenLine)=[];
-0074 
-0075 line_key = regexprep(line_raw,'^ *-? ([^:]+)(:)($| .*)','$1');
-0076 line_key = regexprep(line_key,'(.*!!omap)|(---)|( {4,}.*)','');
-0077 
-0078 line_value = regexprep(line_raw, '.*:$','');
-0079 % Captures everything after "key: " verbatim, quotes and all; any wrapping
-0080 % quotes are left in place here and removed next by the anchored-pair
-0081 % strip below, which handles both quote styles.
-0082 line_value = regexprep(line_value, '[^":]+: (.+)$','$1');
-0083 % The list-marker strip must run before quote detection below, not after:
-0084 % a quoted *list item* (e.g. a pubmed id under an annotation block,
-0085 % `        - "10714900"`) still has its leading "- " attached at this
-0086 % point, so the anchored ^".*"$/^'.*'$ checks --- which require the quote
-0087 % to be the very first character --- would never match it, leaving the
-0088 % quotes on the value uninspected and unescaped. Stripping the marker
-0089 % first means a quoted list item's value starts directly with the quote
-0090 % character, the same shape as a quoted `key: "value"` scalar, so the
-0091 % existing anchored logic below already handles both without further
-0092 % changes.
-0093 line_value = regexprep(line_value, '^ {4,}- ','');
-0094 % Strips wrapping quotes, either style, but only a matching pair anchored
-0095 % at both ends of the value --- not every quote character in it. Some
-0096 % writers single-quote anything YAML requires quoting (a value that would
-0097 % otherwise parse as a date/number/boolean, or one containing a character
-0098 % with special meaning), where RAVEN's own writer only ever double-quotes,
-0099 % so both styles must be handled here. Anchoring (rather than a global
-0100 % strip) also preserves a genuine apostrophe in an unquoted value, e.g.
-0101 % "yeast's hexokinase reaction" --- only the wrapping pair is a delimiter,
-0102 % not every quote-like character.
-0103 wasSingleQuoted = ~cellfun('isempty', regexp(line_value, '^''.*''$', 'once'));
-0104 wasDoubleQuoted = ~cellfun('isempty', regexp(line_value, '^".*"$', 'once'));
-0105 line_value = regexprep(line_value, '^(["''])(.*)\1$','$2');
-0106 % Undo each quote style's own escaping so the reader recovers the exact
-0107 % original text: single-quoted YAML doubles a literal apostrophe ('' -> ');
-0108 % double-quoted YAML backslash-escapes a literal backslash and double quote
-0109 % (\\ -> \, \" -> "). The backslash pass goes through a sentinel character
-0110 % first so an escaped backslash immediately followed by an escaped quote
-0111 % (\\\" in the file, a literal \" in the value) round-trips correctly
-0112 % instead of the second pass misreading the first's output.
-0113 line_value(wasSingleQuoted) = strrep(line_value(wasSingleQuoted), '''''', '''');
-0114 line_value(wasDoubleQuoted) = strrep(line_value(wasDoubleQuoted), '\\', char(1));
-0115 line_value(wasDoubleQuoted) = strrep(line_value(wasDoubleQuoted), '\"', '"');
-0116 line_value(wasDoubleQuoted) = strrep(line_value(wasDoubleQuoted), char(1), '\');
-0117 line_value(strcmp(line_value,'''''')) = {''};
-0118 line_value(strcmp(line_value,line_raw)) = {''};
-0119 
-0120 modelFields =   {'id',char();...
-0121                'name',char();...
-0122         'description',char();...
-0123             'version',char();...
-0124                'date',char();...
-0125          'annotation',struct();...
-0126                'rxns',{};...
-0127            'rxnNames',{};...
-0128                'mets',{};...
-0129            'metNames',{};...
-0130                   'S',sparse([]);...
-0131                  'lb',{};... %Changed to double in the end.
-0132                  'ub',{};... %Changed to double in the end.
-0133                 'rev',{};... %Changed to double in the end.
-0134                   'c',[];...
-0135                   'b',cell(0,0);... %Changed to double in the end.
-0136               'genes',cell(0,0);...
-0137             'grRules',cell(0,0);...
-0138          'rxnGeneMat',sparse([]);...
-0139            'rxnComps',cell(0,0);... %Changed to double in the end.
-0140          'subSystems',cell(0,0);...
-0141             'eccodes',cell(0,0);...
-0142          'rxnMiriams',cell(0,0);...
-0143           'rxnDeltaG',{};... %Changed to double in the end.
-0144            'rxnNotes',cell(0,0);...
-0145       'rxnReferences',cell(0,0);...
-0146 'rxnConfidenceScores',cell(0,0);...
-0147            'metComps',cell(0,0);... %Changed to double in the end.
-0148              'inchis',cell(0,0);...
-0149           'metSmiles',cell(0,0);...
-0150         'metFormulas',cell(0,0);...
-0151          'metMiriams',cell(0,0);...
-0152           'metDeltaG',{};... %Changed to double in the end.
-0153          'metCharges',cell(0,0);... %Changed to double in the end.
-0154            'metNotes',cell(0,0);...
-0155               'comps',cell(0,0);...
-0156           'compNames',cell(0,0);...
-0157         'compOutside',cell(0,0);...
-0158         'compMiriams',cell(0,0);...
-0159           'geneComps',cell(0,0);... %Changed to double in the end.
-0160         'geneMiriams',cell(0,0);...
-0161      'geneShortNames',cell(0,0);...
-0162        'proteins',cell(0,0);...
-0163       'unconstrained',cell(0,0);... %Changed to double in the end.
-0164             'metFrom',cell(0,0);...
-0165             'rxnFrom',cell(0,0)};
-0166 for i=1:size(modelFields,1)
-0167     model.(modelFields{i,1})=modelFields{i,2};
-0168 end
-0169 
-0170 % If GECKO model — accept both the legacy `geckoLight` (inside metaData)
-0171 % and the alternate top-level `gecko_light` key.
-0172 if any(contains(line_key,'geckoLight')) || any(contains(line_key,'gecko_light'))
-0173     isGECKO=true;
-0174     ecFields = {'geckoLight', false;...
-0175                       'rxns', {};...
-0176                       'kcat', {};...
-0177                     'source', cell(0,0);...
-0178                      'notes', cell(0,0);...
-0179                    'eccodes', cell(0,0);...
-0180                      'genes', cell(0,0);...
-0181                    'enzymes', cell(0,0);...
-0182                         'mw', cell(0,0);...
-0183                   'sequence', cell(0,0);...
-0184                      'concs', cell(0,0);...
-0185                  'rxnEnzMat', []};
-0186     for i=1:size(ecFields,1)
-0187         model.ec.(ecFields{i,1})=ecFields{i,2};
-0188     end
-0189     ecGecko=cell(25000,2);      ecGeckoNo=1;
-0190     enzStoich=cell(100000,3);   enzStoichNo=1;
-0191 else
-0192     isGECKO=false;
-0193 end
-0194 
-0195 section = 0;
-0196 metMiriams=cell(100000,3);   metMirNo=1;
-0197 rxnMiriams=cell(100000,3);   rxnMirNo=1;
-0198 geneMiriams=cell(100000,3);  genMirNo=1;
-0199 compMiriams=cell(1000,3);    compMirNo=1;
-0200 subSystems=cell(100000,2);   subSysNo=1;
-0201 eccodes=cell(100000,2);      ecCodeNo=1;
-0202 equations=cell(100000,3);    equatiNo=1;
-0203 
-0204 for i=1:numel(line_key)
-0205     tline_raw = line_raw{i};
-0206     tline_key = line_key{i};
-0207     tline_value = line_value{i};
-0208     % import different sections — accept the !!omap-tagged variant as
-0209     % well, so a file using that tag is recognized too.
-0210     switch tline_raw
-0211         case {'- metaData:', '- metaData: !!omap'}
-0212             section = 1;
-0213             if verbose
-0214                 fprintf('\t%d\n', section);
-0215             end
-0216             continue % Go to next line
-0217         case '- metabolites:'
-0218             section = 2;
-0219             if verbose
-0220                 fprintf('\t%d\n', section);
-0221             end
-0222             pos=0;
-0223             continue
-0224         case '- reactions:'
-0225             section = 3;
-0226             if verbose
-0227                 fprintf('\t%d\n', section);
-0228             end
-0229             pos=0;
-0230             continue
-0231         case '- genes:'
-0232             section = 4;
-0233             if verbose
-0234                 fprintf('\t%d\n', section);
-0235             end
-0236             pos=0;
-0237             continue
-0238         case '- genes: []'
-0239             % Flow-style empty list: a gene-less model. No entries follow,
-0240             % so there is nothing to switch into --- the next recognised
-0241             % header is matched fresh on the next iteration regardless of
-0242             % what `section` is left as.
-0243             continue
-0244         case '- compartments: !!omap'
-0245             section = 5;
-0246             if verbose
-0247                 fprintf('\t%d\n', section);
-0248             end
-0249             pos=0;
-0250             continue
-0251         case '- ec-rxns:'
-0252             section = 6;
-0253             if verbose
-0254                 fprintf('\t%d\n', section);
-0255             end
-0256             pos=0;
-0257             continue
-0258         case '- ec-enzymes:'
-0259             section = 7;
-0260             if verbose
-0261                 fprintf('\t%d\n', section);
-0262             end
-0263             pos=0;
-0264             continue
-0265     end
-0266 
-0267     % Alternate root-level keys (id, name, version, gecko_light), written
-0268     % at the top level with no metaData section. RAVEN's own writer
-0269     % normally puts them inside metaData, but we accept both layouts so a
-0270     % YAML file using the top-level convention can be ingested directly.
-0271     if isempty(regexp(tline_raw, '^ {2,}', 'once'))
-0272         switch tline_key
-0273             case 'id'
-0274                 if isempty(model.id), model.id = tline_value; end
-0275                 continue
-0276             case 'name'
-0277                 if isempty(model.name), model.name = tline_value; end
-0278                 continue
-0279             case 'version'
-0280                 if ~isfield(model,'version') || isempty(model.version)
-0281                     model.version = tline_value;
-0282                 end
-0283                 continue
-0284             case 'gecko_light'
-0285                 if isGECKO && strcmp(tline_value,'true')
-0286                     model.ec.geckoLight = true;
-0287                 end
-0288                 continue
-0289         end
-0290     end
-0291 
-0292     % skip over empty keys
-0293     if isempty(tline_raw) || (isempty(tline_key) && contains(tline_raw,'!!omap'))
-0294         continue;
-0295     end
-0296     
-0297     % import metaData
-0298     if section == 1
-0299         switch tline_key
-0300             case {'short_name','id'} %short_name used by human-GEM
-0301                 model.id = tline_value;
-0302             case 'name'
-0303                 model.name = tline_value;                
-0304             case 'full_name' %used by human-GEM
-0305                 model.description = tline_value;
-0306             case 'version'
-0307                 model.version = tline_value;
-0308             case 'date'
-0309                 model.date = tline_value;                
-0310             case 'taxonomy'
-0311                 model.annotation.taxonomy = tline_value;
-0312             case {'description','note'} %description used by human-GEM
-0313                 model.annotation.note = tline_value;
-0314             case 'github'
-0315                 model.annotation.sourceUrl = tline_value;
-0316             case 'sourceUrl'
-0317                 model.annotation.sourceUrl = tline_value;                
-0318             case 'givenName'
-0319                 model.annotation.givenName = tline_value;
-0320             case 'familyName'
-0321                 model.annotation.familyName = tline_value;
-0322             case 'authors'
-0323                 model.annotation.authorList = tline_value;
-0324             case 'email'
-0325                 model.annotation.email = tline_value;
-0326             case 'organization'
-0327                 model.annotation.organization = tline_value;
-0328             case {'geckoLight','gecko_light'}
-0329                 if strcmp(tline_value,'true')
-0330                     model.ec.geckoLight = true;
-0331                 end
-0332             otherwise
-0333                 % Caller-specific provenance key with no RAVEN-defined slot
-0334                 % (e.g. a tool-specific `_version` key): preserve it instead
-0335                 % of silently dropping it, so a write/read round trip is
-0336                 % lossless. Mirrors writeYAMLmodel.m's symmetric fallback,
-0337                 % which emits any model.annotation field not in its own
-0338                 % fixed list.
-0339                 if isvarname(tline_key)
-0340                     model.annotation.(tline_key) = tline_value;
-0341                 end
-0342         end; continue
-0343     end
-0344 
-0345     % import metabolites:
-0346     if section == 2
-0347         switch tline_key
-0348             case 'id'
-0349                 pos = pos + 1;
-0350                 model = readFieldValue(model, 'mets', tline_value,pos);
-0351                 readList=''; miriamKey='';
-0352             case 'name'
-0353                 model = readFieldValue(model, 'metNames', tline_value, pos);
-0354                 readList=''; miriamKey='';
-0355             case 'compartment'
-0356                 model = readFieldValue(model, 'metComps', tline_value, pos);
-0357                 readList=''; miriamKey='';
-0358             case 'formula'
-0359                 model = readFieldValue(model, 'metFormulas', tline_value, pos);
-0360                 readList=''; miriamKey='';
-0361             case 'charge'
-0362                 model = readFieldValue(model, 'metCharges', tline_value, pos);
-0363                 readList=''; miriamKey='';
-0364             case 'notes'
-0365                 model = readFieldValue(model, 'metNotes', tline_value, pos);
-0366                 readList=''; miriamKey='';
-0367             case 'inchis'
-0368                 model = readFieldValue(model, 'inchis', tline_value, pos);
-0369                 readList=''; miriamKey='';
-0370             case 'smiles'
-0371                 % Top-level (legacy MATLAB) and inside-annotation (current
-0372                 % writer) layouts both land here. The current writer
-0373                 % always emits smiles as a block list, even for a single
-0374                 % value; a state distinct from 'annotation'
-0375                 % ('smilesInAnnotation') reads that list so that once it
-0376                 % ends this can resume annotation gathering rather than
-0377                 % getting stuck treating every later line in the same
-0378                 % annotation block as another smiles entry. A single-line
-0379                 % "smiles: value" (legacy, or empty tline_value absent)
-0380                 % doesn't reset readList — preserves the annotation
-0381                 % gathering state if SMILES is nested inside the
-0382                 % annotation block alongside other entries.
-0383                 if isempty(tline_value)
-0384                     readList = 'smilesInAnnotation';
-0385                 else
-0386                     model = readFieldValue(model, 'metSmiles', tline_value, pos);
-0387                 end
-0388             case 'deltaG'
-0389                 model = readFieldValue(model, 'metDeltaG', tline_value, pos);
-0390                 readList=''; miriamKey='';
-0391             case 'metFrom'
-0392                 model = readFieldValue(model, 'metFrom', tline_value, pos);
-0393                 readList=''; miriamKey='';
-0394             case 'annotation'
-0395                 readList = 'annotation';
-0396             otherwise
-0397                 switch readList
-0398                     case 'annotation'
-0399                         [metMiriams, miriamKey, metMirNo] = gatherAnnotation(pos,metMiriams,tline_key,tline_value,miriamKey,metMirNo);
-0400                     case 'smilesInAnnotation'
-0401                         % A bare list item ("- OC(=O)...") has no colon,
-0402                         % so tline_key is empty --- still inside the list.
-0403                         % Anything else is the annotation block's next
-0404                         % entry, not another smiles value: the list has
-0405                         % ended (RAVEN keeps only one smiles per
-0406                         % metabolite, so a second list item, if any,
-0407                         % simply overwrites the first).
-0408                         if isempty(tline_key)
-0409                             model = readFieldValue(model, 'metSmiles', regexprep(tline_value,'^ +- "?(.*)"?$','$1'), pos);
-0410                         else
-0411                             readList = 'annotation';
-0412                             [metMiriams, miriamKey, metMirNo] = gatherAnnotation(pos,metMiriams,tline_key,tline_value,miriamKey,metMirNo);
-0413                         end
-0414                     otherwise
-0415                         error(['Unknown entry in yaml file: ' tline_raw])
-0416                 end
-0417         end; continue
-0418     end
-0419 
-0420     % import reactions:
-0421     if section == 3
-0422         switch tline_key
-0423             case 'id'
-0424                 pos = pos + 1;
-0425                 model = readFieldValue(model, 'rxns', tline_value,pos);
-0426                 readList=''; miriamKey='';
-0427             case 'name'
-0428                 model = readFieldValue(model, 'rxnNames', tline_value, pos);
-0429                 readList=''; miriamKey='';
-0430             case 'lower_bound'
-0431                 model.lb(pos,1) = {tline_value};
-0432                 readList=''; miriamKey='';
-0433             case 'upper_bound'
-0434                 model.ub(pos,1) = {tline_value};
-0435                 readList=''; miriamKey='';
-0436             case 'rev'
-0437                 model.rev(pos,1) = {tline_value};
-0438                 readList=''; miriamKey='';
-0439             case 'gene_reaction_rule'
-0440                 model = readFieldValue(model, 'grRules', tline_value, pos);
-0441                 readList=''; miriamKey='';
-0442             case {'notes','rxnNotes'}
-0443                 % `notes` is the canonical reaction-side key; `rxnNotes`
-0444                 % is the legacy RAVEN MATLAB key, kept here for
-0445                 % backward-compatible reads.
-0446                 model = readFieldValue(model, 'rxnNotes', tline_value, pos);
-0447                 readList=''; miriamKey='';
-0448             case 'rxnFrom'
-0449                 model = readFieldValue(model, 'rxnFrom', tline_value, pos);
-0450                 readList=''; miriamKey='';
-0451             case 'deltaG'
-0452                 model = readFieldValue(model, 'rxnDeltaG', tline_value, pos);
-0453                 readList=''; miriamKey='';                
-0454             case 'objective_coefficient'
-0455                 model.c(pos,1) = str2double(tline_value);
-0456                 readList=''; miriamKey='';
-0457             case 'references'
-0458                 model = readFieldValue(model, 'rxnReferences', tline_value, pos);
-0459                 readList=''; miriamKey='';
-0460             case 'confidence_score'
-0461                 model = readFieldValue(model, 'rxnConfidenceScores', tline_value, pos);
-0462                 readList=''; miriamKey='';
-0463             case 'eccodes'
-0464                 if isempty(tline_value)
-0465                     readList = 'eccodes';
-0466                 else
-0467                     eccodes(ecCodeNo,1:2)={pos,tline_value};
-0468                     ecCodeNo=ecCodeNo+1;
-0469                 end
-0470             case 'ec-code'
-0471                 % EC numbers emitted inside the `annotation` block
-0472                 % (current writer). Captured into model.eccodes the same
-0473                 % way metabolite `smiles` is pulled out of the annotation
-0474                 % block; the legacy top-level `eccodes` key above is still
-0475                 % accepted for older files. A block list of codes follows
-0476                 % when the value is empty, read via a state distinct from
-0477                 % the legacy 'eccodes' above ('eccodesInAnnotation', not
-0478                 % 'eccodes') specifically so that once the list ends this
-0479                 % can resume annotation gathering rather than getting
-0480                 % stuck treating every later line in the same annotation
-0481                 % block as another EC code.
-0482                 if isempty(tline_value)
-0483                     readList = 'eccodesInAnnotation';
-0484                 else
-0485                     eccodes(ecCodeNo,1:2)={pos,tline_value};
-0486                     ecCodeNo=ecCodeNo+1;
-0487                 end
-0488             case 'subsystem'
-0489                 if isempty(tline_value)
-0490                     readList = 'subsystem';
-0491                 else
-0492                     subSystems(subSysNo,1:2)={pos,tline_value};
-0493                     subSysNo=subSysNo+1;
-0494                 end
-0495             case 'metabolites'
-0496                 readList = 'equation';
-0497             case 'annotation'
-0498                 readList = 'annotation';
-0499                 
-0500             otherwise
-0501                 switch readList
-0502                     case 'eccodes'
-0503                         eccodes(ecCodeNo,1:2)={pos,regexprep(tline_value,'^ +- "?(.*)"?$','$1')};
-0504                         ecCodeNo=ecCodeNo+1;
-0505                     case 'eccodesInAnnotation'
-0506                         % A bare list item ("- 1.1.2.4") has no colon, so
-0507                         % tline_key is empty --- still inside the list.
-0508                         % Anything else is the annotation block's next
-0509                         % entry (e.g. "kegg.reaction: ..."), not another
-0510                         % EC code: the list has ended, so resume normal
-0511                         % annotation gathering for this line and all that
-0512                         % follow, instead of silently absorbing them here.
-0513                         if isempty(tline_key)
-0514                             eccodes(ecCodeNo,1:2)={pos,regexprep(tline_value,'^ +- "?(.*)"?$','$1')};
-0515                             ecCodeNo=ecCodeNo+1;
-0516                         else
-0517                             readList = 'annotation';
-0518                             [rxnMiriams, miriamKey,rxnMirNo] = gatherAnnotation(pos,rxnMiriams,tline_key,tline_value,miriamKey,rxnMirNo);
-0519                         end
-0520                     case 'subsystem'
-0521                         subSystems(subSysNo,1:2)={pos,regexprep(tline_value,'^ +- "?(.*)"?$','$1')};
-0522                         subSysNo=subSysNo+1;
-0523                     case 'annotation'
-0524                         [rxnMiriams, miriamKey,rxnMirNo] = gatherAnnotation(pos,rxnMiriams,tline_key,tline_value,miriamKey,rxnMirNo);
-0525                     case 'equation'
-0526                         coeff = sscanf(tline_value,'%f');
-0527                         equations(equatiNo,1:3)={pos,tline_key,coeff};
-0528                         equatiNo=equatiNo+1;
-0529                     otherwise
-0530                         error(['Unknown entry in yaml file: ' tline_raw])
-0531                 end
-0532         end; continue
-0533     end
-0534 
-0535     % import genes:
-0536     if section == 4
-0537         switch tline_key
-0538             case 'id'
-0539                 pos = pos + 1;
-0540                 model = readFieldValue(model, 'genes', tline_value, pos);
-0541                 readList = '';
-0542                 miriamKey = '';
-0543             case 'name'
-0544                 model = readFieldValue(model, 'geneShortNames', tline_value, pos);
-0545             case 'protein'
-0546                 model = readFieldValue(model, 'proteins', tline_value, pos);
-0547             case 'annotation'
-0548                 readList = 'annotation';
-0549             otherwise
-0550                 switch readList
-0551                     case 'annotation'
-0552                         [geneMiriams, miriamKey,genMirNo] = gatherAnnotation(pos,geneMiriams,tline_key,tline_value,miriamKey,genMirNo);
-0553                     otherwise
-0554                         error(['Unknown entry in yaml file: ' tline_raw])
-0555                 end
-0556         end; continue
-0557     end
-0558 
-0559     % import compartments:
-0560     if section == 5
-0561         switch tline_key
-0562             case 'annotation'
-0563                 readList = 'annotation';
-0564             otherwise
-0565                 % Annotation entries are indented deeper than the
-0566                 % compartment they belong to, so anything at compartment
-0567                 % level ends the annotation and starts a new compartment.
-0568                 if strcmp(readList,'annotation') && ~isempty(regexp(tline_raw,'^ {6,}','once'))
-0569                     [compMiriams, miriamKey, compMirNo] = gatherAnnotation(pos,compMiriams,tline_key,tline_value,miriamKey,compMirNo);
-0570                 else
-0571                     pos = pos + 1;
-0572                     model.comps(end+1,1) = {tline_key};
-0573                     model.compNames(end+1,1) = {tline_value};
-0574                     readList = ''; miriamKey = '';
-0575                 end
-0576         end; continue
-0577     end
-0578 
-0579     % import ec reaction info
-0580     if section == 6
-0581         switch tline_key
-0582             case 'id'
-0583                 pos = pos + 1;
-0584                 model.ec = readFieldValue(model.ec, 'rxns', tline_value, pos);
-0585                 readList='';
-0586             case 'kcat'
-0587                 model.ec = readFieldValue(model.ec, 'kcat', tline_value, pos);
-0588                 readList='';
-0589             case 'source'
-0590                 model.ec = readFieldValue(model.ec, 'source', tline_value, pos);
-0591                 readList='';
-0592             case 'notes'
-0593                 model.ec = readFieldValue(model.ec, 'notes', tline_value, pos);
-0594                 readList='';
-0595             case 'eccodes'
-0596                 if isempty(tline_value)
-0597                     readList = 'eccodes';
-0598                 else
-0599                     ecGecko(ecGeckoNo,1:2)={pos,tline_value};
-0600                     ecGeckoNo=ecGeckoNo+1;
-0601                 end
-0602             case 'enzymes'
-0603                 readList = 'enzStoich';
-0604             otherwise
-0605                 switch readList
-0606                     case 'eccodes'
-0607                         ecGecko(ecGeckoNo,1:2)={pos,regexprep(tline_value,'^ +- "?(.*)"?$','$1')};
-0608                         ecGeckoNo=ecGeckoNo+1;
-0609                     case 'enzStoich'
-0610                         coeff = sscanf(tline_value,'%f');
-0611                         enzStoich(enzStoichNo,1:3)={pos,tline_key,coeff};
-0612                         enzStoichNo=enzStoichNo+1;
-0613                     otherwise
-0614                         error(['Unknown entry in yaml file: ' tline_raw])
-0615                 end
-0616         end; continue
-0617     end
-0618 
-0619     % import ec enzyme info
-0620     if section == 7
-0621         switch tline_key
-0622             case 'genes'
-0623                 pos = pos + 1;
-0624                 model.ec = readFieldValue(model.ec, 'genes', tline_value, pos);
-0625             case 'enzymes'
-0626                 model.ec = readFieldValue(model.ec, 'enzymes', tline_value, pos);
-0627             case 'mw'
-0628                 model.ec = readFieldValue(model.ec, 'mw', tline_value, pos);
-0629             case 'sequence'
-0630                 model.ec = readFieldValue(model.ec, 'sequence', tline_value, pos);
-0631             case 'concs'
-0632                 model.ec = readFieldValue(model.ec, 'concs', tline_value, pos);
-0633             otherwise
-0634                 error(['Unknown entry in yaml file: ' tline_raw])
-0635         end; continue
-0636     end
-0637 end
-0638 
-0639 %Parse annotations
-0640 if ~isempty(metMiriams)
-0641     locs = cell2mat(metMiriams(:,1));
-0642     for i=unique(locs)'
-0643         model.metMiriams{i,1}.name=metMiriams(locs==i,2);
-0644         model.metMiriams{i,1}.value=metMiriams(locs==i,3);
-0645     end
-0646 end
-0647 if ~isempty(rxnMiriams)
-0648     locs = cell2mat(rxnMiriams(:,1));
-0649     for i=unique(locs)'
-0650         model.rxnMiriams{i,1}.name=rxnMiriams(locs==i,2);
-0651         model.rxnMiriams{i,1}.value=rxnMiriams(locs==i,3);
-0652     end
-0653 end
-0654 if ~isempty(geneMiriams)
-0655     locs = cell2mat(geneMiriams(:,1));
-0656     for i=unique(locs)'
-0657         model.geneMiriams{i,1}.name=geneMiriams(locs==i,2);
-0658         model.geneMiriams{i,1}.value=geneMiriams(locs==i,3);
-0659     end
-0660 end
-0661 if ~isempty(compMiriams)
-0662     locs = cell2mat(compMiriams(:,1));
-0663     for i=unique(locs)'
-0664         model.compMiriams{i,1}.name=compMiriams(locs==i,2);
-0665         model.compMiriams{i,1}.value=compMiriams(locs==i,3);
-0666     end
-0667 end
-0668 
-0669 %Parse subSystems
-0670 if ~isempty(subSystems)
-0671     locs = cell2mat(subSystems(:,1));
-0672     for i=unique(locs)'
-0673         model.subSystems{i,1}=subSystems(locs==i,2);
-0674     end
-0675 end
-0676 
-0677 %Parse ec-codes
-0678 if ~isempty(eccodes)
-0679     locs = cell2mat(eccodes(:,1));
-0680     for i=unique(locs)'
-0681         eccodesCat=strjoin(eccodes(locs==i,2),';');
-0682         model.eccodes{i,1}=eccodesCat;
-0683     end
-0684     emptyEc=cellfun('isempty',model.eccodes);
-0685     model.eccodes(emptyEc)={''};
-0686 end
-0687 
-0688 % follow-up data processing
-0689 if verbose
-0690     fprintf('\nimporting completed\nfollow-up processing...');
-0691 end
-0692 [~, model.metComps] = ismember(model.metComps, model.comps);
-0693 [~, model.geneComps] = ismember(model.geneComps, model.comps);
-0694 [~, model.rxnComps] = ismember(model.rxnComps, model.comps);
-0695 % A metabolite with no explicit compartment is still ismember'd above (it
-0696 % parses as ''), so it resolves to 0 -- not the "assume first compartment"
-0697 % default the emptyOrFill call further down intends -- unless it happens
-0698 % to be the trailing metabolite in the file, the only case emptyOrFill
-0699 % actually reaches (it only extends a too-short array, and ismember has
-0700 % already made this one full-length). Catch every 0 here so the default
-0701 % applies regardless of position, not just at the end of the list.
-0702 model.metComps(model.metComps==0) = 1;
-0703 
-0704 % Fill S-matrix
-0705 rxnIdx = cellfun('isempty', equations(:,1));
-0706 equations(rxnIdx,:) = '';
-0707 rxnIdx = cell2mat(equations(:,1));
-0708 [~,metIdx] = ismember(equations(:,2),model.mets);
-0709 coeffs = cell2mat(equations(:,3));
-0710 model.S=sparse(max(metIdx),max(rxnIdx));
-0711 linearIndices = sub2ind([max(metIdx), max(rxnIdx)],metIdx,rxnIdx);
-0712 model.S(linearIndices) = coeffs;
-0713 
-0714 % Convert strings to numeric
-0715 model.metCharges = str2double(model.metCharges);
-0716 model.lb = str2double(model.lb);
-0717 model.ub = str2double(model.ub);
-0718 model.rxnConfidenceScores = str2double(model.rxnConfidenceScores);
-0719 model.b = zeros(length(model.mets),1);
-0720 model.metDeltaG = str2double(model.metDeltaG);
-0721 model.rxnDeltaG = str2double(model.rxnDeltaG);
-0722 
-0723 % Fill some other fields
-0724 model.annotation.defaultLB = min(model.lb);
-0725 model.annotation.defaultUB = max(model.ub);
-0726 if numel(model.lb)<numel(model.rxns) %No LB reported = min
-0727     model.lb(end+1:numel(model.rxns)-numel(model.lb),1) = double(model.annotation.defaultLB);
-0728 end
-0729 if numel(model.ub)<numel(model.rxns) %No UB reported = max
-0730     model.ub(end+1:numel(model.rxns)-numel(model.ub),1) = double(model.annotation.defaultUB);
-0731 end
-0732 if ~all(cellfun('isempty',model.rev))
-0733     model.rev = str2double(model.rev);
-0734 else
-0735     model.rev = [];
-0736 end
-0737 if numel(model.rev)<numel(model.rxns) %No rev reported, assume from LB and UB
-0738     model.rev(end+1:numel(model.rxns)-numel(model.rev),1) = double(model.lb<0 & model.ub>0);
-0739 end
-0740 
-0741 % Remove empty fields, otherwise fill to correct length
-0742 % Reactions
-0743 for i={'rxnNames','grRules','eccodes','rxnNotes','rxnReferences',...
-0744        'rxnFrom','subSystems','rxnMiriams'} % Empty strings
-0745    model = emptyOrFill(model,i{1},{''},'rxns');
-0746 end
-0747 for i={'c'} % Zeros
-0748    model = emptyOrFill(model,i{1},0,'rxns',true);
-0749 end
-0750 for i={'rxnConfidenceScores','rxnDeltaG'} % NaNs
-0751    model = emptyOrFill(model,i{1},NaN,'rxns');
-0752 end
-0753 for i={'rxnComps'} % Ones, assume first compartment
-0754    model = emptyOrFill(model,i{1},1,'rxns');
-0755 end
-0756 % Metabolites
-0757 for i={'metNames','inchis','metFormulas','metMiriams','metFrom','metSmiles','metNotes'} % Empty strings
-0758    model = emptyOrFill(model,i{1},{''},'mets');
-0759 end
-0760 for i={'unconstrained'} % Zeros
-0761    model = emptyOrFill(model,i{1},0,'mets');
-0762 end
-0763 for i={'metCharges','metDeltaG'} % NaNs
-0764     model = emptyOrFill(model,i{1},NaN,'mets');
-0765  end
-0766 for i={'metComps'} % Ones, assume first compartment
-0767    model = emptyOrFill(model,i{1},1,'mets');
-0768 end
-0769 % Genes
-0770 for i={'geneMiriams','geneShortNames','proteins'} % Empty strings
-0771    model = emptyOrFill(model,i{1},{''},'genes');
-0772 end
-0773 for i={'geneComps'} % Ones, assume first compartment
-0774    model = emptyOrFill(model,i{1},1,'genes');
-0775 end
-0776 % Comps
-0777 for i={'compNames','compMiriams'} % Empty strings
-0778    model = emptyOrFill(model,i{1},{''},'comps');
-0779 end
-0780 for i={'compOutside'} % First comp
-0781    model = emptyOrFill(model,i{1},model.comps{1},'comps');
-0782 end
-0783 % Single fields are kept, even if empty
-0784 % for i={'description','name','version','date','annotation'}
-0785 %     if isempty(model.(i{1}))
-0786 %         model = rmfield(model,i{1});
-0787 %     end
-0788 % end
-0789 
-0790 if isfield(model,'subSystems')
-0791 % If all entries are 1x1, then flatten
-0792     if all(cellfun(@(x) numel(x) <= 1, model.subSystems))
-0793         model.subSystems = transpose([model.subSystems{:}]);
-0794     end
-0795 end
-0796 
-0797 % Make rxnGeneMat fields and map to the existing model.genes field
-0798 if isfield(model,'grRules')
-0799     [genes, rxnGeneMat] = getGenesFromGrRules(model.grRules);
-0800     model.rxnGeneMat = sparse(numel(model.rxns),numel(model.genes));
-0801     [~,geneOrder] = ismember(genes,model.genes);
-0802     if any(geneOrder == 0)
-0803         error(['The grRules includes the following gene(s), that are not in '...
-0804             'the list of model genes: ', genes{~geneOrder}])
-0805     end
-0806     model.rxnGeneMat(:,geneOrder) = rxnGeneMat;
-0807 else
-0808     % Gene-less model: keep rxnGeneMat dimensionally consistent with the
-0809     % number of reactions and genes, rather than leaving it empty.
-0810     model.rxnGeneMat = sparse(numel(model.rxns),numel(model.genes));
-0811 end
-0812 
-0813 % Finalize GECKO model
-0814 if isGECKO
-0815     % Fill in empty fields and empty entries
-0816     for i={'kcat','source','notes','eccodes'} % Even keep empty
-0817         model.ec = emptyOrFill(model.ec,i{1},{''},'rxns',true);
-0818     end
-0819     for i={'enzymes','mw','sequence'}
-0820         model.ec = emptyOrFill(model.ec,i{1},{''},'genes',true);
-0821     end
-0822     model.ec = emptyOrFill(model.ec,'concs',{'NaN'},'genes',true);
-0823     model.ec = emptyOrFill(model.ec,'kcat',{'0'},'genes',true);
-0824     % Change string to double
-0825     for i={'kcat','mw','concs'}
-0826         if isfield(model.ec,i{1})
-0827             model.ec.(i{1}) = str2double(model.ec.(i{1}));
-0828         end
-0829     end
-0830     % Fill rxnEnzMat
-0831     rxnIdx              = cellfun('isempty', enzStoich(:,1));
-0832     enzStoich(rxnIdx,:) = '';
-0833     rxnIdx              = cell2mat(enzStoich(:,1));
-0834     [~,enzIdx]          = ismember(enzStoich(:,2),model.ec.enzymes);
-0835     coeffs              = cell2mat(enzStoich(:,3));
-0836     model.ec.rxnEnzMat  = zeros(numel(model.ec.rxns), numel(model.ec.genes));
-0837     linearIndices       = sub2ind([numel(model.ec.rxns), numel(model.ec.genes)], rxnIdx, enzIdx);
-0838     model.ec.rxnEnzMat(linearIndices) = coeffs;
-0839     %Parse ec-codes
-0840     if ~isempty(ecGecko)
-0841         locs = cell2mat(ecGecko(:,1));
-0842         for i=unique(locs)'
-0843             ecGeckoCat=strjoin(ecGecko(locs==i,2),';');
-0844             model.ec.eccodes{i,1}=ecGeckoCat;
-0845         end
-0846         emptyEc=cellfun('isempty',model.ec.eccodes);
-0847         model.ec.eccodes(emptyEc)={''};
-0848     end
-0849 end
-0850 
-0851 if verbose
-0852     fprintf(' Done!\n');
-0853 end
-0854 end
-0855 
-0856 function model = emptyOrFill(model,field,emptyEntry,type,keepEmpty)
-0857 if nargin<5
-0858     keepEmpty=false;
-0859 end
-0860 if isnumeric(emptyEntry)
-0861     emptyCells=isempty(model.(field));
-0862 else
-0863     emptyCells=cellfun('isempty',model.(field));
-0864 end
-0865 if all(emptyCells) && ~keepEmpty
-0866     model = rmfield(model, field);
-0867 elseif numel(model.(field))<numel(model.(type))
-0868     model.(field)(end+1:numel(model.(type)),1)=emptyEntry;
-0869 end
-0870 end
-0871 
-0872 function model = readFieldValue(model, fieldName, value, pos)
-0873 if numel(model.(fieldName))<pos-1
-0874     model.(fieldName)(end+1:pos,1) = {''};
-0875 end
-0876 model.(fieldName)(pos,1) = {value};
-0877 end
-0878 
-0879 function [miriams,miriamKey,entryNumber] = gatherAnnotation(pos,miriams,key,value,miriamKey,entryNumber)
-0880 if isempty(key)
-0881     key=miriamKey;
-0882 else
-0883     miriamKey=key;
-0884 end
-0885 if ~isempty(value)
-0886     miriams(entryNumber,1:3) = {pos, key, strip(value)};
-0887     entryNumber = entryNumber + 1;
-0888 end
-0889 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/localization/parseScores.html b/doc/localization/parseScores.html deleted file mode 100644 index 410e8462..00000000 --- a/doc/localization/parseScores.html +++ /dev/null @@ -1,454 +0,0 @@ - - - - Description of parseScores - - - - - - - - - -
Home > localization > parseScores.m
- - - -

parseScores -

- -

PURPOSE ^

-
parseScores Parse predictor / database output into a gene scoring structure (GSS).
- -

SYNOPSIS ^

-
function GSS = parseScores(inputFile, varargin)
- -

DESCRIPTION ^

-
 parseScores  Parse predictor / database output into a gene scoring structure (GSS).
-
- Reads the output of a subcellular-localization predictor or evidence database and
- builds the gene scoring structure consumed by predictLocalization. Scores are
- normalized so the best score for each gene is 1.0.
-
- Parameters
- ----------
- inputFile : char
-     the file with the predictor / database output.
-
- Name-Value Arguments
- --------------------
- predictor : char
-     'deeploc' (DeepLoc 2 per-protein CSV, default), 'cello' (CELLO),
-     'mulocdeep' (MULocDeep wide table: gene id + one probability column per
-     compartment), 'compartments' (a COMPARTMENTS jensenlab.org channel TSV), or
-     'uniprot' (a UniProtKB TSV export with a "Subcellular location [CC]" column).
- compartmentMap : containers.Map
-     renames the predictor's / database's compartment labels to your model's
-     compartment ids. Keys are the labels as the predictor writes them, lower
-     case; values are the compartment ids you want in the GSS. See
-     Compartment mapping below. Optional for every predictor except 'uniprot',
-     where it also supplies the location vocabulary and defaults to
-     defaultCompartmentMap.
- idColumn : char or double
-     column (name or 1-based index) holding the gene id for 'mulocdeep' / 'uniprot'.
-     Default: the first column. For yeast-GEM, use the UniProt ordered-locus column.
- minConfidence : double
-     for 'compartments', drop annotations with confidence below this (default 0).
-
- Returns
- -------
- GSS : struct
-     a gene scoring structure for predictLocalization, with fields:
-
-     - genes : cell, one entry per gene.
-     - compartments : cell, one entry per compartment column.
-     - scores : double, numel(genes)-by-numel(compartments), normalized so
-       the best score per gene is 1.0.
-
- Compartment mapping
- -------------------
- Predictors emit their own compartment vocabulary ("Endoplasmic reticulum",
- "Golgi apparatus", ...), which rarely matches a model's compartment ids.
- compartmentMap is a containers.Map that translates them, and it is the only
- place that translation happens - the returned GSS already uses your ids.
- defaultCompartmentMap returns a worked example, tuned for yeast/fungal
- models; use it as-is, or as the template for your own. Three rules:
-
- - **A label not in the map is dropped**, together with its whole score
-   column. This is how you exclude compartments your model does not have
-   (defaultCompartmentMap has no 'plastid' key, so plastid scores never reach
-   a fungal model). Passing no map at all keeps every label untouched.
- - **Several labels may share one compartment id**, which merges their
-   columns. The merged score for a gene is the *maximum* over the merged
-   labels, not their sum, so a gene predicted 0.7 cytoplasm and 0.4 cytosol
-   scores 0.7 for 'c'. Use this for synonyms and for deliberately collapsing
-   distinct compartments your model does not separate (defaultCompartmentMap
-   sends both 'vacuole' and 'lysosome' to 'v'). Column order follows the first
-   appearance of each id.
- - **One label cannot be split across two compartment ids**: a containers.Map
-   holds one value per key, and the GSS is what predictLocalization scores
-   against, so a split has to be an explicit statement about the returned
-   scores rather than a mapping rule. Duplicate the column afterwards, e.g. to
-   let a gene predicted "golgi" compete for both 'g' and 'v':
-
-       GSS = parseScores(file, 'compartmentMap', map);
-       g = strcmp(GSS.compartments, 'g');
-       GSS.compartments{end+1} = 'v';
-       GSS.scores(:,end+1) = GSS.scores(:,g);
-
-   The two columns are then independent: predictLocalization assigns each gene
-   to exactly one compartment, so it picks whichever of 'g'/'v' keeps the
-   network better connected. Halve the copied column instead of duplicating it
-   if the evidence should count for less in the second compartment.
-
- For 'uniprot' the keys do double duty: they are also the terms searched for
- in the "Subcellular location [CC]" free text, matched whole-word and case
- insensitively, so they must be UniProt's own noun forms. A term that is not a
- key is never looked for.
-
- Examples
- --------
-     GSS = parseScores(file, 'predictor', 'deeploc');
-     GSS = parseScores(file, 'predictor', 'deeploc', 'compartmentMap', defaultCompartmentMap);
-     GSS = parseScores(file, 'predictor', 'uniprot', 'idColumn', 'Gene Names (ordered locus)');
-
- See also
- --------
- predictLocalization, getUniProtScores, defaultCompartmentMap
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • defaultCompartmentMap defaultCompartmentMap Default predictor/database compartment label -> model compartment id.
-This function is called by: -
    -
  • getUniProtScores getUniProtScores Fetch curated subcellular locations from UniProt -> gene scoring structure.
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function GSS = parseScores(inputFile, varargin)
-0002 % parseScores  Parse predictor / database output into a gene scoring structure (GSS).
-0003 %
-0004 % Reads the output of a subcellular-localization predictor or evidence database and
-0005 % builds the gene scoring structure consumed by predictLocalization. Scores are
-0006 % normalized so the best score for each gene is 1.0.
-0007 %
-0008 % Parameters
-0009 % ----------
-0010 % inputFile : char
-0011 %     the file with the predictor / database output.
-0012 %
-0013 % Name-Value Arguments
-0014 % --------------------
-0015 % predictor : char
-0016 %     'deeploc' (DeepLoc 2 per-protein CSV, default), 'cello' (CELLO),
-0017 %     'mulocdeep' (MULocDeep wide table: gene id + one probability column per
-0018 %     compartment), 'compartments' (a COMPARTMENTS jensenlab.org channel TSV), or
-0019 %     'uniprot' (a UniProtKB TSV export with a "Subcellular location [CC]" column).
-0020 % compartmentMap : containers.Map
-0021 %     renames the predictor's / database's compartment labels to your model's
-0022 %     compartment ids. Keys are the labels as the predictor writes them, lower
-0023 %     case; values are the compartment ids you want in the GSS. See
-0024 %     Compartment mapping below. Optional for every predictor except 'uniprot',
-0025 %     where it also supplies the location vocabulary and defaults to
-0026 %     defaultCompartmentMap.
-0027 % idColumn : char or double
-0028 %     column (name or 1-based index) holding the gene id for 'mulocdeep' / 'uniprot'.
-0029 %     Default: the first column. For yeast-GEM, use the UniProt ordered-locus column.
-0030 % minConfidence : double
-0031 %     for 'compartments', drop annotations with confidence below this (default 0).
-0032 %
-0033 % Returns
-0034 % -------
-0035 % GSS : struct
-0036 %     a gene scoring structure for predictLocalization, with fields:
-0037 %
-0038 %     - genes : cell, one entry per gene.
-0039 %     - compartments : cell, one entry per compartment column.
-0040 %     - scores : double, numel(genes)-by-numel(compartments), normalized so
-0041 %       the best score per gene is 1.0.
-0042 %
-0043 % Compartment mapping
-0044 % -------------------
-0045 % Predictors emit their own compartment vocabulary ("Endoplasmic reticulum",
-0046 % "Golgi apparatus", ...), which rarely matches a model's compartment ids.
-0047 % compartmentMap is a containers.Map that translates them, and it is the only
-0048 % place that translation happens - the returned GSS already uses your ids.
-0049 % defaultCompartmentMap returns a worked example, tuned for yeast/fungal
-0050 % models; use it as-is, or as the template for your own. Three rules:
-0051 %
-0052 % - **A label not in the map is dropped**, together with its whole score
-0053 %   column. This is how you exclude compartments your model does not have
-0054 %   (defaultCompartmentMap has no 'plastid' key, so plastid scores never reach
-0055 %   a fungal model). Passing no map at all keeps every label untouched.
-0056 % - **Several labels may share one compartment id**, which merges their
-0057 %   columns. The merged score for a gene is the *maximum* over the merged
-0058 %   labels, not their sum, so a gene predicted 0.7 cytoplasm and 0.4 cytosol
-0059 %   scores 0.7 for 'c'. Use this for synonyms and for deliberately collapsing
-0060 %   distinct compartments your model does not separate (defaultCompartmentMap
-0061 %   sends both 'vacuole' and 'lysosome' to 'v'). Column order follows the first
-0062 %   appearance of each id.
-0063 % - **One label cannot be split across two compartment ids**: a containers.Map
-0064 %   holds one value per key, and the GSS is what predictLocalization scores
-0065 %   against, so a split has to be an explicit statement about the returned
-0066 %   scores rather than a mapping rule. Duplicate the column afterwards, e.g. to
-0067 %   let a gene predicted "golgi" compete for both 'g' and 'v':
-0068 %
-0069 %       GSS = parseScores(file, 'compartmentMap', map);
-0070 %       g = strcmp(GSS.compartments, 'g');
-0071 %       GSS.compartments{end+1} = 'v';
-0072 %       GSS.scores(:,end+1) = GSS.scores(:,g);
-0073 %
-0074 %   The two columns are then independent: predictLocalization assigns each gene
-0075 %   to exactly one compartment, so it picks whichever of 'g'/'v' keeps the
-0076 %   network better connected. Halve the copied column instead of duplicating it
-0077 %   if the evidence should count for less in the second compartment.
-0078 %
-0079 % For 'uniprot' the keys do double duty: they are also the terms searched for
-0080 % in the "Subcellular location [CC]" free text, matched whole-word and case
-0081 % insensitively, so they must be UniProt's own noun forms. A term that is not a
-0082 % key is never looked for.
-0083 %
-0084 % Examples
-0085 % --------
-0086 %     GSS = parseScores(file, 'predictor', 'deeploc');
-0087 %     GSS = parseScores(file, 'predictor', 'deeploc', 'compartmentMap', defaultCompartmentMap);
-0088 %     GSS = parseScores(file, 'predictor', 'uniprot', 'idColumn', 'Gene Names (ordered locus)');
-0089 %
-0090 % See also
-0091 % --------
-0092 % predictLocalization, getUniProtScores, defaultCompartmentMap
-0093 
-0094 p = parseRAVENargs(varargin, {'predictor',[]; 'compartmentMap',[]; ...
-0095                               'idColumn',[]; 'minConfidence',0});
-0096 predictor = p.predictor;
-0097 if isempty(predictor); predictor = 'deeploc'; else; predictor = char(predictor); end
-0098 compartmentMap = p.compartmentMap;
-0099 
-0100 if ~isfile(inputFile)
-0101     error('RAVEN:badInput', 'Could not open file %s', char(string(inputFile)));
-0102 end
-0103 
-0104 switch lower(predictor)
-0105     case 'cello'
-0106         GSS = i_parseCello(inputFile);
-0107     case 'deeploc'
-0108         GSS = i_parseDeepLoc(inputFile);
-0109     case 'mulocdeep'
-0110         GSS = i_parseWide(inputFile, p.idColumn);
-0111     case 'compartments'
-0112         GSS = i_parseCompartments(inputFile, p.minConfidence);
-0113     case 'uniprot'
-0114         if isempty(compartmentMap); compartmentMap = defaultCompartmentMap(); end
-0115         GSS = i_parseUniProt(inputFile, p.idColumn, compartmentMap);
-0116         compartmentMap = [];   % already applied during parsing
-0117     case 'wolf'
-0118         error('RAVEN:badInput', ['WoLF PSORT support has been removed. Use a modern ' ...
-0119             'predictor (''deeploc'', ''mulocdeep''), the ''compartments'' database, or ' ...
-0120             '''uniprot'' (see getUniProtScores).']);
-0121     otherwise
-0122         error('RAVEN:badInput', 'Unknown predictor "%s"', predictor);
-0123 end
-0124 
-0125 % Remove duplicate genes
-0126 [~, J, K] = unique(GSS.genes);
-0127 if numel(J) ~= numel(K)
-0128     warning('RAVEN:warning', 'There are duplicate genes in the input file');
-0129     GSS.genes = GSS.genes(J);
-0130     GSS.scores = GSS.scores(J,:);
-0131 end
-0132 
-0133 % Optional renaming of compartments to model ids (already done for 'uniprot')
-0134 if ~isempty(compartmentMap)
-0135     GSS = i_applyCompartmentMap(GSS, compartmentMap);
-0136 end
-0137 
-0138 if isempty(GSS.genes) || isempty(GSS.compartments)
-0139     warning('RAVEN:warning', ['parseScores produced an empty gene scoring structure ' ...
-0140         '(no genes or no compartments matched). Check the input file and, if used, that ' ...
-0141         'compartmentMap labels match the predictor/database compartment names.']);
-0142 end
-0143 
-0144 % Normalize so the best score per gene is 1.0 (genes with no positive evidence untouched)
-0145 m = max(GSS.scores, [], 2);
-0146 m(m <= 0) = 1;
-0147 GSS.scores = bsxfun(@times, GSS.scores, 1./m);
-0148 end
-0149 
-0150 % ------------------------------------------------------------------------- parsers
-0151 
-0152 function GSS = i_parseCello(inputFile)
-0153 fid = fopen(inputFile, 'r');
-0154 tline = fgetl(fid);
-0155 tline = regexprep(tline, '^.+#Combined:\t', '');
-0156 tline = regexprep(tline, '\t#Most-likely-Location.+', '');
-0157 GSS.compartments = transpose(regexp(tline, '\t', 'split'));
-0158 GSS.genes = {}; GSS.scores = [];
-0159 row = 0;
-0160 while true
-0161     tline = fgetl(fid);
-0162     if ~ischar(tline); break; end
-0163     tline = regexprep(tline, '^.+:\t', '');
-0164     tline = regexprep(tline, ' .+', '');
-0165     tline = regexp(tline, '\t', 'split');
-0166     if numel(tline) < numel(GSS.compartments)
-0167         error('RAVEN:badInput', 'Malformed CELLO row %d (fewer fields than compartments)', row+1);
-0168     end
-0169     row = row + 1;
-0170     GSS.scores(row,:) = str2double(tline(1:numel(GSS.compartments)));
-0171     GSS.genes{row,1} = tline{1,end};
-0172 end
-0173 fclose(fid);
-0174 end
-0175 
-0176 function GSS = i_parseDeepLoc(inputFile)
-0177 fid = fopen(inputFile, 'r');
-0178 tline = fgetl(fid);
-0179 comps = regexp(tline, ',', 'split');
-0180 GSS.compartments = transpose(comps(4:end));   % cols 1-3 are Protein_ID/Localizations/Signals
-0181 GSS.genes = {}; GSS.scores = [];
-0182 row = 0;
-0183 while true
-0184     tline = fgetl(fid);
-0185     if ~ischar(tline); break; end
-0186     parts = regexp(tline, ',', 'split');
-0187     row = row + 1;
-0188     GSS.scores(row,:) = str2double(parts(4:end));
-0189     GSS.genes{row,1} = parts{1,1};
-0190 end
-0191 fclose(fid);
-0192 end
-0193 
-0194 function GSS = i_parseWide(inputFile, idColumn)
-0195 % MULocDeep / generic wide table: gene id column + one numeric column per compartment.
-0196 T = readtable(inputFile, 'FileType', 'text', 'VariableNamingRule', 'preserve');
-0197 idx = i_idColumnIndex(T, idColumn);
-0198 GSS.genes = cellstr(string(T{:,idx}));
-0199 rest = setdiff(1:width(T), idx, 'stable');
-0200 GSS.compartments = transpose(T.Properties.VariableNames(rest));
-0201 GSS.scores = i_numericMatrix(T, rest);
-0202 end
-0203 
-0204 function GSS = i_parseCompartments(inputFile, minConfidence)
-0205 % COMPARTMENTS channel TSV (no header): col1 gene, col4 GO term name, last col confidence.
-0206 T = readtable(inputFile, 'FileType', 'text', 'Delimiter', '\t', 'ReadVariableNames', false);
-0207 if width(T) < 5
-0208     error('RAVEN:badInput', 'Expected a COMPARTMENTS full TSV (>=5 columns)');
-0209 end
-0210 genes = string(T{:,1});
-0211 comps = string(T{:,4});
-0212 score = T{:,end};
-0213 if ~isnumeric(score); score = str2double(string(score)); end
-0214 keep = ~isnan(score) & score >= minConfidence;
-0215 genes = genes(keep); comps = comps(keep); score = score(keep);
-0216 
-0217 ug = unique(genes, 'stable');
-0218 uc = unique(comps, 'stable');
-0219 M = zeros(numel(ug), numel(uc));
-0220 [~, gi] = ismember(genes, ug);
-0221 [~, ci] = ismember(comps, uc);
-0222 for k = 1:numel(genes)
-0223     if M(gi(k), ci(k)) < score(k); M(gi(k), ci(k)) = score(k); end
-0224 end
-0225 GSS.genes = cellstr(ug);
-0226 GSS.compartments = cellstr(uc);
-0227 GSS.scores = M;
-0228 end
-0229 
-0230 function GSS = i_parseUniProt(inputFile, idColumn, map)
-0231 % UniProtKB TSV export: scan the "Subcellular location [CC]" text for known terms.
-0232 T = readtable(inputFile, 'FileType', 'text', 'Delimiter', '\t', 'VariableNamingRule', 'preserve');
-0233 vn = string(T.Properties.VariableNames);
-0234 locIdx = find(contains(lower(vn), 'subcellular'), 1);
-0235 if isempty(locIdx)
-0236     error('RAVEN:badInput', 'No "Subcellular location" column in the UniProt file');
-0237 end
-0238 idIdx = i_idColumnIndex(T, idColumn);
-0239 ids = string(T{:,idIdx});
-0240 loc = string(T{:,locIdx});
-0241 
-0242 labels = lower(string(keys(map)));
-0243 codesForLabels = string(values(map));
-0244 codeList = unique(codesForLabels, 'stable');
-0245 % Whole-word patterns so a short label (e.g. 'cytoplasm') does not match inside a longer,
-0246 % distinct location term (e.g. 'Cytoplasmic vesicle'). Labels must be the UniProt noun forms.
-0247 patterns = "\<" + arrayfun(@(s) string(regexptranslate('escape', char(s))), labels) + "\>";
-0248 
-0249 M = zeros(numel(ids), numel(codeList));
-0250 hasHit = false(numel(ids), 1);
-0251 for i = 1:numel(ids)
-0252     txt = lower(loc(i));
-0253     txt = regexprep(txt, '\{[^}]*\}', ' ');         % drop {ECO:...} evidence
-0254     % Split into per-isoform blocks and drop the Note=... free text WITHIN each block, so a
-0255     % later isoform's locations survive and Note prose is never read as a location.
-0256     blocks = regexp(char(txt), '\[isoform[^\]]*\]:', 'split');
-0257     for b = 1:numel(blocks)
-0258         seg = regexprep(blocks{b}, 'note=.*', ' ');
-0259         for j = 1:numel(labels)
-0260             if ~isempty(regexp(seg, patterns(j), 'once'))
-0261                 c = find(codeList == codesForLabels(j), 1);
-0262                 M(i, c) = 1; hasHit(i) = true;
-0263             end
-0264         end
-0265     end
-0266 end
-0267 keep = hasHit & strlength(strtrim(ids)) > 0;
-0268 GSS.genes = cellstr(ids(keep));
-0269 GSS.compartments = cellstr(codeList);
-0270 GSS.scores = M(keep, :);
-0271 end
-0272 
-0273 % ------------------------------------------------------------------------- helpers
-0274 
-0275 function idx = i_idColumnIndex(T, idColumn)
-0276 if isempty(idColumn)
-0277     idx = 1;
-0278 elseif isnumeric(idColumn)
-0279     idx = idColumn;
-0280 else
-0281     idx = find(strcmp(T.Properties.VariableNames, char(idColumn)), 1);
-0282     if isempty(idx)
-0283         error('RAVEN:badInput', 'idColumn "%s" not found', char(idColumn));
-0284     end
-0285 end
-0286 end
-0287 
-0288 function M = i_numericMatrix(T, cols)
-0289 M = zeros(height(T), numel(cols));
-0290 for k = 1:numel(cols)
-0291     col = T{:, cols(k)};
-0292     if ~isnumeric(col); col = str2double(string(col)); end
-0293     M(:, k) = col;
-0294 end
-0295 end
-0296 
-0297 function GSS = i_applyCompartmentMap(GSS, map)
-0298 labels = lower(string(keys(map)));
-0299 codes = string(values(map));
-0300 old = lower(string(GSS.compartments));
-0301 mapped = strings(numel(old), 1);
-0302 keepCol = false(numel(old), 1);
-0303 for i = 1:numel(old)
-0304     j = find(labels == old(i), 1);
-0305     if ~isempty(j); mapped(i) = codes(j); keepCol(i) = true; end
-0306 end
-0307 usedCodes = unique(mapped(keepCol), 'stable');
-0308 M = zeros(numel(GSS.genes), numel(usedCodes));
-0309 for c = 1:numel(usedCodes)
-0310     cols = keepCol & mapped == usedCodes(c);
-0311     M(:, c) = max(GSS.scores(:, cols), [], 2);
-0312 end
-0313 GSS.compartments = cellstr(usedCodes);
-0314 GSS.scores = M;
-0315 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/localization/predictLocalization.html b/doc/localization/predictLocalization.html deleted file mode 100644 index b617d046..00000000 --- a/doc/localization/predictLocalization.html +++ /dev/null @@ -1,1279 +0,0 @@ - - - - Description of predictLocalization - - - - - - - - - -
Home > localization > predictLocalization.m
- - - -

predictLocalization -

- -

PURPOSE ^

-
predictLocalization Assign reactions to compartments using localization predictors.
- -

SYNOPSIS ^

-
function [outModel, geneLocalization, transportStruct, scores,removedRxns] = predictLocalization(model, GSS,defaultCompartment, varargin)
- -

DESCRIPTION ^

-
 predictLocalization  Assign reactions to compartments using localization predictors.
-
- Tries to assign reactions to compartments in a manner that is in
- agreement with localization predictors while at the same time maintaining
- connectivity.
-
- Parameters
- ----------
- model : struct
-     a model structure. If the model contains several compartments they
-     will be merged.
- GSS : struct
-     a gene scoring structure as from parseScores.
- defaultCompartment : char
-     transport reactions are expressed as diffusion between the
-     defaultCompartment and the others. This is usually the cytosol. The
-     default compartment must have a match in GSS.
-
- Name-Value Arguments
- --------------------
- transportCost : double
-     the cost for including a transport reaction. If this is a scalar then
-     the same cost is used for all metabolites. It can also be a vector of
-     costs with the same dimension as model.mets. Negative costs encourage
-     transport of the metabolite (default 0.5).
- maxTime : double
-     maximum optimization time in minutes (default 15).
- plotResults : logical
-     true if the results should be plotted during the optimization
-     (default false).
-
- Returns
- -------
- outModel : struct
-     the resulting model structure.
- geneLocalization : struct
-     structure with the genes and their resulting localization.
- transportStruct : struct
-     structure with the transport reactions that had to be inferred and
-     between which compartments.
- scores : struct
-     structure that contains the total score history together with the
-     score based on gene localization and the score based on included
-     transport reactions.
- removedRxns : cell
-     cell array with the reaction ids that had to be removed to have a
-     connected input model.
-
- Notes
- -----
- This function requires that the starting network is connected when it is
- in one compartment. Reactions that are unconnected are removed and saved
- in removedRxns. Try running fillGaps to have a more connected input model
- if there are many such reactions. The input model should also not include
- any exchange, demand or sink reactions, otherwise this function would not
- provide any results.
-
- In the final model all metabolites are produced in at least one reaction.
- This does not guarantee a fully functional model since there can be
- internal loops. Transport reactions are only included as passive diffusion
- (A <=> B).
-
- The score of a model is the sum of scores for all genes in their assigned
- compartment minus the cost of all transport reactions that had to be
- included. A gene can only be assigned to one compartment. This is a
- simplification to keep the problem size down. The problem is solved using
- simulated annealing.
-
- Examples
- --------
-     [outModel, geneLocalization, transportStruct, scores, removedRxns] = ...
-         predictLocalization(model, GSS, defaultCompartment, ...
-             transportCost, maxTime, plotResults);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [outModel, geneLocalization, transportStruct, scores,...
-0002     removedRxns] = predictLocalization(model, GSS,...
-0003     defaultCompartment, varargin)
-0004 % predictLocalization  Assign reactions to compartments using localization predictors.
-0005 %
-0006 % Tries to assign reactions to compartments in a manner that is in
-0007 % agreement with localization predictors while at the same time maintaining
-0008 % connectivity.
-0009 %
-0010 % Parameters
-0011 % ----------
-0012 % model : struct
-0013 %     a model structure. If the model contains several compartments they
-0014 %     will be merged.
-0015 % GSS : struct
-0016 %     a gene scoring structure as from parseScores.
-0017 % defaultCompartment : char
-0018 %     transport reactions are expressed as diffusion between the
-0019 %     defaultCompartment and the others. This is usually the cytosol. The
-0020 %     default compartment must have a match in GSS.
-0021 %
-0022 % Name-Value Arguments
-0023 % --------------------
-0024 % transportCost : double
-0025 %     the cost for including a transport reaction. If this is a scalar then
-0026 %     the same cost is used for all metabolites. It can also be a vector of
-0027 %     costs with the same dimension as model.mets. Negative costs encourage
-0028 %     transport of the metabolite (default 0.5).
-0029 % maxTime : double
-0030 %     maximum optimization time in minutes (default 15).
-0031 % plotResults : logical
-0032 %     true if the results should be plotted during the optimization
-0033 %     (default false).
-0034 %
-0035 % Returns
-0036 % -------
-0037 % outModel : struct
-0038 %     the resulting model structure.
-0039 % geneLocalization : struct
-0040 %     structure with the genes and their resulting localization.
-0041 % transportStruct : struct
-0042 %     structure with the transport reactions that had to be inferred and
-0043 %     between which compartments.
-0044 % scores : struct
-0045 %     structure that contains the total score history together with the
-0046 %     score based on gene localization and the score based on included
-0047 %     transport reactions.
-0048 % removedRxns : cell
-0049 %     cell array with the reaction ids that had to be removed to have a
-0050 %     connected input model.
-0051 %
-0052 % Notes
-0053 % -----
-0054 % This function requires that the starting network is connected when it is
-0055 % in one compartment. Reactions that are unconnected are removed and saved
-0056 % in removedRxns. Try running fillGaps to have a more connected input model
-0057 % if there are many such reactions. The input model should also not include
-0058 % any exchange, demand or sink reactions, otherwise this function would not
-0059 % provide any results.
-0060 %
-0061 % In the final model all metabolites are produced in at least one reaction.
-0062 % This does not guarantee a fully functional model since there can be
-0063 % internal loops. Transport reactions are only included as passive diffusion
-0064 % (A <=> B).
-0065 %
-0066 % The score of a model is the sum of scores for all genes in their assigned
-0067 % compartment minus the cost of all transport reactions that had to be
-0068 % included. A gene can only be assigned to one compartment. This is a
-0069 % simplification to keep the problem size down. The problem is solved using
-0070 % simulated annealing.
-0071 %
-0072 % Examples
-0073 % --------
-0074 %     [outModel, geneLocalization, transportStruct, scores, removedRxns] = ...
-0075 %         predictLocalization(model, GSS, defaultCompartment, ...
-0076 %             transportCost, maxTime, plotResults);
-0077 
-0078 p=parseRAVENargs(varargin, {'transportCost',[]; 'maxTime',[]; 'plotResults',[]});
-0079 transportCost=p.transportCost;
-0080 maxTime=p.maxTime;
-0081 plotResults=p.plotResults;
-0082 
-0083 if isempty(transportCost)
-0084     transportCost=ones(numel(model.mets),1)*0.5;
-0085 end
-0086 if numel(transportCost)==1
-0087     transportCost=ones(numel(model.mets),1)*transportCost;
-0088 end
-0089 transportCost=transportCost(:);
-0090 
-0091 if numel(transportCost)~=numel(model.mets)
-0092     EM='The vector of transport costs must have the same dimension as model.mets';
-0093     error('RAVEN:badInput', '%s', EM);
-0094 end
-0095 if isempty(maxTime)
-0096     maxTime=15;
-0097 end
-0098 if isempty(plotResults)
-0099     plotResults=false;
-0100 end
-0101 
-0102 if isfield(model,'rxnComps')
-0103     model=rmfield(model,'rxnComps');
-0104     EM='The model structure contains information about reaction compartmentalization. This is not supported by this function. The rxnComps field has been deleted';
-0105     warning('RAVEN:warning', '%s', EM);
-0106 end
-0107 if isfield(model,'geneComps')
-0108     model=rmfield(model,'geneComps');
-0109     EM='The model structure contains information about gene compartmentalization. This is not supported by this function. The geneComps field has been deleted';
-0110     warning('RAVEN:warning', '%s', EM);
-0111 end
-0112 
-0113 defaultCompartment=char(defaultCompartment);
-0114 I=ismember(defaultCompartment,GSS.compartments);
-0115 if I==false
-0116     error('RAVEN:badInput', ...
-0117         ['defaultCompartment ''%s'' not found in GSS. Available compartments: %s. ' ...
-0118          '(If you mapped scores to model ids via compartmentMap, pass an id such as ''c''; ' ...
-0119          'otherwise pass the raw predictor label.)'], ...
-0120         defaultCompartment, strjoin(GSS.compartments(:)', ', '));
-0121 end
-0122 
-0123 if numel(model.comps)>1
-0124     EM='The model has several compartments. All compartments will be merged';
-0125     warning('RAVEN:warning', '%s', EM);
-0126     model=mergeCompartments(model,true,true);
-0127 end
-0128 
-0129 noGenes = ismember(model.genes,GSS.genes);
-0130 if ~all(noGenes)
-0131     EM=['For ' num2str(numel(find(~noGenes))) ' of ' num2str(numel(model.genes)) ' model genes no data was found in GSS'];
-0132     warning('RAVEN:warning', '%s', EM);
-0133 end
-0134 
-0135 %***Begin formating the data structures
-0136 
-0137 %Expand the model so that iso-enzymes have different reactions
-0138 model=expandModel(model);
-0139 
-0140 %Identify reactions that have to be deleted because the involved mets are
-0141 %never produced. This is done in an iterative manner
-0142 removedRxns={};
-0143 %This is to keep track of which metabolites are removed in this step. It is
-0144 %needed to adjust the transport costs
-0145 originalModelMets=model.mets;
-0146 while 1
-0147     irrevModel=convertToIrrev(model);
-0148     
-0149     I=sum(irrevModel.S>0,2);
-0150     
-0151     %Pretend that the unconstrained metabolites are made enough
-0152     if isfield(irrevModel,'unconstrained')
-0153         I(irrevModel.unconstrained~=0)=2;
-0154     end
-0155     metsToDelete=false(numel(model.mets),1);
-0156     
-0157     %This is not very neat but iterate through each metabolite and check
-0158     %whether it can be produced (without using only one isolated reversible
-0159     %reaction)
-0160     for i=1:numel(irrevModel.mets)
-0161         %If something can be made in two reactions then everything is fine.
-0162         %If it can be made in one reaction it is fine unless it is through
-0163         %an isolated reversible reaction (which can act as a mini loop)
-0164         if I(i)<2
-0165             if I(i)==1
-0166                 %Find the reaction where this metabolite is produced
-0167                 [~, J]=find(irrevModel.S(i,:)>0);
-0168                 
-0169                 %Check the metabolites that are consumed in this reaction.
-0170                 %The problem is if any of them is only produced in the
-0171                 %opposite reversible reaction
-0172                 K=irrevModel.S(:,J)<0;
-0173                 check=find(K & I<=1);
-0174                 
-0175                 for j=1:numel(check)
-0176                     %Find the reactions where it participates
-0177                     [~, L]=find(irrevModel.S(check(j),:)>0);
-0178                     
-0179                     if ~isempty(L)
-0180                         rxn=irrevModel.rxns(J);
-0181                         rxnRev=irrevModel.rxns(L);
-0182                         if strcmp(strrep(rxn,'_REV',''),strrep(rxnRev,'_REV',''))
-0183                             metsToDelete(i)=true;
-0184                         end
-0185                     else
-0186                         %If the metabolite was never produced then do
-0187                         %nothing and deal with it when the loop gets there
-0188                         continue;
-0189                     end
-0190                 end
-0191             else
-0192                 %Not made anywhere
-0193                 metsToDelete(i)=true;
-0194             end
-0195         end
-0196     end
-0197     
-0198     if any(metsToDelete)
-0199         %Delete any reactions involving any of the metsToDelete
-0200         [~, I]=find(model.S(metsToDelete,:));
-0201         removedRxns=[removedRxns;model.rxns(I)];
-0202         model=removeReactions(model,I,true,true);
-0203     else
-0204         %All bad reactions deleted
-0205         break;
-0206     end
-0207 end
-0208 
-0209 %Adjust the transport costs
-0210 transportCost=transportCost(ismember(originalModelMets,model.mets));
-0211 
-0212 %Assign fake genes to reactions without genes
-0213 I=find(sum(model.rxnGeneMat,2)==0);
-0214 for i=1:numel(I)
-0215     model.genes=[model.genes;['&&FAKE&&' num2str(i)]];
-0216     if isfield(model,'geneShortNames')
-0217         model.geneShortNames=[model.geneShortNames;{''}];
-0218     end
-0219     if isfield(model,'geneMiriams')
-0220         model.geneMiriams=[model.geneMiriams;{[]}];
-0221     end
-0222     if isfield(model,'proteins')
-0223         model.proteins=[model.proteins;{[]}];
-0224     end
-0225     if isfield(model,'geneFrom')
-0226         model.geneFrom=[model.geneFrom;{{'FAKE'}}];
-0227     end
-0228     model.rxnGeneMat(I(i),numel(model.genes))=1;
-0229     model.grRules{I(i)}='';
-0230 end
-0231 
-0232 %Update the GSS. All genes, fake or real, for which there is no evidence
-0233 %gets a score 0.5 in all compartments
-0234 I=setdiff(model.genes,GSS.genes);
-0235 GSS.genes=[GSS.genes;I];
-0236 GSS.scores=[GSS.scores;ones(numel(I),numel(GSS.compartments))*0.5];
-0237 
-0238 %Gene complexes should be moved together to be biologically
-0239 %relevant. The average score for the genes is used for each compartment.
-0240 %This is done by changing the model so that gene complexes are used as a
-0241 %single gene name and then a score is calculated for that "gene".
-0242 
-0243 %Only "and"-relationships exist after expandModel
-0244 genes=unique(model.grRules);
-0245 nGenes=strrep(genes,'(','');
-0246 nGenes=strrep(nGenes,')','');
-0247 %nGenes=strrep(nGenes," and ","_and_");
-0248 complexes=setdiff(nGenes,model.genes);
-0249 if ~isempty(complexes)
-0250     if isempty(complexes{1}) %Empty grRules also come up here
-0251         complexes(1)=[];
-0252     end
-0253 end
-0254 cScores=zeros(numel(complexes),numel(GSS.compartments));
-0255 for i=1:numel(complexes)
-0256     genesInComplex=regexp(complexes{i},' and ','split');
-0257     
-0258     %Find these genes in GSS
-0259     [I, J]=ismember(genesInComplex,GSS.genes);
-0260     
-0261     if any(I)
-0262         %Get the average of the genes that were found
-0263         mScores=mean(GSS.scores(J(I),:));
-0264         
-0265         %And add 0.5 for the genes that were not found, to be
-0266         %consistent with non-complexes
-0267         mScores=(mScores.*sum(I)+(numel(genesInComplex)-sum(I))*0.5)/numel(genesInComplex);
-0268     else
-0269         EM=['Could not parse grRule "' complexes{i} '". Assigning score 0.0 in all compartments'];
-0270         warning('RAVEN:warning', '%s', EM);
-0271         mScores=ones(1,numel(genesInComplex))*0.5;
-0272     end
-0273     cScores(i,:)=mScores;
-0274     
-0275     %Add this complex as a new gene
-0276     model.genes=[model.genes;complexes{i}];
-0277     if isfield(model,'geneMiriams')
-0278         model.geneMiriams=[model.geneMiriams;{[]}];
-0279     end
-0280     if isfield(model,'geneShortNames')
-0281         model.geneShortNames=[model.geneShortNames;{''}];
-0282     end
-0283     if isfield(model,'proteins')
-0284         model.proteins=[model.proteins;{''}];
-0285     end
-0286     if isfield(model,'geneFrom')
-0287         model.geneFrom=[model.geneFrom;{'COMPLEX'}];
-0288     end
-0289     %Find the reactions which had the original complex and change them to
-0290     %use the new "gene"
-0291     I=ismember(model.grRules,['(' complexes{i} ')']);
-0292     
-0293     %Should check more carefully if there can be an error here
-0294     if ~isempty(I)
-0295         model.rxnGeneMat(I,:)=0; %Ok since the split on "or" was applied
-0296         model.rxnGeneMat(I,numel(model.genes))=1;
-0297     end
-0298 end
-0299 
-0300 %Add the new "genes"
-0301 GSS.genes=[GSS.genes;complexes];
-0302 GSS.scores=[GSS.scores;cScores];
-0303 
-0304 %After merging the complexes it could happen that there are genes that are
-0305 %no longer in use. Delete such genes
-0306 model=removeReactions(model,{},false,true);
-0307 
-0308 %Exchange reactions, defined as involving an unconstrained metabolite, are
-0309 %special in that they have to stay in the defaultCompartment. This means
-0310 %that uptake/excretion of metabolites is always via the default
-0311 %compartment. This is a small simplification, but should be valid in most
-0312 %cases
-0313 [~, I]=getExchangeRxns(model);
-0314 
-0315 %It will be easier later on if the same place. Put them in the beginning
-0316 J=1:numel(model.rxns);
-0317 J(I)=[];
-0318 model=permuteModel(model,[I;J'],'rxns');
-0319 
-0320 %Number of exchange reactions
-0321 nER=numel(I);
-0322 
-0323 %Also put the exchange metabolites in the beginning
-0324 if isfield(model,'unconstrained')
-0325     I=find(model.unconstrained);
-0326     J=1:numel(model.mets);
-0327     J(I)=[];
-0328     model=permuteModel(model,[I;J'],'mets');
-0329     %Also reorder the transport costs
-0330     transportCost=transportCost([I;J']);
-0331     %Number of exchange metabolites
-0332     nEM=numel(I);
-0333 else
-0334     nEM=0;
-0335 end
-0336 
-0337 %There is no point of having genes for exchange reactions, so delete them.
-0338 %Also to make computations easier
-0339 model.rxnGeneMat(1:nER,:)=0;
-0340 model.grRules(1:nER)={''};
-0341 
-0342 %Remove unused genes
-0343 model=removeReactions(model,{},false,true);
-0344 
-0345 %Remove genes with no match to the model and reorder so that the genes are
-0346 %in the same order as model.genes. Since the fake genes are already added
-0347 %so that all genes in model exist in GSS it is fine to do like this
-0348 [~, J]=ismember(model.genes,GSS.genes);
-0349 GSS.genes=model.genes;
-0350 GSS.scores=GSS.scores(J,:);
-0351 
-0352 %Reorder the GSS so that the first index corresponds to the default
-0353 %compartment
-0354 [~, J]=ismember(defaultCompartment,GSS.compartments);
-0355 reorder=1:numel(GSS.compartments);
-0356 reorder(J)=[];
-0357 reorder=[J reorder];
-0358 GSS.scores=GSS.scores(:,reorder);
-0359 GSS.compartments=GSS.compartments(reorder);
-0360 
-0361 %Since it is only checked whether the metabolites can be synthesized, there
-0362 %is no need to care about the stoichiometry. Change to -1/1 to simplify
-0363 %later. Keep the S matrix for later though
-0364 oldS=model.S;
-0365 model.S(model.S>0)=1;
-0366 model.S(model.S<0)=-1;
-0367 
-0368 %Here is a bit of a trick. To avoid the recurring calculation which
-0369 %reactions are reversible, the reversible reactions have the coefficients
-0370 %-10/10 instead of -1/1
-0371 model.S(:,model.rev==1)=model.S(:,model.rev==1).*10;
-0372 
-0373 %***Begin problem formulation
-0374 
-0375 %Some numbers that are good to have
-0376 nRxns=numel(model.rxns)-nER; %Excluding exchange rxns
-0377 nMets=numel(model.mets)-nEM; %Excluding exchange mets
-0378 nGenes=numel(model.genes);
-0379 nComps=numel(GSS.compartments);
-0380 
-0381 %Create a big stoichiometric matrix that will be the current model. In
-0382 %order to have faster simulations the maximal model size is declared and
-0383 %reactions are then moved within it.
-0384 
-0385 %First the original model (with the first nE being exchange rxns), then
-0386 %reserve space for number of rxns minus exchange rxns for each non-default
-0387 %compartment, then transport reactions for all non-exchange mets between
-0388 %the default compartment and all others.
-0389 %NOTE: Kept eye()*0 since eye() can be used to include all transport from
-0390 %the beginning
-0391 s=repmat(eye(nMets)*0,1,nComps-1);
-0392 s=[zeros(numel(model.mets)-nMets,size(s,2));s];
-0393 S=[model.S sparse(numel(model.mets),nRxns*(nComps-1)) s];
-0394 s=[sparse(nMets*(nComps-1),numel(model.rxns)+nRxns*(nComps-1)) eye(nMets*(nComps-1))*0];
-0395 S=[S;s];
-0396 
-0397 %Also replicate the transport costs
-0398 transportCost=[transportCost(1:nEM);repmat(transportCost(nEM+1:end),nComps,1)];
-0399 
-0400 %Create a binary matrix that says where the genes are in the current
-0401 %solution
-0402 g2c=false(nGenes,nComps);
-0403 %All genes start in the default compartment
-0404 g2c(:,1)=true;
-0405 
-0406 %Start of main optimization loop
-0407 tic;
-0408 bestScore=-inf;
-0409 bestS=[];
-0410 bestg2c=[];
-0411 
-0412 %Temp for testing
-0413 plotScore=[];
-0414 nTrans=[];
-0415 totScore=[];
-0416 minScore=sum(min(GSS.scores,[],2));
-0417 maxScore=sum(max(GSS.scores,[],2));
-0418 
-0419 while toc<maxTime*60
-0420     %Pick a random gene, weighted by it is current score minus the best
-0421     %score for that gene (often 1.0, but can be 0.5 for no genes or average
-0422     %for complexes). Genes with bad fits are more likely to be moved. This
-0423     %formulation never moves a gene from its best compartment. Therefore a
-0424     %small uniform weight is added
-0425     [I, J]=find(g2c);
-0426     geneToMove=randsample(nGenes,1,true,max(GSS.scores(I,:),[],2)-GSS.scores(sub2ind(size(g2c),I,J))+0.1);
-0427     
-0428     %Sample among possible compartments to move to. Add a larger weight to
-0429     %even out the odds a little. Also a way of getting rid of loops where
-0430     %the same set of genes are moved back and forth several times
-0431     toComp=randsample(nComps,1,true,GSS.scores(geneToMove,:)+0.2);
-0432     
-0433     %Check that it moves to a new compartment
-0434     if toComp==find(g2c(geneToMove,:))
-0435         continue;
-0436     end
-0437     
-0438     %Moves the gene
-0439     [newS, newg2c]=moveGene(S,model,g2c,geneToMove,toComp,nRxns,nMets);
-0440     
-0441     %Tries to connect the network. If this was not possible in 10
-0442     %iterations, then abort. If more than 20 modifications were needed then
-0443     %it is unlikely that it will be a lower score
-0444     wasConnected=false;
-0445     for j=1:10
-0446         %Find the metabolites that are now unconnected
-0447         unconnected=findUnconnected(newS,nEM);
-0448         
-0449         %Continue if there are still unconnected
-0450         if any(unconnected)
-0451             %For each gene find out how many of these could be connected if
-0452             %the gene was moved and how many would be disconnected by
-0453             %moving that gene
-0454             [geneIndex, moveTo, deltaConnected, deltaScore]=selectGenes(newS,nEM,nMets,nER,nRxns,model,unconnected,g2c,GSS);
-0455             
-0456             %Score which gene would be the best to move. The highest
-0457             %deltaScore is 1.0. It should be possible to move a gene from
-0458             %worst to best compartment even if it disconnects, say, 1.5
-0459             %more metabolites
-0460             [score, I]=max(1.5*deltaScore+deltaConnected);
-0461             
-0462             %Check if it has to add a transport or if there is a gene that
-0463             %could be moved order to have a more connected network
-0464             hasToAddTransport=true;
-0465             if ~isempty(deltaConnected)
-0466                 if score>0
-0467                     hasToAddTransport=false;
-0468                 end
-0469             end
-0470             
-0471             %If it is possible to move any gene to have a more
-0472             %connected network, then move the best one
-0473             if hasToAddTransport==false
-0474                 [newS, newg2c]=moveGene(newS,model,g2c,geneIndex(I),moveTo(I),nRxns,nMets);
-0475             else
-0476                 %Choose a random unconnected metabolite that should be
-0477                 %connected
-0478                 transMet=unconnected(randsample(numel(unconnected),1));
-0479                 
-0480                 %First get where the metabolite is now
-0481                 comps=ceil((transMet-nEM)/((size(S,1)-nEM)/nComps));
-0482                 
-0483                 %Find the corresponding metabolite index if it were in the
-0484                 %default compartment
-0485                 dcIndex=transMet-(comps-1)*nMets;
-0486                 
-0487                 %Then get the indexes of that metabolite in all
-0488                 %compartments
-0489                 allIndexes=dcIndex;
-0490                 for k=1:nComps-1
-0491                     allIndexes=[allIndexes;dcIndex+nMets*k];
-0492                 end
-0493                 
-0494                 %It could be that some of these are not used in any
-0495                 %reaction. Get only the ones which are
-0496                 I=sum(newS(allIndexes,:)~=0,2)>0;
-0497                 
-0498                 %Then get the ones that are used but not in unconnected.
-0499                 %These are metabolites that could potentially be
-0500                 %transported to connect transMet
-0501                 connectedUsed=setdiff(allIndexes(I),unconnected);
-0502                 
-0503                 %This may be an error but leave it for now. It seems to
-0504                 %happen if nothing can be connected in one step
-0505                 if isempty(connectedUsed)
-0506                     break;
-0507                 end
-0508                 
-0509                 %If transMet is in the default compartment then everything
-0510                 %is fine, just connect it to a random one
-0511                 if transMet==dcIndex
-0512                     newS=addTransportToS(newS,nRxns,nER,nMets,nEM,nComps,transMet,connectedUsed(randsample(numel(connectedUsed),1)));
-0513                 else
-0514                     %If one of the connectedUsed is in the default
-0515                     %compartment then connect to that one
-0516                     I=connectedUsed(connectedUsed<(nMets+nEM));
-0517                     if any(I)
-0518                         newS=addTransportToS(newS,nRxns,nER,nMets,nEM,nComps,transMet,I(randsample(numel(I),1)));
-0519                     else
-0520                         %This is if the only way to connect it is by adding
-0521                         %two transport reactions, going via the default
-0522                         %compartment
-0523                         break;
-0524                     end
-0525                 end
-0526             end
-0527         else
-0528             wasConnected=true;
-0529             break;
-0530         end
-0531     end
-0532     
-0533     %If the network was connected in a new way, it is possible that some
-0534     %transport reactions are no longer needed. They should be removed
-0535     if wasConnected==true
-0536         %These are the metabolites that are being transported
-0537         activeTransport=find(sum(newS(:,nER+nRxns*nComps+1:end),2));
-0538         
-0539         %Get the metabolites that are unconnected if transport was not used
-0540         unconnected=findUnconnected(newS(:,1:nER+nRxns*nComps),nEM);
-0541         
-0542         %Find the transport reactions that are not needed and delete them
-0543         I=setdiff(activeTransport,unconnected);
-0544         
-0545         %Since both metabolites in a transport rxns must be connected for
-0546         %the reaction to be deleted, the sum over the colums should be 4
-0547         newS(:,find(sum(newS(I,nER+nRxns*nComps+1:end))==4)+nER+nRxns*nComps)=0;
-0548         
-0549         %Score the solution and determine whether to keep it as a new
-0550         %solution
-0551         [score, geneScore, trCost]=scoreLocalization(newS,newg2c,GSS,transportCost);
-0552         
-0553         %If it was the best solution so far, keep it
-0554         if score>bestScore
-0555             bestScore=score;
-0556             bestS=newS;
-0557             bestg2c=newg2c;
-0558         end
-0559         
-0560         %This should not be steepest descent later
-0561         if score>=bestScore% || exp((score-bestScore)*7)>rand()
-0562             plotScore=[plotScore;geneScore];
-0563             nTrans=[nTrans;trCost];
-0564             totScore=[totScore;score];
-0565             S=newS;
-0566             g2c=newg2c;
-0567             
-0568             if plotResults==true
-0569                 subplot(3,2,1);
-0570                 spy(S);
-0571                 subplot(3,2,2);
-0572                 plot(plotScore,'r');
-0573                 xlabel('Gene score');
-0574                 subplot(3,2,3);
-0575                 plot((plotScore-minScore)/(maxScore-minScore),'r');
-0576                 xlabel('Gene score relative to predictions');
-0577                 subplot(3,2,4);
-0578                 plot(nTrans,'g');
-0579                 xlabel('Transport cost');
-0580                 subplot(3,2,5);
-0581                 plot(totScore,'b');
-0582                 xlabel('Total score');
-0583                 subplot(3,2,6);
-0584                 pause(0.2);
-0585             end
-0586         end
-0587     end
-0588 end
-0589 scores.totScore=score;
-0590 scores.geneScore=geneScore;
-0591 scores.transCost=trCost;
-0592 
-0593 %Find which metabolites are transported and to where
-0594 [I, J]=find(bestS(nEM+1:nEM+nMets,end-nMets*(nComps-1)+1:end));
-0595 J=ceil(J/nMets+1);
-0596 transportStruct.mets=model.metNames(I+nEM);
-0597 transportStruct.toComp=GSS.compartments(J);
-0598 
-0599 [I, J]=find(bestg2c);
-0600 geneLocalization.genes=GSS.genes(I);
-0601 geneLocalization.comps=GSS.compartments(J);
-0602 
-0603 %Resort the gene names
-0604 [~, I]=sort(geneLocalization.genes);
-0605 geneLocalization.genes=geneLocalization.genes(I);
-0606 geneLocalization.comps=geneLocalization.comps(I);
-0607 
-0608 %Remove the fake genes
-0609 I=strncmp('&&FAKE&&',geneLocalization.genes,8);
-0610 geneLocalization.genes(I)=[];
-0611 geneLocalization.comps(I)=[];
-0612 
-0613 %Put together the model. This is done by first duplicating the S matrix
-0614 %into the different compartments. Then the transport reactions are added
-0615 %based on transportStruct. By now model.S should have the same size as the
-0616 %S matrix used in the optimization, but with conserved stoichiometry. In
-0617 %the final step all reactions and metabolites that are not used in the S
-0618 %matrix from the optimization are deleted from the model
-0619 outModel=model;
-0620 outModel.S=oldS;
-0621 
-0622 %This is the S matrix without exchange rxns or metabolites
-0623 copyPart=outModel.S(nEM+1:end,nER+1:end);
-0624 
-0625 %Replicate to give the rxnGeneMat for the full system
-0626 copyRxnGeneMat=outModel.rxnGeneMat(nER+1:end,:);
-0627 outModel.rxnGeneMat=[outModel.rxnGeneMat;repmat(copyRxnGeneMat,nComps-1,1)];
-0628 
-0629 %First fix the compartments. The model is already ordered with the exchange
-0630 %metabolites first. The original model may contain one or two compartments,
-0631 %depending on whether any exchange metabolites are defined
-0632 nStartComps=numel(outModel.comps);
-0633 if nStartComps==1
-0634     outModel.comps={'1'};
-0635     outModel.compNames=GSS.compartments(1);
-0636 else
-0637     if model.metComps(1)==1
-0638         outModel.compNames(1)=GSS.compartments(1);
-0639     else
-0640         outModel.compNames(2)=GSS.compartments(1);
-0641     end
-0642 end
-0643 outModel.compNames=[outModel.compNames;GSS.compartments(2:end)];
-0644 
-0645 %Ugly little loop
-0646 for i=1:numel(GSS.compartments)-1
-0647     outModel.comps=[outModel.comps;num2str(numel(outModel.comps)+1)];
-0648 end
-0649 %This information is not known from the data, so empty fields are added
-0650 outModel.compOutside=cell(numel(outModel.comps),1);
-0651 outModel.compOutside(:)={''};
-0652 
-0653 for i=1:nComps-1
-0654     outModel.S=[outModel.S sparse(size(outModel.S,1),nRxns)];
-0655     outModel.S=[outModel.S;[sparse(nMets,nRxns*i+nER) copyPart]];
-0656     outModel.rxns=[outModel.rxns;strcat(outModel.rxns(nER+1:nER+nRxns),'_',GSS.compartments{i+1})];
-0657     outModel.rxnNames=[outModel.rxnNames;strcat(outModel.rxnNames(nER+1:nER+nRxns),' (',GSS.compartments{i+1},')')];
-0658     outModel.lb=[outModel.lb;outModel.lb(nER+1:nER+nRxns)];
-0659     outModel.ub=[outModel.ub;outModel.ub(nER+1:nER+nRxns)];
-0660     outModel.rev=[outModel.rev;outModel.rev(nER+1:nER+nRxns)];
-0661     outModel.c=[outModel.c;outModel.c(nER+1:nER+nRxns)];
-0662     if isfield(outModel,'grRules')
-0663         outModel.grRules=[outModel.grRules;outModel.grRules(nER+1:nER+nRxns)];
-0664     end
-0665     if isfield(outModel,'subSystems')
-0666         outModel.subSystems=[outModel.subSystems;outModel.subSystems(nER+1:nER+nRxns)];
-0667     end
-0668     if isfield(outModel,'eccodes')
-0669         outModel.eccodes=[outModel.eccodes;outModel.eccodes(nER+1:nER+nRxns)];
-0670     end
-0671     if isfield(outModel,'rxnFrom')
-0672         outModel.rxnFrom=[outModel.rxnFrom;outModel.rxnFrom(nER+1:nER+nRxns)];
-0673     end
-0674     if isfield(outModel,'rxnMiriams')
-0675         outModel.rxnMiriams=[outModel.rxnMiriams;outModel.rxnMiriams(nER+1:nER+nRxns)];
-0676     end
-0677     if isfield(outModel,'rxnNotes')
-0678         outModel.rxnNotes=[outModel.rxnNotes;outModel.rxnNotes(nER+1:nER+nRxns)];
-0679     end
-0680     if isfield(outModel,'rxnReferences')
-0681         outModel.rxnReferences=[outModel.rxnReferences;outModel.rxnReferences(nER+1:nER+nRxns)];
-0682     end
-0683     if isfield(outModel,'rxnConfidenceScores')
-0684         outModel.rxnConfidenceScores=[outModel.rxnConfidenceScores;outModel.rxnConfidenceScores(nER+1:nER+nRxns)];
-0685     end
-0686     if isfield(outModel,'rxnDeltaG')
-0687         outModel.rxnDeltaG=[outModel.rxnDeltaG;outModel.rxnDeltaG(nER+1:nER+nRxns)];
-0688     end
-0689     outModel.mets=[outModel.mets;strcat(outModel.mets(nEM+1:nEM+nMets),'_',GSS.compartments{i+1})];
-0690     outModel.metNames=[outModel.metNames;outModel.metNames(nEM+1:nEM+nMets)];
-0691     outModel.b=[outModel.b;outModel.b(nEM+1:nEM+nMets,:)];
-0692     I=ones(nMets,1)*nStartComps+i;
-0693     outModel.metComps=[outModel.metComps;I];
-0694     if isfield(outModel,'inchis')
-0695         outModel.inchis=[outModel.inchis;outModel.inchis(nEM+1:nEM+nMets)];
-0696     end
-0697     if isfield(outModel,'metSmiles')
-0698         outModel.metSmiles=[outModel.metSmiles;outModel.metSmiles(nEM+1:nEM+nMets)];
-0699     end
-0700     if isfield(outModel,'unconstrained')
-0701         outModel.unconstrained=[outModel.unconstrained;outModel.unconstrained(nEM+1:nEM+nMets)];
-0702     end
-0703     if isfield(outModel,'metMiriams')
-0704         outModel.metMiriams=[outModel.metMiriams;outModel.metMiriams(nEM+1:nEM+nMets)];
-0705     end
-0706     if isfield(outModel,'metFormulas')
-0707         outModel.metFormulas=[outModel.metFormulas;outModel.metFormulas(nEM+1:nEM+nMets)];
-0708     end
-0709     if isfield(outModel,'metFrom')
-0710         outModel.metFrom=[outModel.metFrom;outModel.metFrom(nEM+1:nEM+nMets)];
-0711     end
-0712     if isfield(outModel,'metCharges')
-0713         outModel.metCharges=[outModel.metCharges;outModel.metCharges(nEM+1:nEM+nMets)];
-0714     end
-0715     if isfield(outModel,'metDeltaG')
-0716         outModel.metDeltaG=[outModel.metDeltaG;outModel.metDeltaG(nEM+1:nEM+nMets)];
-0717     end
-0718 end
-0719 
-0720 %Add the transport reactions
-0721 transS=bestS(:,numel(outModel.rxns)+1:end);
-0722 J=sum(transS)>0; %Active rxns
-0723 
-0724 %Transport reactions are written in a different way compared to a "real"
-0725 %stoichimetric matrix. This is to fix that
-0726 transS(transS~=0)=1;
-0727 transS(1:nEM+nMets,:)=transS(1:nEM+nMets,:)*-1;
-0728 I=find(sum(transS>0,2));
-0729 nTransRxns=numel(I);
-0730 outModel.S=[outModel.S transS(:,J)];
-0731 filler=ones(nTransRxns,1);
-0732 outModel.lb=[outModel.lb;filler*-1000];
-0733 outModel.ub=[outModel.ub;filler*1000];
-0734 outModel.rev=[outModel.rev;filler];
-0735 outModel.c=[outModel.c;filler*0];
-0736 outModel.rxnGeneMat=[outModel.rxnGeneMat;sparse(nTransRxns,numel(outModel.genes))];
-0737 
-0738 for i=1:numel(I)
-0739     outModel.rxns=[outModel.rxns;strcat('transport',num2str(i))];
-0740     outModel.rxnNames=[outModel.rxnNames;['Transport of ',outModel.metNames{I(i)}]];
-0741     if isfield(outModel,'grRules')
-0742         outModel.grRules=[outModel.grRules;{''}];
-0743     end
-0744     if isfield(outModel,'rxnMiriams')
-0745         outModel.rxnMiriams=[outModel.rxnMiriams;{[]}];
-0746     end
-0747     if isfield(outModel,'subSystems')
-0748         if any(cellfun(@(x) iscell(x), outModel.subSystems))
-0749             outModel.subSystems=[outModel.subSystems;{{'Inferred transport reactions'}}];
-0750         else
-0751             outModel.subSystems=[outModel.subSystems;{'Inferred transport reactions'}];
-0752         end
-0753     end
-0754     if isfield(outModel,'eccodes')
-0755         outModel.eccodes=[outModel.eccodes;{''}];
-0756     end
-0757     if isfield(outModel,'rxnFrom')
-0758         outModel.rxnFrom=[outModel.rxnFrom;{''}];
-0759     end
-0760     if isfield(outModel,'rxnNotes')
-0761         outModel.rxnNotes=[outModel.rxnNotes;{''}];
-0762     end
-0763     if isfield(outModel,'rxnReferences')
-0764         outModel.rxnReferences=[outModel.rxnReferences;{''}];
-0765     end
-0766     if isfield(outModel,'rxnConfidenceScores')
-0767         outModel.rxnConfidenceScores=[outModel.rxnConfidenceScores;NaN];
-0768     end
-0769     if isfield(outModel,'rxnDeltaG')
-0770         outModel.rxnDeltaG=[outModel.rxnDeltaG;NaN];
-0771     end
-0772 end
-0773 
-0774 %Then remove all reactions and metabolites that are not used in the final
-0775 %solution from the optimization
-0776 [~, J]=find(bestS(:,1:nER+nComps*nRxns));
-0777 K=true(numel(outModel.rxns),1);
-0778 K(J)=false;
-0779 K(end-nTransRxns+1:end)=false;
-0780 outModel=removeReactions(outModel,K,true);
-0781 
-0782 %Remove all fake genes
-0783 I=strncmp('&&FAKE&&',outModel.genes,8);
-0784 outModel.genes(I)=[];
-0785 if isfield(outModel,'geneMiriams')
-0786     outModel.geneMiriams(I)=[];
-0787 end
-0788 if isfield(outModel,'geneShortNames')
-0789     outModel.geneShortNames(I)=[];
-0790 end
-0791 if isfield(outModel,'proteins')
-0792     outModel.proteins(I)=[];
-0793 end
-0794 outModel.rxnGeneMat(:,I)=[];
-0795 
-0796 %Fix grRules and reconstruct rxnGeneMat
-0797 [grRules,rxnGeneMat] = standardizeGrRules(outModel,true);
-0798 outModel.grRules = grRules;
-0799 outModel.rxnGeneMat = rxnGeneMat;
-0800 end
-0801 
-0802 %Moves a gene and all associated reactions from one compartment to another
-0803 function [S, g2c]=moveGene(S,model,g2c,geneToMove,toComp,nRxns,nMets)
-0804 %Find the current compartment and update to the new one
-0805 currentComp=find(g2c(geneToMove,:));
-0806 g2c(geneToMove,:)=false;
-0807 g2c(geneToMove,toComp)=true;
-0808 
-0809 %Find the reactions in the original model that the gene controls
-0810 [I, ~]=find(model.rxnGeneMat(:,geneToMove));
-0811 
-0812 %Calculate their current positions in the S matrix
-0813 oldRxns=I+(currentComp-1)*nRxns;
-0814 
-0815 %And their new positions
-0816 newRxns=I+(toComp-1)*nRxns;
-0817 
-0818 %The metabolite ids also have to be changed to match the new
-0819 %compartment
-0820 metChange=nMets*(toComp-currentComp);
-0821 
-0822 %Update the reactions
-0823 [I, J, K]=find(S(:,oldRxns));
-0824 I=I+metChange;
-0825 
-0826 %Move the reactions
-0827 S(:,oldRxns)=0;
-0828 S(sub2ind(size(S),I,newRxns(J)))=K;
-0829 end
-0830 
-0831 %Finds which metabolites are unconnected, in the sense that they are never
-0832 %a product or only a product in a reversible reaction where one reactant is
-0833 %only a product in the opposite direction of that reaction. This function
-0834 %ignores exchange metabolites. Returns a vector of metabolite indexes.
-0835 %metsToCheck is an array of metabolite indexes to check for connectivity.
-0836 %If not supplied then all metabolites are checked
-0837 function unconnected=findUnconnected(S,nEM,metsToCheck)
-0838 if nargin>2
-0839     %Do this by deleting everything from the network that is not in
-0840     %metsToCheck and that is not exchange metabolites
-0841     I=false(size(S,1),1);
-0842     I(1:nEM)=true;
-0843     I(metsToCheck)=true;
-0844     S=S(I,:);
-0845 end
-0846 
-0847 em=false(size(S,1),1);
-0848 em(1:nEM)=true;
-0849 
-0850 %Construct a matrix in which the reversible reactions are inverted
-0851 I=sum(S>2,1) | sum(S>2,1);
-0852 revS=S;
-0853 revS(:,I)=revS(:,I)*-1;
-0854 
-0855 %First calculate the ones that are ok
-0856 %Produced in 2 rxns, is exchange, is not used at all, is produced in
-0857 %non-reversible, involved in more than 1 reversible reactions
-0858 connected=sum(S>0,2)>1 | em | sum(S~=0,2)==0 | sum(S(:,~I)>0,2)>0 | sum(S(:,I)~=0,2)>1;
-0859 
-0860 %Then get the ones that are unconnected because they are never produced
-0861 unconnected=sum(S>0 | revS>0,2)==0 & connected==false;
-0862 
-0863 %Then get the ones that are potentially unconnected
-0864 maybeUnconnected=~connected & ~unconnected;
-0865 %maybeUnconnected=find(maybeUnconnectedS);
-0866 
-0867 %The metabolites in maybeUnconnected are involved in one reversible
-0868 %reaction and not produced in any other reaction. This means that the
-0869 %reactions which have at least one met in maybeUnconnected as reactant and
-0870 %one as product are unconnected. The metabolites in maybeUnconnected that
-0871 %are present in those reactions are then dead ends
-0872 deadRxns=any(S(maybeUnconnected,:)>0) & any(S(maybeUnconnected,:)<0);
-0873 
-0874 %Get the mets involved in any of those reactions
-0875 problematic=any(S(:,deadRxns)~=0,2);
-0876 
-0877 %If any of these are in the maybeUnconnected list then the metabolite is
-0878 %unconnected
-0879 unconnected(problematic & maybeUnconnected)=true;
-0880 
-0881 %Map back to metsToCheck
-0882 if nargin>2
-0883     unconnected=metsToCheck(unconnected(nEM+1:end));
-0884 else
-0885     unconnected=find(unconnected);
-0886 end
-0887 end
-0888 
-0889 %Given a set of unconnected metabolites, this function tries to move each
-0890 %gene that could connect any of them, calculates the number of newly
-0891 %connected metabolites minus the number of newly disconnected metabolites.
-0892 %As some metabolites are very connected, only 25 genes are checked. Genes
-0893 %that have a low score in their current compartment are more likely to be
-0894 %moved
-0895 function [geneIndex, moveTo, deltaConnected, deltaScore]=selectGenes(S,nEM,nMets,nER,nRxns,model,unconnected,g2c,GSS)
-0896 %If moveTo is 0 then the gene cannot connect any of the metabolites
-0897 moveTo=zeros(numel(model.genes),1);
-0898 deltaConnected=zeros(numel(model.genes),1);
-0899 
-0900 %First get where the metabolites are now
-0901 nComps=size(g2c,2);
-0902 comps=ceil((unconnected-nEM)/((size(S,1)-nEM)/nComps));
-0903 
-0904 %Find the corresponding metabolite indexes if they all were in the default
-0905 %compartment
-0906 dcIndexes=unique(unconnected-(comps-1)*nMets);
-0907 
-0908 %Then find them if they were in any other compartment
-0909 allIndexes=dcIndexes;
-0910 for i=1:nComps-1
-0911     allIndexes=[allIndexes;dcIndexes+nMets*i];
-0912 end
-0913 
-0914 %Also check which reversible reactions that could be used
-0915 I=sum(S>2,1) | sum(S>2,1);
-0916 revS=S;
-0917 revS(:,I)=revS(:,I)*-1;
-0918 
-0919 %Find all reactions that could make any of the unconnected metabolites in
-0920 %some other compartment
-0921 newMets=setdiff(allIndexes,unconnected);
-0922 [~, potential]=find(S(newMets,:)>0 | revS(newMets,:)>0);
-0923 potential(potential<=nER | potential>nER+nRxns*nComps)=[]; %No exchange rxns or transport rxns
-0924 
-0925 %Map J to the real metabolic reactions in model
-0926 rxnComps=ceil((potential-nER)/(nRxns));
-0927 
-0928 %Find the corresponding reaction indexes if they all were in the default
-0929 %compartment
-0930 dcRxnIndexes=potential-(rxnComps-1)*nRxns;
-0931 
-0932 %Get the genes for those reactions
-0933 genes=find(sum(model.rxnGeneMat(dcRxnIndexes,:)>0,1));
-0934 
-0935 %For some cases there can be very many reactions to connect something. This
-0936 %is in particular true in the beginning of the optimization if, say, ATP is
-0937 %unconnected. Therefore limit the number of genes to be checked to 25.
-0938 %Weigh so that genes with bad scores in their current compartment are more
-0939 %likely to be moved.
-0940 
-0941 %Get scores for these genes
-0942 [~, J]=find(g2c(genes,:));
-0943 
-0944 %Add a small weight so that genes in their best compartment could be moved
-0945 %as well
-0946 geneScores=GSS.scores(sub2ind(size(g2c),genes(:),J));
-0947 modGeneScores=1.1-geneScores;
-0948 if numel(genes)>25
-0949     rGenes=genes(randsample(numel(genes),min(numel(genes),25),true,modGeneScores));
-0950     
-0951     %The sampling with weights could give duplicates
-0952     rGenes=unique(rGenes);
-0953     
-0954     %Reorder the geneScores to match
-0955     [~, I]=ismember(rGenes,genes);
-0956     geneScores=geneScores(I);
-0957     genes=rGenes;
-0958 end
-0959 for i=1:numel(genes)
-0960     %Since one gene is moved at a time, only metabolites involved in any of
-0961     %the reactions for that gene can become unconnected. This helps to
-0962     %speed up the algorithm. First get all involved reactions in the
-0963     %default compartment
-0964     rxns=find(model.rxnGeneMat(:,genes(i)));
-0965     
-0966     %Then get their mets
-0967     mets=find(sum(model.S(:,rxns)~=0,2)>0);
-0968     
-0969     %Then get their indexes in all compartments
-0970     allIndexes=mets;
-0971     for j=1:nComps-1
-0972         allIndexes=[allIndexes;mets+nMets*j];
-0973     end
-0974     
-0975     %Check which of the unconnected metabolites that these reactions
-0976     %correspond to. This could have been done earlier, but it is fast. The
-0977     %reversibility check is skipped because it is unlikely to be an issue
-0978     %here. Worst case is that the gene is tested once to much
-0979     [I, ~]=find(model.S(:,rxns));
-0980     moveToComps=unique(comps(ismember(dcIndexes,I)));
-0981     
-0982     %Try to move the gene to each of the compartments
-0983     bestMove=-inf;
-0984     bestComp=[];
-0985     for j=1:numel(moveToComps)
-0986         newS=moveGene(S,model,g2c,genes(i),moveToComps(j),nRxns,nMets);
-0987         
-0988         %Check how many metabolites that are unconnected after moving the
-0989         %gene
-0990         dConnected=numel(unconnected)-numel(findUnconnected(newS,nEM,[allIndexes;unconnected]));
-0991         if dConnected>bestMove
-0992             bestMove=dConnected;
-0993             bestComp=moveToComps(j);
-0994         end
-0995     end
-0996     
-0997     %Add the difference in connectivity and where the genes should be moved
-0998     moveTo(genes(i))=bestComp;
-0999     deltaConnected(genes(i))=bestMove;
-1000 end
-1001 
-1002 %Finish up
-1003 geneIndex=genes(:);
-1004 moveTo=moveTo(geneIndex);
-1005 deltaConnected=deltaConnected(geneIndex);
-1006 deltaScore=GSS.scores(sub2ind(size(g2c),geneIndex(:),moveTo))-geneScores;
-1007 end
-1008 
-1009 %Small function to add a transport reactions between two metabolites.
-1010 %Transport reactions are written as having a coefficient 2.0 for both
-1011 %reactant and product. This is not a "real" reaction, but since all normal
-1012 %reactions have coefficient -1/1 or -10/10 it is a compact way of writing
-1013 %it
-1014 function S=addTransportToS(S,nRxns,nER,nMets,nEM,nComps,metA,metB)
-1015 mets=[metA;metB];
-1016 %Find the current compartments for the metabolites
-1017 comps=ceil((mets-nEM)/((size(S,1)-nEM)/nComps));
-1018 
-1019 if sum(comps==1)~=1
-1020     EM='Tried to create a transport reaction from a non-default compartment';
-1021     error('RAVEN:badInput', '%s', EM);
-1022 end
-1023 
-1024 %Calculate the reaction index
-1025 rIndex=(nER+nRxns*nComps)+mets(comps~=1)-nEM-nMets;
-1026 
-1027 S(mets,rIndex)=2;
-1028 end
-1029 
-1030 %Scores a network based on the localization of the genes and the number of
-1031 %transporter reactions used
-1032 function [score, geneScore, transportCost]=scoreLocalization(S,g2c,GSS,transportCost)
-1033 [I, J]=find(g2c);
-1034 geneScore=sum(GSS.scores(sub2ind(size(g2c),I,J)));
-1035 [I, ~]=find(S==2);
-1036 I=unique(I);
-1037 transportCost=sum(transportCost(I));
-1038 score=geneScore-transportCost;
-1039 end
-1040 
-1041 % To avoid dependency on stats toolbox, use this alternative implementation
-1042 % of randsample, source:
-1043 % https://github.com/gpeyre/numerical-tours/blob/dacee30081c04ef5f67b26b387ead85f2b193af9/matlab/toolbox_signal/randsample.m
-1044 function y = randsample(n, k, replace, w)
-1045 %RANDSAMPLE Random sample, with or without replacement.
-1046 %   Y = RANDSAMPLE(N,K) returns Y as a vector of K values sampled uniformly
-1047 %   at random, without replacement, from the integers 1:N.
-1048 %
-1049 %   Y = RANDSAMPLE(POPULATION,K) returns K values sampled uniformly at
-1050 %   random, without replacement, from the values in the vector POPULATION.
-1051 %
-1052 %   Y = RANDSAMPLE(...,REPLACE) returns a sample taken with replacement if
-1053 %   REPLACE is true, or without replacement if REPLACE is false (the default).
-1054 %
-1055 %   Y = RANDSAMPLE(...,true,W) returns a weighted sample, using positive
-1056 %   weights W, taken with replacement.  W is often a vector of probabilities.
-1057 %   This function does not support weighted sampling without replacement.
-1058 %
-1059 %   Example:  Generate a random sequence of the characters ACGT, with
-1060 %   replacement, according to specified probabilities.
-1061 %
-1062 %      R = randsample("ACGT",48,true,[0.15 0.35 0.35 0.15])
-1063 %
-1064 %   See also RAND, RANDPERM.
-1065 
-1066 %   Copyright 1993-2008 The MathWorks, Inc.
-1067 %   $Revision: 1.1.4.3 $  $Date: 2008/12/01 08:09:34 $
-1068 
-1069 if nargin < 2
-1070     error('stats:randsample:TooFewInputs','Requires two input arguments.');
-1071 elseif numel(n) == 1
-1072     population = [];
-1073 else
-1074     population = n;
-1075     n = numel(population);
-1076     if length(population)~=n
-1077        error('stats:randsample:BadPopulation','POPULATION must be a vector.');
-1078     end
-1079 end
-1080 
-1081 if nargin < 3
-1082     replace = false;
-1083 end
-1084 
-1085 if nargin < 4
-1086     w = [];
-1087 elseif ~isempty(w)
-1088     if length(w) ~= n
-1089         if isempty(population)
-1090             error('stats:randsample:InputSizeMismatch',...
-1091                   'W must have length equal to N.');
-1092         else
-1093             error('stats:randsample:InputSizeMismatch',...
-1094                   'W must have the same length as the population.');
-1095         end
-1096     else
-1097         p = w(:)' / sum(w);
-1098     end
-1099 end
-1100 
-1101 switch replace
-1102 
-1103 % Sample with replacement
-1104 case {true, 'true', 1}
-1105     if isempty(w)
-1106         y = ceil(n .* rand(k,1));
-1107     else
-1108         [dum, y] = histc(rand(k,1),[0 cumsum(p)]);
-1109     end
-1110 
-1111 % Sample without replacement
-1112 case {false, 'false', 0}
-1113     if k > n
-1114         if isempty(population)
-1115             error('stats:randsample:SampleTooLarge',...
-1116         'K must be less than or equal to N for sampling without replacement.');
-1117         else
-1118             error('stats:randsample:SampleTooLarge',...
-1119                   'K must be less than or equal to the population size.');
-1120         end
-1121     end
-1122 
-1123     if isempty(w)
-1124         % If the sample is a sizeable fraction of the population,
-1125         % just randomize the whole population (which involves a full
-1126         % sort of n random values), and take the first k.
-1127         if 4*k > n
-1128             rp = randperm(n);
-1129             y = rp(1:k);
-1130 
-1131         % If the sample is a small fraction of the population, a full sort
-1132         % is wasteful.  Repeatedly sample with replacement until there are
-1133         % k unique values.
-1134         else
-1135             x = zeros(1,n); % flags
-1136             sumx = 0;
-1137             while sumx < k
-1138                 x(ceil(n * rand(1,k-sumx))) = 1; % sample w/replacement
-1139                 sumx = sum(x); % count how many unique elements so far
-1140             end
-1141             y = find(x > 0);
-1142             y = y(randperm(k));
-1143         end
-1144     else
-1145         error('stats:randsample:NoWeighting',...
-1146               'Weighted sampling without replacement is not supported.');
-1147     end
-1148 otherwise
-1149     error('stats:randsample:BadReplaceValue',...
-1150           'REPLACE must be either true or false.');
-1151 end
-1152 
-1153 if ~isempty(population)
-1154     y = population(y);
-1155 else
-1156     y = y(:);
-1157 end
-1158 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/addExchangeRxns.html b/doc/manipulation/addExchangeRxns.html deleted file mode 100644 index 4f3834cc..00000000 --- a/doc/manipulation/addExchangeRxns.html +++ /dev/null @@ -1,202 +0,0 @@ - - - - Description of addExchangeRxns - - - - - - - - - -
Home > manipulation > addExchangeRxns.m
- - - -

addExchangeRxns -

- -

PURPOSE ^

-
addExchangeRxns Add exchange reactions for some metabolites.
- -

SYNOPSIS ^

-
function [model, addedRxns]=addExchangeRxns(model,reactionType,varargin)
- -

DESCRIPTION ^

-
 addExchangeRxns  Add exchange reactions for some metabolites.
-
- This is a faster version than addRxns when adding exchange reactions.
- New reactions are named "metName exchange (OUT/IN/BOTH)" while reaction
- ids are formatted as "EXC_OUT/IN/BOTH_METID".
-
- Parameters
- ----------
- model : struct
-     a model structure.
- reactionType : char
-     the type of reactions to add:
-
-     - 'in'   : input reactions
-     - 'out'  : output reactions
-     - 'both' : reversible input/output reactions. Positive direction
-       corresponds to output
-
- Name-Value Arguments
- --------------------
- mets : cell or logical or double
-     either a cell array of metabolite IDs, a logical vector with the same
-     number of elements as metabolites in the model, or a vector of
-     indexes to add for (default model.mets).
-
- Returns
- -------
- model : struct
-     updated model structure.
- addedRxns : cell
-     ids of the added reactions.
-
- Examples
- --------
-     [model, addedRxns] = addExchangeRxns(model, reactionType, mets);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function [model, addedRxns]=addExchangeRxns(model,reactionType,varargin)
-0002 % addExchangeRxns  Add exchange reactions for some metabolites.
-0003 %
-0004 % This is a faster version than addRxns when adding exchange reactions.
-0005 % New reactions are named "metName exchange (OUT/IN/BOTH)" while reaction
-0006 % ids are formatted as "EXC_OUT/IN/BOTH_METID".
-0007 %
-0008 % Parameters
-0009 % ----------
-0010 % model : struct
-0011 %     a model structure.
-0012 % reactionType : char
-0013 %     the type of reactions to add:
-0014 %
-0015 %     - 'in'   : input reactions
-0016 %     - 'out'  : output reactions
-0017 %     - 'both' : reversible input/output reactions. Positive direction
-0018 %       corresponds to output
-0019 %
-0020 % Name-Value Arguments
-0021 % --------------------
-0022 % mets : cell or logical or double
-0023 %     either a cell array of metabolite IDs, a logical vector with the same
-0024 %     number of elements as metabolites in the model, or a vector of
-0025 %     indexes to add for (default model.mets).
-0026 %
-0027 % Returns
-0028 % -------
-0029 % model : struct
-0030 %     updated model structure.
-0031 % addedRxns : cell
-0032 %     ids of the added reactions.
-0033 %
-0034 % Examples
-0035 % --------
-0036 %     [model, addedRxns] = addExchangeRxns(model, reactionType, mets);
-0037 
-0038 p=parseRAVENargs(varargin, {'mets',[]});
-0039 mets=p.mets;
-0040 if isempty(mets)
-0041     mets=model.mets;
-0042 elseif ~islogical(mets) && ~isnumeric(mets)
-0043     mets=convertCharArray(mets);
-0044 end
-0045 J=getIndexes(model,mets,'mets',false);
-0046 mets=model.mets(J);
-0047 
-0048 reactionType=char(upper(reactionType));
-0049 
-0050 %Production is positive for OUT and BOTH
-0051 if strcmp(reactionType,'IN')
-0052     I=speye(numel(model.mets));
-0053 else
-0054     I=speye(numel(model.mets))*-1;
-0055 end
-0056 I=I(:,J);
-0057 
-0058 %Add an I matrix which corresponds to production of all metabolites
-0059 model.S=[model.S I];
-0060 if strcmp(reactionType,'BOTH')
-0061     model.lb=[model.lb;ones(numel(J),1)*-1000];
-0062     model.rev=[model.rev;ones(numel(J),1)];
-0063 else
-0064     model.lb=[model.lb;zeros(numel(J),1)];
-0065     model.rev=[model.rev;zeros(numel(J),1)];
-0066 end
-0067 model.ub=[model.ub;ones(numel(J),1)*1000];
-0068 model.c=[model.c;zeros(numel(J),1)];
-0069 
-0070 filler=cell(numel(J),1);
-0071 filler(:)={''};
-0072 addedRxns=strcat('EXC_',reactionType,'_',mets);
-0073 addedRxnNames=strcat(model.metNames(J),' exchange (',reactionType,')');
-0074 model.rxns=[model.rxns;addedRxns];
-0075 model.rxnNames=[model.rxnNames;addedRxnNames];
-0076 
-0077 if isfield(model,'eccodes')
-0078     model.eccodes=[model.eccodes;filler];
-0079 end
-0080 if isfield(model,'subSystems')
-0081     fillerSub = filler;
-0082     if any(cellfun(@(x) iscell(x), model.subSystems))
-0083         fillerSub(:)={{''}};
-0084     end
-0085     model.subSystems=[model.subSystems;fillerSub];
-0086 end
-0087 if isfield(model,'grRules')
-0088     model.grRules=[model.grRules;filler];
-0089 end
-0090 if isfield(model,'rxnFrom')
-0091     model.rxnFrom=[model.rxnFrom;filler];
-0092 end
-0093 if isfield(model,'rxnMiriams')
-0094     model.rxnMiriams=[model.rxnMiriams;filler];
-0095 end
-0096 if isfield(model,'rxnGeneMat')
-0097     model.rxnGeneMat=[model.rxnGeneMat;sparse(numel(J),numel(model.genes))];
-0098 end
-0099 if isfield(model,'rxnComps')
-0100     model.rxnComps=[model.rxnComps;ones(numel(J),1)];
-0101     fprintf('NOTE: The exchange reactions are assigned to the first compartment\n');
-0102 end
-0103 if isfield(model,'rxnNotes')
-0104     model.rxnNotes=[model.rxnNotes;filler];
-0105 end
-0106 if isfield(model,'rxnReferences')
-0107     model.rxnReferences=[model.rxnReferences;filler];
-0108 end
-0109 if isfield(model,'rxnConfidenceScores')
-0110     model.rxnConfidenceScores=[model.rxnConfidenceScores;NaN(numel(J),1)];
-0111 end
-0112 if isfield(model,'rxnDeltaG')
-0113     model.rxnDeltaG=[model.rxnDeltaG;NaN(numel(J),1)];
-0114 end
-0115 if isfield(model,'pwys')
-0116     model.pwys=[model.pwys;filler];
-0117 end
-0118 if isfield(model,'spontaneous')
-0119     model.spontaneous=[model.spontaneous;false(numel(J),1)];
-0120 end
-0121 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/addGenesRaven.html b/doc/manipulation/addGenesRaven.html deleted file mode 100644 index 2dc1596e..00000000 --- a/doc/manipulation/addGenesRaven.html +++ /dev/null @@ -1,240 +0,0 @@ - - - - Description of addGenesRaven - - - - - - - - - -
Home > manipulation > addGenesRaven.m
- - - -

addGenesRaven -

- -

PURPOSE ^

-
addGenesRaven Add genes to a model.
- -

SYNOPSIS ^

-
function newModel=addGenesRaven(model,genesToAdd)
- -

DESCRIPTION ^

-
 addGenesRaven  Add genes to a model.
-
- This function does not make extensive checks about MIRIAM formats,
- forbidden characters or such.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- genesToAdd : struct
-     the genes to add, which can have the following fields:
-
-     - genes : cell array with unique strings that identifies each gene.
-       Only characters which are allowed in SBML ids are allowed (mainly
-       a-z, 0-9 and "_"). However, there is no check for this performed,
-       as it only matters if the model should be exported to SBML
-     - geneShortNames : cell array of gene abbreviations (optional,
-       default "")
-     - geneMiriams : cell array with MIRIAM structures (optional,
-       default [])
-     - proteins : cell array of protein names associated to each gene
-       (optional, default "")
-
- Returns
- -------
- newModel : struct
-     an updated model structure.
-
- Examples
- --------
-     newModel = addGenesRaven(model, genesToAdd);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • addRxns addRxns Add reactions to a model.
  • changeGrRules changeGrRules Change multiple grRules at the same time.
- - - - -

SOURCE CODE ^

-
0001 function newModel=addGenesRaven(model,genesToAdd)
-0002 % addGenesRaven  Add genes to a model.
-0003 %
-0004 % This function does not make extensive checks about MIRIAM formats,
-0005 % forbidden characters or such.
-0006 %
-0007 % Parameters
-0008 % ----------
-0009 % model : struct
-0010 %     a model structure.
-0011 % genesToAdd : struct
-0012 %     the genes to add, which can have the following fields:
-0013 %
-0014 %     - genes : cell array with unique strings that identifies each gene.
-0015 %       Only characters which are allowed in SBML ids are allowed (mainly
-0016 %       a-z, 0-9 and "_"). However, there is no check for this performed,
-0017 %       as it only matters if the model should be exported to SBML
-0018 %     - geneShortNames : cell array of gene abbreviations (optional,
-0019 %       default "")
-0020 %     - geneMiriams : cell array with MIRIAM structures (optional,
-0021 %       default [])
-0022 %     - proteins : cell array of protein names associated to each gene
-0023 %       (optional, default "")
-0024 %
-0025 % Returns
-0026 % -------
-0027 % newModel : struct
-0028 %     an updated model structure.
-0029 %
-0030 % Examples
-0031 % --------
-0032 %     newModel = addGenesRaven(model, genesToAdd);
-0033 
-0034 newModel=model;
-0035 
-0036 if isempty(genesToAdd)
-0037     return;
-0038 end
-0039 
-0040 %Check some stuff regarding the required fields
-0041 if ~isfield(genesToAdd,'genes')
-0042     EM='genes is a required field in genesToAdd';
-0043     error('RAVEN:badInput', '%s', EM);
-0044 else
-0045     genesToAdd.genes=convertCharArray(genesToAdd.genes);
-0046 end
-0047 
-0048 nOldGenes=numel(model.genes);
-0049 largeFiller=cell(nOldGenes,1);
-0050 largeFiller(:)={''};
-0051 
-0052 %Check that no gene ids are already present in the model
-0053 I=ismember(genesToAdd.genes,model.genes);
-0054 if all(I)
-0055     warning('All genes in genesToAdd.genes are already present in model.genes');
-0056     return
-0057 elseif any(I)
-0058     existingGenes=strjoin(genesToAdd.genes(I), ', ');
-0059     warning(['The following genes are already present in model.genes and will therefore not be added: ', existingGenes])
-0060     genesToAdd.genes(I)=[];
-0061     if isfield(genesToAdd,'geneShortNames')
-0062         genesToAdd.geneShortNames(I)=[];
-0063     end
-0064     if isfield(genesToAdd,'proteins')
-0065         genesToAdd.proteins(I)=[];
-0066     end
-0067     if isfield(genesToAdd,'geneMiriams')
-0068         genesToAdd.geneMiriams(I)=[];
-0069     end
-0070     if isfield(genesToAdd,'geneComps')
-0071         genesToAdd.geneComps(I)=[];
-0072     end
-0073 end
-0074 newModel.genes=[newModel.genes;genesToAdd.genes(:)];
-0075 
-0076 %Number of genes actually being added, now that any already-present genes
-0077 %have been trimmed out; the per-field length checks below compare against
-0078 %this
-0079 nGenes=numel(genesToAdd.genes);
-0080 filler=cell(nGenes,1);
-0081 filler(:)={''};
-0082 
-0083 %Some more checks and if they pass then add each field to the structure
-0084 if isfield(genesToAdd,'geneShortNames')
-0085     genesToAdd.geneShortNames=convertCharArray(genesToAdd.geneShortNames);
-0086     if numel(genesToAdd.geneShortNames)~=nGenes
-0087         EM='genesToAdd.geneShortNames must have the same number of elements as genesToAdd.genes';
-0088         error('RAVEN:badInput', '%s', EM);
-0089     end
-0090     %Add empty field if it does not exist
-0091     if ~isfield(newModel,'geneShortNames')
-0092         newModel.geneShortNames=largeFiller;
-0093     end
-0094     newModel.geneShortNames=[newModel.geneShortNames;genesToAdd.geneShortNames(:)];
-0095 else
-0096     %Add empty strings if the structure is in the model
-0097     if isfield(newModel,'geneShortNames')
-0098         newModel.geneShortNames=[newModel.geneShortNames;filler];
-0099     end
-0100 end
-0101 if isfield(genesToAdd,'proteins')
-0102     genesToAdd.proteins=convertCharArray(genesToAdd.proteins);
-0103     if numel(genesToAdd.proteins)~=nGenes
-0104         EM='genesToAdd.proteins must have the same number of elements as genesToAdd.genes';
-0105         error('RAVEN:badInput', '%s', EM);
-0106     end
-0107     %Add empty field if it does not exist
-0108     if ~isfield(newModel,'proteins')
-0109         newModel.proteins=largeFiller;
-0110     end
-0111     newModel.proteins=[newModel.proteins;genesToAdd.proteins(:)];
-0112 else
-0113     %Add empty strings if the structure is in the model
-0114     if isfield(newModel,'proteins')
-0115         newModel.proteins=[newModel.proteins;filler];
-0116     end
-0117 end
-0118 
-0119 
-0120 %Do not check the type of geneMiriams
-0121 if isfield(genesToAdd,'geneMiriams')
-0122     if numel(genesToAdd.geneMiriams)~=nGenes
-0123         EM='genesToAdd.geneMiriams must have the same number of elements as genesToAdd.genes';
-0124         error('RAVEN:badInput', '%s', EM);
-0125     end
-0126     %Add empty field if it does not exist
-0127     if ~isfield(newModel,'geneMiriams')
-0128         newModel.geneMiriams=cell(nOldGenes,1);
-0129     end
-0130     newModel.geneMiriams=[newModel.geneMiriams;genesToAdd.geneMiriams(:)];
-0131 else
-0132     if isfield(newModel,'geneMiriams')
-0133         newModel.geneMiriams=[newModel.geneMiriams;cell(nGenes,1)];
-0134     end
-0135 end
-0136 
-0137 if isfield(genesToAdd,'geneComps')
-0138     if numel(genesToAdd.geneComps)~=nGenes
-0139         EM='genesToAdd.geneComps must have the same number of elements as genesToAdd.genes';
-0140         error('RAVEN:badInput', '%s', EM);
-0141     end
-0142     %Add empty field if it does not exist
-0143     if ~isfield(newModel,'geneComps')
-0144         newModel.geneComps=ones(nOldGenes,1);
-0145         EM='Adding genes with compartment information to a model without such information. All existing genes will be assigned to the first compartment';
-0146         warning('RAVEN:warning', '%s', EM);
-0147     end
-0148     newModel.geneComps=[newModel.geneComps;genesToAdd.geneComps(:)];
-0149 else
-0150     if isfield(newModel,'geneComps')
-0151         newModel.geneComps=[newModel.geneComps;ones(nGenes,1)];
-0152         fprintf('NOTE: The added genes will be assigned to the first compartment\n');
-0153     end
-0154 end
-0155 
-0156 if isfield(newModel,'geneFrom')
-0157     newModel.geneFrom=[newModel.geneFrom;filler];
-0158 end
-0159 
-0160 if isfield(newModel,'rxnGeneMat')
-0161     newModel.rxnGeneMat=[newModel.rxnGeneMat,zeros(length(newModel.rxns),nGenes)];
-0162 end
-0163 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/addMets.html b/doc/manipulation/addMets.html deleted file mode 100644 index e9e0679c..00000000 --- a/doc/manipulation/addMets.html +++ /dev/null @@ -1,489 +0,0 @@ - - - - Description of addMets - - - - - - - - - -
Home > manipulation > addMets.m
- - - -

addMets -

- -

PURPOSE ^

-
addMets Add metabolites to a model.
- -

SYNOPSIS ^

-
function newModel=addMets(model,metsToAdd,varargin)
- -

DESCRIPTION ^

-
 addMets  Add metabolites to a model.
-
- This function does not make extensive checks about MIRIAM formats,
- forbidden characters or such.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- metsToAdd : struct
-     the metabolite structure, which can have the following fields:
-
-     - mets : cell array with unique strings that identifies each
-       metabolite (optional, default IDs of new metabolites are numbered
-       with the prefix defined below)
-     - metNames : cell array with the names of each metabolite
-     - compartments : cell array with the compartment of each metabolite.
-       Should match model.comps. If this is a string rather than a cell
-       array it is assumed that all mets are in that compartment
-     - b : Nx1 or Nx2 matrix with equality constraints for each
-       metabolite (optional, default 0)
-     - unconstrained : vector describing if each metabolite is an exchange
-       metabolite (1) or not (0) (optional, default 0)
-     - inchis : cell array with InChI strings (optional, default "")
-     - metSmiles : cell array with SMILES strings (optional, default "")
-     - metFormulas : cell array with the formulas (optional, default "")
-     - metMiriams : cell array with MIRIAM structures (optional,
-       default [])
-     - metCharges : metabolite charge (optional, default NaN)
-     - metDeltaG : Gibbs free energy of formation at biochemical standard
-       condition in kJ/mole (optional, default NaN)
-     - metNotes : cell array with metabolite notes as strings (optional,
-       default "")
-
- Name-Value Arguments
- --------------------
- copyInfo : logical
-     when adding metabolites to a compartment where it previously did not
-     exist, the function will copy any available annotation from the
-     metabolite in another compartment (default true).
- prefix : char
-     when metsToAdd.mets is not specified, new metabolite IDs are
-     generated with the prefix specified here. If IDs with the prefix are
-     already used in the model then the numbering will start from the
-     highest existing integer+1 (default "m_").
-
- Returns
- -------
- newModel : struct
-     an updated model structure.
-
- Examples
- --------
-     newModel = addMets(model, metsToAdd, copyInfo, prefix);
-
- Notes
- -----
- If multiple metabolites are added at once, the metMiriams cell array
- should be defined as (example with ChEBI and KEGG):
-
-     metsToAdd.metMiriams{1} = struct("name",{{"chebi";"kegg.compound"}},...
-         "value",{{"CHEBI:18072";"C11821"}});
-     metsToAdd.metMiriams{2} = struct("name",{{"chebi";"kegg.compound"}},...
-         "value",{{"CHEBI:31132";"C12248"}});
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • generateNewIds generateNewIds Generate a list of new metabolite or reaction ids.
-This function is called by: -
    -
  • addRxns addRxns Add reactions to a model.
  • addRxnsGenesMets addRxnsGenesMets Copy reactions from a source model into another model.
  • addTransport addTransport Add transport reactions between compartments.
- - - - -

SOURCE CODE ^

-
0001 function newModel=addMets(model,metsToAdd,varargin)
-0002 % addMets  Add metabolites to a model.
-0003 %
-0004 % This function does not make extensive checks about MIRIAM formats,
-0005 % forbidden characters or such.
-0006 %
-0007 % Parameters
-0008 % ----------
-0009 % model : struct
-0010 %     a model structure.
-0011 % metsToAdd : struct
-0012 %     the metabolite structure, which can have the following fields:
-0013 %
-0014 %     - mets : cell array with unique strings that identifies each
-0015 %       metabolite (optional, default IDs of new metabolites are numbered
-0016 %       with the prefix defined below)
-0017 %     - metNames : cell array with the names of each metabolite
-0018 %     - compartments : cell array with the compartment of each metabolite.
-0019 %       Should match model.comps. If this is a string rather than a cell
-0020 %       array it is assumed that all mets are in that compartment
-0021 %     - b : Nx1 or Nx2 matrix with equality constraints for each
-0022 %       metabolite (optional, default 0)
-0023 %     - unconstrained : vector describing if each metabolite is an exchange
-0024 %       metabolite (1) or not (0) (optional, default 0)
-0025 %     - inchis : cell array with InChI strings (optional, default "")
-0026 %     - metSmiles : cell array with SMILES strings (optional, default "")
-0027 %     - metFormulas : cell array with the formulas (optional, default "")
-0028 %     - metMiriams : cell array with MIRIAM structures (optional,
-0029 %       default [])
-0030 %     - metCharges : metabolite charge (optional, default NaN)
-0031 %     - metDeltaG : Gibbs free energy of formation at biochemical standard
-0032 %       condition in kJ/mole (optional, default NaN)
-0033 %     - metNotes : cell array with metabolite notes as strings (optional,
-0034 %       default "")
-0035 %
-0036 % Name-Value Arguments
-0037 % --------------------
-0038 % copyInfo : logical
-0039 %     when adding metabolites to a compartment where it previously did not
-0040 %     exist, the function will copy any available annotation from the
-0041 %     metabolite in another compartment (default true).
-0042 % prefix : char
-0043 %     when metsToAdd.mets is not specified, new metabolite IDs are
-0044 %     generated with the prefix specified here. If IDs with the prefix are
-0045 %     already used in the model then the numbering will start from the
-0046 %     highest existing integer+1 (default "m_").
-0047 %
-0048 % Returns
-0049 % -------
-0050 % newModel : struct
-0051 %     an updated model structure.
-0052 %
-0053 % Examples
-0054 % --------
-0055 %     newModel = addMets(model, metsToAdd, copyInfo, prefix);
-0056 %
-0057 % Notes
-0058 % -----
-0059 % If multiple metabolites are added at once, the metMiriams cell array
-0060 % should be defined as (example with ChEBI and KEGG):
-0061 %
-0062 %     metsToAdd.metMiriams{1} = struct("name",{{"chebi";"kegg.compound"}},...
-0063 %         "value",{{"CHEBI:18072";"C11821"}});
-0064 %     metsToAdd.metMiriams{2} = struct("name",{{"chebi";"kegg.compound"}},...
-0065 %         "value",{{"CHEBI:31132";"C12248"}});
-0066 
-0067 p=parseRAVENargs(varargin, {'copyInfo',true; 'prefix','m_'});
-0068 copyInfo=p.copyInfo;
-0069 prefix=char(p.prefix);
-0070 
-0071 newModel=model;
-0072 
-0073 if isempty(metsToAdd)
-0074     return;
-0075 end
-0076 
-0077 %Check some stuff regarding the required fields
-0078 if ~isfield(metsToAdd,'mets')
-0079     metsToAdd.metNames=convertCharArray(metsToAdd.metNames);
-0080     metsToAdd.mets=generateNewIds(newModel,'mets',prefix,numel(metsToAdd.metNames));
-0081 else
-0082     metsToAdd.mets=convertCharArray(metsToAdd.mets);
-0083 end
-0084 if ~isfield(metsToAdd,'metNames')
-0085     metsToAdd.metNames=metsToAdd.mets;
-0086 else
-0087     metsToAdd.metNames=convertCharArray(metsToAdd.metNames);
-0088 end
-0089 if ~isfield(metsToAdd,'compartments')
-0090     EM='compartments is a required field in metsToAdd';
-0091     error('RAVEN:badInput', '%s', EM);
-0092 else
-0093     metsToAdd.compartments=convertCharArray(metsToAdd.compartments);
-0094     %If only one compartment is given, assume it is for all metabolites
-0095     if numel(metsToAdd.compartments)==1 && numel(metsToAdd.mets)>1
-0096         temp=cell(numel(metsToAdd.mets),1);
-0097         temp(:)=metsToAdd.compartments;
-0098         metsToAdd.compartments=temp;
-0099     end
-0100 end
-0101 
-0102 %Number of metabolites
-0103 nMets=numel(metsToAdd.mets);
-0104 nOldMets=numel(model.mets);
-0105 filler=cell(nMets,1);
-0106 filler(:)={''};
-0107 largeFiller=cell(nOldMets,1);
-0108 largeFiller(:)={''};
-0109 
-0110 %Check that no metabolite ids are already present in the model
-0111 I=ismember(metsToAdd.mets,model.mets);
-0112 if any(I)
-0113     error('One or more elements in metsToAdd.mets are already present in model.mets: %s',...
-0114         strjoin(metsToAdd.mets(I),', '));
-0115 else
-0116     newModel.mets=[newModel.mets;metsToAdd.mets(:)];
-0117 end
-0118 
-0119 %Check that all the compartments could be found
-0120 [I, compMap]=ismember(metsToAdd.compartments,model.comps);
-0121 if ~all(I)
-0122     EM='metsToAdd.compartments must match model.comps';
-0123     error('RAVEN:badInput', '%s', EM);
-0124 end
-0125 
-0126 %Check that the metabolite names are not present in the same compartment.
-0127 %Not the neatest way maybe..
-0128 t1=strcat(metsToAdd.metNames(:),'***',metsToAdd.compartments(:));
-0129 t2=strcat(model.metNames,'***',model.comps(model.metComps));
-0130 if any(ismember(t1,t2))
-0131     EM='One or more elements in metsToAdd.metNames already exist in the same compartments as the one it is being added to';
-0132     error('RAVEN:badInput', '%s', EM);
-0133 end
-0134 
-0135 %Some more checks and if they pass then add each field to the structure
-0136 if numel(metsToAdd.metNames)~=nMets
-0137     EM='metsToAdd.metNames must have the same number of elements as metsToAdd.mets';
-0138     error('RAVEN:badInput', '%s', EM);
-0139 else
-0140     newModel.metNames=[newModel.metNames;metsToAdd.metNames(:)];
-0141 end
-0142 
-0143 if numel(compMap)~=nMets
-0144     EM='metsToAdd.compartments must have the same number of elements as metsToAdd.mets';
-0145     error('RAVEN:badInput', '%s', EM);
-0146 else
-0147     newModel.metComps=[newModel.metComps;compMap(:)];
-0148 end
-0149 
-0150 if isfield(metsToAdd,'b')
-0151     if size(metsToAdd.b,1)~=nMets
-0152         EM='metsToAdd.b must have the same number of elements as metsToAdd.mets';
-0153         error('RAVEN:badInput', '%s', EM);
-0154     else
-0155         %Add empty field if it does not exist
-0156         if ~isfield(newModel,'b')
-0157             newModel.b=zeros(nOldMets,1);
-0158         end
-0159         
-0160         %If the original is only one vector
-0161         if size(metsToAdd.b,2)>size(newModel.b,2)
-0162             newModel.b=[newModel.b newModel.b];
-0163         end
-0164         %Add the new ones
-0165         newModel.b=[newModel.b;metsToAdd.b];
-0166     end
-0167 else
-0168     if isfield(newModel,'b')
-0169         %Add the default
-0170         newModel.b=[newModel.b;zeros(nMets,size(newModel.b,2))];
-0171     end
-0172 end
-0173 
-0174 if isfield(metsToAdd,'unconstrained')
-0175     if numel(metsToAdd.unconstrained)~=nMets
-0176         EM='metsToAdd.unconstrained must have the same number of elements as metsToAdd.mets';
-0177         error('RAVEN:badInput', '%s', EM);
-0178     else
-0179         %Add empty field if it does not exist
-0180         if ~isfield(newModel,'unconstrained')
-0181             newModel.unconstrained=zeros(nOldMets,1);
-0182         end
-0183         
-0184         %Add the new ones
-0185         newModel.unconstrained=[newModel.unconstrained;metsToAdd.unconstrained(:)];
-0186     end
-0187 else
-0188     if isfield(newModel,'unconstrained')
-0189         %Add the default
-0190         newModel.unconstrained=[newModel.unconstrained;zeros(nMets,1)];
-0191     end
-0192 end
-0193 
-0194 if isfield(metsToAdd,'inchis')
-0195     metsToAdd.inchis=convertCharArray(metsToAdd.inchis);
-0196     if numel(metsToAdd.inchis)~=nMets
-0197         EM='metsToAdd.inchis must have the same number of elements as metsToAdd.mets';
-0198         error('RAVEN:badInput', '%s', EM);
-0199     end
-0200     %Add empty field if it does not exist
-0201     if ~isfield(newModel,'inchis')
-0202         newModel.inchis=largeFiller;
-0203     end
-0204     newModel.inchis=[newModel.inchis;metsToAdd.inchis(:)];
-0205 else
-0206     %Add empty strings if the structure is in the model
-0207     if isfield(newModel,'inchis')
-0208         newModel.inchis=[newModel.inchis;filler];
-0209     end
-0210 end
-0211 
-0212 
-0213 if isfield(metsToAdd,'metSmiles')
-0214     metsToAdd.metSmiles=convertCharArray(metsToAdd.metSmiles);
-0215     if numel(metsToAdd.metSmiles)~=nMets
-0216         EM='metsToAdd.metSmiles must have the same number of elements as metsToAdd.mets';
-0217         error('RAVEN:badInput', '%s', EM);
-0218     end
-0219     %Add empty field if it does not exist
-0220     if ~isfield(newModel,'metSmiles')
-0221         newModel.metSmiles=largeFiller;
-0222     end
-0223     newModel.metSmiles=[newModel.metSmiles;metsToAdd.metSmiles(:)];
-0224 else
-0225     %Add empty strings if the structure is in the model
-0226     if isfield(newModel,'metSmiles')
-0227         newModel.metSmiles=[newModel.metSmiles;filler];
-0228     end
-0229 end
-0230 
-0231 if isfield(metsToAdd,'metFormulas')
-0232     metsToAdd.metFormulas=convertCharArray(metsToAdd.metFormulas);
-0233     if numel(metsToAdd.metFormulas)~=nMets
-0234         EM='metsToAdd.metFormulas must have the same number of elements as metsToAdd.mets';
-0235         error('RAVEN:badInput', '%s', EM);
-0236     end
-0237     %Add empty field if it does not exist
-0238     if ~isfield(newModel,'metFormulas')
-0239         newModel.metFormulas=largeFiller;
-0240     end
-0241     newModel.metFormulas=[newModel.metFormulas;metsToAdd.metFormulas(:)];
-0242 else
-0243     %Add default
-0244     if isfield(newModel,'metFormulas')
-0245         newModel.metFormulas=[newModel.metFormulas;filler];
-0246     end
-0247 end
-0248 
-0249 if isfield(metsToAdd,'metCharges')
-0250     if numel(metsToAdd.metCharges)~=nMets
-0251         EM='metsToAdd.metCharges must have the same number of elements as metsToAdd.mets';
-0252         error('RAVEN:badInput', '%s', EM);
-0253     end
-0254     if ~isnumeric(metsToAdd.metCharges)
-0255         EM='metsToAdd.metCharges must be of type "double"';
-0256         error('RAVEN:badInput', '%s', EM);
-0257     end
-0258     if ~isfield(newModel,'metCharges')
-0259         newModel.metCharges=NaN(numel(largeFiller),1);
-0260     end
-0261     newModel.metCharges=[newModel.metCharges;metsToAdd.metCharges(:)];
-0262 else
-0263     %Add default
-0264     if isfield(newModel,'metCharges')
-0265         newModel.metCharges=[newModel.metCharges;NaN(numel(filler),1)];
-0266     end
-0267 end
-0268 
-0269 if isfield(metsToAdd,'metDeltaG')
-0270     if numel(metsToAdd.metDeltaG)~=nMets
-0271         EM='metsToAdd.metDeltaG must have the same number of elements as metsToAdd.mets';
-0272         error('RAVEN:badInput', '%s', EM);
-0273     end
-0274     if ~isnumeric(metsToAdd.metDeltaG)
-0275         EM='metsToAdd.metDeltaG must be of type "double"';
-0276         error('RAVEN:badInput', '%s', EM);
-0277     end
-0278     if ~isfield(newModel,'metDeltaG')
-0279         newModel.metDeltaG=NaN(numel(largeFiller),1);
-0280     end
-0281     newModel.metDeltaG=[newModel.metDeltaG;metsToAdd.metDeltaG(:)];
-0282 else
-0283     %Add default
-0284     if isfield(newModel,'metDeltaG')
-0285         newModel.metDeltaG=[newModel.metDeltaG;NaN(numel(filler),1)];
-0286     end
-0287 end
-0288 
-0289 
-0290 if isfield(metsToAdd,'metNotes')
-0291     metsToAdd.metNotes=convertCharArray(metsToAdd.metNotes);
-0292     if numel(metsToAdd.metNotes)==1 && numel(metsToAdd.mets)>1
-0293         temp=cell(numel(metsToAdd.mets),1);
-0294         temp(:)=metsToAdd.metNotes;
-0295         metsToAdd.metNotes=temp;
-0296     end
-0297     if numel(metsToAdd.metNotes)~=nMets
-0298         EM='metsToAdd.metNotes must have the same number of elements as metsToAdd.mets';
-0299         error('RAVEN:badInput', '%s', EM);
-0300     end
-0301     %Add empty field if it does not exist
-0302     if ~isfield(newModel,'metNotes')
-0303         newModel.metNotes=largeFiller;
-0304     end
-0305     newModel.metNotes=[newModel.metNotes;metsToAdd.metNotes(:)];
-0306 else
-0307     %Add empty strings if the structure is in the model
-0308     if isfield(newModel,'metNotes')
-0309         newModel.metNotes=[newModel.metNotes;filler];
-0310     end
-0311 end
-0312 
-0313 %Do not check the type of metMiriams
-0314 if isfield(metsToAdd,'metMiriams')
-0315     if numel(metsToAdd.metMiriams)==1 && numel(metsToAdd.mets)>1
-0316         temp=cell(numel(metsToAdd.mets),1);
-0317         temp(:)={metsToAdd.metMiriams};
-0318         metsToAdd.metMiriams=temp;
-0319     end
-0320     if numel(metsToAdd.metMiriams)~=nMets
-0321         EM='metsToAdd.metMiriams must have the same number of elements as metsToAdd.mets';
-0322         error('RAVEN:badInput', '%s', EM);
-0323     end
-0324     %Add empty field if it does not exist
-0325     if ~isfield(newModel,'metMiriams')
-0326         newModel.metMiriams=cell(nOldMets,1);
-0327     end
-0328     newModel.metMiriams=[newModel.metMiriams;metsToAdd.metMiriams(:)];
-0329 else
-0330     if isfield(newModel,'metMiriams')
-0331         newModel.metMiriams=[newModel.metMiriams;cell(nMets,1)];
-0332     end
-0333 end
-0334 
-0335 if isfield(newModel,'metFrom')
-0336     newModel.metFrom=[newModel.metFrom;filler];
-0337 end
-0338 
-0339 %Expand the S matrix
-0340 newModel.S=[newModel.S;sparse(nMets,size(newModel.S,2))];
-0341 
-0342 if copyInfo==true
-0343     [I, J]=ismember(metsToAdd.metNames,model.metNames);
-0344     J=J(I);
-0345     %I is the indexes of the new metabolites for which a metabolite with
-0346     %the same name exists
-0347     I=find(I)+nOldMets;
-0348     %Go through each of the added mets and copy annotation if it does not
-0349     %exist
-0350     for i=1:numel(I)
-0351         if isfield(newModel,'inchis')
-0352             if isempty(newModel.inchis{I(i)})
-0353                 newModel.inchis(I(i))=newModel.inchis(J(i));
-0354             end
-0355         end
-0356         if isfield(newModel,'metSmiles')
-0357             if isempty(newModel.metSmiles{I(i)})
-0358                 newModel.metSmiles(I(i))=newModel.metSmiles(J(i));
-0359             end
-0360         end        
-0361         if isfield(newModel,'metFormulas')
-0362             if isempty(newModel.metFormulas{I(i)})
-0363                 newModel.metFormulas(I(i))=newModel.metFormulas(J(i));
-0364             end
-0365         end
-0366         if isfield(newModel,'metMiriams')
-0367             if isempty(newModel.metMiriams{I(i)})
-0368                 newModel.metMiriams(I(i))=newModel.metMiriams(J(i));
-0369             end
-0370         end
-0371         if isfield(newModel,'metCharges')
-0372             newModel.metCharges(I(i))=newModel.metCharges(J(i));
-0373         end
-0374         if isfield(newModel,'metDeltaG')
-0375             newModel.metDeltaG(I(i))=newModel.metDeltaG(J(i));
-0376         end
-0377     end
-0378 end
-0379 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/addRxns.html b/doc/manipulation/addRxns.html deleted file mode 100644 index df2b0e5f..00000000 --- a/doc/manipulation/addRxns.html +++ /dev/null @@ -1,895 +0,0 @@ - - - - Description of addRxns - - - - - - - - - -
Home > manipulation > addRxns.m
- - - -

addRxns -

- -

PURPOSE ^

-
addRxns Add reactions to a model.
- -

SYNOPSIS ^

-
function newModel=addRxns(model,rxnsToAdd,varargin)
- -

DESCRIPTION ^

-
 addRxns  Add reactions to a model.
-
- This function does not make extensive checks about formatting of
- gene-reaction rules.
-
- When adding metabolites to a compartment where they previously do not
- exist, the function will copy any available information from the
- metabolite in another compartment.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- rxnsToAdd : struct
-     the reaction structure, which can have the following fields:
-
-     - rxns : cell array with unique strings that identifies each reaction
-     - equations : cell array with equation strings. Decimal coefficients
-       are expressed as "1.2". Reversibility is indicated by "<=>" or "=>"
-     - mets : (alternative to equations) cell array with the metabolites
-       involved in each reaction as nested arrays. E.g.:
-       {{"met1","met2"},{"met1","met3","met4"}}. In the case of one single
-       reaction added, it can be a string array: {"met1","met2"}
-     - stoichCoeffs : (alternative to equations) cell array with the
-       corresponding stoichiometries as nested vectors. E.g.:
-       {[-1,+2],[-1,-1,+1]}. In the case of one single reaction added, it
-       can be a vector: [-1,+2]
-     - rxnNames : cell array with the names of each reaction (optional,
-       default "")
-     - lb : vector with the lower bounds (optional, default
-       model.annotations.defaultLB or -inf for reversible reactions and 0
-       for irreversible when "equations" is used. When "mets" and
-       "stoichCoeffs" are used it defaults for all to
-       model.annotations.defaultLB or -inf)
-     - ub : vector with the upper bounds (optional, default
-       model.annotations.defaultUB or inf)
-     - c : vector with the objective function coefficients (optional,
-       default 0)
-     - eccodes : cell array with the EC-numbers for each reaction. Delimit
-       several EC-numbers with ";" (optional, default "")
-     - subSystems : cell array with the subsystems for each reaction
-       (optional, default "")
-     - grRules : cell array with the gene-reaction relationship for each
-       reaction. E.g. "(A and B) or (C)" means that the reaction could be
-       catalyzed by a complex between A & B or by C on its own. All the
-       genes have to be present in model.genes. Add genes with
-       addGenesRaven before calling this function if needed (optional,
-       default "")
-     - rxnMiriams : cell array with Miriam structures (optional,
-       default [])
-     - rxnComps : cell array with compartments (as in model.comps)
-       (optional, default {})
-     - rxnNotes : cell array with reaction notes (optional, default "")
-     - rxnDeltaG : Gibbs free energy at biochemical standard condition in
-       kJ/mole (optional, default NaN)
-     - rxnReferences : cell array with reaction references (optional,
-       default "")
-     - rxnConfidenceScores : vector with reaction confidence scores
-       (optional, default NaN)
-
- Name-Value Arguments
- --------------------
- eqnType : double or char
-     describes how the equation string should be interpreted (default 1).
-     Accepts integer values 1/2/3 or the string aliases "id", "name",
-     "name[comp]":
-
-     - 1 / "id" : the metabolites are matched to model.mets. New
-       metabolites (if allowed) are added to "compartment"
-     - 2 / "name" : the metabolites are matched to model.metNames and all
-       metabolites are assigned to "compartment". Any new metabolites that
-       are added will be assigned IDs "m1", "m2"... If IDs on the same
-       form are already used in the model then the numbering will start
-       from the highest used integer+1
-     - 3 / "name[comp]" : the metabolites are written as "metNames[comps]".
-       Only compartments in model.comps are allowed. Any new metabolites
-       that are added will be assigned IDs "m1", "m2"... If IDs on the
-       same form are already used in the model then the numbering will
-       start from the highest used integer+1
- compartment : char
-     the compartment the metabolites should be placed in when using
-     eqnType=2. Must match model.comps (optional when eqnType=1 or
-     eqnType=3).
- allowNewMets : logical or char
-     true if the function is allowed to add new metabolites. Can also be a
-     string, which will be used as prefix for the new metabolite IDs. It
-     is highly recommended to first add any new metabolites with addMets
-     rather than automatically through this function. addMets supports
-     more annotation of metabolites, allows for the use of exchange
-     metabolites, and using it reduces the risk of parsing errors
-     (default false).
- allowNewGenes : logical
-     true if the function is allowed to add new genes (default false).
-
- Returns
- -------
- newModel : struct
-     an updated model structure.
-
- Examples
- --------
-     newModel = addRxns(model, rxnsToAdd, eqnType, compartment, ...
-                        allowNewMets, allowNewGenes);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
  • addRxnsGenesMets addRxnsGenesMets Copy reactions from a source model into another model.
  • changeRxns changeRxns Modify the equations of reactions in a model.
- - - - -

SOURCE CODE ^

-
0001 function newModel=addRxns(model,rxnsToAdd,varargin)
-0002 % addRxns  Add reactions to a model.
-0003 %
-0004 % This function does not make extensive checks about formatting of
-0005 % gene-reaction rules.
-0006 %
-0007 % When adding metabolites to a compartment where they previously do not
-0008 % exist, the function will copy any available information from the
-0009 % metabolite in another compartment.
-0010 %
-0011 % Parameters
-0012 % ----------
-0013 % model : struct
-0014 %     a model structure.
-0015 % rxnsToAdd : struct
-0016 %     the reaction structure, which can have the following fields:
-0017 %
-0018 %     - rxns : cell array with unique strings that identifies each reaction
-0019 %     - equations : cell array with equation strings. Decimal coefficients
-0020 %       are expressed as "1.2". Reversibility is indicated by "<=>" or "=>"
-0021 %     - mets : (alternative to equations) cell array with the metabolites
-0022 %       involved in each reaction as nested arrays. E.g.:
-0023 %       {{"met1","met2"},{"met1","met3","met4"}}. In the case of one single
-0024 %       reaction added, it can be a string array: {"met1","met2"}
-0025 %     - stoichCoeffs : (alternative to equations) cell array with the
-0026 %       corresponding stoichiometries as nested vectors. E.g.:
-0027 %       {[-1,+2],[-1,-1,+1]}. In the case of one single reaction added, it
-0028 %       can be a vector: [-1,+2]
-0029 %     - rxnNames : cell array with the names of each reaction (optional,
-0030 %       default "")
-0031 %     - lb : vector with the lower bounds (optional, default
-0032 %       model.annotations.defaultLB or -inf for reversible reactions and 0
-0033 %       for irreversible when "equations" is used. When "mets" and
-0034 %       "stoichCoeffs" are used it defaults for all to
-0035 %       model.annotations.defaultLB or -inf)
-0036 %     - ub : vector with the upper bounds (optional, default
-0037 %       model.annotations.defaultUB or inf)
-0038 %     - c : vector with the objective function coefficients (optional,
-0039 %       default 0)
-0040 %     - eccodes : cell array with the EC-numbers for each reaction. Delimit
-0041 %       several EC-numbers with ";" (optional, default "")
-0042 %     - subSystems : cell array with the subsystems for each reaction
-0043 %       (optional, default "")
-0044 %     - grRules : cell array with the gene-reaction relationship for each
-0045 %       reaction. E.g. "(A and B) or (C)" means that the reaction could be
-0046 %       catalyzed by a complex between A & B or by C on its own. All the
-0047 %       genes have to be present in model.genes. Add genes with
-0048 %       addGenesRaven before calling this function if needed (optional,
-0049 %       default "")
-0050 %     - rxnMiriams : cell array with Miriam structures (optional,
-0051 %       default [])
-0052 %     - rxnComps : cell array with compartments (as in model.comps)
-0053 %       (optional, default {})
-0054 %     - rxnNotes : cell array with reaction notes (optional, default "")
-0055 %     - rxnDeltaG : Gibbs free energy at biochemical standard condition in
-0056 %       kJ/mole (optional, default NaN)
-0057 %     - rxnReferences : cell array with reaction references (optional,
-0058 %       default "")
-0059 %     - rxnConfidenceScores : vector with reaction confidence scores
-0060 %       (optional, default NaN)
-0061 %
-0062 % Name-Value Arguments
-0063 % --------------------
-0064 % eqnType : double or char
-0065 %     describes how the equation string should be interpreted (default 1).
-0066 %     Accepts integer values 1/2/3 or the string aliases "id", "name",
-0067 %     "name[comp]":
-0068 %
-0069 %     - 1 / "id" : the metabolites are matched to model.mets. New
-0070 %       metabolites (if allowed) are added to "compartment"
-0071 %     - 2 / "name" : the metabolites are matched to model.metNames and all
-0072 %       metabolites are assigned to "compartment". Any new metabolites that
-0073 %       are added will be assigned IDs "m1", "m2"... If IDs on the same
-0074 %       form are already used in the model then the numbering will start
-0075 %       from the highest used integer+1
-0076 %     - 3 / "name[comp]" : the metabolites are written as "metNames[comps]".
-0077 %       Only compartments in model.comps are allowed. Any new metabolites
-0078 %       that are added will be assigned IDs "m1", "m2"... If IDs on the
-0079 %       same form are already used in the model then the numbering will
-0080 %       start from the highest used integer+1
-0081 % compartment : char
-0082 %     the compartment the metabolites should be placed in when using
-0083 %     eqnType=2. Must match model.comps (optional when eqnType=1 or
-0084 %     eqnType=3).
-0085 % allowNewMets : logical or char
-0086 %     true if the function is allowed to add new metabolites. Can also be a
-0087 %     string, which will be used as prefix for the new metabolite IDs. It
-0088 %     is highly recommended to first add any new metabolites with addMets
-0089 %     rather than automatically through this function. addMets supports
-0090 %     more annotation of metabolites, allows for the use of exchange
-0091 %     metabolites, and using it reduces the risk of parsing errors
-0092 %     (default false).
-0093 % allowNewGenes : logical
-0094 %     true if the function is allowed to add new genes (default false).
-0095 %
-0096 % Returns
-0097 % -------
-0098 % newModel : struct
-0099 %     an updated model structure.
-0100 %
-0101 % Examples
-0102 % --------
-0103 %     newModel = addRxns(model, rxnsToAdd, eqnType, compartment, ...
-0104 %                        allowNewMets, allowNewGenes);
-0105 
-0106 p=parseRAVENargs(varargin, {'eqnType',1; 'compartment',[]; 'allowNewMets',false; 'allowNewGenes',false});
-0107 eqnType=p.eqnType;
-0108 if ischar(eqnType) || isstring(eqnType)
-0109     switch lower(char(eqnType))
-0110         case 'id'
-0111             eqnType=1;
-0112         case 'name'
-0113             eqnType=2;
-0114         case 'name[comp]'
-0115             eqnType=3;
-0116         otherwise
-0117             error('RAVEN:badInput', '%s', ['Unknown eqnType string "' char(eqnType) '". Use ''id'', ''name'', or ''name[comp]''.']);
-0118     end
-0119 end
-0120 if ~isnumeric(eqnType)
-0121     EM='eqnType must be numeric or one of the string aliases ''id'', ''name'', ''name[comp]''';
-0122     error('RAVEN:badInput', '%s', EM);
-0123 elseif ~ismember(eqnType,[1 2 3])
-0124     EM='eqnType must be 1, 2, or 3';
-0125     error('RAVEN:badInput', '%s', EM);
-0126 end
-0127 compartment=p.compartment;
-0128 if ~isempty(compartment)
-0129     compartment=char(compartment);
-0130 end
-0131 allowNewMets=p.allowNewMets;
-0132 if ~islogical(allowNewMets)
-0133     allowNewMets=char(allowNewMets);
-0134 end
-0135 allowNewGenes=p.allowNewGenes;
-0136 
-0137 if allowNewGenes & isfield(rxnsToAdd,'grRules')
-0138     % getGenesFromGrRules splits only on space-bounded "and"/"or" (or their
-0139     % "&"/"|" equivalents), so a gene id that merely contains "and"/"or" as
-0140     % a substring (e.g. "band1", "orfeo2") is not mistaken for an operator
-0141     % the way a plain regexp split on the bare words would.
-0142     genesToAdd.genes = getGenesFromGrRules(convertCharArray(rxnsToAdd.grRules));
-0143     genesToAdd.genes = setdiff(genesToAdd.genes,model.genes); % Only keep new genes
-0144     if isfield(model,'geneComps')
-0145         % No compartment information is available for a gene parsed out of
-0146         % a grRule, so it is assigned to the first compartment, matching
-0147         % addGenesRaven's own default for a gene added with none specified.
-0148         genesToAdd.geneComps(1:numel(genesToAdd.genes)) = repmat(1,numel(genesToAdd.genes),1);
-0149     end
-0150     if ~isempty(genesToAdd.genes)
-0151         fprintf('\nNew genes added to the model:\n')
-0152         %A new gene id is arbitrary text and may contain "%"; print as
-0153         %literal data rather than as an fprintf format string.
-0154         fprintf('%s\n', strjoin(genesToAdd.genes,newline))
-0155         newModel=addGenesRaven(model,genesToAdd);
-0156     else
-0157         newModel=model;
-0158     end
-0159 else
-0160     newModel=model;
-0161 end
-0162 
-0163 %If no reactions should be added
-0164 if isempty(rxnsToAdd)
-0165     return;
-0166 end
-0167 
-0168 if eqnType==2 || (eqnType==1 && allowNewMets==true)
-0169     compartment=char(compartment);
-0170     if ~ismember(compartment,model.comps)
-0171         EM='compartment must match one of the compartments in model.comps';
-0172         error('RAVEN:badInput', '%s', EM);
-0173     end
-0174 end
-0175 
-0176 if ~isfield(rxnsToAdd,'rxns')
-0177     EM='rxns is a required field in rxnsToAdd';
-0178     error('RAVEN:badInput', '%s', EM);
-0179 else
-0180     rxnsToAdd.rxns=convertCharArray(rxnsToAdd.rxns);
-0181     %To fit with some later printing
-0182     rxnsToAdd.rxns=rxnsToAdd.rxns(:);
-0183 end
-0184 if ~(isfield(rxnsToAdd,'equations') || (isfield(rxnsToAdd,'mets') && isfield(rxnsToAdd,'stoichCoeffs')))
-0185     EM='Either "equations" or "mets"+"stoichCoeffs" are required fields in rxnsToAdd';
-0186     error('RAVEN:badInput', '%s', EM);
-0187 end
-0188 
-0189 if any(ismember(rxnsToAdd.rxns,model.rxns))
-0190     EM='One or more reaction id was already present in the model. Use changeRxns or remove the existing reactions before adding new ones';
-0191     error('RAVEN:badInput', '%s', EM);
-0192 end
-0193 
-0194 %Normal case: equations provided
-0195 if isfield(rxnsToAdd,'equations')
-0196     rxnsToAdd.equations=convertCharArray(rxnsToAdd.equations);
-0197 
-0198     %Alternative case: mets+stoichiometry provided
-0199 else
-0200     %In the case of 1 rxn added (array of strings + vector), transform to
-0201     %cells of length=1:
-0202     if iscellstr(rxnsToAdd.mets)
-0203         rxnsToAdd.mets={rxnsToAdd.mets};
-0204     end
-0205     if isnumeric(rxnsToAdd.stoichCoeffs)
-0206         rxnsToAdd.stoichCoeffs = {rxnsToAdd.stoichCoeffs};
-0207     end
-0208     %Now both rxnsToAdd.mets and rxnsToAdd.stoichCoeffs should be cell
-0209     %arrays & of the same size:
-0210     if ~iscell(rxnsToAdd.mets) || ~iscell(rxnsToAdd.stoichCoeffs)
-0211         EM='rxnsToAdd.mets & rxnsToAdd.stoichCoeffs must be cell arrays';
-0212         error('RAVEN:badInput', '%s', EM);
-0213     elseif length(rxnsToAdd.stoichCoeffs) ~= length(rxnsToAdd.mets)
-0214         EM = 'rxnsToAdd.stoichCoeffs must have the same number of elements as rxnsToAdd.mets';
-0215         error('RAVEN:badInput', '%s', EM);
-0216     end
-0217     %In this case we need lb to decide if the reaction is reversible or not:
-0218     if ~isfield(rxnsToAdd,'lb')
-0219         %Fill with standard if it does not exist
-0220         rxnsToAdd.lb=-inf(size(rxnsToAdd.mets));
-0221     elseif ~isnumeric(rxnsToAdd.lb)
-0222         EM = 'rxnsToAdd.lb must be a vector';
-0223         error('RAVEN:badInput', '%s', EM);
-0224     elseif length(rxnsToAdd.lb) ~= length(rxnsToAdd.mets)
-0225         EM = 'rxnsToAdd.lb must have the same number of elements as rxnsToAdd.mets';
-0226         error('RAVEN:badInput', '%s', EM);
-0227     end
-0228     %Now we construct equations, to comply with the rest of the script:
-0229     rxnsToAdd.equations = cell(size(rxnsToAdd.mets));
-0230     for i = 1:length(rxnsToAdd.mets)
-0231         mets         = rxnsToAdd.mets{i};
-0232         stoichCoeffs = rxnsToAdd.stoichCoeffs{i};
-0233         if isfield(rxnsToAdd,'ub')
-0234             isrev        = rxnsToAdd.lb(i) < 0 && rxnsToAdd.ub(i) > 0;
-0235         else
-0236             isrev        = rxnsToAdd.lb(i) < 0;
-0237         end
-0238         rxnsToAdd.equations{i} = buildEquation(mets,stoichCoeffs,isrev);
-0239     end
-0240 end
-0241 
-0242 nRxns=numel(rxnsToAdd.rxns);
-0243 nOldRxns=numel(model.rxns);
-0244 filler=cell(nRxns,1);
-0245 filler(:)={''};
-0246 cellfiller=cell(nRxns,1);
-0247 cellfiller(:)={{''}};
-0248 largeFiller=cell(nOldRxns,1);
-0249 largeFiller(:)={''};
-0250 largeCellFiller=cell(nOldRxns,1);
-0251 largeCellFiller(:)={{''}};
-0252 
-0253 %***Add everything to the model except for the equations.
-0254 if numel(rxnsToAdd.equations)~=nRxns
-0255     EM='rxnsToAdd.equations must have the same number of elements as rxnsToAdd.rxns';
-0256     error('RAVEN:badInput', '%s', EM);
-0257 end
-0258 
-0259 %Parse the equations. This is done at this early stage since I need the
-0260 %reversibility info
-0261 [S, mets, badRxns, reversible]=constructS(rxnsToAdd.equations);
-0262 if any(badRxns)
-0263     EM='The following equations have one or more metabolites both as substrate and product. Only the net equations will be added:';
-0264     warning('RAVEN:warning', '%s', ravenList(EM, rxnsToAdd.rxns(badRxns)));
-0265 end
-0266 
-0267 newModel.rev=[newModel.rev;reversible];
-0268 newModel.rxns=[newModel.rxns;rxnsToAdd.rxns(:)];
-0269 
-0270 if isfield(rxnsToAdd,'rxnNames')
-0271     rxnsToAdd.rxnNames=convertCharArray(rxnsToAdd.rxnNames);
-0272     if numel(rxnsToAdd.rxnNames)~=nRxns
-0273         EM='rxnsToAdd.rxnNames must have the same number of elements as rxnsToAdd.rxns';
-0274         error('RAVEN:badInput', '%s', EM);
-0275     end
-0276     %Fill with standard if it does not exist
-0277     if ~isfield(newModel,'rxnNames')
-0278         newModel.rxnNames=largeFiller;
-0279     end
-0280     newModel.rxnNames=[newModel.rxnNames;rxnsToAdd.rxnNames(:)];
-0281 else
-0282     %Fill with standard if it does not exist
-0283     if isfield(newModel,'rxnNames')
-0284         newModel.rxnNames=[newModel.rxnNames;filler];
-0285     end
-0286 end
-0287 
-0288 if isfield(newModel,'annotation') & isfield(newModel.annotation,'defaultLB')
-0289     newLb=newModel.annotation.defaultLB;
-0290 else
-0291     newLb=-inf;
-0292 end
-0293 
-0294 if isfield(rxnsToAdd,'lb')
-0295     if numel(rxnsToAdd.lb)~=nRxns
-0296         EM='rxnsToAdd.lb must have the same number of elements as rxnsToAdd.rxns';
-0297         error('RAVEN:badInput', '%s', EM);
-0298     end
-0299     %Fill with standard if it does not exist
-0300     if ~isfield(newModel,'lb')
-0301         newModel.lb=zeros(nOldRxns,1);
-0302         newModel.lb(newModel.rev~=0)=newLb;
-0303     end
-0304     newModel.lb=[newModel.lb;rxnsToAdd.lb(:)];
-0305 else
-0306     %Fill with standard if it does not exist
-0307     if isfield(newModel,'lb')
-0308         I=zeros(nRxns,1);
-0309         I(reversible~=0)=newLb;
-0310         newModel.lb=[newModel.lb;I];
-0311     end
-0312 end
-0313 
-0314 if isfield(newModel,'annotation') & isfield(newModel.annotation,'defaultUB')
-0315     newUb=newModel.annotation.defaultUB;
-0316 else
-0317     newUb=inf;
-0318 end
-0319 
-0320 if isfield(rxnsToAdd,'ub')
-0321     if numel(rxnsToAdd.ub)~=nRxns
-0322         EM='rxnsToAdd.ub must have the same number of elements as rxnsToAdd.rxns';
-0323         error('RAVEN:badInput', '%s', EM);
-0324     end
-0325     %Fill with standard if it does not exist
-0326     if ~isfield(newModel,'ub')
-0327         newModel.ub=repmat(newUb,nOldRxns,1);
-0328     end
-0329     newModel.ub=[newModel.ub;rxnsToAdd.ub(:)];
-0330 else
-0331     %Fill with standard if it does not exist
-0332     if isfield(newModel,'ub')
-0333         newModel.ub=[newModel.ub;repmat(newUb,nRxns,1)];
-0334     end
-0335 end
-0336 
-0337 if isfield(rxnsToAdd,'c')
-0338     if numel(rxnsToAdd.c)~=nRxns
-0339         EM='rxnsToAdd.c must have the same number of elements as rxnsToAdd.rxns';
-0340         error('RAVEN:badInput', '%s', EM);
-0341     end
-0342     %Fill with standard if it does not exist
-0343     if ~isfield(newModel,'c')
-0344         newModel.c=zeros(nOldRxns,1);
-0345     end
-0346     newModel.c=[newModel.c;rxnsToAdd.c(:)];
-0347 else
-0348     %Fill with standard if it does not exist
-0349     if isfield(newModel,'c')
-0350         newModel.c=[newModel.c;zeros(nRxns,1)];
-0351     end
-0352 end
-0353 
-0354 if isfield(rxnsToAdd,'eccodes')
-0355     rxnsToAdd.eccodes=convertCharArray(rxnsToAdd.eccodes);
-0356     if numel(rxnsToAdd.eccodes)~=nRxns
-0357         EM='rxnsToAdd.eccodes must have the same number of elements as rxnsToAdd.rxns';
-0358         error('RAVEN:badInput', '%s', EM);
-0359     end
-0360     %Fill with standard if it does not exist
-0361     if ~isfield(newModel,'eccodes')
-0362         newModel.eccodes=largeFiller;
-0363     end
-0364     newModel.eccodes=[newModel.eccodes;rxnsToAdd.eccodes(:)];
-0365 else
-0366     %Fill with standard if it does not exist
-0367     if isfield(newModel,'eccodes')
-0368         newModel.eccodes=[newModel.eccodes;filler];
-0369     end
-0370 end
-0371 
-0372 if isfield(rxnsToAdd,'subSystems')
-0373     % Has to be cell array
-0374     if ischar(rxnsToAdd.subSystems)
-0375         rxnsToAdd.subSystems = {rxnsToAdd.subSystems};
-0376     end
-0377     % If all nested cells are 1x1, then unnest
-0378     if all(cellfun(@(x) iscell(x) && isscalar(x), rxnsToAdd.subSystems))
-0379         rxnsToAdd.subSystems = transpose([rxnsToAdd.subSystems{:}]);
-0380     end
-0381     % Cell array should now be as simple as possible. Check if it is nested
-0382     subSysRxnsNested = any(cellfun(@(x) iscell(x), rxnsToAdd.subSystems));
-0383     if isfield(newModel,'subSystems')
-0384         subSysModelNested = any(cellfun(@(x) iscell(x), newModel.subSystems));
-0385     else
-0386         subSysModelNested = subSysRxnsNested;
-0387         if subSysRxnsNested
-0388             newModel.subSystems=largeCellFiller;
-0389         else
-0390             newModel.subSystems=largeFiller;
-0391         end
-0392     end
-0393     if subSysRxnsNested && ~subSysModelNested
-0394         % Make all existing subSystems nested
-0395         newModel.subSystems = cellfun(@(x) {x}, newModel.subSystems, 'uni', 0);
-0396     elseif ~subSysRxnsNested && subSysModelNested
-0397         rxnsToAdd.subSystems = cellfun(@(x) {x}, rxnsToAdd.subSystems, 'uni', 0);
-0398     end
-0399     if numel(rxnsToAdd.subSystems)~=nRxns
-0400         EM='rxnsToAdd.subSystems must have the same number of elements as rxnsToAdd.rxns';
-0401         error('RAVEN:badInput', '%s', EM);
-0402     end
-0403     newModel.subSystems=[newModel.subSystems;rxnsToAdd.subSystems(:)];
-0404 else
-0405     %Fill with standard if it does not exist
-0406     if isfield(newModel,'subSystems')
-0407         if any(cellfun(@(x) iscell(x), newModel.subSystems))
-0408             newModel.subSystems=[newModel.subSystems;cellfiller];
-0409         else
-0410             newModel.subSystems=[newModel.subSystems;filler];
-0411         end
-0412     end
-0413 end
-0414 if isfield(rxnsToAdd,'rxnMiriams')
-0415     if numel(rxnsToAdd.rxnMiriams)~=nRxns
-0416         EM='rxnsToAdd.rxnMiriams must have the same number of elements as rxnsToAdd.rxns';
-0417         error('RAVEN:badInput', '%s', EM);
-0418     end
-0419     %Fill with standard if it does not exist
-0420     if ~isfield(newModel,'rxnMiriams')
-0421         newModel.rxnMiriams=cell(nOldRxns,1);
-0422     end
-0423     newModel.rxnMiriams=[newModel.rxnMiriams;rxnsToAdd.rxnMiriams(:)];
-0424 else
-0425     %Fill with standard if it does not exist
-0426     if isfield(newModel,'rxnMiriams')
-0427         newModel.rxnMiriams=[newModel.rxnMiriams;cell(nRxns,1)];
-0428     end
-0429 end
-0430 if isfield(rxnsToAdd,'rxnComps')
-0431     if numel(rxnsToAdd.rxnComps)~=nRxns
-0432         EM='rxnsToAdd.rxnComps must have the same number of elements as rxnsToAdd.rxns';
-0433         error('RAVEN:badInput', '%s', EM);
-0434     end
-0435     %Fill with standard if it does not exist
-0436     if ~isfield(newModel,'rxnComps')
-0437         newModel.rxnComps=ones(nOldRxns,1);
-0438         EM='Adding reactions with compartment information to a model without such information. All existing reactions will be assigned to the first compartment';
-0439         warning('RAVEN:warning', '%s', EM);
-0440     end
-0441     newModel.rxnComps=[newModel.rxnComps;rxnsToAdd.rxnComps(:)];
-0442 else
-0443     %Fill with standard if it does not exist
-0444     if isfield(newModel,'rxnComps')
-0445         newModel.rxnComps=[newModel.rxnComps;ones(nRxns,1)];
-0446         %fprintf("NOTE: The added reactions will be assigned to the first
-0447         %compartment\n");
-0448     end
-0449 end
-0450 if isfield(rxnsToAdd,'grRules')
-0451     rxnsToAdd.grRules=convertCharArray(rxnsToAdd.grRules);
-0452     if numel(rxnsToAdd.grRules)~=nRxns
-0453         EM='rxnsToAdd.grRules must have the same number of elements as rxnsToAdd.rxns';
-0454         error('RAVEN:badInput', '%s', EM);
-0455     end
-0456     %Fill with standard if it does not exist
-0457     if ~isfield(newModel,'grRules')
-0458         newModel.grRules=largeFiller;
-0459     end
-0460     newModel.grRules=[newModel.grRules;rxnsToAdd.grRules(:)];
-0461 else
-0462     %Fill with standard if it does not exist
-0463     if isfield(newModel,'grRules')
-0464         newModel.grRules=[newModel.grRules;filler];
-0465     end
-0466 end
-0467 
-0468 if isfield(rxnsToAdd,'rxnFrom')
-0469     rxnsToAdd.rxnFrom=convertCharArray(rxnsToAdd.rxnFrom);
-0470     if numel(rxnsToAdd.rxnFrom)~=nRxns
-0471         EM='rxnsToAdd.rxnFrom must have the same number of elements as rxnsToAdd.rxns';
-0472         error('RAVEN:badInput', '%s', EM);
-0473     end
-0474     %Fill with standard if it does not exist
-0475     if ~isfield(newModel,'rxnFrom')
-0476         newModel.rxnFrom=largeFiller;
-0477     end
-0478     newModel.rxnFrom=[newModel.rxnFrom;rxnsToAdd.rxnFrom(:)];
-0479 else
-0480     %Fill with standard if it does not exist
-0481     if isfield(newModel,'rxnFrom')
-0482         newModel.rxnFrom=[newModel.rxnFrom;filler];
-0483     end
-0484 end
-0485 
-0486 if isfield(rxnsToAdd,'rxnNotes')
-0487     rxnsToAdd.rxnNotes=convertCharArray(rxnsToAdd.rxnNotes);
-0488     if numel(rxnsToAdd.rxnNotes)~=nRxns
-0489         EM='rxnsToAdd.rxnNotes must have the same number of elements as rxnsToAdd.rxns';
-0490         error('RAVEN:badInput', '%s', EM);
-0491     end
-0492     %Fill with standard if it does not exist
-0493     if ~isfield(newModel,'rxnNotes')
-0494         newModel.rxnNotes=largeFiller;
-0495     end
-0496     newModel.rxnNotes=[newModel.rxnNotes;rxnsToAdd.rxnNotes(:)];
-0497 else
-0498     %Fill with standard if it does not exist
-0499     if isfield(newModel,'rxnNotes')
-0500         newModel.rxnNotes=[newModel.rxnNotes;filler];
-0501     end
-0502 end
-0503 
-0504 if isfield(rxnsToAdd,'rxnReferences')
-0505     rxnsToAdd.rxnReferences=convertCharArray(rxnsToAdd.rxnReferences);
-0506     if numel(rxnsToAdd.rxnReferences)~=nRxns
-0507         EM='rxnsToAdd.rxnReferences must have the same number of elements as rxnsToAdd.rxns';
-0508         error('RAVEN:badInput', '%s', EM);
-0509     end
-0510     %Fill with standard if it does not exist
-0511     if ~isfield(newModel,'rxnReferences')
-0512         newModel.rxnReferences=largeFiller;
-0513     end
-0514     newModel.rxnReferences=[newModel.rxnReferences;rxnsToAdd.rxnReferences(:)];
-0515 else
-0516     %Fill with standard if it does not exist
-0517     if isfield(newModel,'rxnReferences')
-0518         newModel.rxnReferences=[newModel.rxnReferences;filler];
-0519     end
-0520 end
-0521 
-0522 if isfield(rxnsToAdd,'pwys')
-0523     rxnsToAdd.pwys=convertCharArray(rxnsToAdd.pwys);
-0524     if numel(rxnsToAdd.pwys)~=nRxns
-0525         EM='rxnsToAdd.pwys must have the same number of elements as rxnsToAdd.rxns';
-0526         error('RAVEN:badInput', '%s', EM);
-0527     end
-0528     %Fill with standard if it does not exist
-0529     if ~isfield(newModel,'pwys')
-0530         newModel.pwys=largeFiller;
-0531     end
-0532     newModel.pwys=[newModel.pwys;rxnsToAdd.pwys(:)];
-0533 else
-0534     %Fill with standard if it does not exist
-0535     if isfield(newModel,'pwys')
-0536         newModel.pwys=[newModel.pwys;filler];
-0537     end
-0538 end
-0539 
-0540 if isfield(rxnsToAdd,'spontaneous')
-0541     if numel(rxnsToAdd.spontaneous)~=nRxns
-0542         EM='rxnsToAdd.spontaneous must have the same number of elements as rxnsToAdd.rxns';
-0543         error('RAVEN:badInput', '%s', EM);
-0544     end
-0545     %Fill with standard if it does not exist
-0546     if ~isfield(newModel,'spontaneous')
-0547         newModel.spontaneous=false(nOldRxns,1);
-0548     end
-0549     newModel.spontaneous=[newModel.spontaneous;logical(rxnsToAdd.spontaneous(:))];
-0550 else
-0551     %Fill with standard if it does not exist
-0552     if isfield(newModel,'spontaneous')
-0553         newModel.spontaneous=[newModel.spontaneous;false(nRxns,1)];
-0554     end
-0555 end
-0556 
-0557 if isfield(rxnsToAdd,'rxnConfidenceScores')
-0558     if numel(rxnsToAdd.rxnConfidenceScores)~=nRxns
-0559         EM='rxnsToAdd.rxnConfidenceScores must have the same number of elements as rxnsToAdd.rxns';
-0560         error('RAVEN:badInput', '%s', EM);
-0561     end
-0562     %Fill with standard if it does not exist
-0563     if ~isfield(newModel,'rxnConfidenceScores')
-0564         newModel.rxnConfidenceScores=NaN(nOldRxns,1);
-0565     end
-0566     newModel.rxnConfidenceScores=[newModel.rxnConfidenceScores;rxnsToAdd.rxnConfidenceScores(:)];
-0567 else
-0568     %Fill with standard if it does not exist
-0569     if isfield(newModel,'rxnConfidenceScores')
-0570         newModel.rxnConfidenceScores=[newModel.rxnConfidenceScores;NaN(nRxns,1)];
-0571     end
-0572 end
-0573 
-0574 if isfield(rxnsToAdd,'rxnDeltaG')
-0575     if numel(rxnsToAdd.rxnDeltaG)~=nRxns
-0576         EM='rxnsToAdd.rxnDeltaG must have the same number of elements as rxnsToAdd.rxns';
-0577         error('RAVEN:badInput', '%s', EM);
-0578     end
-0579     %Fill with standard if it does not exist
-0580     if ~isfield(newModel,'rxnDeltaG')
-0581         newModel.rxnDeltaG=NaN(nOldRxns,1);
-0582     end
-0583     newModel.rxnDeltaG=[newModel.rxnDeltaG;rxnsToAdd.rxnDeltaG(:)];
-0584 else
-0585     %Fill with standard if it does not exist
-0586     if isfield(newModel,'rxnDeltaG')
-0587         newModel.rxnDeltaG=[newModel.rxnDeltaG;NaN(nRxns,1)];
-0588     end
-0589 end
-0590 
-0591 
-0592 %***Start parsing the equations and adding the info to the S matrix The
-0593 %mets are matched to model.mets
-0594 if eqnType==1
-0595     [I, J]=ismember(mets,model.mets);
-0596     if ~all(I)
-0597         if allowNewMets==true || ischar(allowNewMets)
-0598             %Add the new mets
-0599             metsToAdd.mets=mets(~I);
-0600             metsToAdd.metNames=metsToAdd.mets;
-0601             metsToAdd.compartments=compartment;
-0602             if ischar(allowNewMets)
-0603                 newModel=addMets(newModel,metsToAdd,true,allowNewMets);
-0604             else
-0605                 newModel=addMets(newModel,metsToAdd,true);
-0606             end
-0607         else
-0608             EM='One or more equations contain metabolites that are not in model.mets. Set allowNewMets to true to allow this function to add metabolites or use addMets to add them before calling this function. Are you sure that eqnType=1?';
-0609             error('RAVEN:badInput', '%s', EM);
-0610         end
-0611     end
-0612     %Calculate the indexes of the metabolites and add the info
-0613     metIndexes=J;
-0614     metIndexes(~I)=numel(newModel.mets)-sum(~I)+1:numel(newModel.mets);
-0615 end
-0616 
-0617 %Do some stuff that is the same for eqnType=2 and eqnType=3
-0618 if eqnType==2 || eqnType==3
-0619     %For later..
-0620     t2=strcat(model.metNames,'***',model.comps(model.metComps));
-0621 end
-0622 
-0623 %The mets are matched to model.metNames and assigned to "compartment"
-0624 if eqnType==2
-0625     %%Check that the metabolite names are not present in the same
-0626     %%compartment.
-0627     %Not the neatest way maybe..
-0628     t1=strcat(mets,'***',compartment);
-0629     [I, J]=ismember(t1,t2);
-0630     
-0631     if ~all(I)
-0632         if allowNewMets==true || ischar(allowNewMets)
-0633             %Add the new mets
-0634             metsToAdd.metNames=mets(~I);
-0635             metsToAdd.compartments=compartment;
-0636             if ischar(allowNewMets)
-0637                 newModel=addMets(newModel,metsToAdd,true,allowNewMets);
-0638             else
-0639                 newModel=addMets(newModel,metsToAdd,true);
-0640             end
-0641         else
-0642             EM='One or more equations contain metabolites that are not in model.mets. Set allowNewMets to true to allow this function to add metabolites or use addMets to add them before calling this function';
-0643             error('RAVEN:badInput', '%s', EM);
-0644         end
-0645     end
-0646     
-0647     %Calculate the indexes of the metabolites
-0648     metIndexes=J;
-0649     metIndexes(~I)=numel(newModel.mets)-sum(~I)+1:numel(newModel.mets);
-0650 end
-0651 
-0652 %The equations are on the form metNames[compName]
-0653 if eqnType==3
-0654     %Parse the metabolite names
-0655     metNames=cell(numel(mets),1);
-0656     compartments=metNames;
-0657     for i=1:numel(mets)
-0658         starts=max(strfind(mets{i},'['));
-0659         ends=max(strfind(mets{i},']'));
-0660         
-0661         %Check that the formatting is correct
-0662         if isempty(starts) || isempty(ends) || ends<numel(mets{i})
-0663             EM=['The metabolite ' mets{i} ' is not correctly formatted for eqnType=3'];
-0664             error('RAVEN:badInput', '%s', EM);
-0665         end
-0666         
-0667         %Check that the compartment is correct
-0668         compartments{i}=mets{i}(starts+1:ends-1);
-0669         I=ismember(compartments(i),newModel.comps);
-0670         if ~I
-0671             EM=['The metabolite ' mets{i} ' has a compartment that is not in model.comps'];
-0672             error('RAVEN:badInput', '%s', EM);
-0673         end
-0674         metNames{i}=mets{i}(1:starts-1);
-0675     end
-0676     
-0677     %Check if the metabolite exists already
-0678     t1=strcat(metNames,'***',compartments);
-0679     [I, J]=ismember(t1,t2);
-0680     
-0681     if ~all(I)
-0682         if allowNewMets==true | ischar(allowNewMets)
-0683             %Add the new mets
-0684             metsToAdd.metNames=metNames(~I);
-0685             metsToAdd.compartments=compartments(~I);
-0686             if ischar(allowNewMets)
-0687                 newModel=addMets(newModel,metsToAdd,true,allowNewMets);
-0688             else
-0689                 newModel=addMets(newModel,metsToAdd,true);
-0690             end
-0691         else
-0692             EM='One or more equations contain metabolites that are not in model.metNames. Set allowNewMets to true to allow this function to add metabolites or use addMets to add them before calling this function';
-0693             error('RAVEN:badInput', '%s', EM);
-0694         end
-0695     end
-0696     
-0697     %Calculate the indexes of the metabolites
-0698     metIndexes=J;
-0699     metIndexes(~I)=numel(newModel.mets)-sum(~I)+1:numel(newModel.mets);
-0700 end
-0701 
-0702 %Add the info to the stoichiometric matrix
-0703 newModel.S=[newModel.S sparse(size(newModel.S,1),nRxns)];
-0704 
-0705 for i=1:nRxns
-0706     newModel.S(metIndexes,nOldRxns+i)=S(:,i);
-0707     %Parse the grRules and check whether all genes in grRules appear in
-0708     %genes
-0709     if isfield(newModel,'grRules')
-0710         rule=newModel.grRules{nOldRxns+i};
-0711         rule=strrep(rule,'(','');
-0712         rule=strrep(rule,')','');
-0713         rule=strrep(rule,' or ',' ');
-0714         rule=strrep(rule,' and ',' ');
-0715         genes=regexp(rule,' ','split');
-0716         [I, J]=ismember(genes,newModel.genes);
-0717         if ~all(I) && any(rule)
-0718             EM=['Not all genes for reaction ' rxnsToAdd.rxns{i} ' were found in model.genes. If needed, add genes with addGenesRaven before calling this function, or set the ''allowNewGenes'' flag to true'];
-0719             error('RAVEN:badInput', '%s', EM);
-0720         end
-0721     end
-0722 end
-0723 
-0724 %Make temporary minimal model structure with only new rxns, to parse to
-0725 %standardizeGrRules
-0726 newRxnsModel.genes=newModel.genes;
-0727 newRxnsModel.grRules=newModel.grRules(length(model.rxns)+1:end);
-0728 newRxnsModel.rxns=newModel.rxns(length(model.rxns)+1:end);
-0729 
-0730 %Fix grRules and reconstruct rxnGeneMat
-0731 [grRules,rxnGeneMat] = standardizeGrRules(newRxnsModel,true);
-0732 newModel.rxnGeneMat = [newModel.rxnGeneMat; rxnGeneMat];
-0733 newModel.grRules = [newModel.grRules(1:nOldRxns); grRules];
-0734 
-0735 if isfield(newModel,'equations')
-0736     %Reuse the equations as given where available (preserves the caller's
-0737     %own formatting); reconstruct them from the now-populated S otherwise,
-0738     %e.g. when rxnsToAdd used mets/stoichCoeffs instead of equations.
-0739     if isfield(rxnsToAdd,'equations')
-0740         newEquations=convertCharArray(rxnsToAdd.equations);
-0741     else
-0742         newEquations=constructEquations(newModel,newModel.rxns(nOldRxns+1:end));
-0743     end
-0744     newModel.equations=[newModel.equations;newEquations(:)];
-0745 end
-0746 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/addRxnsGenesMets.html b/doc/manipulation/addRxnsGenesMets.html deleted file mode 100644 index 5868ae95..00000000 --- a/doc/manipulation/addRxnsGenesMets.html +++ /dev/null @@ -1,302 +0,0 @@ - - - - Description of addRxnsGenesMets - - - - - - - - - -
Home > manipulation > addRxnsGenesMets.m
- - - -

addRxnsGenesMets -

- -

PURPOSE ^

-
addRxnsGenesMets Copy reactions from a source model into another model.
- -

SYNOPSIS ^

-
function model=addRxnsGenesMets(model,sourceModel,rxns,varargin)
- -

DESCRIPTION ^

-
 addRxnsGenesMets  Copy reactions from a source model into another model.
-
- Copies reactions from a source model to a new model, including (new)
- metabolites and genes.
-
- This function only works if the draft model and source model follow the
- same metabolite and compartment naming convention. Metabolites are only
- matched by metaboliteName[compartment]. Useful if one wants to copy
- additional reactions from source to draft after getModelFromHomology was
- used involving the same models.
-
- Parameters
- ----------
- model : struct
-     draft model where reactions should be copied to.
- sourceModel : struct
-     model where reactions and metabolites are sourced from.
- rxns : cell or char
-     reaction IDs (from source model). Can also be a string if only one
-     reaction is added.
-
- Name-Value Arguments
- --------------------
- addGene : logical or char or cell
-     three options (default false):
-
-     - false : no genes are annotated to the new reactions
-     - true : grRules are copied from the sourceModel and new genes are
-       added when required
-     - string or cell array : new grRules are specified as string or cell
-       array, and any new genes are added when required
- rxnNote : cell or char
-     strings explaining why reactions were copied to the model, to be
-     included as newModel.rxnNotes. Can also be a string if the same
-     rxnNotes should be added for each new reaction, or only one reaction
-     is to be added (default "Added via addRxnsAndMets()").
- confidence : double
-     integer specifying confidence score for all reactions, following
-     doi:10.1038/nprot.2009.203 (default 0):
-
-     - 4 : biochemical data: direct evidence from enzyme assays
-     - 3 : genetic data: knockout/-in or overexpression analysis
-     - 2 : physiological data: indirect evidence, e.g. secretion products
-       or defined medium requirement; sequence data: genome annotation
-     - 1 : modeling data: required for functional model, hypothetical
-       reaction
-     - 0 : no evidence
-
- Returns
- -------
- model : struct
-     an updated model structure.
-
- Examples
- --------
-     newModel = addRxnsGenesMets(model, sourceModel, rxns, addGene, ...
-                                 rxnNote, confidence);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • addMets addMets Add metabolites to a model.
  • addRxns addRxns Add reactions to a model.
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function model=addRxnsGenesMets(model,sourceModel,rxns,varargin)
-0002 % addRxnsGenesMets  Copy reactions from a source model into another model.
-0003 %
-0004 % Copies reactions from a source model to a new model, including (new)
-0005 % metabolites and genes.
-0006 %
-0007 % This function only works if the draft model and source model follow the
-0008 % same metabolite and compartment naming convention. Metabolites are only
-0009 % matched by metaboliteName[compartment]. Useful if one wants to copy
-0010 % additional reactions from source to draft after getModelFromHomology was
-0011 % used involving the same models.
-0012 %
-0013 % Parameters
-0014 % ----------
-0015 % model : struct
-0016 %     draft model where reactions should be copied to.
-0017 % sourceModel : struct
-0018 %     model where reactions and metabolites are sourced from.
-0019 % rxns : cell or char
-0020 %     reaction IDs (from source model). Can also be a string if only one
-0021 %     reaction is added.
-0022 %
-0023 % Name-Value Arguments
-0024 % --------------------
-0025 % addGene : logical or char or cell
-0026 %     three options (default false):
-0027 %
-0028 %     - false : no genes are annotated to the new reactions
-0029 %     - true : grRules are copied from the sourceModel and new genes are
-0030 %       added when required
-0031 %     - string or cell array : new grRules are specified as string or cell
-0032 %       array, and any new genes are added when required
-0033 % rxnNote : cell or char
-0034 %     strings explaining why reactions were copied to the model, to be
-0035 %     included as newModel.rxnNotes. Can also be a string if the same
-0036 %     rxnNotes should be added for each new reaction, or only one reaction
-0037 %     is to be added (default "Added via addRxnsAndMets()").
-0038 % confidence : double
-0039 %     integer specifying confidence score for all reactions, following
-0040 %     doi:10.1038/nprot.2009.203 (default 0):
-0041 %
-0042 %     - 4 : biochemical data: direct evidence from enzyme assays
-0043 %     - 3 : genetic data: knockout/-in or overexpression analysis
-0044 %     - 2 : physiological data: indirect evidence, e.g. secretion products
-0045 %       or defined medium requirement; sequence data: genome annotation
-0046 %     - 1 : modeling data: required for functional model, hypothetical
-0047 %       reaction
-0048 %     - 0 : no evidence
-0049 %
-0050 % Returns
-0051 % -------
-0052 % model : struct
-0053 %     an updated model structure.
-0054 %
-0055 % Examples
-0056 % --------
-0057 %     newModel = addRxnsGenesMets(model, sourceModel, rxns, addGene, ...
-0058 %                                 rxnNote, confidence);
-0059 
-0060 p=parseRAVENargs(varargin, {'addGene',false; 'rxnNote',[]; 'confidence',0});
-0061 confidence=p.confidence;
-0062 rxns=convertCharArray(rxns);
-0063 if isempty(p.rxnNote)
-0064     rxnNote={'Added via addRxnsGenesMets()'};
-0065 else
-0066     rxnNote=convertCharArray(p.rxnNote);
-0067 end
-0068 if numel(rxnNote)==1 && numel(rxns)>1
-0069     rxnNoteArray=cell(1,numel(rxns));
-0070     rxnNoteArray(:)=rxnNote;
-0071     rxnNote=rxnNoteArray;
-0072 end
-0073 addGene=p.addGene;
-0074 
-0075 % Obtain indexes of reactions in source model
-0076 [notNewRxn,oldRxn]=ismember(rxns,model.rxns);
-0077 rxns=rxns(~notNewRxn);
-0078 if isempty(rxns)
-0079     error('All reactions are already in the model.');
-0080 elseif ~isempty(notNewRxn)
-0081     fprintf('The following reactions were already present in the model and will not be added:\n')
-0082     %Reaction ids are arbitrary text and may contain "%"; print as
-0083     %literal data rather than as an fprintf format string.
-0084     fprintf('%s\n', strjoin(model.rxns(oldRxn(find(oldRxn))),newline))
-0085 end
-0086 
-0087 [~, rxnIdx]=ismember(rxns,sourceModel.rxns); % Get rxnIDs
-0088 if any(rxnIdx==0)
-0089     error('RAVEN:badInput', '%s', ravenList('The following reaction IDs could not be found in the source model:', rxns(rxnIdx==0)));
-0090 end
-0091 
-0092 % Add new metabolites
-0093 metIdx=find(any(sourceModel.S(:,rxnIdx),2)); % Get metabolite IDs
-0094 % Many of the metabolites in are already in the draft model, so only add
-0095 % the new metabolites
-0096 
-0097 % Match by metNames[metComps]. First make these structures for each model.
-0098 metCompsN =cellstr(num2str(model.metComps));
-0099 map=containers.Map(cellstr(num2str(transpose([1:length(model.comps)]))),model.comps);
-0100 metCompsN = map.values(metCompsN);
-0101 metCompsN = strcat(model.metNames,'[',metCompsN,']');
-0102 
-0103 sourcemetCompsN=cellstr(num2str(sourceModel.metComps));
-0104 map=containers.Map(cellstr(num2str(transpose([1:length(sourceModel.comps)]))),sourceModel.comps);
-0105 sourcemetCompsN = map.values(sourcemetCompsN);
-0106 sourcemetCompsN=strcat(sourceModel.metNames,'[',sourcemetCompsN,']');
-0107 
-0108 newMetCompsN=sourcemetCompsN(metIdx);
-0109 notNewMet=newMetCompsN(ismember(newMetCompsN,metCompsN));
-0110 
-0111 if ~isempty(notNewMet)
-0112     fprintf('\nThe following metabolites were already present in the model and will not be added:\n')
-0113     %Metabolite names are arbitrary text and may contain "%"; print as
-0114     %literal data rather than as an fprintf format string.
-0115     fprintf('%s\n', strjoin(transpose(notNewMet),newline))
-0116 end
-0117 
-0118 metIdx=metIdx(~ismember(sourcemetCompsN(metIdx),metCompsN));
-0119 
-0120 if ~isempty(metIdx)
-0121     fprintf('\nThe following metabolites will be added to the model:\n')
-0122     %Metabolite names are arbitrary text and may contain "%"; print as
-0123     %literal data rather than as an fprintf format string.
-0124     fprintf('%s\n', strjoin(transpose(sourcemetCompsN(metIdx)),newline))
-0125     
-0126     if isfield(sourceModel,'mets')
-0127         metsToAdd.mets=sourceModel.mets(metIdx);
-0128     end
-0129     if isfield(sourceModel,'metNames')
-0130         metsToAdd.metNames=sourceModel.metNames(metIdx);
-0131     end
-0132     if isfield(sourceModel,'metFormulas')
-0133         metsToAdd.metFormulas=sourceModel.metFormulas(metIdx);
-0134     end
-0135     if isfield(sourceModel,'metCharges')
-0136         metsToAdd.metCharges=sourceModel.metCharges(metIdx);
-0137     end
-0138     if isfield(sourceModel,'metMiriams')
-0139         metsToAdd.metMiriams=sourceModel.metMiriams(metIdx);
-0140     end
-0141     if isfield(sourceModel,'metNotes')
-0142         metsToAdd.metNotes=sourceModel.metNotes(metIdx);
-0143     end
-0144     if isfield(sourceModel,'inchis')
-0145         metsToAdd.inchis=sourceModel.inchis(metIdx);
-0146     end
-0147     if isfield(sourceModel,'metSmiles')
-0148         metsToAdd.metSmiles=sourceModel.metSmiles(metIdx);
-0149     end
-0150     if isfield(sourceModel,'metDeltaG')
-0151         metsToAdd.metDeltaG=sourceModel.metDeltaG(metIdx);
-0152     end
-0153     
-0154     metsToAdd.compartments=strtrim(cellstr(num2str(sourceModel.metComps(metIdx)))); % Convert from compartment string to compartment number
-0155     [~,idx]=ismember(metsToAdd.compartments,strsplit(num2str(1:length(sourceModel.comps)))); % Match compartment number to compartment abbreviation
-0156     metsToAdd.compartments=sourceModel.comps(idx); % Fill in compartment abbreviations
-0157     
-0158     model=addMets(model,metsToAdd);
-0159 end
-0160 fprintf('\nNumber of metabolites added to the model:\n')
-0161 fprintf([num2str(numel(metIdx)),'\n'])
-0162 
-0163 % Add new genes
-0164 if ~islogical(addGene) | addGene ~= false
-0165     if ischar(addGene)
-0166         rxnToAdd.grRules={addGene};
-0167     elseif iscell(addGene)
-0168         rxnToAdd.grRules=addGene(~notNewRxn); 
-0169     else
-0170         rxnToAdd.grRules=sourceModel.grRules(rxnIdx); % Get the relevant grRules
-0171     end
-0172 end
-0173 % Add new reactions
-0174 rxnToAdd.equations=constructEquations(sourceModel,rxnIdx);
-0175 rxnToAdd.rxnNames=sourceModel.rxnNames(rxnIdx);
-0176 rxnToAdd.rxns=sourceModel.rxns(rxnIdx);
-0177 rxnToAdd.lb=sourceModel.lb(rxnIdx);
-0178 rxnToAdd.ub=sourceModel.ub(rxnIdx);
-0179 rxnToAdd.rxnNotes(:)=rxnNote(~notNewRxn);
-0180 rxnToAdd.rxnConfidenceScores=NaN(1,numel(rxnToAdd.rxns));
-0181 if ~isnumeric(confidence)
-0182     EM='confidence score must be numeric';
-0183     error('RAVEN:badInput', '%s', EM);
-0184 end
-0185 rxnToAdd.rxnConfidenceScores(:)=confidence;
-0186 if isfield(sourceModel,'rxnDeltaG')
-0187     rxnToAdd.rxnDeltaG=sourceModel.rxnDeltaG(rxnIdx);
-0188 end
-0189 if isfield(sourceModel,'subSystems')
-0190     rxnToAdd.subSystems=sourceModel.subSystems(rxnIdx);
-0191 end
-0192 if isfield(sourceModel,'eccodes')
-0193     rxnToAdd.eccodes=sourceModel.eccodes(rxnIdx);
-0194 end
-0195 model=addRxns(model,rxnToAdd,3,'',false,true);
-0196 
-0197 fprintf('\nNumber of reactions added to the model:\n')
-0198 fprintf([num2str(numel(rxnIdx)),'\n'])
-0199 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/addTransport.html b/doc/manipulation/addTransport.html deleted file mode 100644 index c03de865..00000000 --- a/doc/manipulation/addTransport.html +++ /dev/null @@ -1,290 +0,0 @@ - - - - Description of addTransport - - - - - - - - - -
Home > manipulation > addTransport.m
- - - -

addTransport -

- -

PURPOSE ^

-
addTransport Add transport reactions between compartments.
- -

SYNOPSIS ^

-
function [model, addedRxns]=addTransport(model,fromComp,toComps,varargin)
- -

DESCRIPTION ^

-
 addTransport  Add transport reactions between compartments.
-
- This is a faster version than addRxns when adding transport reactions.
- New reaction names are formatted as "metaboliteName, fromComp-toComp",
- while new reaction IDs are sequentially counted with a tr_ prefix:
- e.g. tr_0001, tr_0002, etc.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- fromComp : char
-     the id of the compartment to transport from (should match
-     model.comps).
- toComps : cell
-     compartment names to transport to (should match model.comps).
-
- Name-Value Arguments
- --------------------
- metNames : cell
-     the metabolite names to add transport for (default all metabolites
-     in fromComp).
- isRev : logical
-     true if the transport reactions should be reversible (default true).
- onlyToExisting : logical
-     true if transport of a metabolite should only be added if it already
-     exists in toComp. If false, then new metabolites are added with
-     addMets first (default true).
- prefix : char
-     prefix to reaction IDs (default "tr_").
-
- Returns
- -------
- model : struct
-     updated model structure.
- addedRxns : cell
-     ids of the added reactions.
-
- Examples
- --------
-     [model, addedRxns] = addTransport(model, fromComp, toComps, ...
-                          metNames, isRev, onlyToExisting, prefix);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • addMets addMets Add metabolites to a model.
  • generateNewIds generateNewIds Generate a list of new metabolite or reaction ids.
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function [model, addedRxns]=addTransport(model,fromComp,toComps,varargin)
-0002 % addTransport  Add transport reactions between compartments.
-0003 %
-0004 % This is a faster version than addRxns when adding transport reactions.
-0005 % New reaction names are formatted as "metaboliteName, fromComp-toComp",
-0006 % while new reaction IDs are sequentially counted with a tr_ prefix:
-0007 % e.g. tr_0001, tr_0002, etc.
-0008 %
-0009 % Parameters
-0010 % ----------
-0011 % model : struct
-0012 %     a model structure.
-0013 % fromComp : char
-0014 %     the id of the compartment to transport from (should match
-0015 %     model.comps).
-0016 % toComps : cell
-0017 %     compartment names to transport to (should match model.comps).
-0018 %
-0019 % Name-Value Arguments
-0020 % --------------------
-0021 % metNames : cell
-0022 %     the metabolite names to add transport for (default all metabolites
-0023 %     in fromComp).
-0024 % isRev : logical
-0025 %     true if the transport reactions should be reversible (default true).
-0026 % onlyToExisting : logical
-0027 %     true if transport of a metabolite should only be added if it already
-0028 %     exists in toComp. If false, then new metabolites are added with
-0029 %     addMets first (default true).
-0030 % prefix : char
-0031 %     prefix to reaction IDs (default "tr_").
-0032 %
-0033 % Returns
-0034 % -------
-0035 % model : struct
-0036 %     updated model structure.
-0037 % addedRxns : cell
-0038 %     ids of the added reactions.
-0039 %
-0040 % Examples
-0041 % --------
-0042 %     [model, addedRxns] = addTransport(model, fromComp, toComps, ...
-0043 %                          metNames, isRev, onlyToExisting, prefix);
-0044 
-0045 fromComp=char(fromComp);
-0046 [I, fromID]=ismember(model.comps,fromComp);
-0047 fromID=find(fromID);
-0048 if sum(I)~=1
-0049     EM='fromComps must have exactly one match in model.comps';
-0050     error('RAVEN:badInput', '%s', EM);
-0051 end
-0052 toComps=convertCharArray(toComps);
-0053 [I, toIDs]=ismember(toComps,model.comps);
-0054 if ~all(I)
-0055     EM='All compartments in toComps must have a match in model.comps';
-0056     error('RAVEN:badInput', '%s', EM);
-0057 end
-0058 p=parseRAVENargs(varargin, {'metNames',[]; 'isRev',true; 'onlyToExisting',true; 'prefix','tr_'});
-0059 isRev=p.isRev;
-0060 onlyToExisting=p.onlyToExisting;
-0061 prefix=char(p.prefix);
-0062 if isempty(p.metNames)
-0063     %Find all metabolites in fromComp
-0064     metNames=model.metNames(model.metComps==fromID);
-0065 else
-0066     metNames=convertCharArray(p.metNames);
-0067 end
-0068 %Normalise to a column vector regardless of the orientation the caller
-0069 %passed metNames in (e.g. a literal {'a','b','c'} is a row): everything
-0070 %derived from it below is concatenated against model fields, which are
-0071 %always columns.
-0072 metNames=metNames(:);
-0073 
-0074 %Check that the names are unique
-0075 if numel(unique(metNames))~=numel(metNames)
-0076     error('RAVEN:badInput', '%s', 'Not all metabolite names are unique');
-0077 end
-0078 
-0079 %Get the indexes of the mets in fromComp
-0080 I=find(model.metComps==fromID);
-0081 [J, K]=ismember(metNames,model.metNames(I));
-0082 if ~all(J)
-0083     EM='Not all metabolites in metNames exist in fromComp';
-0084     error('RAVEN:badInput', '%s', EM);
-0085 end
-0086 fromMets=I(K); %These are the ids of the metabolites to transport. The order corresponds to metNames
-0087 addedRxns={};
-0088 %Loop through and add for each compartment in toComps
-0089 for i=1:numel(toComps)
-0090     fromMetsInComp=fromMets; %If onlyToExisting==true then not all mets are transported to each compartment
-0091     %Get the indexes of the mets in the compartment
-0092     I=find(model.metComps==toIDs(i));
-0093     [J, K]=ismember(metNames,model.metNames(I));
-0094     if onlyToExisting==true || all(J)
-0095         toMets=I(K(J)); %Only look at the existing ones
-0096         fromMetsInComp=fromMetsInComp(J);
-0097         namesInComp=metNames(J); %Matches fromMetsInComp/toMets below
-0098     else
-0099         %This is if not all metabolites exist in the target compartment,
-0100         %and they should be added
-0101         metsToAdd.metNames=metNames(J==0);
-0102         metsToAdd.compartments=toComps{i};
-0103         model=addMets(model,metsToAdd);
-0104 
-0105         %Redo the mapping when all mets are there. A bit lazy, but it is
-0106         %fast anyways
-0107         I=find(model.metComps==toIDs(i));
-0108         [~, K]=ismember(metNames,model.metNames(I));
-0109         toMets=I(K); %All are guaranteed to be found now
-0110         namesInComp=metNames; %All requested metabolites now exist
-0111     end
-0112     
-0113     %Construct the S matrix directly as sparse (indexed assignment on a
-0114     %sparse matrix still overwrites rather than summing on a repeated
-0115     %index, same as on a dense one): a dense (mets x newRxns) intermediate
-0116     %is wasted allocation for a genome-scale model.
-0117     nRxns=numel(fromMetsInComp);
-0118     newS=sparse(numel(model.mets),nRxns);
-0119     newS(sub2ind(size(newS),fromMetsInComp(:),(1:nRxns)'))=-1;
-0120     newS(sub2ind(size(newS),toMets(:),(1:nRxns)'))=1;
-0121 
-0122     %Add the reactions
-0123     model.S=[model.S newS];
-0124     if isfield(model,'annotation') && isfield(model.annotation,'defaultLB')
-0125         lb = model.annotation.defaultLB;
-0126         ub = model.annotation.defaultUB;
-0127     else
-0128         lb = -inf;
-0129         ub = inf;
-0130     end
-0131     if isRev==true
-0132         model.lb=[model.lb;ones(nRxns,1)*lb];
-0133         model.rev=[model.rev;ones(nRxns,1)];
-0134     else
-0135         model.lb=[model.lb;zeros(nRxns,1)];
-0136         model.rev=[model.rev;zeros(nRxns,1)];
-0137     end
-0138     model.ub=[model.ub;ones(nRxns,1)*ub];
-0139     model.c=[model.c;zeros(nRxns,1)];
-0140     
-0141     %Add annotation
-0142     filler=cell(nRxns,1);
-0143     filler(:)={''};
-0144     addedRxnsID=generateNewIds(model,'rxns',prefix,nRxns);
-0145     addedRxnsName=strcat(namesInComp, {' transport, '}, model.compNames(fromID), '-', model.compNames(toIDs(i)));
-0146     addedRxnsName=addedRxnsName(:); %Defensive: keep column orientation regardless of namesInComp's
-0147     model.rxns=[model.rxns;addedRxnsID];
-0148     model.rxnNames=[model.rxnNames;addedRxnsName];
-0149     
-0150     if isfield(model,'eccodes')
-0151         model.eccodes=[model.eccodes;filler];
-0152     end
-0153     if isfield(model,'subSystems')
-0154         isNested = any(cellfun(@(x) iscell(x), model.subSystems));
-0155         ssFiller = filler;
-0156         if isRev==1
-0157             ssFiller(:) = {['Transport between ' fromComp ' and ' toComps{i}]};
-0158         else
-0159             ssFiller(:) = {['Transport from ' fromComp ' to ' toComps{i}]};
-0160         end
-0161         if isNested
-0162             ssFiller = cellfun(@(x) {x}, ssFiller, 'uni', 0);
-0163         end
-0164         model.subSystems=[model.subSystems;ssFiller];
-0165     end
-0166     if isfield(model,'grRules')
-0167         model.grRules=[model.grRules;filler];
-0168     end
-0169     if isfield(model,'rxnFrom')
-0170         model.rxnFrom=[model.rxnFrom;filler];
-0171     end
-0172     if isfield(model,'rxnMiriams')
-0173         model.rxnMiriams=[model.rxnMiriams;cell(nRxns,1)];
-0174     end
-0175     if isfield(model,'rxnComps')
-0176         model.rxnComps=[model.rxnComps;ones(nRxns,1)];
-0177         fprintf('NOTE: The added transport reactions will be added to the first compartment\n');
-0178     end
-0179     if isfield(model,'rxnGeneMat')
-0180         model.rxnGeneMat=[model.rxnGeneMat;sparse(nRxns,numel(model.genes))];
-0181     end
-0182     if isfield(model,'rxnNotes')
-0183         model.rxnNotes=[model.rxnNotes;filler];
-0184     end
-0185     if isfield(model,'rxnReferences')
-0186         model.rxnReferences=[model.rxnReferences;filler];
-0187     end
-0188     if isfield(model,'rxnConfidenceScores')
-0189         model.rxnConfidenceScores=[model.rxnConfidenceScores;NaN(nRxns,1)];
-0190     end
-0191     if isfield(model,'rxnDeltaG')
-0192         model.rxnDeltaG=[model.rxnDeltaG;NaN(nRxns,1)];
-0193     end
-0194     if isfield(model,'pwys')
-0195         model.pwys=[model.pwys;filler];
-0196     end
-0197     if isfield(model,'spontaneous')
-0198         model.spontaneous=[model.spontaneous;false(nRxns,1)];
-0199     end
-0200     addedRxns = [addedRxns; addedRxnsID];
-0201 end
-0202 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/changeGrRules.html b/doc/manipulation/changeGrRules.html deleted file mode 100644 index 99ee3e9b..00000000 --- a/doc/manipulation/changeGrRules.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - Description of changeGrRules - - - - - - - - - -
Home > manipulation > changeGrRules.m
- - - -

changeGrRules -

- -

PURPOSE ^

-
changeGrRules Change multiple grRules at the same time.
- -

SYNOPSIS ^

-
function model = changeGrRules(model,rxns,grRules,varargin)
- -

DESCRIPTION ^

-
 changeGrRules  Change multiple grRules at the same time.
-
- Parameters
- ----------
- model : struct
-     a model structure to change the gene association.
- rxns : char or cell
-     reaction IDs.
- grRules : char or cell
-     additional or replacement gene association. Should be written with
-     ' and ' to indicate subunits, ' or ' to indicate isoenzymes, and
-     brackets '()' to separate different instances.
-
- Name-Value Arguments
- --------------------
- replace : logical
-     true if old gene association should be replaced with new association.
-     False if new gene association should be concatenated to the old
-     association (default true).
-
- Returns
- -------
- model : struct
-     an updated model structure.
-
- Examples
- --------
-     model = changeGrRules(model, rxns, grRules, replace);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function model = changeGrRules(model,rxns,grRules,varargin)
-0002 % changeGrRules  Change multiple grRules at the same time.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % model : struct
-0007 %     a model structure to change the gene association.
-0008 % rxns : char or cell
-0009 %     reaction IDs.
-0010 % grRules : char or cell
-0011 %     additional or replacement gene association. Should be written with
-0012 %     ' and ' to indicate subunits, ' or ' to indicate isoenzymes, and
-0013 %     brackets '()' to separate different instances.
-0014 %
-0015 % Name-Value Arguments
-0016 % --------------------
-0017 % replace : logical
-0018 %     true if old gene association should be replaced with new association.
-0019 %     False if new gene association should be concatenated to the old
-0020 %     association (default true).
-0021 %
-0022 % Returns
-0023 % -------
-0024 % model : struct
-0025 %     an updated model structure.
-0026 %
-0027 % Examples
-0028 % --------
-0029 %     model = changeGrRules(model, rxns, grRules, replace);
-0030 
-0031 p=parseRAVENargs(varargin, {'replace',true});
-0032 replace=p.replace;
-0033 
-0034 rxns=convertCharArray(rxns);
-0035 grRules=convertCharArray(grRules);
-0036 
-0037 if isscalar(grRules) && ~isscalar(rxns)
-0038     grRules = repmat(grRules,1,numel(rxns));
-0039 end
-0040 if ~(numel(grRules)==numel(rxns))
-0041     error('Number of rxns and grRules should be identical')
-0042 end
-0043 
-0044 % Add genes to model
-0045 geneList = getGenesFromGrRules(grRules);
-0046 genesToAdd.genes=setdiff(geneList,model.genes); % Only keep the genes that are not yet part of the model.genes.
-0047 if ~isempty(genesToAdd.genes)
-0048     model=addGenesRaven(model,genesToAdd); % Add genes
-0049 end
-0050     
-0051 % Find reaction and gene indices
-0052 idx=getIndexes(model,rxns,'rxns');
-0053 
-0054 % Change gene associations
-0055 if replace==true % Replace old gene associations
-0056     model.grRules(idx)=grRules;
-0057 else % Add gene associations, add new gene rules after 'OR'.
-0058     % A reaction with no existing grRule yet gets the new rule directly:
-0059     % wrapping it as '() or (...)' regardless left an empty first clause,
-0060     % a string neither grRuleToDNF nor isDnfGrRule can parse.
-0061     existingRules=model.grRules(idx);
-0062     hasExisting=~cellfun(@isempty,existingRules);
-0063     combined=grRules;
-0064     combined(hasExisting)=strcat('(',existingRules(hasExisting),') or (',grRules(hasExisting),')');
-0065     model.grRules(idx)=combined;
-0066 end
-0067 
-0068 %Fix grRules and reconstruct rxnGeneMat
-0069 [grRules,rxnGeneMat] = standardizeGrRules(model,true);
-0070 model.grRules = grRules;
-0071 model.rxnGeneMat = rxnGeneMat;
-0072 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/changeRxns.html b/doc/manipulation/changeRxns.html deleted file mode 100644 index a4b01fd5..00000000 --- a/doc/manipulation/changeRxns.html +++ /dev/null @@ -1,267 +0,0 @@ - - - - Description of changeRxns - - - - - - - - - -
Home > manipulation > changeRxns.m
- - - -

changeRxns -

- -

PURPOSE ^

-
changeRxns Modify the equations of reactions in a model.
- -

SYNOPSIS ^

-
function model=changeRxns(model,rxns,equations,varargin)
- -

DESCRIPTION ^

-
 changeRxns  Modify the equations of reactions in a model.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- rxns : cell
-     cell array with reaction ids.
- equations : cell or struct
-     cell array with equations. Alternatively, it can be a structure with
-     the fields "mets" and "stoichCoeffs", in the same fashion as addRxns.
-     E.g.:
-
-     - equations.mets = {{"met1","met2"},{"met1","met3"}}
-     - equations.stoichCoeffs = {[-1,+2],[-1,+1]}
-
- Name-Value Arguments
- --------------------
- eqnType : double
-     describes how the equation string should be interpreted (default 1):
-
-     - 1 : the metabolites are matched to model.mets. New metabolites (if
-       allowed) are added to "compartment".
-     - 2 : the metabolites are matched to model.metNames and all
-       metabolites are assigned to "compartment". Any new metabolites that
-       are added will be assigned IDs "m1", "m2"... If IDs on the same form
-       are already used in the model then the numbering will start from the
-       highest used integer+1.
-     - 3 : the metabolites are written as "metNames[compNames]". Only
-       compartments in model.compNames are allowed. Any new metabolites
-       that are added will be assigned IDs "m1", "m2"... If IDs on the same
-       form are already used in the model then the numbering will start
-       from the highest used integer+1.
- compartment : char
-     a string with the compartment the metabolites should be placed in when
-     using eqnType=2. Must match model.compNames (optional when eqnType=1 or
-     eqnType=3).
- allowNewMets : logical
-     true if the function is allowed to add new metabolites. It is highly
-     recommended to first add any new metabolites with addMets rather than
-     automatically through this function. addMets supports more annotation
-     of metabolites, allows for the use of exchange metabolites, and using
-     it reduces the risk of parsing errors (default false).
-
- Returns
- -------
- model : struct
-     an updated model structure.
-
- Examples
- --------
-     model = changeRxns(model, rxns, equations, eqnType, compartment, allowNewMets);
-
- Notes
- -----
- This function should be used with some care, since it does not care about
- bounds on the reactions. Changing an irreversible reaction to a reversible
- one (or the other way around) will only change the model.rev field and not
- the model.lb/model.ub fields. The reaction will therefore still be having
- the same reversibility because of the bounds. Use setParams to change the
- bounds.
-
- When adding metabolites to a compartment where it previously does not
- exist, the function will copy any available information from the metabolite
- in another compartment.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • addRxns addRxns Add reactions to a model.
  • permuteModel permuteModel Change the order of the reactions or metabolites in a model.
  • removeReactions removeReactions Delete a set of reactions from a model.
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function model=changeRxns(model,rxns,equations,varargin)
-0002 % changeRxns  Modify the equations of reactions in a model.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % model : struct
-0007 %     a model structure.
-0008 % rxns : cell
-0009 %     cell array with reaction ids.
-0010 % equations : cell or struct
-0011 %     cell array with equations. Alternatively, it can be a structure with
-0012 %     the fields "mets" and "stoichCoeffs", in the same fashion as addRxns.
-0013 %     E.g.:
-0014 %
-0015 %     - equations.mets = {{"met1","met2"},{"met1","met3"}}
-0016 %     - equations.stoichCoeffs = {[-1,+2],[-1,+1]}
-0017 %
-0018 % Name-Value Arguments
-0019 % --------------------
-0020 % eqnType : double
-0021 %     describes how the equation string should be interpreted (default 1):
-0022 %
-0023 %     - 1 : the metabolites are matched to model.mets. New metabolites (if
-0024 %       allowed) are added to "compartment".
-0025 %     - 2 : the metabolites are matched to model.metNames and all
-0026 %       metabolites are assigned to "compartment". Any new metabolites that
-0027 %       are added will be assigned IDs "m1", "m2"... If IDs on the same form
-0028 %       are already used in the model then the numbering will start from the
-0029 %       highest used integer+1.
-0030 %     - 3 : the metabolites are written as "metNames[compNames]". Only
-0031 %       compartments in model.compNames are allowed. Any new metabolites
-0032 %       that are added will be assigned IDs "m1", "m2"... If IDs on the same
-0033 %       form are already used in the model then the numbering will start
-0034 %       from the highest used integer+1.
-0035 % compartment : char
-0036 %     a string with the compartment the metabolites should be placed in when
-0037 %     using eqnType=2. Must match model.compNames (optional when eqnType=1 or
-0038 %     eqnType=3).
-0039 % allowNewMets : logical
-0040 %     true if the function is allowed to add new metabolites. It is highly
-0041 %     recommended to first add any new metabolites with addMets rather than
-0042 %     automatically through this function. addMets supports more annotation
-0043 %     of metabolites, allows for the use of exchange metabolites, and using
-0044 %     it reduces the risk of parsing errors (default false).
-0045 %
-0046 % Returns
-0047 % -------
-0048 % model : struct
-0049 %     an updated model structure.
-0050 %
-0051 % Examples
-0052 % --------
-0053 %     model = changeRxns(model, rxns, equations, eqnType, compartment, allowNewMets);
-0054 %
-0055 % Notes
-0056 % -----
-0057 % This function should be used with some care, since it does not care about
-0058 % bounds on the reactions. Changing an irreversible reaction to a reversible
-0059 % one (or the other way around) will only change the model.rev field and not
-0060 % the model.lb/model.ub fields. The reaction will therefore still be having
-0061 % the same reversibility because of the bounds. Use setParams to change the
-0062 % bounds.
-0063 %
-0064 % When adding metabolites to a compartment where it previously does not
-0065 % exist, the function will copy any available information from the metabolite
-0066 % in another compartment.
-0067 
-0068 p=parseRAVENargs(varargin, {'eqnType',1; 'compartment',[]; 'allowNewMets',false});
-0069 eqnType=p.eqnType;
-0070 compartment=p.compartment;
-0071 allowNewMets=p.allowNewMets;
-0072 
-0073 rxns=convertCharArray(rxns);
-0074 compartment=char(compartment);
-0075 
-0076 %Find the indexes of the reactions and throw an error if they are not all
-0077 %found
-0078 [I, J]=ismember(rxns,model.rxns);
-0079 if ~all(I)
-0080     EM='All reaction ids must exist in the model';
-0081     error('RAVEN:badInput', '%s', EM);
-0082 end
-0083 
-0084 %The reactions are changed in the following manner. First create a
-0085 %rxns-structure by copying info from the model. Then remove the old
-0086 %reactions. Then add the updated ones using addRxns. Lastly, the model is
-0087 %reordered to match the original order. This is done like this to make use
-0088 %of the advanced parsing of equations that addRxns use.
-0089 rxnsToChange.rxns=rxns;
-0090 if isfield(equations,'mets') && isfield(equations,'stoichCoeffs')
-0091     rxnsToChange.mets=equations.mets;
-0092     rxnsToChange.stoichCoeffs=equations.stoichCoeffs;
-0093 else
-0094     rxnsToChange.equations=convertCharArray(equations);
-0095 end
-0096 if isfield(model,'rxnNames')
-0097     rxnsToChange.rxnNames=model.rxnNames(J);
-0098 end
-0099 if isfield(model,'lb')
-0100     rxnsToChange.lb=model.lb(J);
-0101 end
-0102 if isfield(model,'ub')
-0103     rxnsToChange.ub=model.ub(J);
-0104 end
-0105 if isfield(model,'c')
-0106     rxnsToChange.c=model.c(J);
-0107 end
-0108 if isfield(model,'eccodes')
-0109     rxnsToChange.eccodes=model.eccodes(J);
-0110 end
-0111 if isfield(model,'subSystems')
-0112     rxnsToChange.subSystems=model.subSystems(J);
-0113 end
-0114 if isfield(model,'rxnComps')
-0115     rxnsToChange.rxnComps=model.rxnComps(J);
-0116 end
-0117 if isfield(model,'grRules')
-0118     rxnsToChange.grRules=model.grRules(J);
-0119 end
-0120 if isfield(model,'rxnFrom')
-0121     rxnsToChange.rxnFrom=model.rxnFrom(J);
-0122 end
-0123 if isfield(model,'rxnScores')
-0124     rxnsToChange.rxnScores=model.rxnScores(J);
-0125 end
-0126 if isfield(model,'rxnMiriams')
-0127     rxnsToChange.rxnMiriams=model.rxnMiriams(J);
-0128 end
-0129 if isfield(model,'rxnNotes')
-0130     rxnsToChange.rxnNotes=model.rxnNotes(J);
-0131 end
-0132 if isfield(model,'rxnReferences')
-0133     rxnsToChange.rxnReferences=model.rxnReferences(J);
-0134 end
-0135 if isfield(model,'rxnConfidenceScores')
-0136     rxnsToChange.rxnConfidenceScores=model.rxnConfidenceScores(J);
-0137 end
-0138 if isfield(model,'rxnDeltaG')
-0139     rxnsToChange.rxnDeltaG=model.rxnDeltaG(J);
-0140 end
-0141 if isfield(model,'pwys')
-0142     rxnsToChange.pwys=model.pwys(J);
-0143 end
-0144 
-0145 %Calculate the new order of reactions
-0146 order=ones(numel(model.rxns),1);
-0147 order(J)=0;
-0148 order=cumsum(order);
-0149 order(J)=order(end)+1:order(end)+numel(rxns);
-0150 
-0151 %Remove the original reactions
-0152 model=removeReactions(model,rxns);
-0153 
-0154 model=addRxns(model,rxnsToChange,eqnType,compartment,allowNewMets);
-0155 model=permuteModel(model,order,'rxns');
-0156 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/contractModel.html b/doc/manipulation/contractModel.html deleted file mode 100644 index 5b56454c..00000000 --- a/doc/manipulation/contractModel.html +++ /dev/null @@ -1,285 +0,0 @@ - - - - Description of contractModel - - - - - - - - - -
Home > manipulation > contractModel.m
- - - -

contractModel -

- -

PURPOSE ^

-
contractModel Contract a model by grouping all identical reactions.
- -

SYNOPSIS ^

-
function [reducedModel, removedRxns, indexedDuplicateRxns]=contractModel(model,varargin)
- -

DESCRIPTION ^

-
 contractModel  Contract a model by grouping all identical reactions.
-
- This is what simplifyModel's deleteDuplicates option runs; it is not a
- separate implementation. If the duplicated reactions have "_EXP_*" suffixes
- (where * is a digit), then the model is assumed to have been passed through
- expandModel, and these suffixes are removed here.
-
- Reactions are merged, so their gene associations are combined rather than
- discarded. Use findDuplicateRxns instead to see which reactions are
- duplicates without changing the model.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- distReverse : logical
-     distinguish reactions with same metabolites but different reversibility
-     as different reactions (default true).
- mets : char or cell
-     string or cell array of strings with metabolite identifiers, whose
-     involved reactions should be checked for duplication (by default all
-     reactions are considered). This option is used by replaceMets.
-
- Returns
- -------
- reducedModel : struct
-     a model structure without duplicate reactions.
- removedRxns : cell
-     cell array for the removed duplicate reactions.
- indexedDuplicateRxns : cell
-     indexed cell array for the removed duplicate reactions (multiple values
-     separated by semicolon).
-
- Examples
- --------
-     [reducedModel, removedRxns, indexedDuplicateRxns] = contractModel(model, distReverse, mets);
-
- Notes
- -----
- This code might not work for advanced grRules strings that involve nested
- expressions of "and" and "or".
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • removeReactions removeReactions Delete a set of reactions from a model.
  • sortModel sortModel Standardize a model's internal ordering: by default orients
  • standardizeGrRules standardizeGrRules Standardize gene-reaction rules in a model.
-This function is called by: -
    -
  • mergeCompartments mergeCompartments Merge all compartments in a model.
  • replaceMets replaceMets Replace a metabolite with another already in the model.
  • simplifyModel simplifyModel Simplify a model by deleting reactions and metabolites.
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [reducedModel, removedRxns, indexedDuplicateRxns]=contractModel(model,varargin)
-0002 % contractModel  Contract a model by grouping all identical reactions.
-0003 %
-0004 % This is what simplifyModel's deleteDuplicates option runs; it is not a
-0005 % separate implementation. If the duplicated reactions have "_EXP_*" suffixes
-0006 % (where * is a digit), then the model is assumed to have been passed through
-0007 % expandModel, and these suffixes are removed here.
-0008 %
-0009 % Reactions are merged, so their gene associations are combined rather than
-0010 % discarded. Use findDuplicateRxns instead to see which reactions are
-0011 % duplicates without changing the model.
-0012 %
-0013 % Parameters
-0014 % ----------
-0015 % model : struct
-0016 %     a model structure.
-0017 %
-0018 % Name-Value Arguments
-0019 % --------------------
-0020 % distReverse : logical
-0021 %     distinguish reactions with same metabolites but different reversibility
-0022 %     as different reactions (default true).
-0023 % mets : char or cell
-0024 %     string or cell array of strings with metabolite identifiers, whose
-0025 %     involved reactions should be checked for duplication (by default all
-0026 %     reactions are considered). This option is used by replaceMets.
-0027 %
-0028 % Returns
-0029 % -------
-0030 % reducedModel : struct
-0031 %     a model structure without duplicate reactions.
-0032 % removedRxns : cell
-0033 %     cell array for the removed duplicate reactions.
-0034 % indexedDuplicateRxns : cell
-0035 %     indexed cell array for the removed duplicate reactions (multiple values
-0036 %     separated by semicolon).
-0037 %
-0038 % Examples
-0039 % --------
-0040 %     [reducedModel, removedRxns, indexedDuplicateRxns] = contractModel(model, distReverse, mets);
-0041 %
-0042 % Notes
-0043 % -----
-0044 % This code might not work for advanced grRules strings that involve nested
-0045 % expressions of "and" and "or".
-0046 
-0047 p=parseRAVENargs(varargin, {'distReverse',true; 'mets',[]});
-0048 distReverse=p.distReverse;
-0049 mets=p.mets;
-0050 
-0051 %First sort the model so that reversible reactions are in the same
-0052 %direction
-0053 modelS=sortModel(model);
-0054 
-0055 %Get a list of duplicate reactions
-0056 if distReverse
-0057     x=[modelS.S; model.rev']';
-0058 else
-0059     x=modelS.S';
-0060 end
-0061 if ~isempty(mets)
-0062     toCheck = getIndexes(model,mets,'mets');
-0063     [~, toCheck] = find(modelS.S(toCheck,:));
-0064     x = [x, (1:size(x,1))']; % Make all other rxns unique by additional column
-0065     x(toCheck,end) = 0;
-0066 end
-0067 
-0068 [~,I,J] = unique(x,'rows','first');
-0069 
-0070 %Initialize cell array of indexedDuplicateRxns
-0071 indexedDuplicateRxns=cell(numel(model.rxns),1);
-0072 indexedDuplicateRxns(:)={''};
-0073 
-0074 duplicateRxns=setdiff(1:numel(model.rxns),I);
-0075 mergeTo=I(J(duplicateRxns));
-0076 
-0077 mergedRxns=unique(mergeTo);
-0078 
-0079 %Now add all the info from this one. Print a warning if they have different
-0080 %bounds or objective function coefficients. Uses the widest bounds and
-0081 %largest magnitude of objective coefficient
-0082 for i=1:numel(mergedRxns)
-0083     duplRxn=transpose([mergedRxns(i),duplicateRxns(mergeTo==mergedRxns(i))]);
-0084     if numel(unique(model.lb(duplRxn)))>1
-0085         EM=['Duplicates of reaction ' model.rxns{mergedRxns(i)} ' have different lower bound. Uses the most negative/smallest lower bound'];
-0086         warning('RAVEN:warning', '%s', EM);
-0087         model.lb(mergedRxns(i))=min(model.lb(duplRxn));
-0088     end
-0089     if numel(unique(model.ub(duplRxn)))>1
-0090         EM=['Duplicates of reaction ' model.rxns{mergedRxns(i)} ' have different upper bound. Uses the most positive/largest upper bound'];
-0091         warning('RAVEN:warning', '%s', EM);
-0092         model.ub(mergedRxns(i))=max(model.ub(duplRxn));
-0093     end
-0094     if numel(unique(model.c(duplRxn)))>1
-0095         EM=['Duplicates of reaction ' model.rxns{mergedRxns(i)} ' has a different objective function coefficient. Uses the largest coefficient'];
-0096         warning('RAVEN:warning', '%s', EM);
-0097         model.c(mergedRxns(i))=max(model.c(duplRxn));
-0098     end
-0099     if isfield(model,'grRules') && any(~isempty(model.grRules(duplRxn)))
-0100         rules=model.grRules(duplRxn);
-0101         allRules={};
-0102         for j=1:numel(rules)
-0103             rules{j}=ignoreORinComplex(rules{j});
-0104             allRules=[allRules regexp(rules{j},' or ','split')];
-0105         end
-0106         allRules=unique(allRules);
-0107         allRules=strrep(allRules,'__OR__',' or ');
-0108         andRules=contains(allRules,' and ');
-0109         allRules(andRules)=strcat('(',allRules(andRules),')');
-0110         if numel(allRules)==1
-0111             model.grRules{mergedRxns(i)}=allRules{1};
-0112         else
-0113             model.grRules{mergedRxns(i)}=strjoin(allRules,' or ');
-0114         end
-0115     end    
-0116     if isfield(model,'eccodes') && any(~isempty(model.eccodes(duplRxn)))
-0117         codes=model.eccodes(duplRxn);
-0118         allCodes={};
-0119         for j=1:numel(codes)
-0120             allCodes=[allCodes regexp(codes{j},';','split')];
-0121         end
-0122         allCodes=unique(allCodes);
-0123         if numel(allCodes)==1
-0124             model.eccodes{mergedRxns(i)}=allCodes{1};
-0125         else
-0126             model.eccodes{mergedRxns(i)}=strjoin(allCodes,';');
-0127         end
-0128     end
-0129     %Generate indexedDuplicateRxns cell array
-0130     if numel(duplRxn)==2
-0131         indexedDuplicateRxns{duplRxn(1)}=model.rxns{duplRxn(2)};
-0132     else
-0133         indexedDuplicateRxns{duplRxn(1)}=strjoin(model.rxns(duplRxn(2:end)),';');
-0134     end
-0135     %If all reactions have _EXP_* (where * is digit) as suffix, then the
-0136     %model had passed through expandModel(), and these suffixes are removed.
-0137     if all(~cellfun(@isempty,regexp(model.rxns(duplRxn),'_EXP_\d+$')))
-0138         model.rxns(duplRxn(1))=regexprep(model.rxns(duplRxn(1)),'_EXP_\d+$','');
-0139     end
-0140 end
-0141 
-0142 %Delete the duplicate reactions
-0143 reducedModel=removeReactions(model,duplicateRxns);
-0144 removedRxns=model.rxns(duplicateRxns);
-0145 [~, index]=ismember(reducedModel.rxns,model.rxns);
-0146 indexedDuplicateRxns=indexedDuplicateRxns(index);
-0147 
-0148 if isfield(reducedModel,'rxnGeneMat')
-0149     %Fix grRules and reconstruct rxnGeneMat
-0150     [grRules,rxnGeneMat] = standardizeGrRules(reducedModel,true);
-0151     reducedModel.grRules = grRules;
-0152     reducedModel.rxnGeneMat = rxnGeneMat;
-0153 end
-0154 end
-0155 
-0156 function grRule = ignoreORinComplex(grRule)
-0157 %In a grRule, if OR relationship is nested in an AND relationship, then
-0158 %obfuscate the OR before splitting isoenzymes
-0159 grRule=['(' grRule ')'];
-0160 brOpen=strfind(grRule,'(');
-0161 brClose=strfind(grRule,')');
-0162 andPos=strfind(grRule,' and ');
-0163 %Find opening bracket closest before AND
-0164 stillCapturing = 0;
-0165 for i=1:numel(andPos)
-0166     searchPos = andPos(i);
-0167     while stillCapturing == 0
-0168         closestOpen = brOpen(max(find(brOpen<searchPos)));
-0169         inbetweenClose = brClose(brClose<searchPos & brClose>closestOpen);
-0170         if ~isempty(inbetweenClose)
-0171             searchPos=max(inbetweenClose);
-0172         else
-0173             stillCapturing = 1;
-0174             beginPos = closestOpen;
-0175         end
-0176     end
-0177     stillCapturing = 0;
-0178     searchPos = andPos(i);
-0179     while stillCapturing == 0
-0180         closestClose = brClose(min(find(brClose>searchPos)));
-0181         inbetweenOpen = brOpen(brOpen>searchPos & brOpen<closestOpen);
-0182         if ~isempty(inbetweenOpen)
-0183             searchPos=min(closestClose);
-0184         else
-0185             stillCapturing = 1;
-0186             endPos = closestClose;
-0187         end
-0188     end
-0189     replacePart=regexprep(grRule(beginPos:endPos),' or ','__OR__');
-0190     grRule=strrep(grRule,grRule(beginPos:endPos),replacePart);
-0191 end
-0192 grRule=grRule(2:end-1);
-0193 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/copyToComps.html b/doc/manipulation/copyToComps.html deleted file mode 100644 index 5e8a2896..00000000 --- a/doc/manipulation/copyToComps.html +++ /dev/null @@ -1,210 +0,0 @@ - - - - Description of copyToComps - - - - - - - - - -
Home > manipulation > copyToComps.m
- - - -

copyToComps -

- -

PURPOSE ^

-
copyToComps Copy reactions to new compartment(s).
- -

SYNOPSIS ^

-
function model=copyToComps(model,toComps,varargin)
- -

DESCRIPTION ^

-
 copyToComps  Copy reactions to new compartment(s).
-
- Parameters
- ----------
- model : struct
-     a model structure.
- toComps : cell
-     cell array of compartment ids. If there is no match to model.comps
-     then it is added as a new compartment (see compNames and
-     compOutside).
-
- Name-Value Arguments
- --------------------
- rxns : cell or logical or double
-     either a cell array of reaction IDs, a logical vector with the same
-     number of elements as reactions in the model, or a vector of indexes
-     to copy (default model.rxns).
- deleteOriginal : logical
-     true if the original reactions should be removed, making it move the
-     reactions instead (default false).
- compNames : cell
-     cell array of compartment names. Used if new compartments should be
-     added (default toComps).
- compOutside : cell
-     cell array of the id (as in comps) for the compartment surrounding
-     each of the compartments. Used if new compartments should be added
-     (default all {''}).
-
- Returns
- -------
- model : struct
-     an updated model structure.
-
- Examples
- --------
-     model=copyToComps(model,toComps,rxns,deleteOriginal,compNames,compOutside);
-
- Notes
- -----
- New reactions and metabolites will be named as "id_toComps(i)".
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • mergeModels mergeModels Merge models into one model structure.
  • removeReactions removeReactions Delete a set of reactions from a model.
-This function is called by: -
    -
  • mergeModels mergeModels Merge models into one model structure.
- - - - -

SOURCE CODE ^

-
0001 function model=copyToComps(model,toComps,varargin)
-0002 % copyToComps  Copy reactions to new compartment(s).
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % model : struct
-0007 %     a model structure.
-0008 % toComps : cell
-0009 %     cell array of compartment ids. If there is no match to model.comps
-0010 %     then it is added as a new compartment (see compNames and
-0011 %     compOutside).
-0012 %
-0013 % Name-Value Arguments
-0014 % --------------------
-0015 % rxns : cell or logical or double
-0016 %     either a cell array of reaction IDs, a logical vector with the same
-0017 %     number of elements as reactions in the model, or a vector of indexes
-0018 %     to copy (default model.rxns).
-0019 % deleteOriginal : logical
-0020 %     true if the original reactions should be removed, making it move the
-0021 %     reactions instead (default false).
-0022 % compNames : cell
-0023 %     cell array of compartment names. Used if new compartments should be
-0024 %     added (default toComps).
-0025 % compOutside : cell
-0026 %     cell array of the id (as in comps) for the compartment surrounding
-0027 %     each of the compartments. Used if new compartments should be added
-0028 %     (default all {''}).
-0029 %
-0030 % Returns
-0031 % -------
-0032 % model : struct
-0033 %     an updated model structure.
-0034 %
-0035 % Examples
-0036 % --------
-0037 %     model=copyToComps(model,toComps,rxns,deleteOriginal,compNames,compOutside);
-0038 %
-0039 % Notes
-0040 % -----
-0041 % New reactions and metabolites will be named as "id_toComps(i)".
-0042 
-0043 p=parseRAVENargs(varargin, {'rxns',[]; 'deleteOriginal',false; 'compNames',[]; 'compOutside',[]});
-0044 rxns=p.rxns;
-0045 if isempty(rxns)
-0046     rxns=model.rxns;
-0047 elseif ~islogical(rxns) && ~isnumeric(rxns)
-0048     rxns=convertCharArray(rxns);
-0049 end
-0050 deleteOriginal=p.deleteOriginal;
-0051 compNames=p.compNames;
-0052 if isempty(compNames)
-0053     compNames=toComps;
-0054 else
-0055     compNames=convertCharArray(compNames);
-0056 end
-0057 compOutside=p.compOutside;
-0058 if isempty(compOutside)
-0059     % Default is {''} for every new compartment, not '': compOutside(i)
-0060     % below indexes into it once per new compartment, which a bare ''
-0061     % cannot support.
-0062     compOutside=repmat({''},size(compNames));
-0063 else
-0064     compOutside=convertCharArray(compOutside);
-0065     if length(compOutside) ~= length(compNames)
-0066         error('compOutside and compNames should be of equal size.');
-0067     end
-0068 end
-0069 
-0070 originalID=model.id;
-0071 originalName=model.name;
-0072 
-0073 rxns=getIndexes(model,rxns,'rxns');
-0074 
-0075 for i=1:numel(toComps)
-0076     %Check if the compartment exists, otherwise add it
-0077     [I,J]=ismember(toComps(i),model.comps);
-0078     if I==false
-0079         model.comps=[model.comps;toComps(i)];
-0080         model.compNames=[model.compNames;compNames(i)];
-0081         if isfield(model,'compOutside')
-0082             model.compOutside=[model.compOutside;compOutside(i)];
-0083         end
-0084         if isfield(model,'compMiriams')
-0085             model.compMiriams=[model.compMiriams;cell(1,1)];
-0086         end
-0087         J=numel(model.comps);
-0088     end
-0089     %Copy the reactions by making a model structure with only them, then
-0090     %change the localization, and finally merge with the original model
-0091     modelToAdd=model;
-0092     modelToAdd=removeReactions(modelToAdd,setdiff(1:numel(model.rxns),rxns),true,true);
-0093     modelToAdd.rxns=strcat(modelToAdd.rxns,'_',toComps(i));
-0094     modelToAdd.mets=strcat(modelToAdd.mets,'_',toComps(i));
-0095     modelToAdd.comps=modelToAdd.comps(J);
-0096     modelToAdd.compNames=modelToAdd.compNames(J);
-0097     if isfield(modelToAdd,'compOutside')
-0098         modelToAdd.compOutside=modelToAdd.compOutside(J);
-0099     end
-0100     if isfield(modelToAdd,'compMiriams')
-0101         modelToAdd.compMiriams=modelToAdd.compMiriams(J);
-0102     end
-0103     modelToAdd.metComps=ones(numel(modelToAdd.mets),1);
-0104     if isfield(modelToAdd,'metFrom')
-0105         modelToAdd = rmfield(modelToAdd,'metFrom');
-0106     end
-0107     if isfield(modelToAdd,'rxnFrom')
-0108         modelToAdd = rmfield(modelToAdd,'rxnFrom');
-0109     end
-0110     if isfield(modelToAdd,'geneFrom')
-0111         modelToAdd = rmfield(modelToAdd,'geneFrom');
-0112     end
-0113   
-0114     %Merge the models
-0115     model=mergeModels({model;modelToAdd},'metNames',[],true);
-0116 end
-0117 
-0118 if deleteOriginal==true
-0119     model=removeReactions(model,rxns,true,true,true); %Also delete unused compartments
-0120 end
-0121 
-0122 model.id=originalID;
-0123 model.name=originalName;
-0124 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/findPotentialErrors.html b/doc/manipulation/findPotentialErrors.html deleted file mode 100644 index 85395a27..00000000 --- a/doc/manipulation/findPotentialErrors.html +++ /dev/null @@ -1,177 +0,0 @@ - - - - Description of findPotentialErrors - - - - - - - - - -
Home > manipulation > findPotentialErrors.m
- - - -

findPotentialErrors -

- -

PURPOSE ^

-
findPotentialErrors Identify reactions with non-DNF GPR rules.
- -

SYNOPSIS ^

-
function issues=findPotentialErrors(model)
- -

DESCRIPTION ^

-
 findPotentialErrors  Identify reactions with non-DNF GPR rules.
-
- Detects grRules that are not in disjunctive normal form (DNF) —
- i.e. rules where an OR operator is nested inside an AND context.
- Standard RAVEN format requires DNF: groups of genes that must all be
- present together (enzyme complexes) are written as conjunctions, and
- the reaction is catalysed by at least one complex (disjunction of
- conjunctions).
-
- Parameters
- ----------
- model : struct
-     a model structure with grRules and rxns fields.
-
- Returns
- -------
- issues : struct
-     struct array — one element per problematic reaction — with fields:
-
-     - index  : double — 1-based index into model.rxns.
-     - rxn    : char   — reaction ID.
-     - grRule : char   — the problematic grRule string.
-     - reason : char   — short explanation.
-
- Notes
- -----
- Detection walks the parse tree from parseGrRule rather than searching the
- raw string, so gene IDs that contain substrings such as "and" or "or" do
- not cause false positives or negatives.
-
- A rule is non-DNF when an AND operator has an OR anywhere beneath it.
- Bracketing alone never makes a rule non-DNF: "((G1 and G2) or G3)" and
- "(G1 or G2) or (G3 and G4)" are both fine.
- Use standardizeGrRules to attempt automatic repair.
-
- Rules that cannot be parsed at all are reported as issues too, with the
- parser's message as the reason.
-
- Examples
- --------
-     issues = findPotentialErrors(model);
-     if ~isempty(issues)
-         fprintf("%d reactions with non-DNF grRules\n", numel(issues));
-     end
-     indexes2check = vertcat(issues.index);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: - - - - - -

SOURCE CODE ^

-
0001 function issues=findPotentialErrors(model)
-0002 % findPotentialErrors  Identify reactions with non-DNF GPR rules.
-0003 %
-0004 % Detects grRules that are not in disjunctive normal form (DNF) —
-0005 % i.e. rules where an OR operator is nested inside an AND context.
-0006 % Standard RAVEN format requires DNF: groups of genes that must all be
-0007 % present together (enzyme complexes) are written as conjunctions, and
-0008 % the reaction is catalysed by at least one complex (disjunction of
-0009 % conjunctions).
-0010 %
-0011 % Parameters
-0012 % ----------
-0013 % model : struct
-0014 %     a model structure with grRules and rxns fields.
-0015 %
-0016 % Returns
-0017 % -------
-0018 % issues : struct
-0019 %     struct array — one element per problematic reaction — with fields:
-0020 %
-0021 %     - index  : double — 1-based index into model.rxns.
-0022 %     - rxn    : char   — reaction ID.
-0023 %     - grRule : char   — the problematic grRule string.
-0024 %     - reason : char   — short explanation.
-0025 %
-0026 % Notes
-0027 % -----
-0028 % Detection walks the parse tree from parseGrRule rather than searching the
-0029 % raw string, so gene IDs that contain substrings such as "and" or "or" do
-0030 % not cause false positives or negatives.
-0031 %
-0032 % A rule is non-DNF when an AND operator has an OR anywhere beneath it.
-0033 % Bracketing alone never makes a rule non-DNF: "((G1 and G2) or G3)" and
-0034 % "(G1 or G2) or (G3 and G4)" are both fine.
-0035 % Use standardizeGrRules to attempt automatic repair.
-0036 %
-0037 % Rules that cannot be parsed at all are reported as issues too, with the
-0038 % parser's message as the reason.
-0039 %
-0040 % Examples
-0041 % --------
-0042 %     issues = findPotentialErrors(model);
-0043 %     if ~isempty(issues)
-0044 %         fprintf("%d reactions with non-DNF grRules\n", numel(issues));
-0045 %     end
-0046 %     indexes2check = vertcat(issues.index);
-0047 
-0048 issues=struct('index',{},'rxn',{},'grRule',{},'reason',{});
-0049 
-0050 if ~isfield(model,'grRules') || ~isfield(model,'rxns')
-0051     return
-0052 end
-0053 
-0054 for i=1:numel(model.grRules)
-0055     rule=strtrim(model.grRules{i});
-0056     if isempty(rule)
-0057         continue
-0058     end
-0059 
-0060     try
-0061         isDnf=isDnfGrRule(rule);
-0062     catch ME
-0063         if ~strcmp(ME.identifier,'RAVEN:badGrRule')
-0064             rethrow(ME)
-0065         end
-0066         % A rule that cannot be parsed is itself worth reporting: it is
-0067         % certainly not usable by the isoenzyme/complex reasoning, and
-0068         % silently skipping it would hide the very thing this function
-0069         % exists to surface.
-0070         issues(end+1,1)=struct( ...
-0071             'index',  i, ...
-0072             'rxn',    model.rxns{i}, ...
-0073             'grRule', rule, ...
-0074             'reason', ['Cannot be parsed: ' ME.message]); %#ok<AGROW>
-0075         continue
-0076     end
-0077 
-0078     if ~isDnf
-0079         issues(end+1,1)=struct( ...
-0080             'index',  i, ...
-0081             'rxn',    model.rxns{i}, ...
-0082             'grRule', rule, ...
-0083             'reason', 'Non-DNF: OR operator nested inside AND context'); %#ok<AGROW>
-0084     end
-0085 end
-0086 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/generateNewIds.html b/doc/manipulation/generateNewIds.html deleted file mode 100644 index 763a130c..00000000 --- a/doc/manipulation/generateNewIds.html +++ /dev/null @@ -1,153 +0,0 @@ - - - - Description of generateNewIds - - - - - - - - - -
Home > manipulation > generateNewIds.m
- - - -

generateNewIds -

- -

PURPOSE ^

-
generateNewIds Generate a list of new metabolite or reaction ids.
- -

SYNOPSIS ^

-
function newIds=generateNewIds(model,type,prefix,varargin)
- -

DESCRIPTION ^

-
 generateNewIds  Generate a list of new metabolite or reaction ids.
-
- The ids are sequentially numbered with a defined prefix. The model is
- queried for the highest existing number of that type of id.
-
- Parameters
- ----------
- model : struct
-     model structure.
- type : char
-     type of id, 'rxns' or 'mets'.
- prefix : char
-     prefix to be used in all ids, e.g. 's_' or 'r_'.
-
- Name-Value Arguments
- --------------------
- quantity : double
-     number of new ids that should be generated (default 1).
- numLength : double
-     length of the numerical part of the id. E.g. 4 gives ids like r_0001
-     and 6 gives ids like r_000001. If the prefix is already used in the
-     model, then the model-defined length will be used instead
-     (default 4).
-
- Returns
- -------
- newIds : cell
-     cell array with the generated ids.
-
- Examples
- --------
-     newIds = generateNewIds(model, type, prefix, quantity, numLength);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • addMets addMets Add metabolites to a model.
  • addTransport addTransport Add transport reactions between compartments.
- - - - -

SOURCE CODE ^

-
0001 function newIds=generateNewIds(model,type,prefix,varargin)
-0002 % generateNewIds  Generate a list of new metabolite or reaction ids.
-0003 %
-0004 % The ids are sequentially numbered with a defined prefix. The model is
-0005 % queried for the highest existing number of that type of id.
-0006 %
-0007 % Parameters
-0008 % ----------
-0009 % model : struct
-0010 %     model structure.
-0011 % type : char
-0012 %     type of id, 'rxns' or 'mets'.
-0013 % prefix : char
-0014 %     prefix to be used in all ids, e.g. 's_' or 'r_'.
-0015 %
-0016 % Name-Value Arguments
-0017 % --------------------
-0018 % quantity : double
-0019 %     number of new ids that should be generated (default 1).
-0020 % numLength : double
-0021 %     length of the numerical part of the id. E.g. 4 gives ids like r_0001
-0022 %     and 6 gives ids like r_000001. If the prefix is already used in the
-0023 %     model, then the model-defined length will be used instead
-0024 %     (default 4).
-0025 %
-0026 % Returns
-0027 % -------
-0028 % newIds : cell
-0029 %     cell array with the generated ids.
-0030 %
-0031 % Examples
-0032 % --------
-0033 %     newIds = generateNewIds(model, type, prefix, quantity, numLength);
-0034 type=char(type);
-0035 prefix=char(prefix);
-0036 
-0037 if type=='rxns'
-0038     existingIds=model.rxns;
-0039 elseif type=='mets'
-0040     existingIds=model.mets;
-0041 else
-0042     error('type should be either ''rxns'' or ''mets''.')
-0043 end
-0044 p=parseRAVENargs(varargin, {'quantity',1; 'numLength',4});
-0045 quantity=p.quantity;
-0046 numLength=p.numLength;
-0047 
-0048 % Subset only existingIds that have the prefix. Escaped: prefix is
-0049 % arbitrary caller-supplied text, not a regex pattern.
-0050 escapedPrefix=regexptranslate('escape',prefix);
-0051 existingIds=existingIds(~cellfun(@isempty,regexp(existingIds,['^' escapedPrefix])));
-0052 
-0053 if ~isempty(existingIds)
-0054     existingIds=regexprep(existingIds,['^' escapedPrefix],'');
-0055     existingIds=sort(existingIds);
-0056     lastId=existingIds{end};
-0057     numLength=length(lastId);
-0058     lastId=str2double(lastId);
-0059 else
-0060     lastId=0;
-0061     fprintf(['No ' type ' ids with prefix "' prefix ...
-0062         '" currently exist in the model. The first new id will be "' ...
-0063         [prefix,num2str(1,['%0' num2str(numLength) 'd'])] '"\n'],'%s')
-0064 end
-0065 
-0066 if isnan(lastId)
-0067     lastId = 0;
-0068 end
-0069 
-0070 newIds=cell(quantity,1);
-0071 
-0072 for k=1:quantity
-0073     newIds{k}=[prefix,num2str(k+lastId,['%0' num2str(numLength) 'd'])];
-0074 end
-0075 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/index.html b/doc/manipulation/index.html deleted file mode 100644 index 60619481..00000000 --- a/doc/manipulation/index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - Index for Directory manipulation - - - - - - - - - - -
< Master indexIndex for manipulation >
- -

Index for manipulation

- -

Matlab files in this directory:

- -
 addExchangeRxnsaddExchangeRxns Add exchange reactions for some metabolites.
 addGenesRavenaddGenesRaven Add genes to a model.
 addMetsaddMets Add metabolites to a model.
 addRxnsaddRxns Add reactions to a model.
 addRxnsGenesMetsaddRxnsGenesMets Copy reactions from a source model into another model.
 addTransportaddTransport Add transport reactions between compartments.
 changeGrRuleschangeGrRules Change multiple grRules at the same time.
 changeRxnschangeRxns Modify the equations of reactions in a model.
 closeModelcloseModel Add boundary metabolites and their exchange reactions.
 contractModelcontractModel Contract a model by grouping all identical reactions.
 convertToIrrevconvertToIrrev Convert a model to irreversible form.
 copyToCompscopyToComps Copy reactions to new compartment(s).
 deleteUnusedGenesdeleteUnusedGenes Delete all genes not associated to any reaction.
 expandModelexpandModel Expand reactions that use several gene associations.
 findDuplicateRxnsfindDuplicateRxns Find reactions that share identical stoichiometry.
 findPotentialErrorsfindPotentialErrors Identify reactions with non-DNF GPR rules.
 generateNewIdsgenerateNewIds Generate a list of new metabolite or reaction ids.
 mergeCompartmentsmergeCompartments Merge all compartments in a model.
 mergeModelsmergeModels Merge models into one model structure.
 permuteModelpermuteModel Change the order of the reactions or metabolites in a model.
 removeBadRxnsremoveBadRxns Remove reactions that enable production/consumption from nothing.
 removeGenesremoveGenes Delete a set of genes from a model.
 removeLowScoreGenesremoveLowScoreGenes Remove low-scoring genes from model.
 removeMetsremoveMets Delete a set of metabolites from a model.
 removeReactionsremoveReactions Delete a set of reactions from a model.
 replaceMetsreplaceMets Replace a metabolite with another already in the model.
 reverseRxnsreverseRxns
 setExchangeBoundssetExchangeBounds Define exchange flux bounds for a set of metabolites.
 setParamsetParam Set parameters for reactions.
 simplifyModelsimplifyModel Simplify a model by deleting reactions and metabolites.
 sortIdentifierssortIdentifiers Sort model identifiers alphabetically.
 sortModelsortModel Standardize a model's internal ordering: by default orients
 standardizeGrRulesstandardizeGrRules Standardize gene-reaction rules in a model.
- - - - -
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/mergeCompartments.html b/doc/manipulation/mergeCompartments.html deleted file mode 100644 index 99f2ad43..00000000 --- a/doc/manipulation/mergeCompartments.html +++ /dev/null @@ -1,241 +0,0 @@ - - - - Description of mergeCompartments - - - - - - - - - -
Home > manipulation > mergeCompartments.m
- - - -

mergeCompartments -

- -

PURPOSE ^

-
mergeCompartments Merge all compartments in a model.
- -

SYNOPSIS ^

-
function [model, deletedRxns, duplicateRxns]=mergeCompartments(model,varargin)
- -

DESCRIPTION ^

-
 mergeCompartments  Merge all compartments in a model.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- keepUnconstrained : logical
-     keep metabolites that are unconstrained in a "unconstrained"
-     compartment. If these are merged the exchange reactions will most often
-     be deleted (default false).
- deleteRxnsWithOneMet : logical
-     delete reactions with only one metabolite. These reactions come from
-     reactions such as A[c] + B[c] => A[m]. In some models hydrogen is
-     balanced around each membrane with reactions like this (default
-     false).
- distReverse : logical
-     distinguish reactions with same metabolites but different reversibility
-     as different reactions (default true).
-
- Returns
- -------
- model : struct
-     a model with all reactions located to one compartment.
- deletedRxns : cell
-     reactions that were deleted because of only having one metabolite
-     after merging.
- duplicateRxns : cell
-     identical reactions that occurred in different compartments and were
-     deleted because they turned to be duplicated after merging.
-
- Examples
- --------
-     [model, deletedRxns, duplicateRxns] = mergeCompartments(model);
-
- Notes
- -----
- Merges all compartments into one "s" compartment (for "System"). This can
- be useful for example to ensure that there are metabolic capabilities to
- synthesize all metabolites.
-
- If the metabolite IDs reflect the compartment that they are in the IDs may
- no longer be representative.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • contractModel contractModel Contract a model by grouping all identical reactions.
  • removeMets removeMets Delete a set of metabolites from a model.
  • removeReactions removeReactions Delete a set of reactions from a model.
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function [model, deletedRxns, duplicateRxns]=mergeCompartments(model,varargin)
-0002 % mergeCompartments  Merge all compartments in a model.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % model : struct
-0007 %     a model structure.
-0008 %
-0009 % Name-Value Arguments
-0010 % --------------------
-0011 % keepUnconstrained : logical
-0012 %     keep metabolites that are unconstrained in a "unconstrained"
-0013 %     compartment. If these are merged the exchange reactions will most often
-0014 %     be deleted (default false).
-0015 % deleteRxnsWithOneMet : logical
-0016 %     delete reactions with only one metabolite. These reactions come from
-0017 %     reactions such as A[c] + B[c] => A[m]. In some models hydrogen is
-0018 %     balanced around each membrane with reactions like this (default
-0019 %     false).
-0020 % distReverse : logical
-0021 %     distinguish reactions with same metabolites but different reversibility
-0022 %     as different reactions (default true).
-0023 %
-0024 % Returns
-0025 % -------
-0026 % model : struct
-0027 %     a model with all reactions located to one compartment.
-0028 % deletedRxns : cell
-0029 %     reactions that were deleted because of only having one metabolite
-0030 %     after merging.
-0031 % duplicateRxns : cell
-0032 %     identical reactions that occurred in different compartments and were
-0033 %     deleted because they turned to be duplicated after merging.
-0034 %
-0035 % Examples
-0036 % --------
-0037 %     [model, deletedRxns, duplicateRxns] = mergeCompartments(model);
-0038 %
-0039 % Notes
-0040 % -----
-0041 % Merges all compartments into one "s" compartment (for "System"). This can
-0042 % be useful for example to ensure that there are metabolic capabilities to
-0043 % synthesize all metabolites.
-0044 %
-0045 % If the metabolite IDs reflect the compartment that they are in the IDs may
-0046 % no longer be representative.
-0047 
-0048 p=parseRAVENargs(varargin, {'keepUnconstrained',false; 'deleteRxnsWithOneMet',false; 'distReverse',true});
-0049 keepUnconstrained=p.keepUnconstrained;
-0050 deleteRxnsWithOneMet=p.deleteRxnsWithOneMet;
-0051 distReverse=p.distReverse;
-0052 
-0053 if ~isfield(model,'unconstrained')
-0054     keepUnconstrained=false;
-0055 end
-0056 
-0057 %Keep track of which reactions only contained one metabolite before the
-0058 %merging as they are probable exchange reactions.
-0059 if deleteRxnsWithOneMet==true
-0060     reservedRxns=model.rxns(sum(model.S~=0)==1);
-0061     if ~isempty(reservedRxns) && ~isfield(model,'unconstrained')
-0062         %If there is no unconstrained field these reactions are probably
-0063         %exchange reactions and shall be kept. If not then print a warning
-0064         EM='There are reactions with only one metabolite. Cannot determine whether they are exchange reactions since there is no unconstrained field';
-0065         warning('RAVEN:warning', '%s', EM);
-0066     end
-0067 end
-0068 
-0069 %Loop through each metabolite, and if it is not unconstrained then change
-0070 %the S matrix to use the metabolite with the lowest index in model.comps
-0071 %instead
-0072 %Group metabolite indices by name once, rather than re-scanning all of
-0073 %model.metNames (via ismember) for every unique name, which is quadratic
-0074 %in a genome-scale model's (unique names x mets) count.
-0075 [uNames,~,nameGroup]=unique(model.metNames);
-0076 sameNameIdx=accumarray(nameGroup,(1:numel(nameGroup))',[numel(uNames) 1],@(v) {v});
-0077 for i=1:numel(uNames)
-0078     %Find all metabolites with this name..
-0079     I=false(numel(model.metNames),1);
-0080     I(sameNameIdx{i})=true;
-0081 
-0082     %Find the first of those that is not unconstrained. This is the one
-0083     %that the other "un-unconstrained" should be changed to.
-0084     if keepUnconstrained==true
-0085         mergeTo=find(I & model.unconstrained==false,1);
-0086         
-0087         %This could happen if there is only one metabolite and it is
-0088         %unconstrained
-0089         if isempty(mergeTo)
-0090             continue;
-0091         end
-0092     else
-0093         mergeTo=find(I,1);
-0094     end
-0095     I(mergeTo)=false; %Do not do anything for itself
-0096     I=find(I);
-0097     
-0098     %Go through each of the metabolites with this name and update them to
-0099     %mergeTo
-0100     for j=1:numel(I)
-0101         if keepUnconstrained==true && model.unconstrained(I(j))==true
-0102             continue;
-0103         end
-0104         %Update the S matrix
-0105         model.S(mergeTo,:)=model.S(mergeTo,:)+model.S(I(j),:);
-0106         model.S(I(j),:)=0;
-0107     end
-0108 end
-0109 
-0110 %Remove all metabolites that are no longer used (with a bit of a trick)
-0111 model=removeReactions(model,{},true);
-0112 
-0113 %Update all mets so that they are in compartment "s" with id "1"
-0114 model.compNames={'System'};
-0115 model.comps={'s'};
-0116 model.compOutside={''};
-0117 model.metComps=ones(numel(model.mets),1);
-0118 %The per-compartment MIRIAM annotations no longer apply once every
-0119 %compartment is merged into one, and leaving them would desync compMiriams
-0120 %from the rebuilt comps. Drop them.
-0121 if isfield(model,'compMiriams')
-0122     model=rmfield(model,'compMiriams');
-0123 end
-0124 
-0125 %Add exchange mets to another compartment "b" with id "2"
-0126 if keepUnconstrained==true
-0127     model.compNames=[model.compNames; {'Unconstrained'}];
-0128     model.comps=[model.comps; {'b'}];
-0129     model.compOutside=[model.compOutside; {'s'}];
-0130     model.metComps(model.unconstrained~=0)=2;
-0131 end
-0132 
-0133 %Transport reactions on the form A <=> B will have been deleted by the
-0134 %merging. Remove those reactions
-0135 model=removeMets(model,{},false,true,true);
-0136 
-0137 if deleteRxnsWithOneMet==true
-0138     I=model.rxns(sum(model.S~=0)==1);
-0139     
-0140     %Delete the reactions that contain only one metabolite after the
-0141     %merging but not before
-0142     deletedRxns=setdiff(I,reservedRxns);
-0143     model=removeReactions(model,deletedRxns,true,true);
-0144 else
-0145     deletedRxns={};
-0146 end
-0147 
-0148 %And then finally merge the identical reactions
-0149 [model, ~, duplicateRxns]=contractModel(model,distReverse);
-0150 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/removeBadRxns.html b/doc/manipulation/removeBadRxns.html deleted file mode 100644 index a9dd8445..00000000 --- a/doc/manipulation/removeBadRxns.html +++ /dev/null @@ -1,387 +0,0 @@ - - - - Description of removeBadRxns - - - - - - - - - -
Home > manipulation > removeBadRxns.m
- - - -

removeBadRxns -

- -

PURPOSE ^

-
removeBadRxns Remove reactions that enable production/consumption from nothing.
- -

SYNOPSIS ^

-
function [newModel, removedRxns]=removeBadRxns(model,varargin)
- -

DESCRIPTION ^

-
 removeBadRxns  Remove reactions that enable production/consumption from nothing.
-
- Iteratively removes reactions which enable production/consumption of some
- metabolite without any uptake/excretion.
-
- Parameters
- ----------
- model : struct
-     a model structure. For the intended function, the model should not
-     allow for any uptake/excretion. The easiest way to achieve this is to
-     import the model using importModel("filename", false).
-
- Name-Value Arguments
- --------------------
- rxnRules : double
-     which reactions may be removed (default 1):
-
-     - 1 : only remove reactions which are unbalanced
-     - 2 : also remove reactions which could not be checked for mass
-       balancing
-     - 3 : all reactions can be removed
- ignoreMets : cell or logical or double
-     either a cell array of metabolite IDs, a logical vector with the same
-     number of elements as metabolites in the model, or a vector of indexes
-     for metabolites to exclude from this analysis (default []).
- isNames : logical
-     true if the supplied mets represent metabolite names (as opposed to
-     IDs). This is a way to delete metabolites in several compartments at
-     once without knowing the exact IDs. This only works if ignoreMets is a
-     cell array (default false).
- balanceElements : cell
-     a cell array with the elements for which to balance the reactions. May
-     contain any combination of the elements defined in parseFormulas
-     (default {"C";"P";"S";"N";"O"}).
- refModel : struct
-     a reference model which can be used to ensure that the resulting model
-     is still functional. The intended use is that the reference model is a
-     copy of model, but with uptake/excretion allowed and some objectives
-     (such as production of biomass) constrained to a non-zero flux. Before
-     a reaction is removed from "model" the function first checks that the
-     same deletion in "refModel" does not render the problem unfeasible.
- ignoreIntBounds : logical
-     true if internal bounds (including reversibility) should be ignored.
-     Exchange reactions are not affected. This can be used to find
-     unbalanced solutions which are not possible using the default
-     constraints (default false).
- printReport : logical
-     true if a report should be printed (default false).
- seed : double
-     seed for the random number generator, so that which reaction is
-     removed among several equally-valid candidates is reproducible
-     across runs (default: current rng state is left untouched).
-
- Returns
- -------
- newModel : struct
-     a model structure after the problematic reactions have been deleted.
- removedRxns : cell
-     a cell array with the reactions that were removed.
-
- Notes
- -----
- The purpose of this function is to remove reactions which enable
- production/consumption of metabolites even when exchange reactions are not
- used. Many models, especially if they are automatically inferred from
- databases, will have unbalanced reactions which allow for
- net-production/consumption of metabolites without any consumption or
- excretion. A common reason for this is when general compounds have
- different meaning in different reactions (as DNA has in these two
- reactions):
-
-     dATP + dGTP + dCTP + dTTP <=> DNA + 4 PPi
-     0.25 dATP + 0.25 dGTP + 0.25 dCTP + 0.25 dTTP <=> DNA + PPi
-
- Reactions that are problematic like this are always elementally
- unbalanced, but it is not always the case that you would like to exclude
- all unbalanced reactions from your model. This function tries to remove as
- few problematic reactions as possible so that the model cannot
- produce/consume anything from nothing. This is done by repeatedly calling
- findLeakMetabolite, checking if any of the involved reactions are
- elementally unbalanced, removing one of them, and then iterating until no
- metabolites can be produced/consumed. The 'produce' direction is run
- before the 'consume' one.
-
- Examples
- --------
-     [newModel, removedRxns] = removeBadRxns(model, rxnRules, ignoreMets, ...
-         isNames, balanceElements, refModel, ignoreIntBounds, printReport);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function [newModel, removedRxns]=removeBadRxns(model,varargin)
-0002 % removeBadRxns  Remove reactions that enable production/consumption from nothing.
-0003 %
-0004 % Iteratively removes reactions which enable production/consumption of some
-0005 % metabolite without any uptake/excretion.
-0006 %
-0007 % Parameters
-0008 % ----------
-0009 % model : struct
-0010 %     a model structure. For the intended function, the model should not
-0011 %     allow for any uptake/excretion. The easiest way to achieve this is to
-0012 %     import the model using importModel("filename", false).
-0013 %
-0014 % Name-Value Arguments
-0015 % --------------------
-0016 % rxnRules : double
-0017 %     which reactions may be removed (default 1):
-0018 %
-0019 %     - 1 : only remove reactions which are unbalanced
-0020 %     - 2 : also remove reactions which could not be checked for mass
-0021 %       balancing
-0022 %     - 3 : all reactions can be removed
-0023 % ignoreMets : cell or logical or double
-0024 %     either a cell array of metabolite IDs, a logical vector with the same
-0025 %     number of elements as metabolites in the model, or a vector of indexes
-0026 %     for metabolites to exclude from this analysis (default []).
-0027 % isNames : logical
-0028 %     true if the supplied mets represent metabolite names (as opposed to
-0029 %     IDs). This is a way to delete metabolites in several compartments at
-0030 %     once without knowing the exact IDs. This only works if ignoreMets is a
-0031 %     cell array (default false).
-0032 % balanceElements : cell
-0033 %     a cell array with the elements for which to balance the reactions. May
-0034 %     contain any combination of the elements defined in parseFormulas
-0035 %     (default {"C";"P";"S";"N";"O"}).
-0036 % refModel : struct
-0037 %     a reference model which can be used to ensure that the resulting model
-0038 %     is still functional. The intended use is that the reference model is a
-0039 %     copy of model, but with uptake/excretion allowed and some objectives
-0040 %     (such as production of biomass) constrained to a non-zero flux. Before
-0041 %     a reaction is removed from "model" the function first checks that the
-0042 %     same deletion in "refModel" does not render the problem unfeasible.
-0043 % ignoreIntBounds : logical
-0044 %     true if internal bounds (including reversibility) should be ignored.
-0045 %     Exchange reactions are not affected. This can be used to find
-0046 %     unbalanced solutions which are not possible using the default
-0047 %     constraints (default false).
-0048 % printReport : logical
-0049 %     true if a report should be printed (default false).
-0050 % seed : double
-0051 %     seed for the random number generator, so that which reaction is
-0052 %     removed among several equally-valid candidates is reproducible
-0053 %     across runs (default: current rng state is left untouched).
-0054 %
-0055 % Returns
-0056 % -------
-0057 % newModel : struct
-0058 %     a model structure after the problematic reactions have been deleted.
-0059 % removedRxns : cell
-0060 %     a cell array with the reactions that were removed.
-0061 %
-0062 % Notes
-0063 % -----
-0064 % The purpose of this function is to remove reactions which enable
-0065 % production/consumption of metabolites even when exchange reactions are not
-0066 % used. Many models, especially if they are automatically inferred from
-0067 % databases, will have unbalanced reactions which allow for
-0068 % net-production/consumption of metabolites without any consumption or
-0069 % excretion. A common reason for this is when general compounds have
-0070 % different meaning in different reactions (as DNA has in these two
-0071 % reactions):
-0072 %
-0073 %     dATP + dGTP + dCTP + dTTP <=> DNA + 4 PPi
-0074 %     0.25 dATP + 0.25 dGTP + 0.25 dCTP + 0.25 dTTP <=> DNA + PPi
-0075 %
-0076 % Reactions that are problematic like this are always elementally
-0077 % unbalanced, but it is not always the case that you would like to exclude
-0078 % all unbalanced reactions from your model. This function tries to remove as
-0079 % few problematic reactions as possible so that the model cannot
-0080 % produce/consume anything from nothing. This is done by repeatedly calling
-0081 % findLeakMetabolite, checking if any of the involved reactions are
-0082 % elementally unbalanced, removing one of them, and then iterating until no
-0083 % metabolites can be produced/consumed. The 'produce' direction is run
-0084 % before the 'consume' one.
-0085 %
-0086 % Examples
-0087 % --------
-0088 %     [newModel, removedRxns] = removeBadRxns(model, rxnRules, ignoreMets, ...
-0089 %         isNames, balanceElements, refModel, ignoreIntBounds, printReport);
-0090 
-0091 p=parseRAVENargs(varargin, {'rxnRules',1; 'ignoreMets',[]; 'isNames',false; 'balanceElements',{'C';'P';'S';'N';'O'}; 'refModel',[]; 'ignoreIntBounds',false; 'printReport',false; 'seed',[]});
-0092 if ~isempty(p.seed)
-0093     rng(p.seed);
-0094 end
-0095 rxnRules=p.rxnRules;
-0096 ignoreMets=p.ignoreMets;
-0097 if ~isempty(ignoreMets) && ~islogical(ignoreMets) && ~isnumeric(ignoreMets)
-0098     ignoreMets=convertCharArray(ignoreMets);
-0099 end
-0100 isNames=p.isNames;
-0101 balanceElements=convertCharArray(p.balanceElements);
-0102 refModel=p.refModel;
-0103 if ~isempty(refModel)
-0104     EM='The feature to supply a reference model is currently not supported';
-0105     warning('RAVEN:warning', '%s', EM);
-0106 end
-0107 ignoreIntBounds=p.ignoreIntBounds;
-0108 printReport=p.printReport;
-0109 
-0110 %Check if the model has open exchange reactions and print a warning in that
-0111 %case
-0112 if ~isfield(model,'unconstrained')
-0113     [~, I]=getExchangeRxns(model);
-0114     if any(I)
-0115         if any(model.lb(I)~=0) || any(model.ub(I)~=0)
-0116             EM='The model contains open exchange reactions. This is not the intended use of this function. Consider importing your model using importModel(filename,false)';
-0117             warning('RAVEN:warning', '%s', EM);
-0118         end
-0119     end
-0120 end
-0121 
-0122 %Check that the model is feasible
-0123 sol=solveLP(model);
-0124 if isempty(sol.f)
-0125     EM='The model is not feasible. Consider removing lower bounds (such as ATP maintenance)';
-0126     error('RAVEN:badInput', '%s', EM);
-0127 end
-0128 
-0129 %Check that the reference model is feasible
-0130 if any(refModel)
-0131     sol=solveLP(refModel);
-0132     if isempty(sol.f)
-0133         EM='The reference model is not feasible';
-0134         error('RAVEN:badInput', '%s', EM);
-0135     end
-0136 end
-0137 
-0138 %Initialize some stuff
-0139 removedRxns={};
-0140 balanceStructure=getElementalBalance(model);
-0141 
-0142 %Check which elements to balance for
-0143 if ~isempty(setdiff(balanceElements,balanceStructure.elements.abbrevs))
-0144     EM='Could not recognize all elements to balance for';
-0145     error('RAVEN:badInput', '%s', EM);
-0146 end
-0147 bal=ismember(balanceStructure.elements.abbrevs,balanceElements);
-0148 
-0149 %Main loop. First run for production, second for consumption
-0150 warned=false(2,1); %This is to prevent the same warning being printed multiple times if rxnRules==3
-0151 for i=1:2
-0152     while 1
-0153         %Make some metabolite using as few reactions as possible
-0154         if i==1
-0155             [solution, metabolite]=findLeakMetabolite(model,'produce', ...
-0156                 'ignoreMets',ignoreMets,'isNames',isNames,'minNrFluxes',false, ...
-0157                 'allowExcretion',true,'ignoreIntBounds',ignoreIntBounds);
-0158             if ~isempty(solution)
-0159                 if printReport
-0160                     fprintf(['Can make: ' model.metNames{metabolite(1)} '\n']);
-0161                 end
-0162             else
-0163                 %If no solution could be found, then finish
-0164                 break;
-0165             end
-0166         else
-0167             [solution, metabolite]=findLeakMetabolite(model,'consume', ...
-0168                 'ignoreMets',ignoreMets,'isNames',isNames,'minNrFluxes',false, ...
-0169                 'ignoreIntBounds',ignoreIntBounds);
-0170             if ~isempty(solution)
-0171                 if printReport
-0172                     fprintf(['Can consume: ' model.metNames{metabolite(1)} '\n']);
-0173                 end
-0174             else
-0175                 %If no solution could be found, then finish
-0176                 break;
-0177             end
-0178         end
-0179         
-0180         %Find all reactions that are unbalanced and still carry flux
-0181         I=find(abs(solution)>10^-8 & balanceStructure.balanceStatus>=0 & ~all(balanceStructure.leftComp(:,bal)==balanceStructure.rightComp(:,bal),2));
-0182         
-0183         %If there are unbalanced rxns then delete one of them and iterate
-0184         if any(I)
-0185             rxnToRemove=I(randperm(numel(I),1));
-0186         else
-0187             %If there are no unbalanced rxns in the solution
-0188             if rxnRules==1
-0189                 if i==1
-0190                     EM=['No unbalanced reactions were found in the solution, but the model can still make "' model.metNames{metabolite} '". Aborting search. Consider setting rxnRules to 2 or 3 for a more exhaustive search'];
-0191                     warning('RAVEN:warning', '%s', EM);
-0192                 else
-0193                     EM=['No unbalanced reactions were found in the solution, but the model can still consume "' model.metNames{metabolite} '". Aborting search. Consider setting rxnRules to 2 or 3 for a more exhaustive search'];
-0194                     warning('RAVEN:warning', '%s', EM);
-0195                 end
-0196                 break;
-0197             else
-0198                 %Find reactions which are not checked for mass balancing,
-0199                 %but that still carry flux
-0200                 I=find(abs(solution)>10^-8 & balanceStructure.balanceStatus<0);
-0201                 
-0202                 %If there are any such reactions, remove one of them and
-0203                 %iterate
-0204                 if any(I)
-0205                     rxnToRemove=I(randperm(numel(I),1));
-0206                 else
-0207                     if rxnRules==2
-0208                         %This happens when all reactions used are balanced
-0209                         %according to the metabolite formulas. This cannot
-0210                         %be, and indicates that one or more of the formulas
-0211                         %are wrong. Print a warning and delete any reaction
-0212                         %with flux
-0213                         if i==1
-0214                             EM=['No unbalanced or unparsable reactions were found in the solution, but the model can still make "' model.metNames{metabolite} '". Aborting search. Consider setting rxnRules to 3 for a more exhaustive search'];
-0215                             warning('RAVEN:warning', '%s', EM);
-0216                         else
-0217                             EM=['No unbalanced or unparsable reactions were found in the solution, but the model can still consume "' model.metNames{metabolite} '". Aborting search. Consider setting rxnRules to 3 for a more exhaustive search'];
-0218                             warning('RAVEN:warning', '%s', EM);
-0219                         end
-0220                         break;
-0221                     else
-0222                         if i==1
-0223                             if warned(1)==false
-0224                                 EM=['No unbalanced or unparsable reactions were found in the solution, but the model can still make "' model.metNames{metabolite} '". This indicates some error in the metabolite formulas. Removing random reactions in the solution'];
-0225                                 warning('RAVEN:warning', '%s', EM);
-0226                                 warned(1)=true;
-0227                             end
-0228                         else
-0229                             if warned(2)==false
-0230                                 EM=['No unbalanced or unparsable reactions were found in the solution, but the model can still consume "' model.metNames{metabolite} '". This indicates some error in the metabolite formulas. Removing random reactions in the solution'];
-0231                                 warning('RAVEN:warning', '%s', EM);
-0232                                 warned(2)=true;
-0233                             end
-0234                         end
-0235                         I=find(abs(solution)>10^-8);
-0236                         rxnToRemove=I(randperm(numel(I),1));
-0237                     end
-0238                 end
-0239             end
-0240         end
-0241         removedRxns=[removedRxns;model.rxns(rxnToRemove)];
-0242         if printReport
-0243             fprintf(['\tRemoved: '  model.rxns{rxnToRemove} '\n']);
-0244         end
-0245         model=removeReactions(model,rxnToRemove);
-0246         balanceStructure.balanceStatus(rxnToRemove)=[];
-0247         balanceStructure.leftComp(rxnToRemove,:)=[];
-0248         balanceStructure.rightComp(rxnToRemove,:)=[];
-0249     end
-0250 end
-0251 
-0252 newModel=model;
-0253 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/removeGenes.html b/doc/manipulation/removeGenes.html deleted file mode 100644 index 8aa7a0da..00000000 --- a/doc/manipulation/removeGenes.html +++ /dev/null @@ -1,255 +0,0 @@ - - - - Description of removeGenes - - - - - - - - - -
Home > manipulation > removeGenes.m
- - - -

removeGenes -

- -

PURPOSE ^

-
removeGenes Delete a set of genes from a model.
- -

SYNOPSIS ^

-
function reducedModel = removeGenes(model,genesToRemove,varargin)
- -

DESCRIPTION ^

-
 removeGenes  Delete a set of genes from a model.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- genesToRemove : cell or logical or double
-     either a cell array of gene IDs, a logical vector with the same number
-     of elements as genes in the model, or a vector of indexes to remove.
-
- Name-Value Arguments
- --------------------
- removeUnusedMets : logical
-     remove metabolites that are no longer in use (default false).
- removeBlockedRxns : logical
-     remove reactions that get blocked after deleting the genes (default
-     false).
- standardizeRules : logical
-     format gene rules to be compliant with the standard format (default
-     true).
-
- Returns
- -------
- reducedModel : struct
-     an updated model structure.
-
- Examples
- --------
-     reducedModel = removeGenes(model, genesToRemove, removeUnusedMets, ...
-                        removeBlockedRxns, standardizeRules);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function reducedModel = removeGenes(model,genesToRemove,varargin)
-0002 % removeGenes  Delete a set of genes from a model.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % model : struct
-0007 %     a model structure.
-0008 % genesToRemove : cell or logical or double
-0009 %     either a cell array of gene IDs, a logical vector with the same number
-0010 %     of elements as genes in the model, or a vector of indexes to remove.
-0011 %
-0012 % Name-Value Arguments
-0013 % --------------------
-0014 % removeUnusedMets : logical
-0015 %     remove metabolites that are no longer in use (default false).
-0016 % removeBlockedRxns : logical
-0017 %     remove reactions that get blocked after deleting the genes (default
-0018 %     false).
-0019 % standardizeRules : logical
-0020 %     format gene rules to be compliant with the standard format (default
-0021 %     true).
-0022 %
-0023 % Returns
-0024 % -------
-0025 % reducedModel : struct
-0026 %     an updated model structure.
-0027 %
-0028 % Examples
-0029 % --------
-0030 %     reducedModel = removeGenes(model, genesToRemove, removeUnusedMets, ...
-0031 %                        removeBlockedRxns, standardizeRules);
-0032 
-0033 p=parseRAVENargs(varargin, {'removeUnusedMets',false; 'removeBlockedRxns',false; 'standardizeRules',true});
-0034 removeUnusedMets=p.removeUnusedMets;
-0035 removeBlockedRxns=p.removeBlockedRxns;
-0036 standardizeRules=p.standardizeRules;
-0037 %Format grRules and rxnGeneMatrix:
-0038 if standardizeRules
-0039     [grRules,rxnGeneMat,toCheck] = standardizeGrRules(model,true);
-0040     model.grRules    = grRules;
-0041     model.rxnGeneMat = rxnGeneMat;
-0042 else
-0043     toCheck    = [];
-0044     rxnGeneMat = model.rxnGeneMat;
-0045 end
-0046 reducedModel = model;
-0047 %Only remove genes that are actually in the model
-0048 if ~(islogical(genesToRemove) || isnumeric(genesToRemove))
-0049     genesToRemove=convertCharArray(genesToRemove);
-0050     genesToRemove=genesToRemove(ismember(genesToRemove,model.genes));
-0051 end
-0052 if ~isempty(genesToRemove)
-0053     indexesToRemove = getIndexes(model,genesToRemove,'genes');
-0054     if ~isempty(indexesToRemove)
-0055         %Make 0 corresponding columns from rxnGeneMat:
-0056         reducedModel.rxnGeneMat(:,indexesToRemove) = 0;        
-0057         genes = model.genes(indexesToRemove);
-0058         %Check if conflicting grRules contain any of the genes to remove
-0059         if ~isempty(toCheck)
-0060             for i=1:numel(toCheck)
-0061                 index   = toCheck(i);
-0062                 gIdxs   = find(rxnGeneMat(index,:));
-0063                 g2check = model.genes(gIdxs);
-0064                 if any(ismember(g2check,genes))
-0065                     warning(['Conflicting grRule #' num2str(index) ' (' model.rxns{index} ') contains at least one of the genes to be removed, this grRule will be bypassed in order to avoid logical errors'])
-0066                 end
-0067             end
-0068         end
-0069         canCarryFlux = true(size(model.rxns));       
-0070         %Loop through genes and adapt rxns:
-0071         for i = 1:length(genes)
-0072             %Find all reactions for this gene and loop through them:
-0073             geneRxns = find(rxnGeneMat(:,indexesToRemove(i)));
-0074             if ~isempty(geneRxns)
-0075                 for j = 1:numel(geneRxns)
-0076                     index  = geneRxns(j);
-0077                     grRule = reducedModel.grRules{index};
-0078                     if ~ismember(index,toCheck) && canCarryFlux(index) && ~isempty(grRule)
-0079                         %Check if rxn can carry flux without this gene:
-0080                         canCarryFlux(index) = canRxnCarryFlux(grRule,genes{i});
-0081                         %Adapt gene rule & gene matrix:
-0082                         grRule = removeGeneFromRule(grRule,genes{i});
-0083                         reducedModel.grRules{index} = grRule;
-0084                     end
-0085                 end
-0086             end
-0087         end
-0088         %Delete or block the reactions that cannot carry flux:
-0089         if removeBlockedRxns
-0090             rxnsToRemove = reducedModel.rxns(~canCarryFlux);
-0091             reducedModel = removeReactions(reducedModel,rxnsToRemove,removeUnusedMets,true);
-0092         else
-0093             reducedModel = removeReactions(reducedModel,[],removeUnusedMets,true);
-0094             reducedModel.lb(~canCarryFlux) = 0;
-0095             reducedModel.ub(~canCarryFlux) = 0;
-0096         end
-0097     end
-0098 end
-0099 %Format grRules and rxnGeneMatrix after all modifications
-0100 if standardizeRules
-0101     [grRules,rxnGeneMat] = standardizeGrRules(reducedModel,true);
-0102     reducedModel.grRules = grRules;
-0103     reducedModel.rxnGeneMat = rxnGeneMat;
-0104 end
-0105 end
-0106 
-0107 function canIt = canRxnCarryFlux(geneRule,geneToRemove)
-0108 %Evaluate the rule with geneToRemove absent and every other gene present.
-0109 %A complex (AND) needs all of its subunits, isozymes (OR) need only one.
-0110 tree = parseGrRule(geneRule);
-0111 if isempty(tree)
-0112     canIt = true;
-0113     return
-0114 end
-0115 canIt = evalWithout(tree,geneToRemove);
-0116 end
-0117 
-0118 function tf = evalWithout(node,geneToRemove)
-0119 switch node.type
-0120     case 'gene'
-0121         tf = ~strcmp(node.id,geneToRemove);
-0122     case 'and'
-0123         tf = all(cellfun(@(c) evalWithout(c,geneToRemove),node.children));
-0124     case 'or'
-0125         tf = any(cellfun(@(c) evalWithout(c,geneToRemove),node.children));
-0126     otherwise
-0127         error('RAVEN:badGrRule',['Unexpected grRule node type: ' node.type]);
-0128 end
-0129 end
-0130 
-0131 function geneRule = removeGeneFromRule(geneRule,geneToRemove)
-0132 %This function receives a gene rule and it returns it without the chosen
-0133 %gene. A complex that loses a subunit is dropped whole; an isozyme is
-0134 %dropped from its OR without disturbing the alternatives.
-0135 tree = parseGrRule(geneRule);
-0136 geneRule = grRuleToString(pruneGene(tree,geneToRemove));
-0137 end
-0138 
-0139 function node = pruneGene(node,geneToRemove)
-0140 %Remove geneToRemove from the tree, returning [] when nothing is left that
-0141 %can catalyse the reaction.
-0142 if isempty(node)
-0143     node = [];
-0144     return
-0145 end
-0146 switch node.type
-0147     case 'gene'
-0148         if strcmp(node.id,geneToRemove)
-0149             node = [];
-0150         end
-0151     case 'and'
-0152         %A complex missing any subunit cannot form at all.
-0153         for k = 1:numel(node.children)
-0154             if isempty(pruneGene(node.children{k},geneToRemove))
-0155                 node = [];
-0156                 return
-0157             end
-0158         end
-0159     case 'or'
-0160         kept = {};
-0161         for k = 1:numel(node.children)
-0162             child = pruneGene(node.children{k},geneToRemove);
-0163             if ~isempty(child)
-0164                 kept{end+1} = child; %#ok<AGROW>
-0165             end
-0166         end
-0167         if isempty(kept)
-0168             node = [];
-0169         elseif isscalar(kept)
-0170             node = kept{1};
-0171         else
-0172             node.children = kept;
-0173         end
-0174     otherwise
-0175         error('RAVEN:badGrRule',['Unexpected grRule node type: ' node.type]);
-0176 end
-0177 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/removeLowScoreGenes.html b/doc/manipulation/removeLowScoreGenes.html deleted file mode 100644 index 75cea5e8..00000000 --- a/doc/manipulation/removeLowScoreGenes.html +++ /dev/null @@ -1,468 +0,0 @@ - - - - Description of removeLowScoreGenes - - - - - - - - - -
Home > manipulation > removeLowScoreGenes.m
- - - -

removeLowScoreGenes -

- -

PURPOSE ^

-
removeLowScoreGenes Remove low-scoring genes from model.
- -

SYNOPSIS ^

-
function [newModel,remGenes] = removeLowScoreGenes(model,geneScores,varargin)
- -

DESCRIPTION ^

-
 removeLowScoreGenes  Remove low-scoring genes from model.
-
- This function removes genes from a model based on their scores, a step used
- by the tINIT package. The function recognizes and differentiates between
- isozymes and subunits of an enzyme complex. Genes are removed from each
- grRule, subject to the following conditions:
-
-     1) At least one gene must remain associated with the reaction.
-     2) Genes involved in a complex (joined by ANDs) are not removed.
-
- Parameters
- ----------
- model : struct
-     model structure from which genes are to be removed.
- geneScores : double
-     a vector of scores associated with the model genes. Genes with a
-     positive score will remain in the model, whereas genes with a negative
-     score will try to be removed.
-
-     If all genes associated with a reaction have a negative score, then the
-     least-negative gene will remain; if there is a tie, one will be selected
-     at random.
-
-     If a negative-scoring gene is a subunit in a complex, it will not be
-     removed; however, the entire complex may be removed. See the following
-     example cases:
-
-     - Original: G1 or (G2 and G3 and G4); Negative: G1, G2; New: G2 and G3
-       and G4.
-     - Original: G1 or (G2 and G3) or (G4 and G5); Negative: G1, G2; New: G4
-       and G5 [using default complexScoring].
-     - Original: (G1 and (G2 or G3) and G4); Negative: G2; New: (G1 and G3
-       and G4).
-
- Name-Value Arguments
- --------------------
- isozymeScoring : char
-     method used to combine the scores of multiple isozymes; "min", "max",
-     "median", or "average" (default "max").
- complexScoring : char
-     method used to combine the scores of enzyme complex subunits: "min",
-     "max", "median", or "average" (default "min").
-
- Returns
- -------
- newModel : struct
-     model with updated gene, grRules, and rxnGeneMat fields after attempting
-     to remove negative-score genes.
- remGenes : cell
-     a list of negative-score genes that were fully removed from the model.
-     Negative-score genes that were removed from some grRules but not all
-     will not be included in this list.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [newModel,remGenes] = removeLowScoreGenes(model,geneScores,varargin)
-0002 % removeLowScoreGenes  Remove low-scoring genes from model.
-0003 %
-0004 % This function removes genes from a model based on their scores, a step used
-0005 % by the tINIT package. The function recognizes and differentiates between
-0006 % isozymes and subunits of an enzyme complex. Genes are removed from each
-0007 % grRule, subject to the following conditions:
-0008 %
-0009 %     1) At least one gene must remain associated with the reaction.
-0010 %     2) Genes involved in a complex (joined by ANDs) are not removed.
-0011 %
-0012 % Parameters
-0013 % ----------
-0014 % model : struct
-0015 %     model structure from which genes are to be removed.
-0016 % geneScores : double
-0017 %     a vector of scores associated with the model genes. Genes with a
-0018 %     positive score will remain in the model, whereas genes with a negative
-0019 %     score will try to be removed.
-0020 %
-0021 %     If all genes associated with a reaction have a negative score, then the
-0022 %     least-negative gene will remain; if there is a tie, one will be selected
-0023 %     at random.
-0024 %
-0025 %     If a negative-scoring gene is a subunit in a complex, it will not be
-0026 %     removed; however, the entire complex may be removed. See the following
-0027 %     example cases:
-0028 %
-0029 %     - Original: G1 or (G2 and G3 and G4); Negative: G1, G2; New: G2 and G3
-0030 %       and G4.
-0031 %     - Original: G1 or (G2 and G3) or (G4 and G5); Negative: G1, G2; New: G4
-0032 %       and G5 [using default complexScoring].
-0033 %     - Original: (G1 and (G2 or G3) and G4); Negative: G2; New: (G1 and G3
-0034 %       and G4).
-0035 %
-0036 % Name-Value Arguments
-0037 % --------------------
-0038 % isozymeScoring : char
-0039 %     method used to combine the scores of multiple isozymes; "min", "max",
-0040 %     "median", or "average" (default "max").
-0041 % complexScoring : char
-0042 %     method used to combine the scores of enzyme complex subunits: "min",
-0043 %     "max", "median", or "average" (default "min").
-0044 %
-0045 % Returns
-0046 % -------
-0047 % newModel : struct
-0048 %     model with updated gene, grRules, and rxnGeneMat fields after attempting
-0049 %     to remove negative-score genes.
-0050 % remGenes : cell
-0051 %     a list of negative-score genes that were fully removed from the model.
-0052 %     Negative-score genes that were removed from some grRules but not all
-0053 %     will not be included in this list.
-0054 
-0055 
-0056 p=parseRAVENargs(varargin, {'isozymeScoring',[]; 'complexScoring','min'});
-0057 isozymeScoring=p.isozymeScoring;
-0058 complexScoring=p.complexScoring;
-0059 if isempty(isozymeScoring)
-0060     isozymeScoring = 'max';
-0061 end
-0062 
-0063 if ~isequal(size(model.genes),size(geneScores))
-0064     error('The dimensions of model genes and geneScores do not match.');
-0065 end
-0066 
-0067 % convert logical operators to symbols
-0068 grRules = model.grRules;
-0069 if any(contains(grRules,{'&','|'}))
-0070     symbolicRules = true;
-0071 else
-0072     symbolicRules = false;
-0073 end
-0074 grRules = regexprep(grRules,' and ',' & ');
-0075 grRules = regexprep(grRules,' or ',' | ');
-0076 
-0077 % get unique list of grRules
-0078 [uRules,~,rule_ind] = unique(grRules);
-0079 
-0080 % iterate through each rule
-0081 newRules = uRules;  %initialize newRules
-0082 for i = 1:numel(uRules)
-0083     if isempty(uRules{i}) || ~contains(uRules{i},'|')
-0084         continue
-0085     elseif contains(uRules{i},'&')
-0086         newRules{i} = processComplexRule(uRules{i},model.genes,geneScores,isozymeScoring,complexScoring);
-0087     else
-0088         newRules{i} = processSimpleRule(uRules{i},model.genes,geneScores,isozymeScoring,complexScoring);
-0089     end
-0090 end
-0091 
-0092 % re-map unique rules to model
-0093 newModel = model;
-0094 newModel.grRules = newRules(rule_ind);
-0095 
-0096 % restore original logical operator formatting if it was changed
-0097 if ~symbolicRules
-0098     newModel.grRules = regexprep(newModel.grRules, ' \| ', ' or ');
-0099     newModel.grRules = regexprep(newModel.grRules, ' & ', ' and ');
-0100 end
-0101 
-0102 % regenerate "genes" and "rxnGeneMat" model fields
-0103 [genes,rxnGeneMat] = getGenesFromGrRules(newModel.grRules);
-0104 
-0105 % determine which of the original genes were removed
-0106 remInd = ~ismember(model.genes,genes);
-0107 remGenes = model.genes(remInd);
-0108 
-0109 % Keep the retained genes in their original order rather than the sorted
-0110 % order returned by getGenesFromGrRules. Gene removal never introduces new
-0111 % genes, so the remaining genes are model.genes(~remInd). Preserving this
-0112 % order ensures the gene-associated fields trimmed below (geneShortNames,
-0113 % proteins, etc.), which are indexed by remInd, stay aligned with genes.
-0114 newModel.genes = model.genes(~remInd);
-0115 [~,reorderInd] = ismember(newModel.genes,genes);
-0116 newModel.rxnGeneMat = rxnGeneMat(:,reorderInd);
-0117 
-0118 if isfield(newModel,'geneShortNames')
-0119     newModel.geneShortNames(remInd) = [];
-0120 end
-0121 if isfield(newModel,'proteins')
-0122     newModel.proteins(remInd) = [];
-0123 end
-0124 if isfield(newModel,'geneMiriams')
-0125     newModel.geneMiriams(remInd) = [];
-0126 end
-0127 if isfield(newModel,'geneFrom')
-0128     newModel.geneFrom(remInd) = [];
-0129 end
-0130 if isfield(newModel,'geneComps')
-0131     newModel.geneComps(remInd) = [];
-0132 end
-0133 
-0134 
-0135 end
-0136 
-0137 
-0138 
-0139 function [updatedRule,rScore] = processSimpleRule(rule,genes,gScores,isozymeScoring,complexScoring)
-0140 % Either score or modify a reaction gene rule containig only ANDs or ORs.
-0141 %
-0142 % If the rule contains an enzyme complex (all ANDs), the complex will be
-0143 % scored based on the score of its subunits. Subunits without a score (NaN)
-0144 % will be excluded from the score calculation.
-0145 %
-0146 % If the rule contains only isozymes (all ORs), the negative-score genes
-0147 % will be removed from the rule. Isozymes without a score (NaN) will not be
-0148 % removed from the rule. The resuling rule will then be scored.
-0149 
-0150 
-0151 % get IDs and indices of genes involved in rule
-0152 ruleGenes = unique(regexp(rule,'[^&|\(\) ]+','match'));
-0153 [~,geneInd] = ismember(ruleGenes,genes);
-0154 
-0155 % rules with one or no genes remain unchanged
-0156 if numel(ruleGenes) < 2
-0157     rScore = gScores(geneInd);
-0158     updatedRule = rule;
-0159     return
-0160 end
-0161 
-0162 if ~contains(rule,'&')  % rule contains isozymes
-0163     
-0164     scoreMethod = isozymeScoring;
-0165     negInd = gScores(geneInd) < 0;  % NaNs will return false here
-0166     if all(negInd)
-0167         % deterministic least-negative: sort descending; stable sort breaks
-0168         % ties by original gene order (left-to-right in the rule string)
-0169         [~,sortOrder] = sort(gScores(geneInd),'descend');
-0170         maxInd = sortOrder(1);
-0171         updatedRule = ruleGenes{maxInd};
-0172     elseif sum(~negInd) == 1
-0173         updatedRule = ruleGenes{~negInd};
-0174     else
-0175         updatedRule = strjoin(ruleGenes(~negInd),' | ');
-0176         if startsWith(rule,'(')
-0177             updatedRule = ['(',updatedRule,')'];
-0178         end
-0179     end
-0180     
-0181     % update ruleGenes and their indices
-0182     ruleGenes = unique(regexp(updatedRule,'[^&|\(\) ]+','match'));
-0183     [~,geneInd] = ismember(ruleGenes,genes);
-0184     
-0185 elseif ~contains(rule,'|')  % rule contains enzyme complex
-0186     scoreMethod = complexScoring;
-0187     updatedRule = rule;
-0188 else
-0189     error('This function cannot handle rules with both "OR" and "AND" expressions.');
-0190 end
-0191 
-0192 % score rule
-0193 switch lower(scoreMethod)
-0194     case 'min'
-0195         rScore = min(gScores(geneInd),[],'omitnan');
-0196     case 'max'
-0197         rScore = max(gScores(geneInd),[],'omitnan');
-0198     case 'median'
-0199         rScore = median(gScores(geneInd),'omitnan');
-0200     case 'average'
-0201         rScore = mean(gScores(geneInd),'omitnan');
-0202 end
-0203 
-0204 end
-0205 
-0206 
-0207 
-0208 function updatedRule = processComplexRule(rule,genes,gScores,isozymeScoring,complexScoring)
-0209 % Update reactions containing both AND and OR expressions via recursive descent.
-0210 %
-0211 % Walks the boolean parse tree: OR nodes (isozymes) prune negative-score
-0212 % branches while keeping at least one; AND nodes (complexes) are scored as
-0213 % a unit and may be removed at the enclosing OR level. Nested isozyme groups
-0214 % within a complex are simplified in place.
-0215 updatedRule = evalGPR(rule, genes, gScores, isozymeScoring, complexScoring);
-0216 end
-0217 
-0218 
-0219 
-0220 function [rule, score] = evalGPR(expr, genes, gScores, isozymeScoring, complexScoring)
-0221 % Recursively evaluate and simplify a GPR expression.
-0222 % NaN-scored genes/complexes are not pruned (treated as non-negative).
-0223 
-0224 expr = strtrim(expr);
-0225 
-0226 % Strip redundant outer parentheses
-0227 while numel(expr) >= 2 && expr(1) == '('
-0228     closePos = findMatchingClose(expr, 1);
-0229     if closePos == numel(expr)
-0230         expr = strtrim(expr(2:end-1));
-0231     else
-0232         break
-0233     end
-0234 end
-0235 
-0236 % OR split: isozyme level
-0237 orTerms = splitDepth0(expr, '|');
-0238 if numel(orTerms) > 1
-0239     termScores = nan(1, numel(orTerms));
-0240     termRules  = cell(1,  numel(orTerms));
-0241     for k = 1:numel(orTerms)
-0242         [termRules{k}, termScores(k)] = evalGPR(orTerms{k}, genes, gScores, isozymeScoring, complexScoring);
-0243     end
-0244     % NaN-scored terms are kept; negatives pruned when alternatives exist
-0245     negIdx = termScores < 0;  % NaN -> false, so NaN terms are retained
-0246     if all(negIdx)
-0247         % All terms negative: keep the least-negative (deterministic)
-0248         [~, ord] = sort(termScores, 'descend');
-0249         keepIdx  = ord(1);
-0250     else
-0251         keepIdx = find(~negIdx);
-0252     end
-0253     termRules  = termRules(keepIdx);
-0254     termScores = termScores(keepIdx);
-0255     rule  = joinOR(termRules);
-0256     score = applyScoring(termScores, isozymeScoring);
-0257     return
-0258 end
-0259 
-0260 % AND split: complex level
-0261 andTerms = splitDepth0(expr, '&');
-0262 if numel(andTerms) > 1
-0263     termScores = nan(1, numel(andTerms));
-0264     termRules  = cell(1,  numel(andTerms));
-0265     for k = 1:numel(andTerms)
-0266         [termRules{k}, termScores(k)] = evalGPR(andTerms{k}, genes, gScores, isozymeScoring, complexScoring);
-0267     end
-0268     score = applyScoring(termScores, complexScoring);
-0269     rule  = joinAND(termRules);
-0270     return
-0271 end
-0272 
-0273 % Leaf: single gene
-0274 geneIdx = find(strcmp(genes, expr), 1);
-0275 if ~isempty(geneIdx)
-0276     score = gScores(geneIdx);
-0277 else
-0278     score = NaN;
-0279 end
-0280 rule = expr;
-0281 end
-0282 
-0283 
-0284 
-0285 function terms = splitDepth0(expr, sep)
-0286 % Split expr on ' sep ' at bracket depth 0.
-0287 terms = {};
-0288 depth = 0;
-0289 start = 1;
-0290 n     = numel(expr);
-0291 i     = 1;
-0292 while i <= n
-0293     c = expr(i);
-0294     if c == '('
-0295         depth = depth + 1;
-0296         i = i + 1;
-0297     elseif c == ')'
-0298         depth = depth - 1;
-0299         i = i + 1;
-0300     elseif c == sep && depth == 0 && i >= 2 && i <= n-1 && expr(i-1) == ' ' && expr(i+1) == ' '
-0301         terms{end+1} = strtrim(expr(start:i-2));  %#ok<AGROW>
-0302         start = i + 2;
-0303         i     = i + 2;
-0304     else
-0305         i = i + 1;
-0306     end
-0307 end
-0308 terms{end+1} = strtrim(expr(start:end));
-0309 end
-0310 
-0311 
-0312 
-0313 function pos = findMatchingClose(expr, openPos)
-0314 % Return position of ')' matching '(' at openPos.
-0315 depth = 0;
-0316 for i = openPos:numel(expr)
-0317     if     expr(i) == '('; depth = depth + 1;
-0318     elseif expr(i) == ')'; depth = depth - 1;
-0319         if depth == 0; pos = i; return; end
-0320     end
-0321 end
-0322 pos = numel(expr);
-0323 end
-0324 
-0325 
-0326 
-0327 function rule = joinOR(rules)
-0328 % Join rules with ' | '; wrap AND-containing subterms in parens.
-0329 n = numel(rules);
-0330 if n > 1
-0331     for k = 1:n
-0332         if contains(rules{k}, '&')
-0333             rules{k} = ['(' rules{k} ')'];
-0334         end
-0335     end
-0336 end
-0337 rule = strjoin(rules, ' | ');
-0338 end
-0339 
-0340 
-0341 
-0342 function rule = joinAND(rules)
-0343 % Join rules with ' & '; wrap OR-containing subterms in parens.
-0344 n = numel(rules);
-0345 if n > 1
-0346     for k = 1:n
-0347         if contains(rules{k}, '|')
-0348             rules{k} = ['(' rules{k} ')'];
-0349         end
-0350     end
-0351 end
-0352 rule = strjoin(rules, ' & ');
-0353 end
-0354 
-0355 
-0356 
-0357 function s = applyScoring(scores, method)
-0358 switch lower(method)
-0359     case 'min';     s = min(scores, [], 'omitnan');
-0360     case 'max';     s = max(scores, [], 'omitnan');
-0361     case 'median';  s = median(scores, 'omitnan');
-0362     case 'average'; s = mean(scores, 'omitnan');
-0363     otherwise;      s = max(scores, [], 'omitnan');
-0364 end
-0365 end
-0366 
-0367 
-0368
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/removeMets.html b/doc/manipulation/removeMets.html deleted file mode 100644 index ef308511..00000000 --- a/doc/manipulation/removeMets.html +++ /dev/null @@ -1,210 +0,0 @@ - - - - Description of removeMets - - - - - - - - - -
Home > manipulation > removeMets.m
- - - -

removeMets -

- -

PURPOSE ^

-
removeMets Delete a set of metabolites from a model.
- -

SYNOPSIS ^

-
function reducedModel=removeMets(model,metsToRemove,varargin)
- -

DESCRIPTION ^

-
 removeMets  Delete a set of metabolites from a model.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- metsToRemove : cell or logical or double
-     either a cell array of metabolite IDs, a logical vector with the same
-     number of elements as metabolites in the model, or a vector of
-     indexes to remove.
-
- Name-Value Arguments
- --------------------
- isNames : logical
-     true if the supplied mets represent metabolite names (as opposed to
-     IDs). This is a way to delete metabolites in several compartments at
-     once without knowing the exact IDs. This only works if metsToRemove
-     is a cell array (default false).
- removeUnusedRxns : logical
-     remove reactions that are no longer in use (default false).
- removeUnusedGenes : logical
-     remove genes that are no longer in use (default false).
- removeUnusedComps : logical
-     remove compartments that are no longer in use (default false).
-
- Returns
- -------
- reducedModel : struct
-     an updated model structure.
-
- Examples
- --------
-     reducedModel = removeMets(model, metsToRemove, isNames, ...
-         removeUnusedRxns, removeUnusedGenes, removeUnusedComps);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
  • mergeCompartments mergeCompartments Merge all compartments in a model.
  • removeReactions removeReactions Delete a set of reactions from a model.
  • simplifyModel simplifyModel Simplify a model by deleting reactions and metabolites.
- - - - -

SOURCE CODE ^

-
0001 function reducedModel=removeMets(model,metsToRemove,varargin)
-0002 % removeMets  Delete a set of metabolites from a model.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % model : struct
-0007 %     a model structure.
-0008 % metsToRemove : cell or logical or double
-0009 %     either a cell array of metabolite IDs, a logical vector with the same
-0010 %     number of elements as metabolites in the model, or a vector of
-0011 %     indexes to remove.
-0012 %
-0013 % Name-Value Arguments
-0014 % --------------------
-0015 % isNames : logical
-0016 %     true if the supplied mets represent metabolite names (as opposed to
-0017 %     IDs). This is a way to delete metabolites in several compartments at
-0018 %     once without knowing the exact IDs. This only works if metsToRemove
-0019 %     is a cell array (default false).
-0020 % removeUnusedRxns : logical
-0021 %     remove reactions that are no longer in use (default false).
-0022 % removeUnusedGenes : logical
-0023 %     remove genes that are no longer in use (default false).
-0024 % removeUnusedComps : logical
-0025 %     remove compartments that are no longer in use (default false).
-0026 %
-0027 % Returns
-0028 % -------
-0029 % reducedModel : struct
-0030 %     an updated model structure.
-0031 %
-0032 % Examples
-0033 % --------
-0034 %     reducedModel = removeMets(model, metsToRemove, isNames, ...
-0035 %         removeUnusedRxns, removeUnusedGenes, removeUnusedComps);
-0036 if ~islogical(metsToRemove) && ~isnumeric(metsToRemove)
-0037     metsToRemove=convertCharArray(metsToRemove);
-0038 end
-0039 
-0040 p=parseRAVENargs(varargin, {'isNames',false; 'removeUnusedRxns',false; 'removeUnusedGenes',false; 'removeUnusedComps',false});
-0041 isNames=p.isNames;
-0042 removeUnusedRxns=p.removeUnusedRxns;
-0043 removeUnusedGenes=p.removeUnusedGenes;
-0044 removeUnusedComps=p.removeUnusedComps;
-0045 
-0046 %Check that metsToRemove is a cell array
-0047 if isNames==true && ~iscell(metsToRemove)
-0048     error('Must supply a cell array of strings if isNames=true');
-0049 end
-0050 
-0051 reducedModel=model;
-0052 
-0053 if isNames==false
-0054     indexesToDelete=getIndexes(model,metsToRemove,'mets');
-0055 else
-0056     indexesToDelete=[];
-0057     for i=1:numel(metsToRemove)
-0058         indexesToDelete=[indexesToDelete;find(strcmp(metsToRemove(i),model.metNames))];
-0059     end
-0060 end
-0061 
-0062 %Remove metabolites
-0063 if ~isempty(indexesToDelete)
-0064     reducedModel.mets(indexesToDelete)=[];
-0065     reducedModel.S(indexesToDelete,:)=[];
-0066     if isfield(reducedModel,'b')
-0067         reducedModel.b(indexesToDelete,:)=[];
-0068     end
-0069     if isfield(reducedModel,'metNames')
-0070         reducedModel.metNames(indexesToDelete)=[];
-0071     end
-0072     if isfield(reducedModel,'metComps')
-0073         reducedModel.metComps(indexesToDelete)=[];
-0074     end
-0075     if isfield(reducedModel,'inchis')
-0076         reducedModel.inchis(indexesToDelete)=[];
-0077     end
-0078     if isfield(reducedModel,'metSmiles')
-0079         reducedModel.metSmiles(indexesToDelete)=[];
-0080     end
-0081     if isfield(reducedModel,'metFormulas')
-0082         reducedModel.metFormulas(indexesToDelete)=[];
-0083     end
-0084     if isfield(reducedModel,'metMiriams')
-0085         reducedModel.metMiriams(indexesToDelete)=[];
-0086     end
-0087     if isfield(reducedModel,'unconstrained')
-0088         reducedModel.unconstrained(indexesToDelete)=[];
-0089     end
-0090     if isfield(reducedModel,'metFrom')
-0091         reducedModel.metFrom(indexesToDelete)=[];
-0092     end
-0093     if isfield(reducedModel,'metCharges')
-0094         reducedModel.metCharges(indexesToDelete)=[];
-0095     end
-0096     if isfield(reducedModel,'metDeltaG')
-0097         reducedModel.metDeltaG(indexesToDelete)=[];
-0098     end
-0099     if isfield(reducedModel,'metNotes')
-0100         reducedModel.metNotes(indexesToDelete)=[];
-0101     end    
-0102 end
-0103 
-0104 %Remove unused reactions
-0105 if removeUnusedRxns==true
-0106     %Get unused reactions
-0107     [~, a]=find(reducedModel.S);
-0108     rxnsToRemove=1:numel(reducedModel.rxns);
-0109     rxnsToRemove(a)=[];
-0110     reducedModel=removeReactions(reducedModel,rxnsToRemove,false,removeUnusedGenes);
-0111 end
-0112 
-0113 %Remove unused compartments
-0114 if removeUnusedComps==true
-0115     oldComps=reducedModel.comps;
-0116     I=ismember(1:numel(oldComps),reducedModel.metComps);
-0117     if ~all(I)
-0118         reducedModel.comps(~I)=[];
-0119         reducedModel.compNames(~I)=[];
-0120         if isfield(reducedModel,'compOutside')
-0121             reducedModel.compOutside(~I)=[];
-0122         end
-0123         if isfield(reducedModel,'compMiriams')
-0124             reducedModel.compMiriams(~I)=[];
-0125         end
-0126         [~, J]=ismember(oldComps(reducedModel.metComps),reducedModel.comps);
-0127         reducedModel.metComps=J;
-0128     end
-0129 end
-0130 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/removeReactions.html b/doc/manipulation/removeReactions.html deleted file mode 100644 index 69edfa9b..00000000 --- a/doc/manipulation/removeReactions.html +++ /dev/null @@ -1,167 +0,0 @@ - - - - Description of removeReactions - - - - - - - - - -
Home > manipulation > removeReactions.m
- - - -

removeReactions -

- -

PURPOSE ^

-
removeReactions Delete a set of reactions from a model.
- -

SYNOPSIS ^

-
function reducedModel=removeReactions(model,rxnsToRemove,varargin)
- -

DESCRIPTION ^

-
 removeReactions  Delete a set of reactions from a model.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- rxnsToRemove : cell or logical or double
-     either a cell array of reaction IDs, a logical vector with the same
-     number of elements as reactions in the model, or a vector of indexes
-     to remove.
-
- Name-Value Arguments
- --------------------
- removeUnusedMets : logical
-     remove metabolites that are no longer in use (default false).
- removeUnusedGenes : logical
-     remove genes that are no longer in use (default false).
- removeUnusedComps : logical
-     remove compartments that are no longer in use (default false).
-
- Returns
- -------
- reducedModel : struct
-     an updated model structure.
-
- Examples
- --------
-     reducedModel = removeReactions(model, rxnsToRemove, removeUnusedMets, ...
-         removeUnusedGenes, removeUnusedComps);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • removeMets removeMets Delete a set of metabolites from a model.
-This function is called by: -
    -
  • changeRxns changeRxns Modify the equations of reactions in a model.
  • contractModel contractModel Contract a model by grouping all identical reactions.
  • copyToComps copyToComps Copy reactions to new compartment(s).
  • deleteUnusedGenes deleteUnusedGenes Delete all genes not associated to any reaction.
  • mergeCompartments mergeCompartments Merge all compartments in a model.
  • removeBadRxns removeBadRxns Remove reactions that enable production/consumption from nothing.
  • removeGenes removeGenes Delete a set of genes from a model.
  • removeMets removeMets Delete a set of metabolites from a model.
  • simplifyModel simplifyModel Simplify a model by deleting reactions and metabolites.
- - - - -

SOURCE CODE ^

-
0001 function reducedModel=removeReactions(model,rxnsToRemove,varargin)
-0002 % removeReactions  Delete a set of reactions from a model.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % model : struct
-0007 %     a model structure.
-0008 % rxnsToRemove : cell or logical or double
-0009 %     either a cell array of reaction IDs, a logical vector with the same
-0010 %     number of elements as reactions in the model, or a vector of indexes
-0011 %     to remove.
-0012 %
-0013 % Name-Value Arguments
-0014 % --------------------
-0015 % removeUnusedMets : logical
-0016 %     remove metabolites that are no longer in use (default false).
-0017 % removeUnusedGenes : logical
-0018 %     remove genes that are no longer in use (default false).
-0019 % removeUnusedComps : logical
-0020 %     remove compartments that are no longer in use (default false).
-0021 %
-0022 % Returns
-0023 % -------
-0024 % reducedModel : struct
-0025 %     an updated model structure.
-0026 %
-0027 % Examples
-0028 % --------
-0029 %     reducedModel = removeReactions(model, rxnsToRemove, removeUnusedMets, ...
-0030 %         removeUnusedGenes, removeUnusedComps);
-0031 
-0032 p=parseRAVENargs(varargin, {'removeUnusedMets',false; 'removeUnusedGenes',false; 'removeUnusedComps',false});
-0033 removeUnusedMets=p.removeUnusedMets;
-0034 removeUnusedGenes=p.removeUnusedGenes;
-0035 removeUnusedComps=p.removeUnusedComps;
-0036 if ~islogical(rxnsToRemove) && ~isnumeric(rxnsToRemove)
-0037     rxnsToRemove=convertCharArray(rxnsToRemove);
-0038 end
-0039 
-0040 reducedModel=model;
-0041 reg=ravenModelFields();
-0042 
-0043 if ~isempty(rxnsToRemove) || removeUnusedMets || removeUnusedGenes
-0044     indexesToDelete=getIndexes(model,rxnsToRemove,'rxns');
-0045     
-0046     %Remove reactions
-0047     if ~isempty(indexesToDelete)
-0048         rxnReg=reg(strcmp({reg.type},'rxn'));
-0049         for ri_=1:numel(rxnReg)
-0050             fname=rxnReg(ri_).name;
-0051             if isfield(reducedModel,fname)
-0052                 reducedModel.(fname)(indexesToDelete)=[];
-0053             end
-0054         end
-0055         if isfield(reducedModel,'S')
-0056             reducedModel.S(:,indexesToDelete)=[];
-0057         end
-0058         if isfield(reducedModel,'rxnGeneMat')
-0059             reducedModel.rxnGeneMat(indexesToDelete,:)=[];
-0060         end
-0061     end
-0062     
-0063     %Remove unused metabolites
-0064     if removeUnusedMets==true
-0065         if isfield(reducedModel,'S')
-0066             [usedMets, ~]=find(reducedModel.S);
-0067             unUsedMets=true(numel(reducedModel.mets),1);
-0068             unUsedMets(usedMets)=false;
-0069             reducedModel=removeMets(reducedModel,unUsedMets,false,false,false,removeUnusedComps);
-0070         end
-0071     end
-0072     
-0073     %Remove unused genes
-0074     if removeUnusedGenes==true && isfield(reducedModel,'rxnGeneMat')
-0075         %Find all genes that are not used
-0076         [~, b]=find(reducedModel.rxnGeneMat);
-0077         toKeep=false(numel(reducedModel.genes),1);
-0078         toKeep(b)=true;
-0079         
-0080         geneReg=reg(strcmp({reg.type},'gene'));
-0081         for ri_=1:numel(geneReg)
-0082             fname=geneReg(ri_).name;
-0083             if isfield(reducedModel,fname)
-0084                 reducedModel.(fname)=reducedModel.(fname)(toKeep);
-0085             end
-0086         end
-0087         reducedModel.rxnGeneMat=reducedModel.rxnGeneMat(:,toKeep);
-0088     end
-0089 else
-0090     reducedModel=model;
-0091 end
-0092 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/replaceMets.html b/doc/manipulation/replaceMets.html deleted file mode 100644 index 18deda2f..00000000 --- a/doc/manipulation/replaceMets.html +++ /dev/null @@ -1,273 +0,0 @@ - - - - Description of replaceMets - - - - - - - - - -
Home > manipulation > replaceMets.m
- - - -

replaceMets -

- -

PURPOSE ^

-
replaceMets Replace a metabolite with another already in the model.
- -

SYNOPSIS ^

-
function [model, removedRxns, idxDuplRxns]=replaceMets(model,metabolite,replacement,varargin)
- -

DESCRIPTION ^

-
 replaceMets  Replace a metabolite with another already in the model.
-
- Replaces metabolite names and annotation with a replacement metabolite
- that is already in the model. If this results in duplicate metabolites,
- the replacement metabolite will be kept, while the S matrix is updated to
- use the replacement metabolite instead. At the end, contractModel is run
- to remove any duplicate reactions that might have occurred.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- metabolite : char
-     string with name of metabolite to be replaced.
- replacement : char
-     string with name of replacement metabolite.
-
- Name-Value Arguments
- --------------------
- verbose : logical
-     whether to print the ids of reactions that involve the replaced
-     metabolite (default false).
- identifiers : logical
-     true if 'metabolite' and 'replacement' refer to metabolite
-     identifiers instead of metabolite names (default false).
-
- Returns
- -------
- model : struct
-     model structure with selected metabolites replaced.
- removedRxns : cell
-     identifiers of duplicate reactions that were removed.
- idxDuplRxns : double
-     index of removedRxns in original model.
-
- Examples
- --------
-     [model, removedRxns, idxDuplRxns] = replaceMets(model, metabolite, ...
-         replacement, verbose);
-
- Notes
- -----
- This function is useful when the model contains both 'oxygen' and 'o2' as
- metabolite names. If 'oxygen' and 'o2' are identifiers instead, then the
- 'identifiers' flag should be set to true.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • contractModel contractModel Contract a model by grouping all identical reactions.
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function [model, removedRxns, idxDuplRxns]=replaceMets(model,metabolite,replacement,varargin)
-0002 % replaceMets  Replace a metabolite with another already in the model.
-0003 %
-0004 % Replaces metabolite names and annotation with a replacement metabolite
-0005 % that is already in the model. If this results in duplicate metabolites,
-0006 % the replacement metabolite will be kept, while the S matrix is updated to
-0007 % use the replacement metabolite instead. At the end, contractModel is run
-0008 % to remove any duplicate reactions that might have occurred.
-0009 %
-0010 % Parameters
-0011 % ----------
-0012 % model : struct
-0013 %     a model structure.
-0014 % metabolite : char
-0015 %     string with name of metabolite to be replaced.
-0016 % replacement : char
-0017 %     string with name of replacement metabolite.
-0018 %
-0019 % Name-Value Arguments
-0020 % --------------------
-0021 % verbose : logical
-0022 %     whether to print the ids of reactions that involve the replaced
-0023 %     metabolite (default false).
-0024 % identifiers : logical
-0025 %     true if 'metabolite' and 'replacement' refer to metabolite
-0026 %     identifiers instead of metabolite names (default false).
-0027 %
-0028 % Returns
-0029 % -------
-0030 % model : struct
-0031 %     model structure with selected metabolites replaced.
-0032 % removedRxns : cell
-0033 %     identifiers of duplicate reactions that were removed.
-0034 % idxDuplRxns : double
-0035 %     index of removedRxns in original model.
-0036 %
-0037 % Examples
-0038 % --------
-0039 %     [model, removedRxns, idxDuplRxns] = replaceMets(model, metabolite, ...
-0040 %         replacement, verbose);
-0041 %
-0042 % Notes
-0043 % -----
-0044 % This function is useful when the model contains both 'oxygen' and 'o2' as
-0045 % metabolite names. If 'oxygen' and 'o2' are identifiers instead, then the
-0046 % 'identifiers' flag should be set to true.
-0047 
-0048 metabolite=char(metabolite);
-0049 replacement=char(replacement);
-0050 
-0051 p=parseRAVENargs(varargin, {'verbose',false; 'identifiers',false});
-0052 verbose=p.verbose;
-0053 if isempty(verbose)
-0054     verbose=false;
-0055 end
-0056 identifiers=p.identifiers;
-0057 
-0058 % Find occurence of replacement metabolites. Annotation will be taken from
-0059 % first metabolite found.
-0060 if identifiers
-0061     repIdx = find(strcmp(replacement,model.mets));
-0062 else
-0063     repIdx = find(strcmp(replacement,model.metNames));
-0064 end
-0065 if isempty(repIdx)
-0066     error('The replacement metabolite cannot be found in the model.');
-0067 end
-0068 % Captured by id, not position: idxDelete below removes rows, which would
-0069 % otherwise shift repIdx out from under the metabolite it is meant to name
-0070 repMetIds = model.mets(repIdx);
-0071 
-0072 % Change name and information from metabolite to replacement metabolite
-0073 if identifiers
-0074     metIdx = find(strcmp(metabolite,model.mets));
-0075 else
-0076     metIdx = find(strcmp(metabolite,model.metNames));
-0077 end
-0078 if isempty(metIdx)
-0079     error('The to-be-replaced metabolite cannot be found in the model.');
-0080 end
-0081 
-0082 rxnsWithMet = find(model.S(metIdx,:));
-0083 if verbose==true
-0084     fprintf('\n\nThe following reactions contain the to-be-replaced metabolite as reactant:\n')
-0085     %Reaction ids are arbitrary text and may contain "%"; print as
-0086     %literal data rather than as an fprintf format string.
-0087     fprintf('%s\n', strjoin(model.rxns(rxnsWithMet),newline))
-0088 end
-0089 
-0090 model.metNames(metIdx) = model.metNames(repIdx(1));
-0091 if isfield(model,'metFormulas')
-0092     model.metFormulas(metIdx) = model.metFormulas(repIdx(1));
-0093 end
-0094 if isfield(model,'metMiriams')
-0095     model.metMiriams(metIdx) = model.metMiriams(repIdx(1));
-0096 end
-0097 if isfield(model,'metCharges')
-0098     model.metCharges(metIdx) = model.metCharges(repIdx(1));
-0099 end
-0100 if isfield(model,'metDeltaG')
-0101     model.metDeltaG(metIdx) = model.metDeltaG(repIdx(1));
-0102 end
-0103 if isfield(model,'inchis')
-0104     model.inchis(metIdx) = model.inchis(repIdx(1));
-0105 end
-0106 if isfield(model,'metSmiles')
-0107     model.metSmiles(metIdx) = model.metSmiles(repIdx(1));
-0108 end
-0109 
-0110 idxDelete=[];
-0111 if identifiers
-0112     originalStoch = model.S(metIdx,rxnsWithMet);
-0113     % Add rather than overwrite: a reaction where the replacement
-0114     % metabolite is already itself a participant must keep that
-0115     % contribution, not have it clobbered by the replaced metabolite's
-0116     model.S(repIdx,rxnsWithMet) = model.S(repIdx,rxnsWithMet) + originalStoch;
-0117     model.S(metIdx,rxnsWithMet) = 0;
-0118     idxDelete = metIdx;
-0119 else
-0120     % Run through replacement metabolites and their compartments. If any of the
-0121     % to-be-replaced metabolites is already present (checked by
-0122     % metaboliteName[compartment], then the replacement metabolite is kept and
-0123     % the to-be-replace metabolite ID deleted.
-0124     
-0125     % Build list of metaboliteName[compartment]
-0126     metCompsN =cellstr(num2str(model.metComps));
-0127     map = containers.Map(cellstr(num2str(transpose(1:length(model.comps)))),model.comps);
-0128     metCompsN = map.values(metCompsN);
-0129     metCompsN = strcat(lower(model.metNames),'[',metCompsN,']');
-0130     
-0131     for i = 1:length(repIdx)
-0132         metCompsNidx=find(strcmp(metCompsN(repIdx(i)), metCompsN));
-0133         if length(metCompsNidx)>1
-0134             for j = 2:length(metCompsNidx)
-0135                 model.S(metCompsNidx(1),:) = model.S(metCompsNidx(1),:) + model.S(metCompsNidx(j),:);
-0136                 idxDelete=[idxDelete; metCompsNidx(j)]; % Make list of metabolite IDs to delete
-0137             end
-0138         end
-0139     end
-0140 end
-0141 
-0142 if ~isempty(idxDelete)
-0143     model.S(idxDelete,:) =[];
-0144     model.mets(idxDelete) = [];
-0145     model.metNames(idxDelete) = [];
-0146     model.metComps(idxDelete) = [];
-0147     model.b(idxDelete,:) = [];
-0148     if isfield(model,'metFormulas')
-0149         model.metFormulas(idxDelete) = [];
-0150     end
-0151     if isfield(model,'unconstrained')
-0152         model.unconstrained(idxDelete) = [];
-0153     end
-0154     if isfield(model,'metMiriams')
-0155         model.metMiriams(idxDelete) = [];
-0156     end
-0157     if isfield(model,'metCharges')
-0158         model.metCharges(idxDelete) = [];
-0159     end
-0160     if isfield(model,'metDeltaG')
-0161         model.metDeltaG(idxDelete) = [];
-0162     end
-0163     if isfield(model,'inchis')
-0164         model.inchis(idxDelete) = [];
-0165     end
-0166     if isfield(model,'metSmiles')
-0167         model.metSmiles(idxDelete) = [];
-0168     end
-0169     if isfield(model,'metFrom')
-0170         model.metFrom(idxDelete) = [];
-0171     end
-0172 end
-0173 
-0174 % This could now have created duplicate reactions. Contract model.
-0175 % repIdx may point past the end, or at the wrong row, once idxDelete has
-0176 % shifted things above; re-resolve the replacement metabolite by id.
-0177 % distReverse is passed by name rather than positionally as [], which
-0178 % parseRAVENargs would take literally instead of falling back to its
-0179 % documented default of true.
-0180 repIdx = find(ismember(model.mets, repMetIds));
-0181 model=contractModel(model,'mets',repIdx);
-0182 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/reverseRxns.html b/doc/manipulation/reverseRxns.html deleted file mode 100644 index 82aaf3d2..00000000 --- a/doc/manipulation/reverseRxns.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - Description of reverseRxns - - - - - - - - - -
Home > manipulation > reverseRxns.m
- - - -

reverseRxns -

- -

PURPOSE ^

-
reverseRxns
- -

SYNOPSIS ^

-
function model=reverseRxns(model, rxns)
- -

DESCRIPTION ^

-
 reverseRxns
- Reverses the given reactions in place: negates their stoichiometry and
- objective coefficient, and swaps/negates their lower and upper bounds,
- so flux in the new positive direction is what was previously negative flux.
- model     the model to change (input/output)
- rxns      the rxns to reverse
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function model=reverseRxns(model, rxns)
-0002 % reverseRxns
-0003 % Reverses the given reactions in place: negates their stoichiometry and
-0004 % objective coefficient, and swaps/negates their lower and upper bounds,
-0005 % so flux in the new positive direction is what was previously negative flux.
-0006 % model     the model to change (input/output)
-0007 % rxns      the rxns to reverse
-0008 
-0009 rxnInd = find(ismember(model.rxns,rxns));
-0010 
-0011 %reverse the reactions
-0012 model.S(:,rxnInd) = model.S(:,rxnInd)*-1;
-0013 %swap the bounds
-0014 ub = model.ub(rxnInd);
-0015 model.ub(rxnInd) = -model.lb(rxnInd);
-0016 model.lb(rxnInd) = -ub;
-0017 %flip the objective
-0018 model.c(rxnInd) = -model.c(rxnInd);
-0019 
-0020 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/setExchangeBounds.html b/doc/manipulation/setExchangeBounds.html deleted file mode 100644 index be306766..00000000 --- a/doc/manipulation/setExchangeBounds.html +++ /dev/null @@ -1,332 +0,0 @@ - - - - Description of setExchangeBounds - - - - - - - - - -
Home > manipulation > setExchangeBounds.m
- - - -

setExchangeBounds -

- -

PURPOSE ^

-
setExchangeBounds Define exchange flux bounds for a set of metabolites.
- -

SYNOPSIS ^

-
function [exchModel,unusedMets] = setExchangeBounds(model,varargin)
- -

DESCRIPTION ^

-
 setExchangeBounds  Define exchange flux bounds for a set of metabolites.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- mets : cell or double
-     a cell array of metabolite names (case insensitive) or metabolite
-     IDs, or a vector of metabolite indices (default all exchanged
-     metabolites).
- lb : double
-     lower bound of exchange flux. Can be either a vector of bounds
-     corresponding to each of the provided metabolites, or a single value
-     that will be applied to all (default model.annotation.defaultLB if it
-     exists, otherwise -1000).
- ub : double
-     upper bound of exchange flux. Can be either a vector of bounds
-     corresponding to each of the provided metabolites, or a single value
-     that will be applied to all (default model.annotation.defaultUB if it
-     exists, otherwise 1000).
- closeOthers : logical
-     close exchange reactions for all other exchanged metabolites not
-     present in the provided list. This will prevent IMPORT of the
-     metabolites, but their EXPORT will not be modified (default true).
- mediaOnly : logical
-     only consider exchange reactions involving exchange to or from the
-     extracellular (media) compartment. Reactions such as "sink" reactions
-     that exchange metabolites directly with an intracellular compartment
-     will therefore be ignored even though "getExchangeRxns" identifies
-     such reactions as exchange reactions. The function will attempt to
-     identify the extracellular compartment by the "compNames" field, and
-     also requires the "metComps" field to be present, otherwise the
-     mediaOnly flag will be ignored (default false).
-
- Returns
- -------
- exchModel : struct
-     a model structure with updated exchange flux bounds for the provided
-     set of metabolites.
- unusedMets : cell
-     metabolites provided by the user that were not used because they are
-     not involved in any exchange reactions in the model.
-
- Examples
- --------
-     exchModel = setExchangeBounds(model, mets, lb, ub, closeOthers, ...
-         mediaOnly);
-
- Notes
- -----
- Exchange reactions involving more than one metabolite will be ignored.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • setParam setParam Set parameters for reactions.
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function [exchModel,unusedMets] = setExchangeBounds(model,varargin)
-0002 % setExchangeBounds  Define exchange flux bounds for a set of metabolites.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % model : struct
-0007 %     a model structure.
-0008 %
-0009 % Name-Value Arguments
-0010 % --------------------
-0011 % mets : cell or double
-0012 %     a cell array of metabolite names (case insensitive) or metabolite
-0013 %     IDs, or a vector of metabolite indices (default all exchanged
-0014 %     metabolites).
-0015 % lb : double
-0016 %     lower bound of exchange flux. Can be either a vector of bounds
-0017 %     corresponding to each of the provided metabolites, or a single value
-0018 %     that will be applied to all (default model.annotation.defaultLB if it
-0019 %     exists, otherwise -1000).
-0020 % ub : double
-0021 %     upper bound of exchange flux. Can be either a vector of bounds
-0022 %     corresponding to each of the provided metabolites, or a single value
-0023 %     that will be applied to all (default model.annotation.defaultUB if it
-0024 %     exists, otherwise 1000).
-0025 % closeOthers : logical
-0026 %     close exchange reactions for all other exchanged metabolites not
-0027 %     present in the provided list. This will prevent IMPORT of the
-0028 %     metabolites, but their EXPORT will not be modified (default true).
-0029 % mediaOnly : logical
-0030 %     only consider exchange reactions involving exchange to or from the
-0031 %     extracellular (media) compartment. Reactions such as "sink" reactions
-0032 %     that exchange metabolites directly with an intracellular compartment
-0033 %     will therefore be ignored even though "getExchangeRxns" identifies
-0034 %     such reactions as exchange reactions. The function will attempt to
-0035 %     identify the extracellular compartment by the "compNames" field, and
-0036 %     also requires the "metComps" field to be present, otherwise the
-0037 %     mediaOnly flag will be ignored (default false).
-0038 %
-0039 % Returns
-0040 % -------
-0041 % exchModel : struct
-0042 %     a model structure with updated exchange flux bounds for the provided
-0043 %     set of metabolites.
-0044 % unusedMets : cell
-0045 %     metabolites provided by the user that were not used because they are
-0046 %     not involved in any exchange reactions in the model.
-0047 %
-0048 % Examples
-0049 % --------
-0050 %     exchModel = setExchangeBounds(model, mets, lb, ub, closeOthers, ...
-0051 %         mediaOnly);
-0052 %
-0053 % Notes
-0054 % -----
-0055 % Exchange reactions involving more than one metabolite will be ignored.
-0056 
-0057 
-0058 % handle input arguments
-0059 p=parseRAVENargs(varargin, {'mets',[]; 'lb',[]; 'ub',[]; 'closeOthers',true; 'mediaOnly',false});
-0060 mets=p.mets;
-0061 if ~isempty(mets) && ~islogical(mets) && ~isnumeric(mets)
-0062     mets=convertCharArray(mets);
-0063 end
-0064 
-0065 lb=p.lb;
-0066 if isempty(lb)
-0067     if isfield(model,'annotation') && isfield(model.annotation,'defaultLB')
-0068         lb = model.annotation.defaultLB;
-0069     else
-0070         lb = -1000;
-0071     end
-0072 end
-0073 
-0074 ub=p.ub;
-0075 if isempty(ub)
-0076     if isfield(model,'annotation') && isfield(model.annotation,'defaultUB')
-0077         ub = model.annotation.defaultUB;
-0078     else
-0079         ub = 1000;
-0080     end
-0081 end
-0082 
-0083 closeOthers=p.closeOthers;
-0084 if isempty(closeOthers)
-0085     closeOthers = true;
-0086 end
-0087 
-0088 mediaOnly=p.mediaOnly;
-0089 if mediaOnly
-0090     if ~all(isfield(model,{'compNames','metComps'}))
-0091         error('mediaOnly option requires the "compNames" and "metComps" model fields.');
-0092     end
-0093 end
-0094 
-0095 
-0096 % for models with an "unconstrained" field, generate a version of the model
-0097 % where all the unconstrained metabolites have stoichiometric coeffs set to
-0098 % zero, but remain in the model to avoid index changes
-0099 model_temp = model;
-0100 if isfield(model,'unconstrained')
-0101     model_temp.S(model_temp.unconstrained == 1,:) = 0;
-0102 end
-0103 
-0104 % find exchange rxns, ignoring those involving more than one metabolite
-0105 [~,exchRxnInd] = getExchangeRxns(model);
-0106 exchRxnInd(sum(model_temp.S(:,exchRxnInd) ~= 0,1) > 1) = [];
-0107 
-0108 % find all exchanged metabolites
-0109 exchMetInd = find(any(model_temp.S(:,exchRxnInd) ~= 0,2));
-0110 
-0111 if ( mediaOnly )
-0112     % ignore exchanged metabolites in non-extracellular compartments and
-0113     % any exchange reactions involving these metabolites
-0114     [~,extCompInd] = ismember('extracellular',lower(model.compNames));
-0115     if extCompInd > 0
-0116         ignoreMet = (model.metComps(exchMetInd) ~= extCompInd);
-0117     else
-0118         error('Could not find any compartments named "extracellular".');
-0119     end
-0120     ignoreRxn = any(model.S(exchMetInd(ignoreMet),exchRxnInd) ~= 0,1);
-0121     exchMetInd(ignoreMet) = [];
-0122     exchRxnInd(ignoreRxn) = [];
-0123 end
-0124 
-0125 % Check that all exchange reactions are formulated in the same direction.
-0126 % If not, this means that negative flux indicates import for some exchange
-0127 % reactions, but indicates export for others. Therefore, the LB and UB
-0128 % would need to be specified differently depending on the exchange reaction
-0129 % direction, which is error-prone.
-0130 if all(all(model.S(exchMetInd,exchRxnInd) <= 0))
-0131     importDir = 'backward';
-0132 elseif all(all(model.S(exchMetInd,exchRxnInd) >= 0))
-0133     importDir = 'forward';
-0134 else
-0135     fprintf('WARNING: Some exchange reactions differ in direction, and therefore have opposite meanings of LB and UB.\n');
-0136     if closeOthers
-0137         fprintf('         Therefore, the "closeOthers" option will be set to FALSE.\n');
-0138     end
-0139     closeOthers = false;
-0140 end
-0141 
-0142 % prepare exchanged metabolites and bounds
-0143 if ~isempty(mets)
-0144     
-0145     % prepare lb and ub variables
-0146     if numel(lb) == 1
-0147         lb = lb*ones(size(mets));
-0148     elseif numel(lb) ~= numel(mets)
-0149         error('lb must be a single value or a vector of equal length as mets');
-0150     end
-0151     if numel(ub) == 1
-0152         ub = ub*ones(size(mets));
-0153     elseif numel(ub) ~= numel(mets)
-0154         error('ub must be a single value or a vector of equal length as mets');
-0155     end
-0156     
-0157     % map provided mets to exchanged metabolites
-0158     if isnumeric(mets)
-0159         % mets are provided as indices
-0160         [isExch,metInd] = ismember(mets,exchMetInd);    
-0161     elseif sum(ismember(lower(mets),lower(model.metNames))) > sum(ismember(mets,model.mets))
-0162         % assume that mets are provided as names
-0163         [isExch,metInd] = ismember(lower(mets),lower(model.metNames(exchMetInd)));
-0164     else
-0165         % assume that mets are provided as met IDs
-0166         [isExch,metInd] = ismember(mets,model.mets(exchMetInd));
-0167     end
-0168     
-0169     % get exchanged met indices and determine unused (non-exchanged) mets
-0170     unusedMets = mets(~isExch);
-0171     exchMetInd = exchMetInd(metInd(isExch));
-0172     
-0173     % update bound vectors
-0174     lb = lb(isExch);
-0175     ub = ub(isExch);
-0176     
-0177 else
-0178     % if no mets were provided, use all exchanged mets
-0179     if numel(lb) > 1 || numel(ub) > 1
-0180         error('Only one upper and one lower bound may be provide if metabolites are not specified.');
-0181     else
-0182         lb = lb*ones(size(exchMetInd));
-0183         ub = ub*ones(size(exchMetInd));
-0184     end
-0185     unusedMets = {};
-0186 end
-0187 
-0188 % check that at least one exchanged metabolite matches
-0189 if isempty(exchMetInd)
-0190     fprintf('None of the provided metabolites were found in any exchange reactions.\n');
-0191     exchModel = model;
-0192     return
-0193 end
-0194 
-0195 % determine which metabolite is exchanged in each exchange reaction
-0196 [metInd,rxnInd] = find(model_temp.S(exchMetInd,exchRxnInd) ~= 0);
-0197 
-0198 % check for any metabolites that are exchanged in more than one reaction.
-0199 % metInd holds one entry per (metabolite,reaction) pair, so a metabolite
-0200 % involved in more than one exchange reaction is a repeated *value* in
-0201 % metInd, not a position past numel(unique(metInd)) -- comparing those two
-0202 % index spaces directly (as a previous version of this check did) compares
-0203 % positions in the unique list against indices into metInd itself.
-0204 [uniqueMetInd,~,ic] = unique(metInd);
-0205 counts = accumarray(ic,1);
-0206 multiMetInd = exchMetInd(uniqueMetInd(counts>1));
-0207 if ~isempty(multiMetInd)
-0208     fprintf('WARNING: The following metabolites are involved in more than one exchange reaction:\n');
-0209     fprintf('\t%s\n',model.metNames{multiMetInd(1:min(numel(multiMetInd),10))});
-0210     if numel(multiMetInd) > 10
-0211         fprintf('\t... and %u more.\n',numel(multiMetInd)-10);
-0212     end
-0213 end
-0214 
-0215 % set exchange reaction bounds
-0216 model = setParam(model,'lb',exchRxnInd(rxnInd),lb(metInd));
-0217 model = setParam(model,'ub',exchRxnInd(rxnInd),ub(metInd));
-0218 
-0219 if closeOthers
-0220     % constrain import of all other exchange reactions to zero
-0221     constrainInd = setdiff(exchRxnInd,exchRxnInd(rxnInd));
-0222     if strcmp(importDir,'backward')
-0223         model = setParam(model,'lb',constrainInd,0);
-0224     else
-0225         model = setParam(model,'ub',constrainInd,0);
-0226     end
-0227 end
-0228 
-0229 % assign output model
-0230 exchModel = model;
-0231 
-0232
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/setParam.html b/doc/manipulation/setParam.html deleted file mode 100644 index d4a0487b..00000000 --- a/doc/manipulation/setParam.html +++ /dev/null @@ -1,220 +0,0 @@ - - - - Description of setParam - - - - - - - - - -
Home > manipulation > setParam.m
- - - -

setParam -

- -

PURPOSE ^

-
setParam Set parameters for reactions.
- -

SYNOPSIS ^

-
function model=setParam(model, paramType, rxnList, params, var)
- -

DESCRIPTION ^

-
 setParam  Set parameters for reactions.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- paramType : char
-     the type of parameter to set:
-
-     - "lb" : lower bound.
-     - "ub" : upper bound.
-     - "eq" : both upper and lower bound (equality constraint).
-     - "obj" : objective coefficient.
-     - "rev" : reversibility (only changes the model.rev fields, does not
-       affect model.lb and model.ub).
-     - "var" : variance around measured bound.
-     - "unc" : unconstrained, set lower and upper bound to the default
-       values (-1000 and 1000, or any other values that are defined in
-       model.annotation.defaultLB and .defaultUB).
- rxnList : cell or double
-     a cell array of reaction IDs or a vector with their corresponding
-     indexes.
- params : double
-     a vector of the corresponding values.
- var : double, optional
-     percentage of variance around measured value, if "var" is set as
-     paramType. Defining "var" as 5 results in lb and ub at 97.5% and
-     102.5% of the provided params value (if params value is negative,
-     then lb and ub are 102.5% and 97.5%).
-
- Returns
- -------
- model : struct
-     an updated model structure.
-
- Examples
- --------
-     model = setParam(model, paramType, rxnList, params, var);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • setExchangeBounds setExchangeBounds Define exchange flux bounds for a set of metabolites.
- - - - -

SOURCE CODE ^

-
0001 function model=setParam(model, paramType, rxnList, params, var)
-0002 % setParam  Set parameters for reactions.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % model : struct
-0007 %     a model structure.
-0008 % paramType : char
-0009 %     the type of parameter to set:
-0010 %
-0011 %     - "lb" : lower bound.
-0012 %     - "ub" : upper bound.
-0013 %     - "eq" : both upper and lower bound (equality constraint).
-0014 %     - "obj" : objective coefficient.
-0015 %     - "rev" : reversibility (only changes the model.rev fields, does not
-0016 %       affect model.lb and model.ub).
-0017 %     - "var" : variance around measured bound.
-0018 %     - "unc" : unconstrained, set lower and upper bound to the default
-0019 %       values (-1000 and 1000, or any other values that are defined in
-0020 %       model.annotation.defaultLB and .defaultUB).
-0021 % rxnList : cell or double
-0022 %     a cell array of reaction IDs or a vector with their corresponding
-0023 %     indexes.
-0024 % params : double
-0025 %     a vector of the corresponding values.
-0026 % var : double, optional
-0027 %     percentage of variance around measured value, if "var" is set as
-0028 %     paramType. Defining "var" as 5 results in lb and ub at 97.5% and
-0029 %     102.5% of the provided params value (if params value is negative,
-0030 %     then lb and ub are 102.5% and 97.5%).
-0031 %
-0032 % Returns
-0033 % -------
-0034 % model : struct
-0035 %     an updated model structure.
-0036 %
-0037 % Examples
-0038 % --------
-0039 %     model = setParam(model, paramType, rxnList, params, var);
-0040 
-0041 paramType=convertCharArray(paramType);
-0042 if ~any(strcmpi(paramType,{'lb','ub','eq','obj','rev','var','unc'}))
-0043     EM=['Incorrect parameter type: "' paramType{1} '"'];
-0044     error('RAVEN:badInput', '%s', EM);
-0045 end
-0046 if isnumeric(rxnList) || islogical(rxnList)
-0047     rxnList=model.rxns(rxnList);
-0048 else
-0049     rxnList=convertCharArray(rxnList);
-0050 end
-0051 if isempty(rxnList)
-0052     return;
-0053 end
-0054 
-0055 %Allow to set several parameters to the same value
-0056 if numel(rxnList)~=numel(params) && numel(params)~=1
-0057     EM='The number of parameter values and the number of reactions must be the same';
-0058     error('RAVEN:badInput', '%s', EM);
-0059 end
-0060 
-0061 if length(rxnList)>1
-0062     if length(paramType)==1
-0063         paramType(1:length(rxnList))=paramType;
-0064     end
-0065     if length(params)==1
-0066         params(1:length(rxnList))=params;
-0067     end
-0068 end
-0069 
-0070 %Find the indexes for the reactions in rxnList. Do not use getIndexes
-0071 %as we do not want to throw errors if matches fail
-0072 indexes=zeros(numel(rxnList),1);
-0073 
-0074 [Lia,Locb] = ismember(rxnList,model.rxns);
-0075 indexes(Lia) = Locb;
-0076 if any(~Lia)
-0077     params(~Lia)=[];
-0078     indexes(~Lia)=[];
-0079     paramType(~Lia)=[];
-0080     warning('RAVEN:warning', '%s', ravenList('Reactions not present in model, will be ignored:', rxnList(~Lia)));
-0081 end
-0082 
-0083 %Change the parameters
-0084 if ~isempty(indexes)
-0085     if contains(paramType,'obj')
-0086         model.c=zeros(numel(model.c),1); % parameter is changed, not added
-0087     end
-0088     for j=1:length(paramType)
-0089         if strcmpi(paramType{j},'eq')
-0090             model.lb(indexes(j))=params(j);
-0091             model.ub(indexes(j))=params(j);
-0092         end
-0093         if strcmpi(paramType{j},'lb')
-0094             model.lb(indexes(j))=params(j);
-0095         end
-0096         if strcmpi(paramType{j},'ub')
-0097             model.ub(indexes(j))=params(j);
-0098         end
-0099         if strcmpi(paramType{j},'obj')
-0100             model.c(indexes(j))=params(j);
-0101         end
-0102         if strcmpi(paramType{j},'rev')
-0103             %Non-zero values are interpreted as reversible
-0104             model.rev(indexes(j))=params(j)~=0;
-0105         end
-0106         if strcmpi(paramType{j},'var')
-0107             if params(j) < 0
-0108                 model.lb(indexes(j)) = params(j) * (1+var/200);
-0109                 model.ub(indexes(j)) = params(j) * (1-var/200);
-0110             else
-0111                 model.lb(indexes(j)) = params(j) * (1-var/200);
-0112                 model.ub(indexes(j)) = params(j) * (1+var/200);
-0113             end
-0114         end
-0115         if strcmpi(paramType{j},'unc')
-0116             if isfield(model,'annotation') && isfield(model.annotation,'defaultLB')
-0117                 lb = model.annotation.defaultLB;
-0118             else
-0119                 lb = -1000;
-0120             end
-0121             if isfield(model,'annotation') && isfield(model.annotation,'defaultUB')
-0122                 ub = model.annotation.defaultUB;
-0123             else
-0124                 ub = 1000;
-0125             end
-0126             model.lb(indexes(j)) = lb;
-0127             model.ub(indexes(j)) = ub;
-0128         end
-0129     end
-0130 end
-0131 if any(ismember(paramType,{'lb','ub','unc'}))
-0132     invalidBound = model.lb(indexes) > model.ub(indexes);
-0133     if any(invalidBound)
-0134         error(['Invalid set of bounds for reaction(s): ', strjoin(model.rxns(indexes(invalidBound)),', '), '.'])
-0135     end
-0136 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/simplifyModel.html b/doc/manipulation/simplifyModel.html deleted file mode 100644 index 01eea06d..00000000 --- a/doc/manipulation/simplifyModel.html +++ /dev/null @@ -1,432 +0,0 @@ - - - - Description of simplifyModel - - - - - - - - - -
Home > manipulation > simplifyModel.m
- - - -

simplifyModel -

- -

PURPOSE ^

-
simplifyModel Simplify a model by deleting reactions and metabolites.
- -

SYNOPSIS ^

-
function [reducedModel, deletedReactions, deletedMetabolites]=simplifyModel(model,varargin)
- -

DESCRIPTION ^

-
 simplifyModel  Simplify a model by deleting reactions and metabolites.
-
- This function is for reducing the model size by removing reactions and
- associated metabolites that cannot carry flux. It can also be used for
- identifying different types of gaps.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- deleteUnconstrained : logical
-     delete metabolites marked as unconstrained (default true).
- deleteDuplicates : logical
-     delete all but one of duplicate reactions (default false).
- deleteZeroInterval : logical
-     delete reactions that are constrained to zero flux (default false).
- deleteInaccessible : logical
-     delete dead end reactions (default false).
- deleteMinMax : logical
-     delete reactions that cannot carry a flux by trying to
-     minimize/maximize the flux through that reaction. May be time
-     consuming (default false).
- groupLinear : logical
-     group linearly dependent pathways (default false).
- constrainReversible : logical
-     check if there are reversible reactions which can only carry flux in
-     one direction, and if so constrain them to be irreversible. This
-     tends to allow for more reactions grouped when using groupLinear
-     (default false).
- reservedRxns : cell
-     cell array with reaction IDs that are not allowed to be removed
-     (default none).
- suppressWarnings : logical
-     true if warnings should be suppressed (default false).
-
- Returns
- -------
- reducedModel : struct
-     an updated model structure.
- deletedReactions : cell
-     a cell array with the IDs of all deleted reactions.
- deletedMetabolites : cell
-     a cell array with the IDs of all deleted metabolites.
-
- Examples
- --------
-     [reducedModel, deletedReactions, deletedMetabolites] = ...
-         simplifyModel(model, deleteUnconstrained, deleteDuplicates, ...
-         deleteZeroInterval, deleteInaccessible, deleteMinMax, ...
-         groupLinear, constrainReversible, reservedRxns, suppressWarnings);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • contractModel contractModel Contract a model by grouping all identical reactions.
  • convertToIrrev convertToIrrev Convert a model to irreversible form.
  • removeMets removeMets Delete a set of metabolites from a model.
  • removeReactions removeReactions Delete a set of reactions from a model.
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [reducedModel, deletedReactions, deletedMetabolites]=simplifyModel(model,varargin)
-0002 % simplifyModel  Simplify a model by deleting reactions and metabolites.
-0003 %
-0004 % This function is for reducing the model size by removing reactions and
-0005 % associated metabolites that cannot carry flux. It can also be used for
-0006 % identifying different types of gaps.
-0007 %
-0008 % Parameters
-0009 % ----------
-0010 % model : struct
-0011 %     a model structure.
-0012 %
-0013 % Name-Value Arguments
-0014 % --------------------
-0015 % deleteUnconstrained : logical
-0016 %     delete metabolites marked as unconstrained (default true).
-0017 % deleteDuplicates : logical
-0018 %     delete all but one of duplicate reactions (default false).
-0019 % deleteZeroInterval : logical
-0020 %     delete reactions that are constrained to zero flux (default false).
-0021 % deleteInaccessible : logical
-0022 %     delete dead end reactions (default false).
-0023 % deleteMinMax : logical
-0024 %     delete reactions that cannot carry a flux by trying to
-0025 %     minimize/maximize the flux through that reaction. May be time
-0026 %     consuming (default false).
-0027 % groupLinear : logical
-0028 %     group linearly dependent pathways (default false).
-0029 % constrainReversible : logical
-0030 %     check if there are reversible reactions which can only carry flux in
-0031 %     one direction, and if so constrain them to be irreversible. This
-0032 %     tends to allow for more reactions grouped when using groupLinear
-0033 %     (default false).
-0034 % reservedRxns : cell
-0035 %     cell array with reaction IDs that are not allowed to be removed
-0036 %     (default none).
-0037 % suppressWarnings : logical
-0038 %     true if warnings should be suppressed (default false).
-0039 %
-0040 % Returns
-0041 % -------
-0042 % reducedModel : struct
-0043 %     an updated model structure.
-0044 % deletedReactions : cell
-0045 %     a cell array with the IDs of all deleted reactions.
-0046 % deletedMetabolites : cell
-0047 %     a cell array with the IDs of all deleted metabolites.
-0048 %
-0049 % Examples
-0050 % --------
-0051 %     [reducedModel, deletedReactions, deletedMetabolites] = ...
-0052 %         simplifyModel(model, deleteUnconstrained, deleteDuplicates, ...
-0053 %         deleteZeroInterval, deleteInaccessible, deleteMinMax, ...
-0054 %         groupLinear, constrainReversible, reservedRxns, suppressWarnings);
-0055 
-0056 p=parseRAVENargs(varargin, {'deleteUnconstrained',true; 'deleteDuplicates',false; 'deleteZeroInterval',false; 'deleteInaccessible',false; 'deleteMinMax',false; 'groupLinear',false; 'constrainReversible',false; 'reservedRxns',[]; 'suppressWarnings',false});
-0057 deleteUnconstrained=p.deleteUnconstrained;
-0058 deleteDuplicates=p.deleteDuplicates;
-0059 deleteZeroInterval=p.deleteZeroInterval;
-0060 deleteInaccessible=p.deleteInaccessible;
-0061 deleteMinMax=p.deleteMinMax;
-0062 groupLinear=p.groupLinear;
-0063 constrainReversible=p.constrainReversible;
-0064 reservedRxns=p.reservedRxns;
-0065 if ~isempty(reservedRxns)
-0066     reservedRxns=convertCharArray(reservedRxns);
-0067 end
-0068 suppressWarnings=p.suppressWarnings;
-0069 
-0070 reducedModel=model;
-0071 deletedReactions={};
-0072 deletedMetabolites={};
-0073 
-0074 if deleteUnconstrained==true
-0075     if isfield(reducedModel,'unconstrained')
-0076         %Remove unbalanced metabolites
-0077         deletedMetabolites=reducedModel.mets(reducedModel.unconstrained~=0);
-0078         reducedModel=removeMets(reducedModel,reducedModel.unconstrained~=0,false,false,false,true);
-0079         reducedModel=rmfield(reducedModel,'unconstrained');
-0080     end
-0081 end
-0082 
-0083 if deleteDuplicates==true
-0084     %Delete all but the last occurrence of duplicate reactions. The
-0085     %reactions must have the same bounds, reversibility, and objective
-0086     %coefficient to be regarded as duplicate
-0087     [reducedModel, rxnsToDelete, ~]=contractModel(reducedModel);
-0088     deletedReactions=[deletedReactions; rxnsToDelete];
-0089 end
-0090 
-0091 if deleteZeroInterval==true
-0092     %Find all reactions where both LB and UB are 0
-0093     zeroIntervalReactions=and(reducedModel.lb==0,reducedModel.ub==0);
-0094     
-0095     rxnsToDelete=setdiff(reducedModel.rxns(zeroIntervalReactions),reservedRxns);
-0096     deletedReactions=[deletedReactions; rxnsToDelete];
-0097     
-0098     %Remove reactions
-0099     reducedModel=removeReactions(reducedModel,rxnsToDelete);
-0100     
-0101     %Find metabolites that no longer are used and delete them
-0102     notInUse=orphanedMets(reducedModel);
-0103     deletedMetabolites=[deletedMetabolites;reducedModel.mets(notInUse)];
-0104     
-0105     %Remove metabolites
-0106     reducedModel=removeMets(reducedModel,notInUse);
-0107 end
-0108 
-0109 if deleteInaccessible==true
-0110     %Print a warning if exchange metabolites have not been deleted yet. This
-0111     %often means that the only allowed products are the metabolites that
-0112     %are taken up be the system
-0113     if isfield(reducedModel,'unconstrained') && suppressWarnings==false
-0114         EM='Removing dead-end reactions before removing exchange metabolites';
-0115         warning('RAVEN:warning', '%s', EM);
-0116     end
-0117     
-0118     while true
-0119         %Find all metabolites that are only reactants or only products.
-0120         %Delete those metabolites and reactions. This is done until no more
-0121         %such reactions are found
-0122         
-0123         %Construct a stoichiometric matrix where all reactions are written
-0124         %as reversible
-0125         
-0126         %Add fake exchange reactions if model.b~=0
-0127         in=any(reducedModel.b<0,2);
-0128         out=any(reducedModel.b>0,2);
-0129         I=speye(numel(reducedModel.mets));
-0130         revS=[reducedModel.S,reducedModel.S(:,reducedModel.lb<0)*-1 I(:,in) I(:,out)*-1];
-0131         
-0132         metUsage=sum(abs(revS')>0);
-0133         onlyProducts=sum(revS'>0) == metUsage;
-0134         onlyReactants=sum(revS'<0) == metUsage;
-0135         
-0136         %Also remove metabolites that only participate in one reversible
-0137         %reaction Do not remove the ones in one reversible reaction if is
-0138         %also has a non-zero coefficient in model.b
-0139         notInUse=onlyProducts | onlyReactants | (sum(abs(reducedModel.S')>0)<=1 & (~in & ~out)');
-0140         deletedRxn=false;
-0141         if any(notInUse)
-0142             %Find their corresponding reactions
-0143             rxnsNotInUse=sum(abs(reducedModel.S(notInUse,:))>0,1)>0;
-0144             rxnsToDelete=setdiff(reducedModel.rxns(rxnsNotInUse),reservedRxns);
-0145             deletedReactions=[deletedReactions; rxnsToDelete];
-0146             
-0147             %Remove reactions
-0148             reducedModel=removeReactions(reducedModel,rxnsToDelete);
-0149             
-0150             %Remove metabolites. Recalculate since it could be that some
-0151             %cannot be deleted due to reserved rxns
-0152             notInUse=orphanedMets(reducedModel);
-0153             deletedMetabolites=[deletedMetabolites; reducedModel.mets(notInUse)];
-0154             reducedModel=removeMets(reducedModel,notInUse);
-0155             
-0156             %It could happen that it just finds reserved reactions and gets
-0157             %stuck
-0158             if ~isempty(rxnsToDelete)
-0159                 deletedRxn=true;
-0160             end
-0161         else
-0162             break;
-0163         end
-0164         if deletedRxn==false
-0165             break;
-0166         end
-0167     end
-0168 end
-0169 
-0170 if deleteMinMax==true
-0171     %Get reactions that cannot carry fluxes. This should be done
-0172     %algebraically if possible
-0173     I=~haveFlux(reducedModel);
-0174     
-0175     %Remove reactions
-0176     rxnsToDelete=setdiff(reducedModel.rxns(I),reservedRxns);
-0177     deletedReactions=[deletedReactions; rxnsToDelete];
-0178     reducedModel=removeReactions(reducedModel,rxnsToDelete);
-0179     
-0180     %Remove metabolites
-0181     notInUse=orphanedMets(reducedModel);
-0182     deletedMetabolites=[deletedMetabolites; reducedModel.mets(notInUse)];
-0183     reducedModel=removeMets(reducedModel,notInUse);
-0184 end
-0185 
-0186 if constrainReversible==true
-0187     revs=find(reducedModel.rev);
-0188     [I,J]=getAllowedBounds(reducedModel,revs);
-0189     
-0190     I=abs(I);
-0191     J=abs(J);
-0192     
-0193     %Get the "small" values
-0194     K=I<10^-10;
-0195     L=J<10^-10;
-0196     
-0197     %Keep the values where only one direction is zero (small)
-0198     I(K==L)=[];
-0199     J(K==L)=[];
-0200     revs(K==L)=[];
-0201     
-0202     %Change the reversibility of the remaining reactions
-0203     reducedModel.rev(revs(J>10^-10))=0; %Ignore reverse direction
-0204     reducedModel.lb(revs(J>10^-10))=0;
-0205     
-0206     toSwitch=revs(I>10^-10);
-0207     reducedModel.rev(toSwitch)=0; %Change directionality
-0208     reducedModel.ub(toSwitch)=reducedModel.lb(toSwitch)*-1;
-0209     reducedModel.lb(toSwitch)=0;
-0210     reducedModel.S(:,toSwitch)=reducedModel.S(:,toSwitch).*-1;
-0211     reducedModel.c(toSwitch)=reducedModel.c(toSwitch)*-1;
-0212 end
-0213 if groupLinear==true
-0214     if  suppressWarnings==false
-0215         fprintf('NOTE: You have chosen to group linear reactions. This option does not keep track of gene/reaction associations when reactions are merged. Deleting all gene information\n');
-0216     end
-0217     
-0218     reducedModel.genes={};
-0219     reducedModel.rxnGeneMat=sparse(numel(reducedModel.rxns),0);
-0220     reducedModel.grRules(:)={''};
-0221     
-0222     if isfield(reducedModel,'geneShortNames')
-0223         reducedModel.geneShortNames={};
-0224     end
-0225     if isfield(reducedModel,'proteins')
-0226         reducedModel.proteins={};
-0227     end
-0228     if isfield(reducedModel,'geneMiriams')
-0229         reducedModel.geneMiriams={};
-0230     end
-0231     if isfield(reducedModel,'geneComps')
-0232         reducedModel.geneComps=[];
-0233     end
-0234     
-0235     %Convert the model to irreversible
-0236     irrevModel=convertToIrrev(reducedModel);
-0237     
-0238     %Loop through and iteratively group linear reactions
-0239     while 1
-0240         %Get the banned reaction indexes. Note that the indexes will change
-0241         %in each iteration, but the names will not as they will not be merged
-0242         %with any other reaction
-0243         bannedIndexes=getIndexes(irrevModel,reservedRxns,'rxns');
-0244         
-0245         %Select all metabolites that are only present as reactants/products
-0246         %in one reaction
-0247         singleNegative=find(sum(irrevModel.S'<0)==1);
-0248         singlePositive=find(sum(irrevModel.S'>0)==1);
-0249         
-0250         %Retrieve the common metabolites
-0251         common=intersect(singleNegative,singlePositive);
-0252         
-0253         mergedSome=false;
-0254         
-0255         %Loop through each of them and see if the reactions should be
-0256         %merged
-0257         for i=1:numel(common)
-0258             involvedRxns=find(irrevModel.S(common(i),:));
-0259             
-0260             %Check so that one or both of the reactions have not been merged
-0261             %already
-0262             if numel(involvedRxns)==2 && isempty(intersect(bannedIndexes,involvedRxns))
-0263                 %Calculate how many times the second reaction has to be
-0264                 %multiplied before being merged with the first
-0265                 stoichRatio=abs(irrevModel.S(common(i),involvedRxns(1))/irrevModel.S(common(i),involvedRxns(2)));
-0266                 
-0267                 %Add the second to the first
-0268                 irrevModel.S(:,involvedRxns(1))=irrevModel.S(:,involvedRxns(1))+irrevModel.S(:,involvedRxns(2))*stoichRatio;
-0269                 
-0270                 %Clear the second reaction
-0271                 irrevModel.S(:,involvedRxns(2))=0;
-0272                 
-0273                 %This is to prevent numerical issues. It should be 0
-0274                 %already
-0275                 irrevModel.S(common(i),involvedRxns(1))=0;
-0276                 
-0277                 %At this point the second reaction is certain to be deleted
-0278                 %in a later step and can therefore be ignored
-0279                 
-0280                 %Recalculate the bounds for the new reaction. This can be
-0281                 %problematic since the scale of the bounds may change
-0282                 %dramatically. Let the most constraining reaction determine
-0283                 %the new bound
-0284                 lb1=irrevModel.lb(involvedRxns(1));
-0285                 lb2=irrevModel.lb(involvedRxns(2));
-0286                 ub1=irrevModel.ub(involvedRxns(1));
-0287                 ub2=irrevModel.ub(involvedRxns(2));
-0288                 
-0289                 if lb2~=-inf
-0290                     irrevModel.lb(involvedRxns(1))=max(lb1,lb2/stoichRatio);
-0291                 end
-0292                 if ub2~=inf
-0293                     irrevModel.ub(involvedRxns(1))=min(ub1,ub2/stoichRatio);
-0294                 end
-0295                 
-0296                 %Then recalculate the objective coefficient. The resulting
-0297                 %coefficient is the weighted sum of the previous
-0298                 irrevModel.c(involvedRxns(1))=irrevModel.c(involvedRxns(1))+irrevModel.c(involvedRxns(2))*stoichRatio;
-0299                 
-0300                 %Iterate again
-0301                 mergedSome=true;
-0302             end
-0303         end
-0304         
-0305         %All possible reactions merged
-0306         if mergedSome==false
-0307             break;
-0308         end
-0309         
-0310         %Now delete all reactions that involve no metabolites
-0311         I=find(sum(irrevModel.S~=0)==0);
-0312         
-0313         %Remove reactions
-0314         irrevModel=removeReactions(irrevModel,I);
-0315         
-0316         %Remove metabolites
-0317         notInUse=orphanedMets(irrevModel);
-0318         irrevModel=removeMets(irrevModel,notInUse);
-0319     end
-0320     
-0321     reducedModel=irrevModel;
-0322 end
-0323 end
-0324 
-0325 function I=orphanedMets(model)
-0326 %Metabolites that take part in no reaction, and can therefore be removed
-0327 %without changing what the model can do. Metabolites with a non-zero
-0328 %model.b are kept: b is a boundary condition on the metabolite, so removing
-0329 %the row would drop that constraint along with it.
-0330 I=sum(model.S~=0,2)==0 & ~any(model.b~=0,2);
-0331 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/sortModel.html b/doc/manipulation/sortModel.html deleted file mode 100644 index 90e1a67a..00000000 --- a/doc/manipulation/sortModel.html +++ /dev/null @@ -1,291 +0,0 @@ - - - - Description of sortModel - - - - - - - - - -
Home > manipulation > sortModel.m
- - - -

sortModel -

- -

PURPOSE ^

-
sortModel Standardize a model's internal ordering: by default orients
- -

SYNOPSIS ^

-
function model=sortModel(model,varargin)
- -

DESCRIPTION ^

-
 sortModel  Standardize a model's internal ordering: by default orients
- reversible reactions consistently; can also reorder metabolites within
- reaction equations and/or reactions within each subsystem, depending on
- which options are enabled.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- sortReversible : logical
-     sorts the reversible reactions so that the metabolite that is first
-     in lexicographical order is a reactant (default true).
- sortMetName : logical
-     sort the metabolite names in the equation, also uses compartment
-     abbreviation (default false).
- sortReactionOrder : logical
-     sorts the reaction order within each subsystem so that reactions
-     consuming some metabolite come after reactions producing it. This
-     overrides the sortReversible option and reactions are sorted so that
-     the production direction matches the consumption direction (default
-     false).
- seed : double
-     seed for the random number generator used by the sortReactionOrder
-     local search, so its result is reproducible across runs (default:
-     current rng state is left untouched).
-
- Returns
- -------
- model : struct
-     an updated model structure.
-
- Examples
- --------
-     model = sortModel(model, sortReversible, sortMetName, sortReactionOrder);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • permuteModel permuteModel Change the order of the reactions or metabolites in a model.
-This function is called by: -
    -
  • contractModel contractModel Contract a model by grouping all identical reactions.
- - - - -

SOURCE CODE ^

-
0001 function model=sortModel(model,varargin)
-0002 % sortModel  Standardize a model's internal ordering: by default orients
-0003 % reversible reactions consistently; can also reorder metabolites within
-0004 % reaction equations and/or reactions within each subsystem, depending on
-0005 % which options are enabled.
-0006 %
-0007 % Parameters
-0008 % ----------
-0009 % model : struct
-0010 %     a model structure.
-0011 %
-0012 % Name-Value Arguments
-0013 % --------------------
-0014 % sortReversible : logical
-0015 %     sorts the reversible reactions so that the metabolite that is first
-0016 %     in lexicographical order is a reactant (default true).
-0017 % sortMetName : logical
-0018 %     sort the metabolite names in the equation, also uses compartment
-0019 %     abbreviation (default false).
-0020 % sortReactionOrder : logical
-0021 %     sorts the reaction order within each subsystem so that reactions
-0022 %     consuming some metabolite come after reactions producing it. This
-0023 %     overrides the sortReversible option and reactions are sorted so that
-0024 %     the production direction matches the consumption direction (default
-0025 %     false).
-0026 % seed : double
-0027 %     seed for the random number generator used by the sortReactionOrder
-0028 %     local search, so its result is reproducible across runs (default:
-0029 %     current rng state is left untouched).
-0030 %
-0031 % Returns
-0032 % -------
-0033 % model : struct
-0034 %     an updated model structure.
-0035 %
-0036 % Examples
-0037 % --------
-0038 %     model = sortModel(model, sortReversible, sortMetName, sortReactionOrder);
-0039 
-0040 p=parseRAVENargs(varargin, {'sortReversible',true; 'sortMetName',false; 'sortReactionOrder',false; 'seed',[]});
-0041 if ~isempty(p.seed)
-0042     rng(p.seed);
-0043 end
-0044 sortReversible=p.sortReversible;
-0045 sortMetName=p.sortMetName;
-0046 sortReactionOrder=p.sortReactionOrder;
-0047 
-0048 if sortMetName==true
-0049     %Assuming that metComps are the indexes.
-0050     [~, metIndexes]=sort(strcat(model.metNames,'[',model.comps(model.metComps),']'));
-0051     model=permuteModel(model,metIndexes,'mets');
-0052 end
-0053 
-0054 if sortReversible==true && sortReactionOrder==false
-0055     revIndexes=find(model.rev);
-0056 
-0057     for i=1:numel(revIndexes)
-0058         %Create a cell array with all the metabolite names
-0059         mets=find(model.S(:,revIndexes(i)));
-0060         metNames=strcat(model.metNames(mets),model.comps(model.metComps(mets)));
-0061         
-0062         if iscellstr(metNames)
-0063             [~, indexes]=sort(metNames);
-0064             
-0065             if model.S(mets(indexes(1)),revIndexes(i))>0
-0066                 model.S(:,revIndexes(i))=model.S(:,revIndexes(i))*-1;
-0067             end
-0068         end
-0069     end
-0070 end
-0071 
-0072 if sortReactionOrder==true
-0073     if ~isfield(model,'subSystems')
-0074         EM='The model must contain a subSystems field in order to sort reaction order';
-0075         error('RAVEN:badInput', '%s', EM);
-0076     end
-0077     
-0078     subsystemsUnique='';
-0079     subsystemsConcatenated='';
-0080     for i=1:numel(model.subSystems)
-0081         if ~iscell(model.subSystems{i})
-0082             model.subSystems{i} = {model.subSystems{i}};
-0083         end    
-0084         subsystemsConcatenated{i,1}=strjoin(model.subSystems{i,1},';');
-0085         if ~isempty(model.subSystems{i,1})
-0086             for j=1:numel(model.subSystems{i,1})
-0087                 subsystemsUnique{numel(subsystemsUnique)+1,1}=model.subSystems{i,1}{1,j};
-0088             end
-0089         end
-0090     end
-0091     subsystemsUnique=unique(subsystemsUnique);
-0092     for i=1:numel(subsystemsUnique)
-0093         %Get all reactions for that subsystem. The subsystem name is
-0094         %escaped (it is arbitrary model text, not a regex pattern), and
-0095         %the match is anchored to a whole ';'-delimited entry, since a
-0096         %plain substring search would also match e.g. "Glycolysis /
-0097         %Gluconeogenesis" when looking for "Glycolysis".
-0098         pattern=['(^|;)' regexptranslate('escape',subsystemsUnique{i}) '(;|$)'];
-0099         rxns=find(~cellfun(@isempty,regexp(subsystemsConcatenated,pattern)));
-0100         
-0101         %Temporarily ignore large subsystems because of inefficient
-0102         %implementation
-0103         if numel(rxns)<2 || numel(rxns)>250
-0104             continue;
-0105         end
-0106         
-0107         nRxns=numel(rxns);
-0108         revRxns=rxns(model.rev(rxns)~=0);
-0109 
-0110         %This variable will hold the current reversibility directions of
-0111         %the reversible reactions. 1 means the same direction as in the
-0112         %original model and -1 means the opposite direction.
-0113         oldRev=ones(numel(revRxns),1);
-0114 
-0115         %Work on just this subsystem's own (mets x nRxns) columns rather
-0116         %than a full copy of model.S every iteration below: the score
-0117         %only ever depends on these columns, whose involved-metabolite
-0118         %rows and reversible-reaction positions are fixed for the whole
-0119         %optimization (only their order/sign changes), so both can be
-0120         %precomputed once instead of per iteration.
-0121         subS=model.S(:,rxns);
-0122         subS=subS(any(subS,2),:);
-0123         [~,revLocalIdx]=ismember(revRxns,rxns);
-0124 
-0125         %The problem could be solved analytically, but a simple random
-0126         %method is used instead. Two reactions are chosen randomly and
-0127         %their positions are switched. A score is calculated based on the
-0128         %number of metabolites that are produced before they are consumed.
-0129         %If the perturbed model has a better or equal score than the
-0130         %original the reaction order is switched. If no increase in score
-0131         %has been seen after 1000*rxnsInSubsystem then the optimization is
-0132         %terminated
-0133 
-0134         rxnOrder=1:nRxns;
-0135         oldScore=-inf;
-0136         counter=0;
-0137         firstIter=true;
-0138         while 1==1
-0139             counter=counter+1;
-0140             if counter==100*nRxns
-0141                 break;
-0142             end
-0143 
-0144             newRxnOrder=rxnOrder;
-0145             rev=oldRev;
-0146 
-0147             if firstIter==false
-0148                 y=randperm(nRxns,2);
-0149 
-0150                 newRxnOrder(y(1))=rxnOrder(y(2));
-0151                 newRxnOrder(y(2))=rxnOrder(y(1));
-0152 
-0153                 %With a 50% chance, also switch the reversibility of one of
-0154                 %the reactions
-0155                 if rand()>0.5 && numel(rev)>1
-0156                     n=randperm(numel(rev),1);
-0157                     rev(n)=rev(n)*-1;
-0158                 end
-0159             end
-0160             firstIter=false;
-0161 
-0162             tempS=subS;
-0163 
-0164             %Switch the directionalities
-0165             for j=1:numel(rev)
-0166                 if rev(j)==-1
-0167                     tempS(:,revLocalIdx(j))=tempS(:,revLocalIdx(j)).*-1;
-0168                 end
-0169             end
-0170 
-0171             %Get the metabolites that are involved and when they are
-0172             %produced/consumed
-0173             s=tempS(:,newRxnOrder);
-0174 
-0175             %Add so that all mets are produced and consumed in the end
-0176             s=[s ones(size(s,1),1) ones(size(s,1),1)*-1];
-0177             
-0178             %For each metabolite, find the reaction where it is first
-0179             %produced and the reaction where it is first consumed
-0180             s1=s>0;
-0181             r1=arrayfun(@(x) find(s1(x,:),1,'first'),1:size(s1,1));
-0182             s2=s<0;
-0183             r2=arrayfun(@(x) find(s2(x,:),1,'first'),1:size(s2,1));
-0184             
-0185             score=sum(r1<r2);
-0186             
-0187             if score>=oldScore
-0188                 if score>oldScore
-0189                     counter=0;
-0190                 end
-0191                 oldScore=score;
-0192                 oldRev=rev;
-0193                 rxnOrder=newRxnOrder;
-0194             end
-0195         end
-0196         
-0197         %Update the model for this subsystem
-0198         for j=1:numel(oldRev)
-0199             if oldRev(j)==-1
-0200                 model.S(:,revRxns(j))=model.S(:,revRxns(j)).*-1;
-0201             end
-0202         end
-0203         order=1:numel(model.rxns);
-0204         order(rxns)=rxns(rxnOrder);
-0205         model=permuteModel(model, order, 'rxns');
-0206     end
-0207 end
-0208 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/manipulation/standardizeGrRules.html b/doc/manipulation/standardizeGrRules.html deleted file mode 100644 index d7c9dc3a..00000000 --- a/doc/manipulation/standardizeGrRules.html +++ /dev/null @@ -1,279 +0,0 @@ - - - - Description of standardizeGrRules - - - - - - - - - -
Home > manipulation > standardizeGrRules.m
- - - -

standardizeGrRules -

- -

PURPOSE ^

-
standardizeGrRules Standardize gene-reaction rules in a model.
- -

SYNOPSIS ^

-
function [grRules,rxnGeneMat,indexes2check] = standardizeGrRules(model,varargin)
- -

DESCRIPTION ^

-
 standardizeGrRules  Standardize gene-reaction rules in a model.
-
- The grRules are standardized according to the following:
-
- - No overall containing brackets
- - Just enzyme complexes are enclosed into brackets
- - " and " and " or " strings are strictly set to lowercase
-
- A rxnGeneMat matrix consistent with the standardized grRules is created.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- embedded : logical
-     if true, suppresses the console warning about potentially
-     problematic grRules relationships, for use when this function is
-     called inside of another RAVEN function that handles reporting
-     itself (default false).
-
- Returns
- -------
- grRules : cell
-     [nRxns x 1] cell array with the standardized grRules.
- rxnGeneMat : double
-     [nRxns x nGenes] sparse matrix consistent with the standardized
-     grRules.
- indexes2check : double
-     indices of the grRules with potentially problematic relationships
-     that should be checked manually.
-
- Examples
- --------
-     [grRules, rxnGeneMat, indexes2check] = standardizeGrRules(model, embedded);
-
- Notes
- -----
- If used in a model reconstruction or modification pipeline, run this
- function first.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
  • addRxns addRxns Add reactions to a model.
  • changeGrRules changeGrRules Change multiple grRules at the same time.
  • contractModel contractModel Contract a model by grouping all identical reactions.
  • expandModel expandModel Expand reactions that use several gene associations.
  • mergeModels mergeModels Merge models into one model structure.
  • removeGenes removeGenes Delete a set of genes from a model.
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [grRules,rxnGeneMat,indexes2check] = standardizeGrRules(model,varargin)
-0002 % standardizeGrRules  Standardize gene-reaction rules in a model.
-0003 %
-0004 % The grRules are standardized according to the following:
-0005 %
-0006 % - No overall containing brackets
-0007 % - Just enzyme complexes are enclosed into brackets
-0008 % - " and " and " or " strings are strictly set to lowercase
-0009 %
-0010 % A rxnGeneMat matrix consistent with the standardized grRules is created.
-0011 %
-0012 % Parameters
-0013 % ----------
-0014 % model : struct
-0015 %     a model structure.
-0016 %
-0017 % Name-Value Arguments
-0018 % --------------------
-0019 % embedded : logical
-0020 %     if true, suppresses the console warning about potentially
-0021 %     problematic grRules relationships, for use when this function is
-0022 %     called inside of another RAVEN function that handles reporting
-0023 %     itself (default false).
-0024 %
-0025 % Returns
-0026 % -------
-0027 % grRules : cell
-0028 %     [nRxns x 1] cell array with the standardized grRules.
-0029 % rxnGeneMat : double
-0030 %     [nRxns x nGenes] sparse matrix consistent with the standardized
-0031 %     grRules.
-0032 % indexes2check : double
-0033 %     indices of the grRules with potentially problematic relationships
-0034 %     that should be checked manually.
-0035 %
-0036 % Examples
-0037 % --------
-0038 %     [grRules, rxnGeneMat, indexes2check] = standardizeGrRules(model, embedded);
-0039 %
-0040 % Notes
-0041 % -----
-0042 % If used in a model reconstruction or modification pipeline, run this
-0043 % function first.
-0044 
-0045 %Preallocate fields
-0046 n          = length(model.rxns);
-0047 [g,~]      = size(model.genes);
-0048 rxnGeneMat = sparse(n,g);
-0049 grRules    = cell(n,1);
-0050 genes      = model.genes;
-0051 p=parseRAVENargs(varargin, {'embedded',false});
-0052 embedded=p.embedded;
-0053 
-0054 %Gene id -> index lookup, built once rather than linearly scanning all of
-0055 %model.genes (via strcmpi) for every gene token of every reaction, which
-0056 %is quadratic in genome-scale models.
-0057 if isempty(genes)
-0058     geneMap = containers.Map('KeyType','char','ValueType','double');
-0059 else
-0060     geneMap = containers.Map(lower(genes),num2cell(1:numel(genes)));
-0061 end
-0062 
-0063 if isfield(model,'grRules')
-0064     originalGrRules=model.grRules; 
-0065     originalGrRules=grRulesPreparation(originalGrRules);
-0066     %Search for potential logical errors in the grRules field
-0067     tmpModel.rxns=model.rxns; tmpModel.grRules=originalGrRules;
-0068     fpeIssues=findPotentialErrors(tmpModel);
-0069     if isempty(fpeIssues)
-0070         indexes2check=[];
-0071     else
-0072         indexes2check=vertcat(fpeIssues.index);
-0073     end
-0074     if ~isempty(indexes2check) && ~embedded
-0075         %Escaped: rxn id and grRule are arbitrary model text and may
-0076         %contain "%", which sprintf/warning would otherwise misread as a
-0077         %format directive and truncate the rest of the message.
-0078         esc=@(s) strrep(s,'%','%%');
-0079         STR=['Potentially problematic ") AND (", ") AND" or "AND ("relat' ...
-0080              'ionships found in\n\n'];
-0081         for fpeI=1:numel(fpeIssues)
-0082             STR=[STR '  - grRule #' esc(fpeIssues(fpeI).rxn) ': ' esc(fpeIssues(fpeI).grRule) '\n']; %#ok<AGROW>
-0083         end
-0084         STR=[STR '\n This kind of relationships should only be present ' ...
-0085              'in reactions catalysed by complexes of isoenzymes e.g.\n\n' ...
-0086              '  - (G1 or G2) and (G3 or G4)\n\n For these cases modify ' ...
-0087              'the grRules manually, writing all the possible combinations ' ...
-0088              'e.g.\n\n  - (G1 and G3) or (G1 and G4) or (G2 and G3) or ' ...
-0089              '(G2 and G4)\n\n For other cases modify the correspondent ' ...
-0090              'grRules avoiding:\n\n  1) Overall container brackets, e.g.' ...
-0091              '\n        "(G1 and G2)" should be "G1 and G2"\n\n  2) ' ...
-0092              'Single unit enzymes enclosed into brackets, e.g.\n        ' ...
-0093              '"(G1)" should be "G1"\n\n  3) The use of uppercases for ' ...
-0094              'logical operators, e.g.\n        "G1 OR G2" should be "G1 ' ...
-0095              'or G2"\n\n  4) Unbalanced brackets, e.g.\n        "((G1 ' ...
-0096              'and G2) or G3" should be "(G1 and G2) or G3"\n'];
-0097         %warning() re-parses its argument as a format string too, so the
-0098         %already-resolved text must be passed via '%s', not directly.
-0099         warning('%s', sprintf(STR))
-0100     end
-0101     
-0102     for i=1:length(originalGrRules)
-0103         originalSTR = originalGrRules{i};
-0104         grRules{i,:}  = originalSTR;
-0105         %Non-empty grRules are splitted in all their different isoenzymes
-0106         genesSets   = getSimpleGeneSets(originalSTR);
-0107         rxnGeneMat  = modifyRxnGeneMat(genesSets,geneMap,rxnGeneMat,i);
-0108         %standardize the non-conflicting grRules
-0109         if ~ismember(i,indexes2check)
-0110             newSTR = [];
-0111             if ~isempty(genesSets)
-0112                 for j=1:length(genesSets)
-0113                     simpleSet  = genesSets{j};
-0114                     %Enclose simpleSet in brackets
-0115                     if length(genesSets)>1
-0116                         if ~isempty(strfind(simpleSet,' and '))
-0117                             simpleSet = horzcat('(',simpleSet,')');
-0118                         end
-0119                     end
-0120                     %Separate genesSets in the substring (in case of
-0121                     %isoenzymes)
-0122                     if j<length(genesSets)
-0123                         newSTR = [newSTR, simpleSet, ' or '];
-0124                         %Add the last simpleSet
-0125                     else
-0126                         newSTR = [newSTR, simpleSet];
-0127                     end
-0128                 end
-0129                 %Update grRule
-0130                 grRules{i} = char(newSTR);
-0131             end
-0132         end
-0133     end
-0134 else
-0135     error('The model does not have a grRules field.')
-0136 end
-0137 
-0138 end
-0139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-0140 %Function that gets a cell array with all the simple geneSets in a given
-0141 %grRule string
-0142 function genesSets = getSimpleGeneSets(originalSTR)
-0143 genesSets  = [];
-0144 if ~isempty(originalSTR)
-0145     originalSTR = strtrim(originalSTR);
-0146     %Remove all brackets
-0147     originalSTR = strrep(originalSTR,'(','');
-0148     originalSTR = strrep(originalSTR,')','');
-0149     %Split all the different genesSets
-0150     genesSets  = transpose(strsplit(originalSTR,' or '));
-0151 end
-0152 end
-0153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-0154 %Function that gets a cell array of simple genes sets (single genes or
-0155 %enzyme complexes) associated with the i-th reaction and modifies the
-0156 %correspondent row in the rxnGeneMat accordingly.
-0157 function rxnGeneMat = modifyRxnGeneMat(genesSets,geneMap,rxnGeneMat,i)
-0158 
-0159 if ~isempty(genesSets)
-0160     for j=1:length(genesSets)
-0161         simpleSet  = genesSets{j};
-0162         %        rxnGeneMat = modifyRxnGeneMat(simpleSet,model.genes,rxnGeneMat,i);
-0163         %Get individual genes
-0164         STR   = strrep(simpleSet,') and (',' and ');
-0165         genes = strsplit(STR,' ');
-0166         for k=1:length(genes)
-0167             if ~strcmpi(genes(k),' and ')
-0168                 geneKey = lower(genes{k});
-0169                 if isKey(geneMap,geneKey)
-0170                     rxnGeneMat(i,geneMap(geneKey)) = 1;
-0171                     %else
-0172                     %In this case the gene should be added to the
-0173                     %genes field (and to all of its dependencies)
-0174                 end
-0175             end
-0176         end
-0177     end
-0178 end
-0179 end
-0180 
-0181 function grRules = grRulesPreparation(grRules)
-0182 %Remove unnecessary blanks
-0183 grRules=strrep(grRules,'  ',' ');
-0184 grRules=strrep(grRules,'( ','(');
-0185 grRules=strrep(grRules,' )',')');
-0186 % Make sure that AND and OR strings are in lowercase
-0187 grRules=strrep(grRules,' AND ',' and ');
-0188 grRules=strrep(grRules,' OR ',' or ');
-0189 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/omics/index.html b/doc/omics/index.html deleted file mode 100644 index 896ac0e9..00000000 --- a/doc/omics/index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - Index for Directory omics - - - - - - - - - - -
< Master indexIndex for omics >
- -

Index for omics

- -

Matlab files in this directory:

- -
 parseHPAparseHPA Parse a database dump of the Human Protein Atlas (HPA).
 parseHPArnaparseHPArna Parse a dump of Human Protein Atlas (HPA) RNA-Seq data.
 scoreModelscoreModel Score reactions and genes in a model with complex gene rules.
- - - - -
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/queries/checkModelStruct.html b/doc/queries/checkModelStruct.html deleted file mode 100644 index 524309b2..00000000 --- a/doc/queries/checkModelStruct.html +++ /dev/null @@ -1,662 +0,0 @@ - - - - Description of checkModelStruct - - - - - - - - - -
Home > queries > checkModelStruct.m
- - - -

checkModelStruct -

- -

PURPOSE ^

-
checkModelStruct Perform a number of checks to ensure a model structure is ok.
- -

SYNOPSIS ^

-
function issues=checkModelStruct(model,varargin)
- -

DESCRIPTION ^

-
 checkModelStruct  Perform a number of checks to ensure a model structure is ok.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- throwErrors : logical
-     true if structural problems (wrong types, missing required fields,
-     duplicate IDs, etc.) should throw an error. Advisory notices such as
-     unused elements and bound inconsistencies are always reported as
-     warnings regardless of this flag (default true).
- trimWarnings : logical
-     true if only a maximum of 10 items should be displayed in a given
-     warning or error (default true).
-
- Returns
- -------
- issues : struct
-     When called with an output argument, returns a struct array with one
-     element per finding (does not throw or print) instead of the default
-     print/throw behaviour. Fields:
-
-     - category : char — type of issue: "missing_field", "wrong_type",
-       "empty_id", "duplicate", "invalid_id", "invalid_bounds",
-       "unused", "objective", "gpr", "invalid_formula",
-       "cross_reference", or "other".
-     - target : char — the specific item involved (field name, reaction
-       ID, metabolite ID, etc.), or "" when not applicable.
-     - message : char — the full diagnostic text.
-
- Notes
- -----
- This is typically performed after importing or constructing a model, or
- before attempting to export a model to SBML format.
-
- Examples
- --------
-     checkModelStruct(model);
-     checkModelStruct(model, "throwErrors", false);
-     issues = checkModelStruct(model);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • getGenesFromGrRules getGenesFromGrRules Extract gene list and rxnGeneMat from grRules array.
  • parseFormulas parseFormulas Get the elemental composition from formulas.
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function issues=checkModelStruct(model,varargin)
-0002 % checkModelStruct  Perform a number of checks to ensure a model structure is ok.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % model : struct
-0007 %     a model structure.
-0008 %
-0009 % Name-Value Arguments
-0010 % --------------------
-0011 % throwErrors : logical
-0012 %     true if structural problems (wrong types, missing required fields,
-0013 %     duplicate IDs, etc.) should throw an error. Advisory notices such as
-0014 %     unused elements and bound inconsistencies are always reported as
-0015 %     warnings regardless of this flag (default true).
-0016 % trimWarnings : logical
-0017 %     true if only a maximum of 10 items should be displayed in a given
-0018 %     warning or error (default true).
-0019 %
-0020 % Returns
-0021 % -------
-0022 % issues : struct
-0023 %     When called with an output argument, returns a struct array with one
-0024 %     element per finding (does not throw or print) instead of the default
-0025 %     print/throw behaviour. Fields:
-0026 %
-0027 %     - category : char — type of issue: "missing_field", "wrong_type",
-0028 %       "empty_id", "duplicate", "invalid_id", "invalid_bounds",
-0029 %       "unused", "objective", "gpr", "invalid_formula",
-0030 %       "cross_reference", or "other".
-0031 %     - target : char — the specific item involved (field name, reaction
-0032 %       ID, metabolite ID, etc.), or "" when not applicable.
-0033 %     - message : char — the full diagnostic text.
-0034 %
-0035 % Notes
-0036 % -----
-0037 % This is typically performed after importing or constructing a model, or
-0038 % before attempting to export a model to SBML format.
-0039 %
-0040 % Examples
-0041 % --------
-0042 %     checkModelStruct(model);
-0043 %     checkModelStruct(model, "throwErrors", false);
-0044 %     issues = checkModelStruct(model);
-0045 
-0046 p=parseRAVENargs(varargin, {'throwErrors',true; 'trimWarnings',true});
-0047 throwErrors=p.throwErrors;
-0048 trimWarnings=p.trimWarnings;
-0049 
-0050 collecting = nargout > 0;
-0051 issues = struct('category',{},'target',{},'message',{});
-0052 
-0053     function reportIssue(severity, msg, items)
-0054         % Report one issue: accumulate in collect mode, or warn/error.
-0055         % severity: "error" | "warning"
-0056         % When items is supplied (3 args) but is empty, silently do nothing.
-0057         listProvided = nargin >= 3;
-0058         if nargin < 3; items = {}; end
-0059         if ~isempty(items)
-0060             items = convertCharArray(items);
-0061         end
-0062         if listProvided && isempty(items); return; end
-0063         if trimWarnings && numel(items) > 10
-0064             items{10} = sprintf('...and %d more', numel(items)-9);
-0065             items(11:end) = [];
-0066         end
-0067         cat = issueCategory(msg);
-0068         if collecting
-0069             if isempty(items)
-0070                 issues(end+1,1) = struct('category',cat,'target','','message',msg);
-0071             else
-0072                 for ki_ = 1:numel(items)
-0073                     issues(end+1,1) = struct('category',cat,'target',items{ki_},'message',msg); %#ok<AGROW>
-0074                 end
-0075             end
-0076             return
-0077         end
-0078         isErr = strcmp(severity,'error') && throwErrors;
-0079         if isErr
-0080             if isempty(items)
-0081                 error('RAVEN:modelError', '%s', msg);
-0082             else
-0083                 error('RAVEN:modelError', '%s', ravenList(msg, items, false));
-0084             end
-0085         else
-0086             if isempty(items)
-0087                 warning('RAVEN:modelError', '%s', msg);
-0088             else
-0089                 warning('RAVEN:modelError', '%s', ravenList(msg, items, false));
-0090             end
-0091         end
-0092     end
-0093 
-0094 %Missing elements — checked inline so missing fields do not cause cascading
-0095 %errors in the rest of the body
-0096 fields={'id';'name';'rxns';'mets';'S';'lb';'ub';'rev';'c';'b';'comps';'metComps'};
-0097 missingField=false;
-0098 for i=1:numel(fields)
-0099     if ~isfield(model,fields{i})
-0100         EM=['The model is missing the "' fields{i} '" field'];
-0101         reportIssue('error',EM);
-0102         missingField=true;
-0103     end
-0104 end
-0105 if missingField
-0106     return  % missing required fields would cause access errors below; this
-0107             % must fire whenever a field is missing, not only when
-0108             % collecting -- throwErrors=false otherwise warns here and
-0109             % then crashes on the very field it just warned about
-0110 end
-0111 
-0112 %Type check
-0113 if ~ischar(model.id)
-0114     EM='The "id" field must be a string';
-0115     reportIssue('error',EM);
-0116 end
-0117 if ~ischar(model.name)
-0118     EM='The "name" field must be a string';
-0119     reportIssue('error',EM);
-0120 end
-0121 if ~iscellstr(model.rxns)
-0122     EM='The "rxns" field must be a cell array of strings';
-0123     reportIssue('error',EM);
-0124 end
-0125 if ~iscellstr(model.mets)
-0126     EM='The "mets" field must be a cell array of strings';
-0127     reportIssue('error',EM);
-0128 end
-0129 if ~isnumeric(model.S)
-0130     EM='The "S" field must be of type "double"';
-0131     reportIssue('error',EM);
-0132 end
-0133 if ~isnumeric(model.lb)
-0134     EM='The "lb" field must be of type "double"';
-0135     reportIssue('error',EM);
-0136 end
-0137 if ~isnumeric(model.ub)
-0138     EM='The "ub" field must be of type "double"';
-0139     reportIssue('error',EM);
-0140 end
-0141 if ~isnumeric(model.rev)
-0142     EM='The "rev" field must be of type "double"';
-0143     reportIssue('error',EM);
-0144 end
-0145 if ~isnumeric(model.c)
-0146     EM='The "c" field must be of type "double"';
-0147     reportIssue('error',EM);
-0148 end
-0149 if ~isnumeric(model.b)
-0150     EM='The "b" field must be of type "double"';
-0151     reportIssue('error',EM);
-0152 end
-0153 if ~iscellstr(model.comps)
-0154     EM='The "comps" field must be a cell array of strings';
-0155     reportIssue('error',EM);
-0156 end
-0157 if ~isnumeric(model.metComps)
-0158     EM='The "metComps" field must be of type "double"';
-0159     reportIssue('error',EM);
-0160 end
-0161 if isfield(model,'compNames')
-0162     if ~iscellstr(model.compNames)
-0163         EM='The "compNames" field must be a cell array of strings';
-0164         reportIssue('error',EM);
-0165     end
-0166 end
-0167 if isfield(model,'compOutside')
-0168     if ~iscellstr(model.compOutside)
-0169         EM='The "compOutside" field must be a cell array of strings';
-0170         reportIssue('error',EM);
-0171     end
-0172 end
-0173 if isfield(model,'rxnNames')
-0174     if ~iscellstr(model.rxnNames)
-0175         EM='The "rxnNames" field must be a cell array of strings';
-0176         reportIssue('error',EM);
-0177     end
-0178 end
-0179 if isfield(model,'metNames')
-0180     if ~iscellstr(model.metNames)
-0181         EM='The "metNames" field must be a cell array of strings';
-0182         reportIssue('error',EM);
-0183     end
-0184 end
-0185 if isfield(model,'genes')
-0186     if ~iscellstr(model.genes)
-0187         EM='The "genes" field must be a cell array of strings';
-0188         reportIssue('error',EM);
-0189     end
-0190 end
-0191 if isfield(model,'rxnGeneMat')
-0192     if ~isnumeric(model.rxnGeneMat)
-0193         EM='The "rxnGeneMat" field must be of type "double"';
-0194         reportIssue('error',EM);
-0195     end
-0196 end
-0197 if isfield(model,'grRules')
-0198     if ~iscellstr(model.grRules)
-0199         EM='The "grRules" field must be a cell array of strings';
-0200         reportIssue('error',EM);
-0201     end
-0202     if ~isfield(model,'genes')
-0203         EM='If "grRules" field exists, the model should also contain a "genes" field';
-0204         reportIssue('error',EM);
-0205     else
-0206         %Erroneous grRules that start/end with OR/AND
-0207         EM='The following reaction(s) have grRules that start or end with ''OR'' or ''AND'':';
-0208         reportIssue('error',EM,model.rxns(startsWith(model.grRules,{'or ','and '}) | endsWith(model.grRules,{' or',' and'})));
-0209         %grRules that are not in genes field
-0210         geneList = getGenesFromGrRules(model.grRules);
-0211         geneList = setdiff(unique(geneList),model.genes);
-0212         if ~isempty(geneList)
-0213             problemGrRules = model.rxns(contains(model.grRules,geneList));
-0214             problemGrRules = strjoin(problemGrRules(:),'; ');
-0215             EM=['The reaction(s) "' problemGrRules '" contain the following genes in its "grRules" field, but these are not in the "genes" field:'];
-0216             reportIssue('error',EM,geneList);
-0217         end
-0218     end
-0219 end
-0220 if isfield(model,'rxnComps')
-0221     if ~isnumeric(model.rxnComps)
-0222         EM='The "rxnComps" field must be of type "double"';
-0223         reportIssue('error',EM);
-0224     end
-0225 end
-0226 if isfield(model,'inchis')
-0227     if ~iscellstr(model.inchis)
-0228         EM='The "inchis" field must be a cell array of strings';
-0229         reportIssue('error',EM);
-0230     end
-0231 end
-0232 if isfield(model,'metSmiles')
-0233     if ~iscellstr(model.metSmiles)
-0234         EM='The "metSmiles" field must be a cell array of strings';
-0235         reportIssue('error',EM);
-0236     end
-0237 end
-0238 if isfield(model,'metFormulas')
-0239     if ~iscellstr(model.metFormulas)
-0240         EM='The "metFormulas" field must be a cell array of strings';
-0241         reportIssue('error',EM);
-0242     end
-0243 end
-0244 if isfield(model,'metCharges')
-0245     if ~isnumeric(model.metCharges)
-0246         EM='The "metCharges" field must be a double';
-0247         reportIssue('error',EM);
-0248     end
-0249 end
-0250 if isfield(model,'metDeltaG')
-0251     if ~isnumeric(model.metDeltaG)
-0252         EM='The "metDeltaG" field must be a double';
-0253         reportIssue('error',EM);
-0254     end
-0255 end
-0256 if isfield(model,'subSystems')
-0257     isNested  = any(cellfun(@(x) iscell(x), model.subSystems));
-0258     isCellStr = any(cellfun(@(x) ischar(x), model.subSystems));
-0259     if ~xor(isNested,isCellStr)
-0260         EM='The "subSystems" field must be a cell array of chars, *or* a cell array of cell arrays of chars';
-0261         reportIssue('error',EM);
-0262     end
-0263 end
-0264 if isfield(model,'eccodes')
-0265     if ~iscellstr(model.eccodes)
-0266         EM='The "eccodes" field must be a cell array of strings';
-0267         reportIssue('error',EM);
-0268     end
-0269 end
-0270 if isfield(model,'unconstrained')
-0271     if ~isnumeric(model.unconstrained)
-0272         EM='The "unconstrained" field must be of type "double"';
-0273         reportIssue('error',EM);
-0274     end
-0275 end
-0276 if isfield(model,'rxnNotes')
-0277     if ~iscellstr(model.rxnNotes)
-0278         EM='The "rxnNotes" field must be a cell array of strings';
-0279         reportIssue('error',EM);
-0280     end
-0281 end
-0282 if isfield(model,'rxnReferences')
-0283     if ~iscellstr(model.rxnReferences)
-0284         EM='The "rxnReferences" field must be a cell array of strings';
-0285         reportIssue('error',EM);
-0286     end
-0287 end
-0288 if isfield(model,'rxnConfidenceScores')
-0289     if ~isnumeric(model.rxnConfidenceScores)
-0290         EM='The "rxnConfidenceScores" field must be a double';
-0291         reportIssue('error',EM);
-0292     end
-0293 end
-0294 if isfield(model,'rxnDeltaG')
-0295     if ~isnumeric(model.rxnDeltaG)
-0296         EM='The "rxnDeltaG" field must be a double';
-0297         reportIssue('error',EM);
-0298     end
-0299 end
-0300 
-0301 %Empty strings
-0302 if isempty(model.id)
-0303     EM='The "id" field cannot be empty';
-0304     reportIssue('error',EM);
-0305 end
-0306 if any(cellfun(@isempty,model.rxns))
-0307     EM='The model contains empty reaction IDs';
-0308     reportIssue('error',EM);
-0309 end
-0310 if any(cellfun(@isempty,model.mets))
-0311     EM='The model contains empty metabolite IDs';
-0312     reportIssue('error',EM);
-0313 end
-0314 if any(cellfun(@isempty,model.comps))
-0315     EM='The model contains empty compartment IDs';
-0316     reportIssue('error',EM);
-0317 end
-0318 if isfield(model,'metNames')
-0319     EM='The following metabolites have empty names:';
-0320     reportIssue('error',EM,model.mets(cellfun(@isempty,model.metNames)));
-0321 end
-0322 
-0323 if isfield(model,'genes')
-0324     if any(cellfun(@isempty,model.genes))
-0325         EM='The model contains empty gene IDs';
-0326         reportIssue('error',EM);
-0327     end
-0328 end
-0329 
-0330 %Validate format of ids
-0331 fields      = {'rxns';'mets';'comps';'genes'};
-0332 fieldNames  = {'reaction';'metabolite';'compartment';'gene'};
-0333 fieldPrefix = {'R_';'M_';'C_';'G_'};
-0334 for i=1:numel(fields)
-0335     try
-0336         numIDs = ~startsWith(model.(fields{i}),regexpPattern('^[a-zA-Z_]'));
-0337     catch
-0338         numIDs = [];
-0339     end
-0340     if any(numIDs)
-0341         EM = ['The following ' fieldNames{i} ' identifiers do not start '...
-0342             'with a letter or _ (conflicting with SBML specifications). '...
-0343             'This does not impact RAVEN functionality, but be aware that '...
-0344             'exportModel will automatically add ' fieldPrefix{i} ...
-0345             ' prefixes to all ' fieldNames{i} ' identifiers:'];
-0346         reportIssue('warning',EM,{model.(fields{i}){numIDs}});
-0347     end
-0348 end
-0349 
-0350 %Duplicates
-0351 EM='The following reaction IDs are duplicates:';
-0352 reportIssue('error',EM,model.rxns(duplicates(model.rxns)));
-0353 EM='The following metabolite IDs are duplicates:';
-0354 reportIssue('error',EM,model.mets(duplicates(model.mets)));
-0355 EM='The following compartment IDs are duplicates:';
-0356 reportIssue('error',EM,model.comps(duplicates(model.comps)));
-0357 if isfield(model,'genes')
-0358     EM='The following genes are duplicates:';
-0359     reportIssue('error',EM,model.genes(duplicates(model.genes)));
-0360 end
-0361 if isfield(model,'metNames')
-0362     metInComp=strcat(model.metNames,'[',model.comps(model.metComps),']');
-0363     EM='The following metabolites already exist in the same compartment:';
-0364     reportIssue('error',EM,metInComp(duplicates(metInComp)));
-0365 end
-0366 
-0367 %Elements never used (always reported as warnings)
-0368 EM='The following reactions are empty (no involved metabolites):';
-0369 reportIssue('warning',EM,model.rxns(~any(model.S,1)));
-0370 EM='The following metabolites are never used in a reaction:';
-0371 reportIssue('warning',EM,model.mets(~any(model.S,2)));
-0372 if isfield(model,'genes')
-0373     EM='The following genes are not associated to a reaction:';
-0374     reportIssue('warning',EM,model.genes(~any(model.rxnGeneMat,1)));
-0375 end
-0376 I=true(numel(model.comps),1);
-0377 I(model.metComps)=false;
-0378 EM='The following compartments contain no metabolites:';
-0379 reportIssue('warning',EM,model.comps(I));
-0380 
-0381 %Contradicting bounds
-0382 EM='The following reactions have contradicting bounds (lower bound is higher than upper bound):';
-0383 reportIssue('error',EM,model.rxns(model.lb>model.ub));
-0384 EM='The following reactions have lower and upper bounds that indicate reversibility, but are indicated as irreversible in model.rev:';
-0385 reportIssue('warning',EM,model.rxns(model.lb < 0 & model.ub > 0 & model.rev==0));
-0386 
-0387 %Multiple or no objective functions not allowed in SBML L3V1 FBCv2
-0388 if numel(find(model.c))>1
-0389     EM='Multiple objective functions found. This might be intended, but results in FBCv2 non-compliant SBML file when exported';
-0390     reportIssue('warning',EM,model.rxns(find(model.c)));
-0391 elseif ~any(model.c)
-0392     EM='No objective function found. This might be intended, but results in FBCv2 non-compliant SBML file when exported';
-0393     reportIssue('warning',EM);
-0394 end
-0395 
-0396 %Mapping of compartments
-0397 if isfield(model,'compOutside')
-0398     EM='The following compartments are in "compOutside" but not in "comps":';
-0399     reportIssue('error',EM,setdiff(model.compOutside,[{''};model.comps]));
-0400 end
-0401 
-0402 %Met names which start with number
-0403 if isfield(model,'metNames')
-0404     I=false(numel(model.metNames),1);
-0405     for i=1:numel(model.metNames)
-0406         index=strfind(model.metNames{i},' ');
-0407         if any(index)
-0408             if ~isnan(str2double(model.metNames{i}(1:index(1)-1)))
-0409                 I(i)=true;
-0410             end
-0411         end
-0412     end
-0413     EM='The following metabolite names begin with a number directly followed by space, which could potentially cause problems:';
-0414     reportIssue('warning',EM,model.metNames(I));
-0415 end
-0416 
-0417 %Non-parseable composition
-0418 if isfield(model,'metFormulas')
-0419     [~, ~, exitFlag]=parseFormulas(model.metFormulas,true,false);
-0420     EM='The composition for the following metabolites could not be parsed:';
-0421     reportIssue('warning',EM,model.mets(exitFlag==-1));
-0422 end
-0423 
-0424 %Check if there are metabolites with different names but the same MIRIAM
-0425 %codes
-0426 if isfield(model,'metMiriams')
-0427     miriams=containers.Map();
-0428     for i=1:numel(model.mets)
-0429         if ~isempty(model.metMiriams{i})
-0430             %Loop through and add for each miriam
-0431             for j=1:numel(model.metMiriams{i}.name)
-0432                 %Get existing metabolite indexes
-0433                 current=strcat(model.metMiriams{i}.name{j},'/',model.metMiriams{i}.value{j});
-0434                 if isKey(miriams,current)
-0435                     existing=miriams(current);
-0436                 else
-0437                     existing=[];
-0438                 end
-0439                 miriams(current)=[existing;i];
-0440             end
-0441         end
-0442     end
-0443 
-0444     %Get all keys
-0445     allMiriams=keys(miriams);
-0446 
-0447     hasMultiple=false(numel(allMiriams),1);
-0448     for i=1:numel(allMiriams)
-0449         if numel(miriams(allMiriams{i}))>1
-0450             %Check if they all have the same name
-0451             if numel(unique(model.metNames(miriams(allMiriams{i}))))>1
-0452                 % regexp returns [] (not a false-like 0) when there is no
-0453                 % match, and ~[] is [] too, so the negation itself is
-0454                 % never true: use isempty to actually test for "no match"
-0455                 if isempty(regexp(allMiriams{i},'^sbo\/SBO:','once')) % SBO terms are expected to be multiple
-0456                     hasMultiple(i)=true;
-0457                 end
-0458             end
-0459         end
-0460     end
-0461 
-0462     %Print output
-0463     EM='The following MIRIAM strings are associated to more than one unique metabolite name:';
-0464     reportIssue('warning',EM,allMiriams(hasMultiple));
-0465 end
-0466 
-0467 %Check if there are metabolites with different names but the same InChI
-0468 %codes
-0469 if isfield(model,'inchis')
-0470     inchis=containers.Map();
-0471     for i=1:numel(model.mets)
-0472         if ~isempty(model.inchis{i})
-0473             %Get existing metabolite indexes
-0474             if isKey(inchis,model.inchis{i})
-0475                 existing=inchis(model.inchis{i});
-0476             else
-0477                 existing=[];
-0478             end
-0479             inchis(model.inchis{i})=[existing;i];
-0480         end
-0481     end
-0482 
-0483     %Get all keys
-0484     allInchis=keys(inchis);
-0485 
-0486     hasMultiple=false(numel(allInchis),1);
-0487     for i=1:numel(allInchis)
-0488         if numel(inchis(allInchis{i}))>1
-0489             %Check if they all have the same name
-0490             if numel(unique(model.metNames(inchis(allInchis{i}))))>1
-0491                 hasMultiple(i)=true;
-0492             end
-0493         end
-0494     end
-0495 
-0496     %Print output
-0497     EM='The following InChI strings are associated to more than one unique metabolite name:';
-0498     reportIssue('warning',EM,allInchis(hasMultiple));
-0499 end
-0500 
-0501 % %Check if there are metabolites with different names but the same SMILES
-0502 % if isfield(model,"metSmiles")
-0503 %     metSmiles=containers.Map();
-0504 %     for i=1:numel(model.mets)
-0505 %         if ~isempty(model.metSmiles{i})
-0506 %             %Get existing metabolite indexes
-0507 %             if isKey(metSmiles,model.metSmiles{i})
-0508 %                 existing=metSmiles(model.metSmiles{i});
-0509 %             else
-0510 %                 existing=[];
-0511 %             end
-0512 %             metSmiles(model.metSmiles{i})=[existing;i];
-0513 %         end
-0514 %     end
-0515 %
-0516 %     %Get all keys
-0517 %     allmetSmiles=keys(metSmiles);
-0518 %
-0519 %     hasMultiple=false(numel(metSmiles),1);
-0520 %     for i=1:numel(metSmiles)
-0521 %         if numel(metSmiles(metSmiles{i}))>1
-0522 %             %Check if they all have the same name
-0523 %             if numel(unique(model.metNames(metSmiles(allmetSmiles{i}))))>1
-0524 %                 hasMultiple(i)=true;
-0525 %             end
-0526 %         end
-0527 %     end
-0528 %
-0529 %     %Print output
-0530 %     EM="The following metSmiles strings are associated to more than one unique metabolite name:";
-0531 %     reportIssue("warning",EM,allmetSmiles(hasMultiple));
-0532 % end
-0533 end
-0534 
-0535 function I=duplicates(strings)
-0536 I=false(numel(strings),1);
-0537 [J, K]=unique(strings);
-0538 if numel(J)~=numel(strings)
-0539     L=1:numel(strings);
-0540     L(K)=[];
-0541     I(L)=true;
-0542 end
-0543 end
-0544 
-0545 function cat=issueCategory(msg)
-0546 if contains(msg,'missing')
-0547     cat='missing_field';
-0548 elseif contains(msg,'must be') || contains(msg,'should')
-0549     cat='wrong_type';
-0550 elseif contains(msg,'empty')
-0551     cat='empty_id';
-0552 elseif contains(msg,'duplicate') || contains(msg,'already exist in the same')
-0553     cat='duplicate';
-0554 elseif contains(msg,'bound')
-0555     cat='invalid_bounds';
-0556 elseif contains(msg,'SBML') || contains(msg,'identifier') || contains(msg,'do not start with')
-0557     cat='invalid_id';
-0558 elseif contains(msg,'never used') || contains(msg,'not associated') || contains(msg,'contain no metabolites') || contains(msg,'are empty (no')
-0559     cat='unused';
-0560 elseif contains(msg,'objective')
-0561     cat='objective';
-0562 elseif contains(msg,'grRule') || contains(msg,'start or end with') || contains(msg,'"genes"')
-0563     cat='gpr';
-0564 elseif contains(msg,'could not be parsed') || contains(msg,'composition')
-0565     cat='invalid_formula';
-0566 elseif contains(msg,'MIRIAM') || contains(msg,'InChI') || contains(msg,'SMILES')
-0567     cat='cross_reference';
-0568 else
-0569     cat='other';
-0570 end
-0571 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/queries/constructS.html b/doc/queries/constructS.html deleted file mode 100644 index fc5846b1..00000000 --- a/doc/queries/constructS.html +++ /dev/null @@ -1,293 +0,0 @@ - - - - Description of constructS - - - - - - - - - -
Home > queries > constructS.m
- - - -

constructS -

- -

PURPOSE ^

-
constructS Construct a stoichiometric matrix from a cell array of equations.
- -

SYNOPSIS ^

-
function [S, mets, badRxns, reversible]=constructS(equations,varargin)
- -

DESCRIPTION ^

-
 constructS  Construct a stoichiometric matrix from a cell array of equations.
-
- Parameters
- ----------
- equations : cell
-     cell array of equations on the form "A + 2 B <=> 3 C", where <=>
-     indicates reversible and => irreversible reactions.
-
- Name-Value Arguments
- --------------------
- mets : cell
-     cell array of metabolites. All metabolites in the equations must be
-     present in the list (default generated from the equations).
- rxns : cell
-     cell array of reaction ids. This is only used for printing reaction ids
-     instead of equations in warnings/errors (default []).
-
- Returns
- -------
- S : double
-     the resulting stoichiometric matrix.
- mets : cell
-     cell array with metabolites that corresponds to the order in the S
-     matrix.
- badRxns : logical
-     boolean vector with the reactions that have one or more metabolites as
-     both substrate and product. An example would be the phosphotransferase
-     ATP + ADP <=> ADP + ATP. In the stoichiometric matrix this equals to an
-     empty reaction which can be problematic.
- reversible : logical
-     boolean vector with true if the equation is reversible.
-
- Examples
- --------
-     [S, mets, badRxns, reversible] = constructS(equations, mets);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • parseRxnEqu parseRxnEqu Get all metabolite names from a cell array of equations.
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [S, mets, badRxns, reversible]=constructS(equations,varargin)
-0002 % constructS  Construct a stoichiometric matrix from a cell array of equations.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % equations : cell
-0007 %     cell array of equations on the form "A + 2 B <=> 3 C", where <=>
-0008 %     indicates reversible and => irreversible reactions.
-0009 %
-0010 % Name-Value Arguments
-0011 % --------------------
-0012 % mets : cell
-0013 %     cell array of metabolites. All metabolites in the equations must be
-0014 %     present in the list (default generated from the equations).
-0015 % rxns : cell
-0016 %     cell array of reaction ids. This is only used for printing reaction ids
-0017 %     instead of equations in warnings/errors (default []).
-0018 %
-0019 % Returns
-0020 % -------
-0021 % S : double
-0022 %     the resulting stoichiometric matrix.
-0023 % mets : cell
-0024 %     cell array with metabolites that corresponds to the order in the S
-0025 %     matrix.
-0026 % badRxns : logical
-0027 %     boolean vector with the reactions that have one or more metabolites as
-0028 %     both substrate and product. An example would be the phosphotransferase
-0029 %     ATP + ADP <=> ADP + ATP. In the stoichiometric matrix this equals to an
-0030 %     empty reaction which can be problematic.
-0031 % reversible : logical
-0032 %     boolean vector with true if the equation is reversible.
-0033 %
-0034 % Examples
-0035 % --------
-0036 %     [S, mets, badRxns, reversible] = constructS(equations, mets);
-0037 
-0038 equations=convertCharArray(equations);
-0039 p=parseRAVENargs(varargin, {'mets',[]; 'rxns',[]});
-0040 mets=p.mets;
-0041 rxns=p.rxns;
-0042 if ~isempty(mets)
-0043     mets=convertCharArray(mets);
-0044 end
-0045 if ~isempty(rxns)
-0046     rxns=convertCharArray(rxns);
-0047 end
-0048 
-0049 badRxns=false(numel(equations),1);
-0050 
-0051 %Check that no equations are too short to have reversibility data
-0052 I=cellfun(@numel,equations);
-0053 I=find(I<4,1);
-0054 if any(I)
-0055     if isempty(rxns)
-0056         EM=['The following equation does not have reversibility data: ' equations{I} ];
-0057         error('RAVEN:badInput', '%s', EM);
-0058     else
-0059         EM=['The reaction ' rxns{I} ' does not have reversibility data'];
-0060         error('RAVEN:badInput', '%s', EM);
-0061     end
-0062 end
-0063 
-0064 %Makes life a little easier
-0065 equations=strtrim(equations);
-0066 equations=fixEquations(equations);
-0067 
-0068 %A supplied list is authoritative, so an entry in it can be matched in full
-0069 %before the leading number of "2 oxoglutarate" is read as a coefficient. A
-0070 %list derived from the equations cannot disambiguate that.
-0071 metsSupplied=~isempty(mets);
-0072 if isempty(mets)
-0073     mets=parseRxnEqu(equations);
-0074 end
-0075 
-0076 %Get which reactions are reversible
-0077 reversible=cellfun(@any,strfind(equations,' <=> '));
-0078 
-0079 %Make them all reversible. This is not all that neat, but nevermind
-0080 equations=strrep(equations,' => ',' <=> ');
-0081 
-0082 %Replace the the plus signs with some weird character that will be used for
-0083 %parsing
-0084 equations=strrep(equations,' + ', '€');
-0085 
-0086 %Keep track of coefficients to be added to S-matrix
-0087 metsToS = cell(100000,1);
-0088 rxnsToS = zeros(100000,1);
-0089 coefToS = zeros(100000,1);
-0090 newEntry = 1;
-0091 %Loop through the equations and add the info to the S matrix
-0092 for i=1:numel(equations)
-0093     %Start by finding the position of the (=> or <=>)
-0094     arrowIndex=strfind(equations{i},' <=> ');
-0095     
-0096     if numel(arrowIndex)~=1
-0097         if isempty(rxns)
-0098             EM=['The following equation does not have reversibility data: ' equations{i} ];
-0099             error('RAVEN:badInput', '%s', EM);
-0100         else
-0101             EM=['The reaction ' rxns{i} ' does not have reversibility data'];
-0102             error('RAVEN:badInput', '%s', EM);
-0103         end
-0104     end
-0105     
-0106     reactants=regexp(equations{i}(1:arrowIndex-1),'€','split');
-0107     products=regexp(equations{i}(arrowIndex+5:end),'€','split');
-0108     
-0109     %If the splitting character is at the end (if exchange rxns), then an
-0110     %empty string will exist together with the real ones. Remove it
-0111     reactants(cellfun(@isempty,reactants))=[];
-0112     products(cellfun(@isempty,products))=[];
-0113     
-0114     %A vector where an element is -1 is the corresponding metabolite is a
-0115     %reactant and 1 if it is a product
-0116     multiplyWith=[ones(numel(reactants),1)*-1; ones(numel(products),1)];
-0117     
-0118     metabolites=strtrim([reactants products]);
-0119     
-0120     %Now loop through the reactants and see if the metabolite has a
-0121     %coefficient (it will look as "number name")
-0122     for j=1:numel(metabolites)
-0123         space=strfind(metabolites{j},' ');
-0124         
-0125         if isempty(space)
-0126             %No coefficient
-0127             coeff=1;
-0128             name=metabolites{j};
-0129         elseif metsSupplied && any(strcmp(strtrim(metabolites{j}),mets))
-0130             %The whole entry is a known metabolite, so its leading number is
-0131             %part of its name ("2 oxoglutarate") rather than a coefficient
-0132             coeff=1;
-0133             name=strtrim(metabolites{j});
-0134         else
-0135             coeff=str2double(metabolites{j}(1:space(1)));
-0136             
-0137             %If it was not a coefficiant
-0138             if isnan(coeff)
-0139                 coeff=1;
-0140                 name=strtrim(metabolites{j});
-0141             else
-0142                 name=strtrim(metabolites{j}(space(1)+1:end));
-0143             end
-0144         end
-0145         
-0146         %Find the name in the mets list [a b]=ismember(name,mets);
-0147         metsToS{newEntry}=name;
-0148         rxnsToS(newEntry)=i;
-0149         coefToS(newEntry)=coeff*multiplyWith(j);
-0150         newEntry=newEntry+1;
-0151     end
-0152 end
-0153 %Remove unused fields
-0154 metsToS(newEntry:end)=[];
-0155 rxnsToS(newEntry:end)=[];
-0156 coefToS(newEntry:end)=[];
-0157 
-0158 %Match to mets array
-0159 [metsPresent,metsLoc]=ismember(metsToS,mets);
-0160 
-0161 %Find badRxns
-0162 [~,I]=unique([rxnsToS,metsLoc],'rows','stable');
-0163 x=1:length(rxnsToS);
-0164 x(I)=[];
-0165 x=unique(rxnsToS(x));
-0166 badRxns(x)=true;
-0167 
-0168 if any(~metsPresent)
-0169     if isempty(rxns)
-0170         EM=['Could not find the following metabolites in the metabolite list: ',...
-0171             strjoin(unique(metsToS(~metsPresent)),', ')];
-0172         error('RAVEN:badInput','%s',EM)
-0173     else
-0174         %Escaped: metabolite/reaction names are arbitrary model text and
-0175         %may contain "%", which the sprintf below (needed to interpret
-0176         %the template's own "\n") would otherwise misread as a format
-0177         %directive and truncate the rest of the message.
-0178         esc=@(c) strrep(c,'%','%%');
-0179         missingMet = find(~metsPresent);
-0180         missingMet = strcat(esc(metsToS(missingMet)),' (reaction:',esc(rxns(rxnsToS(missingMet))),')\n');
-0181         missingMet = strjoin(missingMet,'');
-0182         EM=sprintf(['Could not find the following metabolites (reaction indicated) in the metabolite list: \n' ...
-0183             missingMet]);
-0184         error('RAVEN:badInput','%s',EM)
-0185     end
-0186 end
-0187 %sparse() adds up repeated (metabolite,reaction) entries, which is what a
-0188 %metabolite occurring more than once in one equation means: "2 H2O + H2O =>"
-0189 %is a coefficient of 3, and a metabolite on both sides cancels to an empty
-0190 %column, as badRxns above reports. Assigning into a preallocated matrix
-0191 %instead kept only the last occurrence, and materialised the whole matrix
-0192 %densely on the way.
-0193 S=sparse(metsLoc,rxnsToS,coefToS,numel(mets),numel(equations));
-0194 end
-0195 
-0196 function equ=fixEquations(equ)
-0197 %If the equation starts with "=>" or "<=>" then add a space again. This is
-0198 %an alternative way to represent uptake reactions. The opposite way for
-0199 %producing reactions
-0200 equ=equ(:);
-0201 for i=1:numel(equ)
-0202     if strcmp(equ{i}(1:2),'=>') || strcmp(equ{i}(1:3),'<=>')
-0203         equ{i}=[' ' equ{i}];
-0204     else
-0205         if strcmp(equ{i}(end-1:end),'=>') || strcmp(equ{i}(end-2:end),'<=>')
-0206             equ{i}=[equ{i} ' '];
-0207         end
-0208     end
-0209 end
-0210 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/queries/getElementalBalance.html b/doc/queries/getElementalBalance.html deleted file mode 100644 index 3b57ae20..00000000 --- a/doc/queries/getElementalBalance.html +++ /dev/null @@ -1,281 +0,0 @@ - - - - Description of getElementalBalance - - - - - - - - - -
Home > queries > getElementalBalance.m
- - - -

getElementalBalance -

- -

PURPOSE ^

-
getElementalBalance Check whether the reactions of a model are balanced.
- -

SYNOPSIS ^

-
function balanceStructure=getElementalBalance(model,varargin)
- -

DESCRIPTION ^

-
 getElementalBalance  Check whether the reactions of a model are balanced.
-
- Checks a model to see if the reactions are elementally balanced.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- rxns : cell or logical or double
-     either a cell array of reaction IDs, a logical vector with the same
-     number of elements as reactions in the model, or a vector of
-     indexes. Only these reactions will be checked (default model.rxns).
- printUnbalanced : logical
-     print warnings about the reactions that were unbalanced
-     (default false).
- printUnparsable : logical
-     print warnings about the reactions that cannot be parsed
-     (default false).
-
- Returns
- -------
- balanceStructure : struct
-     elemental balance structure with fields:
-
-     - balanceStatus : 1 if the reaction is balanced, 0 if it is
-       unbalanced, -1 if it could not be balanced due to missing
-       information, -2 if it could not be balanced due to an error.
-       Elemental only; charge is reported separately below
-     - chargeStatus : 1 if the reaction is charge balanced, 0 if it is
-       not, -1 if any participating metabolite has no charge (or the
-       model has no metCharges field), in which case the charge balance
-       is unknown rather than zero
-     - chargeResidual : the sum of charges over the reaction, NaN where
-       chargeStatus is -1
-     - elements : struct with fields abbrevs (cell array with
-       abbreviations for all used elements) and names (cell array with
-       the names for all used elements)
-     - leftComp : MxN matrix with the sum of coefficients for each of the
-       elements (N) for the left side of the reactions (M)
-     - rightComp : the corresponding matrix for the right side
-
- Examples
- --------
-     balanceStructure = getElementalBalance(model, rxns, printUnbalanced, printUnparsable);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • getIndexes getIndexes Retrieve the indexes for a list of reactions or metabolites.
  • parseFormulas parseFormulas Get the elemental composition from formulas.
-This function is called by: -
    -
  • printFluxes printFluxes Print reactions and fluxes to the screen or to a file.
  • printModelStats printModelStats Print some statistics about a model to the screen.
- - - - -

SOURCE CODE ^

-
0001 function balanceStructure=getElementalBalance(model,varargin)
-0002 % getElementalBalance  Check whether the reactions of a model are balanced.
-0003 %
-0004 % Checks a model to see if the reactions are elementally balanced.
-0005 %
-0006 % Parameters
-0007 % ----------
-0008 % model : struct
-0009 %     a model structure.
-0010 %
-0011 % Name-Value Arguments
-0012 % --------------------
-0013 % rxns : cell or logical or double
-0014 %     either a cell array of reaction IDs, a logical vector with the same
-0015 %     number of elements as reactions in the model, or a vector of
-0016 %     indexes. Only these reactions will be checked (default model.rxns).
-0017 % printUnbalanced : logical
-0018 %     print warnings about the reactions that were unbalanced
-0019 %     (default false).
-0020 % printUnparsable : logical
-0021 %     print warnings about the reactions that cannot be parsed
-0022 %     (default false).
-0023 %
-0024 % Returns
-0025 % -------
-0026 % balanceStructure : struct
-0027 %     elemental balance structure with fields:
-0028 %
-0029 %     - balanceStatus : 1 if the reaction is balanced, 0 if it is
-0030 %       unbalanced, -1 if it could not be balanced due to missing
-0031 %       information, -2 if it could not be balanced due to an error.
-0032 %       Elemental only; charge is reported separately below
-0033 %     - chargeStatus : 1 if the reaction is charge balanced, 0 if it is
-0034 %       not, -1 if any participating metabolite has no charge (or the
-0035 %       model has no metCharges field), in which case the charge balance
-0036 %       is unknown rather than zero
-0037 %     - chargeResidual : the sum of charges over the reaction, NaN where
-0038 %       chargeStatus is -1
-0039 %     - elements : struct with fields abbrevs (cell array with
-0040 %       abbreviations for all used elements) and names (cell array with
-0041 %       the names for all used elements)
-0042 %     - leftComp : MxN matrix with the sum of coefficients for each of the
-0043 %       elements (N) for the left side of the reactions (M)
-0044 %     - rightComp : the corresponding matrix for the right side
-0045 %
-0046 % Examples
-0047 % --------
-0048 %     balanceStructure = getElementalBalance(model, rxns, printUnbalanced, printUnparsable);
-0049 
-0050 p=parseRAVENargs(varargin, {'rxns',[]; 'printUnbalanced',false; 'printUnparsable',false});
-0051 rxns=p.rxns;
-0052 if ~isempty(rxns) && ~islogical(rxns) && ~isnumeric(rxns)
-0053     rxns=convertCharArray(rxns);
-0054 end
-0055 printUnbalanced=p.printUnbalanced;
-0056 printUnparsable=p.printUnparsable;
-0057 
-0058 if ~isempty(rxns)
-0059     indexes=~getIndexes(model,rxns,'rxns',true);
-0060     model=removeReactions(model,indexes,true);
-0061 end
-0062 
-0063 balanceStructure.balanceStatus=nan(numel(model.rxns),1);
-0064 
-0065 %Get the formulas
-0066 if isfield(model,'metFormulas')
-0067     [balanceStructure.elements, useMat, exitFlag]=parseFormulas(model.metFormulas, true);
-0068 else
-0069     if isfield(model,'inchis')
-0070         [balanceStructure.elements, useMat, exitFlag]=parseFormulas(model.inchis, true,true);
-0071     else
-0072         EM='The model must contain either the "metFormulas" or the "inchis" field in order to test for elemental balancing';
-0073         error('RAVEN:badInput', '%s', EM);
-0074     end
-0075 end
-0076 
-0077 balanceStructure.leftComp=zeros(numel(model.rxns),numel(balanceStructure.elements.names));
-0078 balanceStructure.rightComp=balanceStructure.leftComp;
-0079 
-0080 %Look at the right and left side of the reactions separately
-0081 S{1}=model.S;
-0082 S{2}=model.S;
-0083 S{1}(S{1}>0)=0; %Left side
-0084 S{2}(S{2}<0)=0; %Right side
-0085 S{1}=abs(S{1}); %Both should have positive values
-0086 
-0087 %First do it for left side and then for right
-0088 for i=1:2
-0089     for j=1:numel(model.rxns)
-0090         %Get the balance status of the involved mets
-0091         I=exitFlag(S{i}(:,j)~=0);
-0092         if any(I==-1)
-0093             balanceStructure.balanceStatus(j)=-2;
-0094         end
-0095         if any(I==0)
-0096             %Do not change a more serious error to a less serious one
-0097             balanceStructure.balanceStatus(j)=min(-1,balanceStructure.balanceStatus(j));
-0098         end
-0099     end
-0100 end
-0101 %Elemental composition per reaction, vectorized as a single matrix
-0102 %product per side instead of a per-reaction, per-element loop (which is
-0103 %quadratic in a genome-scale model's reaction and element counts).
-0104 balanceStructure.leftComp=full(S{1}'*useMat);
-0105 balanceStructure.rightComp=full(S{2}'*useMat);
-0106 
-0107 %Now compare the left and right sides to find which are unbalanced. This is
-0108 %done even if the reaction as a whole could not be balanced
-0109 total=abs(balanceStructure.rightComp-balanceStructure.leftComp)>10^-8; %To deal with roundoff error
-0110 
-0111 %Get which reactions are unbalanced. Do not change an error to just
-0112 %unbalanced
-0113 balanceStructure.balanceStatus(any(total,2))=min(balanceStructure.balanceStatus(any(total,2)),0);
-0114 
-0115 %Reactions with no metabolites are not balanced (empty S column skips both loops above)
-0116 emptyRxns = full(sum(model.S ~= 0, 1).' == 0);
-0117 balanceStructure.balanceStatus(emptyRxns) = min(-1, balanceStructure.balanceStatus(emptyRxns));
-0118 
-0119 %The remaining ones are all balanced
-0120 balanceStructure.balanceStatus(isnan(balanceStructure.balanceStatus))=1;
-0121 
-0122 %Charge balance. This is reported separately from balanceStatus, which
-0123 %callers such as removeBadRxns and printModelStats read as a purely
-0124 %elemental verdict.
-0125 balanceStructure.chargeStatus=zeros(numel(model.rxns),1);
-0126 balanceStructure.chargeResidual=nan(numel(model.rxns),1);
-0127 if ~isfield(model,'metCharges')
-0128     balanceStructure.chargeStatus(:)=-1;
-0129 else
-0130     for j=1:numel(model.rxns)
-0131         %Only the participating metabolites may be touched: S is sparse and
-0132         %0*NaN is NaN, so a single unset charge anywhere in the model would
-0133         %otherwise poison every reaction. Summing with 'omitnan' instead
-0134         %would be worse, silently reporting an unknown residual as 0.
-0135         idx=find(model.S(:,j));
-0136         if isempty(idx) || any(isnan(model.metCharges(idx)))
-0137             balanceStructure.chargeStatus(j)=-1;
-0138         else
-0139             balanceStructure.chargeResidual(j)=sum(full(model.S(idx,j)).*model.metCharges(idx));
-0140             if abs(balanceStructure.chargeResidual(j))>10^-8 %Roundoff error
-0141                 balanceStructure.chargeStatus(j)=0;
-0142             else
-0143                 balanceStructure.chargeStatus(j)=1;
-0144             end
-0145         end
-0146     end
-0147 end
-0148 
-0149 %Print warnings
-0150 toPrint=[];
-0151 if printUnbalanced==true
-0152     toPrint=[toPrint;find(balanceStructure.balanceStatus==0)];
-0153 end
-0154 if printUnparsable==true
-0155     toPrint=[toPrint;find(balanceStructure.balanceStatus<0)];
-0156 end
-0157 
-0158 toPrint=sort(toPrint);
-0159 for i=1:numel(toPrint)
-0160     if balanceStructure.balanceStatus(toPrint(i))<0
-0161         if balanceStructure.balanceStatus(toPrint(i))==-1
-0162             EM=['The reaction ' model.rxns{toPrint(i)} ' could not be balanced due to missing information'];
-0163             warning('RAVEN:warning', '%s', EM);
-0164         else
-0165             EM=['The reaction ' model.rxns{toPrint(i)} ' could not be balanced due to a parsing error'];
-0166             warning('RAVEN:warning', '%s', EM);
-0167         end
-0168     else
-0169         %Find the compounds that are not balanced
-0170         notBalanced=find(total(toPrint(i),:));
-0171         for j=1:numel(notBalanced)
-0172             EM=['The reaction ' model.rxns{toPrint(i)} ' is not balanced with respect to ' balanceStructure.elements.names{notBalanced(j)}];
-0173             warning('RAVEN:warning', '%s', EM);
-0174         end
-0175     end
-0176 end
-0177 
-0178 % Re-order the structure entries so they are consistent with the ordering of
-0179 % the input reaction indexes
-0180 if ~isempty(rxns)
-0181     rxns = getIndexes(model,rxns,'rxns');
-0182     [~,i] = sort(rxns);
-0183     balanceStructure.balanceStatus(i) = balanceStructure.balanceStatus;
-0184     balanceStructure.chargeStatus(i) = balanceStructure.chargeStatus;
-0185     balanceStructure.chargeResidual(i) = balanceStructure.chargeResidual;
-0186     balanceStructure.leftComp(i,:) = balanceStructure.leftComp;
-0187     balanceStructure.rightComp(i,:) = balanceStructure.rightComp;
-0188 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/queries/getExchangeRxns.html b/doc/queries/getExchangeRxns.html deleted file mode 100644 index e8b8859a..00000000 --- a/doc/queries/getExchangeRxns.html +++ /dev/null @@ -1,210 +0,0 @@ - - - - Description of getExchangeRxns - - - - - - - - - -
Home > queries > getExchangeRxns.m
- - - -

getExchangeRxns -

- -

PURPOSE ^

-
getExchangeRxns Retrieve the exchange reactions from a model.
- -

SYNOPSIS ^

-
function [exchangeRxns, exchangeRxnsIndexes, exchangedMets]=getExchangeRxns(model,varargin)
- -

DESCRIPTION ^

-
 getExchangeRxns  Retrieve the exchange reactions from a model.
-
- Exchange reactions are identified by having either no substrates or no
- products.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- reactionType : char
-     which exchange reactions should be returned (default 'all'):
-
-     - 'all' : all reactions, irrespective of reaction bounds
-     - 'uptake' : reactions with bounds that imply that only uptake is
-       allowed. Reaction direction, upper and lower bounds are all
-       considered
-     - 'excrete' : reactions with bounds that imply that only excretion is
-       allowed. Reaction direction, upper and lower bounds are all
-       considered
-     - 'reverse' : reactions with non-zero upper and lower bounds that
-       imply that both uptake and excretion are allowed
-     - 'blocked' : reactions that have zero upper and lower bounds, not
-       allowing any flux
-     - 'in' : reactions where the boundary metabolite is the substrate of
-       the reaction; a positive flux value would imply uptake, but
-       reaction bounds are not considered
-     - 'out' : reactions where the boundary metabolite is the product of
-       the reaction; a negative flux value would imply uptake, but
-       reaction bounds are not considered
-
- Returns
- -------
- exchangeRxns : cell
-     cell array with the IDs of the exchange reactions.
- exchangeRxnsIndexes : double
-     vector with the indexes of the exchange reactions.
- exchangedMets : double
-     vector with the metabolite index for each exchange reaction (one
-     metabolite per exchange reaction). Only computed when requested.
-
- Notes
- -----
- The union of 'in' and 'out' equals 'all'. Also, the union of 'uptake',
- 'excrete', 'reverse' and 'blocked' equals 'all'.
-
- Examples
- --------
-     [exchangeRxns, exchangeRxnsIndexes] = getExchangeRxns(model, reactionType);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • getTransportRxns getTransportRxns Retrieve the transport reactions from a model.
  • modelSummary modelSummary Print a concise overview of a RAVEN model and its flux state.
  • printFluxes printFluxes Print reactions and fluxes to the screen or to a file.
- - - - -

SOURCE CODE ^

-
0001 function [exchangeRxns, exchangeRxnsIndexes, exchangedMets]=getExchangeRxns(model,varargin)
-0002 % getExchangeRxns  Retrieve the exchange reactions from a model.
-0003 %
-0004 % Exchange reactions are identified by having either no substrates or no
-0005 % products.
-0006 %
-0007 % Parameters
-0008 % ----------
-0009 % model : struct
-0010 %     a model structure.
-0011 %
-0012 % Name-Value Arguments
-0013 % --------------------
-0014 % reactionType : char
-0015 %     which exchange reactions should be returned (default 'all'):
-0016 %
-0017 %     - 'all' : all reactions, irrespective of reaction bounds
-0018 %     - 'uptake' : reactions with bounds that imply that only uptake is
-0019 %       allowed. Reaction direction, upper and lower bounds are all
-0020 %       considered
-0021 %     - 'excrete' : reactions with bounds that imply that only excretion is
-0022 %       allowed. Reaction direction, upper and lower bounds are all
-0023 %       considered
-0024 %     - 'reverse' : reactions with non-zero upper and lower bounds that
-0025 %       imply that both uptake and excretion are allowed
-0026 %     - 'blocked' : reactions that have zero upper and lower bounds, not
-0027 %       allowing any flux
-0028 %     - 'in' : reactions where the boundary metabolite is the substrate of
-0029 %       the reaction; a positive flux value would imply uptake, but
-0030 %       reaction bounds are not considered
-0031 %     - 'out' : reactions where the boundary metabolite is the product of
-0032 %       the reaction; a negative flux value would imply uptake, but
-0033 %       reaction bounds are not considered
-0034 %
-0035 % Returns
-0036 % -------
-0037 % exchangeRxns : cell
-0038 %     cell array with the IDs of the exchange reactions.
-0039 % exchangeRxnsIndexes : double
-0040 %     vector with the indexes of the exchange reactions.
-0041 % exchangedMets : double
-0042 %     vector with the metabolite index for each exchange reaction (one
-0043 %     metabolite per exchange reaction). Only computed when requested.
-0044 %
-0045 % Notes
-0046 % -----
-0047 % The union of 'in' and 'out' equals 'all'. Also, the union of 'uptake',
-0048 % 'excrete', 'reverse' and 'blocked' equals 'all'.
-0049 %
-0050 % Examples
-0051 % --------
-0052 %     [exchangeRxns, exchangeRxnsIndexes] = getExchangeRxns(model, reactionType);
-0053 
-0054 p=parseRAVENargs(varargin, {'reactionType','all'});
-0055 reactionType=char(p.reactionType);
-0056 
-0057 % Find exchange reactions. Both branches must produce a column of reaction
-0058 % indexes: every use below (concatenation, bound lookups, model.rxns
-0059 % indexing) reads them as indexes, not as a mask over the reactions.
-0060 if isfield(model, 'unconstrained') && any(model.unconstrained~=0)
-0061     % With boundary metabolites present, an exchange reaction is one that
-0062     % involves a boundary metabolite. Producing the boundary metabolite is
-0063     % what leaves the reaction without a product once the boundary
-0064     % metabolite is removed, which is the definition the branch below uses.
-0065     boundaryMets = model.unconstrained~=0;
-0066     [~, I]=find(model.S(boundaryMets,:)>0);
-0067     hasNoProd = unique(I(:));
-0068     [~, I]=find(model.S(boundaryMets,:)<0);
-0069     hasNoSubs = unique(I(:));
-0070 else
-0071     hasNoProd = transpose(find(sum(model.S>0)==0));
-0072     hasNoSubs = transpose(find(sum(model.S<0)==0));
-0073 end
-0074 allExch   = [hasNoProd; hasNoSubs];
-0075 
-0076 switch reactionType
-0077     case {'both','all'} % For legacy reasons, 'both' is also allowed
-0078         exchangeRxnsIndexes = allExch;
-0079     case 'in'
-0080         exchangeRxnsIndexes = hasNoSubs;
-0081     case 'out'
-0082         exchangeRxnsIndexes = hasNoProd;
-0083     case 'blocked'
-0084         exchangeRxnsIndexes = allExch(model.lb(allExch) == 0 & model.ub(allExch) == 0);
-0085     case 'reverse'
-0086         exchangeRxnsIndexes = allExch(model.lb(allExch) < 0 & model.ub(allExch) > 0);
-0087     case 'uptake'
-0088         
-0089         exchangeRxnsIndexes = allExch([(model.lb(hasNoProd) < 0 & model.ub(hasNoProd) <= 0); ...
-0090                               (model.lb(hasNoSubs) >= 0 & model.ub(hasNoSubs) > 0)]);
-0091     case 'excrete'
-0092         exchangeRxnsIndexes = allExch([(model.lb(hasNoProd) >= 0 & model.ub(hasNoProd) > 0); ...
-0093                               (model.lb(hasNoSubs) < 0 & model.ub(hasNoSubs) <= 0)]);
-0094     otherwise
-0095         error('Invalid reactionType specified')
-0096 end
-0097 % unique rather than sort: a reaction that involves two boundary metabolites,
-0098 % and an empty reaction (no substrates and no products), each appear in both
-0099 % halves of allExch and would otherwise be reported twice.
-0100 exchangeRxnsIndexes = unique(exchangeRxnsIndexes);
-0101 exchangeRxns = model.rxns(exchangeRxnsIndexes);
-0102 
-0103 if nargout > 2
-0104     nExch=numel(exchangeRxnsIndexes);
-0105     exchangedMets=zeros(nExch,1);
-0106     for ei=1:nExch
-0107         k=find(model.S(:,exchangeRxnsIndexes(ei)),1);
-0108         if ~isempty(k)
-0109             exchangedMets(ei)=k;
-0110         end
-0111     end
-0112 end
-0113 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/queries/getIndexes.html b/doc/queries/getIndexes.html deleted file mode 100644 index ffb774b0..00000000 --- a/doc/queries/getIndexes.html +++ /dev/null @@ -1,225 +0,0 @@ - - - - Description of getIndexes - - - - - - - - - -
Home > queries > getIndexes.m
- - - -

getIndexes -

- -

PURPOSE ^

-
getIndexes Retrieve the indexes for a list of reactions or metabolites.
- -

SYNOPSIS ^

-
function indexes=getIndexes(model, objects, type, varargin)
- -

DESCRIPTION ^

-
 getIndexes  Retrieve the indexes for a list of reactions or metabolites.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- objects : cell or logical or double
-     either a cell array of IDs, a logical vector with the same number of
-     elements as metabolites in the model, or a vector of indexes.
- type : char
-     'rxns', 'mets', or 'genes' depending on what to retrieve. 'metnames'
-     queries metabolite names, while 'metcomps' allows providing specific
-     metabolites and their compartments in the format metaboliteName[comp].
-     If a model.ec structure exists (GECKO 3), then also 'ecenzymes',
-     'ecrxns' and 'ecgenes' are allowed.
-
- Name-Value Arguments
- --------------------
- returnLogical : logical
-     sets whether to return a logical array or an array with the indexes
-     (default false).
-
- Returns
- -------
- indexes : logical or double
-     can be a logical array or a double array depending on the value of
-     returnLogical.
-
- Notes
- -----
- If 'ecenzymes', 'ecrxns' or 'ecgenes' are used with a GECKO 3 model, then
- the indexes are from the model.ec.enzymes, model.ec.rxns or model.ec.genes
- fields, respectively.
-
- Examples
- --------
-     indexes = getIndexes(model, objects, type, returnLogical);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: - - - - - -

SOURCE CODE ^

-
0001 function indexes=getIndexes(model, objects, type, varargin)
-0002 % getIndexes  Retrieve the indexes for a list of reactions or metabolites.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % model : struct
-0007 %     a model structure.
-0008 % objects : cell or logical or double
-0009 %     either a cell array of IDs, a logical vector with the same number of
-0010 %     elements as metabolites in the model, or a vector of indexes.
-0011 % type : char
-0012 %     'rxns', 'mets', or 'genes' depending on what to retrieve. 'metnames'
-0013 %     queries metabolite names, while 'metcomps' allows providing specific
-0014 %     metabolites and their compartments in the format metaboliteName[comp].
-0015 %     If a model.ec structure exists (GECKO 3), then also 'ecenzymes',
-0016 %     'ecrxns' and 'ecgenes' are allowed.
-0017 %
-0018 % Name-Value Arguments
-0019 % --------------------
-0020 % returnLogical : logical
-0021 %     sets whether to return a logical array or an array with the indexes
-0022 %     (default false).
-0023 %
-0024 % Returns
-0025 % -------
-0026 % indexes : logical or double
-0027 %     can be a logical array or a double array depending on the value of
-0028 %     returnLogical.
-0029 %
-0030 % Notes
-0031 % -----
-0032 % If 'ecenzymes', 'ecrxns' or 'ecgenes' are used with a GECKO 3 model, then
-0033 % the indexes are from the model.ec.enzymes, model.ec.rxns or model.ec.genes
-0034 % fields, respectively.
-0035 %
-0036 % Examples
-0037 % --------
-0038 %     indexes = getIndexes(model, objects, type, returnLogical);
-0039 
-0040 p=parseRAVENargs(varargin, {'returnLogical', false});
-0041 returnLogical=p.returnLogical;
-0042 
-0043 if ~islogical(objects) && ~isnumeric(objects)
-0044     objects=convertCharArray(objects);
-0045 end
-0046 type=char(type);
-0047 
-0048 indexes=[];
-0049 
-0050 switch type
-0051     case 'rxns'
-0052         searchIn=model.rxns;
-0053     case 'mets'
-0054         searchIn=model.mets;
-0055     case 'genes'
-0056         searchIn=model.genes;
-0057     case 'metnames'
-0058         searchIn=model.metNames;
-0059     case 'metcomps'
-0060         % If provided as metaboliteName[comp], then index search is quite
-0061         % different from general approach, and therefore coded separately.
-0062         mets=regexprep(objects,'(^.+)\[(.+)\]$','$1');
-0063         comps=regexprep(objects,'(^.+)\[(.+)\]$','$2');
-0064         indexes=zeros(1,numel(objects));
-0065         for i=1:numel(objects)
-0066             index=find(strcmp(mets(i),model.metNames));
-0067             index=index(strcmp(comps(i),model.comps(model.metComps(index))));
-0068             if ~isempty(index)
-0069                 indexes(i)=index;
-0070             else
-0071                 error(['Could not find object ' objects{i} ' in the model']);
-0072             end
-0073         end
-0074         indexes=indexes(:);
-0075         if returnLogical==true
-0076             tempIndexes=false(numel(model.mets),1);
-0077             tempIndexes(indexes)=true;
-0078             indexes=tempIndexes;
-0079         end
-0080         return %None of the remaining function needs to run if metcomps
-0081     case 'ecrxns'
-0082         if ~isfield(model,'ec')
-0083             error('Type %s cannot be used if no model.ec structure is present.',type)
-0084         end
-0085         searchIn=model.ec.rxns;
-0086     case 'ecenzymes'
-0087         if ~isfield(model,'ec')
-0088             error('Type %s cannot be used if no model.ec structure is present.',type)
-0089         end
-0090         searchIn=model.ec.enzymes;
-0091     case 'ecgenes'
-0092         if ~isfield(model,'ec')
-0093             error('Type %s cannot be used if no model.ec structure is present.',type)
-0094         end
-0095         searchIn=model.ec.genes;
-0096     otherwise
-0097         if contains(objects,{'rxns','mets','metnames','metcomps','genes','ecgenes','ecenzymes','ecrxns'})
-0098             error('The second and third parameter provided to run getIndexes are likely switched. Note that the second parameter is the object to find the index for, while the third parameter is the object type (''rxns'', ''mets'', ''metnames'', ''metcomps'', ''genes'', ''ecgenes'', ''ecenzymes'' or ''ecrxns'').')
-0099         else
-0100             error('Incorrect value of the ''type'' parameter. Allowed values are ''rxns'', ''mets'', ''metnames'', ''metcomps'', ''genes'', ''ecgenes'', ''ecenzymes'' or ''ecrxns''.');
-0101        end
-0102 end
-0103 
-0104 if iscell(objects)
-0105     if strcmpi(type,'metnames')
-0106         % metnames allows multiple matches per name (same metabolite in different
-0107         % compartments), so return a cell array of index vectors.
-0108         for i=1:numel(objects)
-0109             indexes{i}=find(strcmp(objects(i),searchIn));
-0110         end
-0111     else
-0112         % Build a hash map once (O(m)) for O(1) per-object lookup instead of
-0113         % O(m) strcmp per object.
-0114         searchMap = containers.Map(searchIn, 1:numel(searchIn));
-0115         for i=1:numel(objects)
-0116             if isKey(searchMap, objects{i})
-0117                 indexes(i) = searchMap(objects{i});
-0118             else
-0119                 error(['Could not find object ''' objects{i} ''' in the model']);
-0120             end
-0121         end
-0122     end
-0123 else
-0124     %Now it's either a logical mask or an array with indexes. We want indexes.
-0125     if islogical(objects)
-0126         indexes=find(objects);
-0127     else
-0128         indexes=objects;
-0129     end
-0130 end
-0131 
-0132 if returnLogical==true
-0133     tempIndexes=false(numel(searchIn),1);
-0134     tempIndexes(indexes)=true;
-0135     indexes=tempIndexes;
-0136 end
-0137 
-0138 indexes=indexes(:);
-0139 if iscell(indexes) && length(indexes)==1
-0140     indexes=indexes{1};
-0141 end
-0142 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/queries/index.html b/doc/queries/index.html deleted file mode 100644 index 1042551c..00000000 --- a/doc/queries/index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - Index for Directory queries - - - - - - - - - - -
< Master indexIndex for queries >
- -

Index for queries

- -

Matlab files in this directory:

- -
 buildEquationbuildEquation Construct single equation string for a given reaction.
 checkModelStructcheckModelStruct Perform a number of checks to ensure a model structure is ok.
 constructEquationsconstructEquations Construct equation strings for reactions.
 constructSconstructS Construct a stoichiometric matrix from a cell array of equations.
 getAllRxnsFromGenesgetAllRxnsFromGenes Find all reactions annotated by the genes of a set.
 getElementalBalancegetElementalBalance Check whether the reactions of a model are balanced.
 getExchangeRxnsgetExchangeRxns Retrieve the exchange reactions from a model.
 getGenesFromGrRulesgetGenesFromGrRules Extract gene list and rxnGeneMat from grRules array.
 getIndexesgetIndexes Retrieve the indexes for a list of reactions or metabolites.
 getRxnsInCompgetRxnsInComp Get the reactions in a specified compartment.
 getTransportRxnsgetTransportRxns Retrieve the transport reactions from a model.
 modelSummarymodelSummary Print a concise overview of a RAVEN model and its flux state.
 parseFormulasparseFormulas Get the elemental composition from formulas.
 parseRxnEquparseRxnEqu Get all metabolite names from a cell array of equations.
 printFluxesprintFluxes Print reactions and fluxes to the screen or to a file.
 printModelStatsprintModelStats Print some statistics about a model to the screen.
- - - - -
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/queries/modelSummary.html b/doc/queries/modelSummary.html deleted file mode 100644 index 6dd7c296..00000000 --- a/doc/queries/modelSummary.html +++ /dev/null @@ -1,228 +0,0 @@ - - - - Description of modelSummary - - - - - - - - - -
Home > queries > modelSummary.m
- - - -

modelSummary -

- -

PURPOSE ^

-
modelSummary Print a concise overview of a RAVEN model and its flux state.
- -

SYNOPSIS ^

-
function modelSummary(model, varargin)
- -

DESCRIPTION ^

-
 modelSummary  Print a concise overview of a RAVEN model and its flux state.
-
- Prints model dimensions, the objective function, and — when a flux vector
- is supplied — the objective value plus all net exchange fluxes partitioned
- into uptake and secretion.
-
- Parameters
- ----------
- model : struct
-     RAVEN model structure.
-
- Name-Value Arguments
- --------------------
- fluxes : double
-     flux vector (length = numel(model.rxns)). When provided, shows the
-     objective value and the exchange-flux summary (default []).
- cutoff : double
-     minimum absolute net exchange flux to include (default 1e-8).
- nMax : double
-     maximum exchange reactions displayed per direction (default 20).
-
- Examples
- --------
-     modelSummary(model)
-     modelSummary(model, 'fluxes', sol.x)
-     modelSummary(model, 'fluxes', sol.x, 'nMax', 10)
-
- See also
- --------
- printModelStats : the structural view instead. It breaks the counts down per
-     compartment, adds unique EC codes, biochemically unique reactions and
-     unique metabolite names, and optionally reports dead ends and elemental
-     imbalances. It knows nothing about fluxes or the objective.
- printFluxes : the exchange fluxes on their own, with more control over
-     formatting and filtering than the summary here offers.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • getExchangeRxns getExchangeRxns Retrieve the exchange reactions from a model.
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function modelSummary(model, varargin)
-0002 % modelSummary  Print a concise overview of a RAVEN model and its flux state.
-0003 %
-0004 % Prints model dimensions, the objective function, and — when a flux vector
-0005 % is supplied — the objective value plus all net exchange fluxes partitioned
-0006 % into uptake and secretion.
-0007 %
-0008 % Parameters
-0009 % ----------
-0010 % model : struct
-0011 %     RAVEN model structure.
-0012 %
-0013 % Name-Value Arguments
-0014 % --------------------
-0015 % fluxes : double
-0016 %     flux vector (length = numel(model.rxns)). When provided, shows the
-0017 %     objective value and the exchange-flux summary (default []).
-0018 % cutoff : double
-0019 %     minimum absolute net exchange flux to include (default 1e-8).
-0020 % nMax : double
-0021 %     maximum exchange reactions displayed per direction (default 20).
-0022 %
-0023 % Examples
-0024 % --------
-0025 %     modelSummary(model)
-0026 %     modelSummary(model, 'fluxes', sol.x)
-0027 %     modelSummary(model, 'fluxes', sol.x, 'nMax', 10)
-0028 %
-0029 % See also
-0030 % --------
-0031 % printModelStats : the structural view instead. It breaks the counts down per
-0032 %     compartment, adds unique EC codes, biochemically unique reactions and
-0033 %     unique metabolite names, and optionally reports dead ends and elemental
-0034 %     imbalances. It knows nothing about fluxes or the objective.
-0035 % printFluxes : the exchange fluxes on their own, with more control over
-0036 %     formatting and filtering than the summary here offers.
-0037 
-0038 p = parseRAVENargs(varargin, {'fluxes',[]; 'cutoff',1e-8; 'nMax',20});
-0039 fluxes = p.fluxes;
-0040 cutoff = p.cutoff;
-0041 nMax   = p.nMax;
-0042 
-0043 nRxns  = numel(model.rxns);
-0044 nMets  = numel(model.mets);
-0045 nGenes = 0; if isfield(model,'genes'), nGenes = numel(model.genes); end
-0046 nComps = 0; if isfield(model,'comps'), nComps = numel(model.comps); end
-0047 
-0048 W   = 62;
-0049 bar = repmat('=', 1, W);
-0050 sep = repmat('-', 1, W);
-0051 
-0052 fprintf('%s\n', bar);
-0053 modelID = ''; if isfield(model,'id') && ~isempty(model.id), modelID = model.id; end
-0054 if isfield(model,'name') && ~isempty(model.name)
-0055     fprintf('  %s  (%s)\n', model.name, modelID);
-0056 elseif ~isempty(modelID)
-0057     fprintf('  %s\n', modelID);
-0058 else
-0059     fprintf('  (unnamed model)\n');
-0060 end
-0061 fprintf('%s\n', bar);
-0062 fprintf('  %-15s %5d    %-15s %5d\n', 'Reactions:',  nRxns, 'Metabolites:',  nMets);
-0063 fprintf('  %-15s %5d    %-15s %5d\n', 'Genes:',      nGenes,'Compartments:', nComps);
-0064 fprintf('%s\n', sep);
-0065 
-0066 % Objective
-0067 objIdx = find(model.c ~= 0);
-0068 if isempty(objIdx)
-0069     fprintf('  Objective: (none)\n');
-0070 else
-0071     fprintf('  Objective:\n');
-0072     for i = 1:numel(objIdx)
-0073         j = objIdx(i);
-0074         rName = '';
-0075         if isfield(model,'rxnNames') && j <= numel(model.rxnNames) && ~isempty(model.rxnNames{j})
-0076             rName = [' (' model.rxnNames{j} ')'];
-0077         end
-0078         fprintf('    %+.4g \xD7 %s%s\n', model.c(j), model.rxns{j}, rName);
-0079     end
-0080 end
-0081 
-0082 if isempty(fluxes)
-0083     fprintf('%s\n', bar);
-0084     return
-0085 end
-0086 
-0087 objVal = model.c' * fluxes;
-0088 fprintf('  Objective value: %.6g\n', objVal);
-0089 fprintf('%s\n', sep);
-0090 
-0091 % Classify exchange fluxes
-0092 [~, exchIdx] = getExchangeRxns(model);
-0093 
-0094 upRows  = {};   % {metLabel, rxnID, absFlux}
-0095 outRows = {};
-0096 
-0097 for i = 1:numel(exchIdx)
-0098     f = fluxes(exchIdx(i));
-0099     if abs(f) < cutoff, continue; end
-0100     col = model.S(:, exchIdx(i));
-0101     m = find(col, 1);           % exchange reactions have exactly one metabolite
-0102     if isempty(m), continue; end
-0103     netProd = full(col(m)) * f; % positive = metabolite secreted; full() avoids sparse scalar in cell
-0104     mLabel  = metLabel_(model, m);
-0105     rID     = model.rxns{exchIdx(i)};
-0106     if netProd > 0
-0107         outRows{end+1} = {mLabel, rID, netProd};   %#ok<AGROW>
-0108     else
-0109         upRows{end+1}  = {mLabel, rID, -netProd};  %#ok<AGROW>
-0110     end
-0111 end
-0112 
-0113 printSection_('  UPTAKE',    upRows,  nMax, W);
-0114 printSection_('  SECRETION', outRows, nMax, W);
-0115 fprintf('%s\n', bar);
-0116 end
-0117 
-0118 %--------------------------------------------------------------------------
-0119 function printSection_(title, rows, nMax, W)
-0120     fprintf('\n%s\n', title);
-0121     fprintf('  %-26s  %-20s  %10s\n', 'Metabolite', 'Reaction', 'Flux');
-0122     fprintf('  %s\n', repmat('-', 1, W-2));
-0123     if isempty(rows)
-0124         fprintf('  (none)\n');
-0125         return
-0126     end
-0127     vals = cellfun(@(r) r{3}, rows);
-0128     [~, si] = sort(vals, 'descend');
-0129     rows = rows(si);
-0130     for i = 1:min(numel(rows), nMax)
-0131         fprintf('  %-26s  %-20s  %10.4g\n', rows{i}{1}, rows{i}{2}, rows{i}{3});
-0132     end
-0133     if numel(rows) > nMax
-0134         fprintf('  ... (%d more, use nMax to show all)\n', numel(rows) - nMax);
-0135     end
-0136 end
-0137 
-0138 function s = metLabel_(model, metIdx)
-0139     if isfield(model,'metNames') && ~isempty(model.metNames{metIdx})
-0140         s = model.metNames{metIdx};
-0141     else
-0142         s = model.mets{metIdx};
-0143     end
-0144     if numel(s) > 26, s = [s(1:23) '...']; end
-0145 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/queries/printFluxes.html b/doc/queries/printFluxes.html deleted file mode 100644 index d8e63abc..00000000 --- a/doc/queries/printFluxes.html +++ /dev/null @@ -1,303 +0,0 @@ - - - - Description of printFluxes - - - - - - - - - -
Home > queries > printFluxes.m
- - - -

printFluxes -

- -

PURPOSE ^

-
printFluxes Print reactions and fluxes to the screen or to a file.
- -

SYNOPSIS ^

-
function printFluxes(model, fluxes, varargin)
- -

DESCRIPTION ^

-
 printFluxes  Print reactions and fluxes to the screen or to a file.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- fluxes : double
-     a vector with fluxes.
-
- Name-Value Arguments
- --------------------
- onlyExchange : logical
-     only print exchange fluxes (default true).
- cutOffFlux : double
-     only print fluxes with absolute values above or equal to this value
-     (default 10^-8).
- outputFile : char
-     a file to save the print-out to (default is output to the command
-     window).
- outputString : char
-     a string that specifies the output of each reaction (default
-     "%rxnID\t(%rxnName):\t%flux\n").
- metaboliteList : cell
-     cell array of metabolite names. Only reactions involving any of these
-     metabolites will be printed.
-
- Notes
- -----
- The following codes are available for user-defined output strings:
-
- - %rxnID : reaction ID
- - %rxnName : reaction name
- - %lower : lower bound
- - %upper : upper bound
- - %obj : objective coefficient
- - %eqn : equation
- - %flux : flux
- - %element : equation using the metabolite formulas rather than metabolite
-   names
- - %unbalanced : "(*)" if the reaction is unbalanced and "(-)" if it could
-   not be parsed
- - %lumped : equation where the elemental compositions for the left/right
-   hand sides are lumped
-
- Examples
- --------
-     printFluxes(model, fluxes, onlyExchange, cutOffFlux, outputFile, ...
-                 outputString, metaboliteList);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function printFluxes(model, fluxes, varargin)
-0002 % printFluxes  Print reactions and fluxes to the screen or to a file.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % model : struct
-0007 %     a model structure.
-0008 % fluxes : double
-0009 %     a vector with fluxes.
-0010 %
-0011 % Name-Value Arguments
-0012 % --------------------
-0013 % onlyExchange : logical
-0014 %     only print exchange fluxes (default true).
-0015 % cutOffFlux : double
-0016 %     only print fluxes with absolute values above or equal to this value
-0017 %     (default 10^-8).
-0018 % outputFile : char
-0019 %     a file to save the print-out to (default is output to the command
-0020 %     window).
-0021 % outputString : char
-0022 %     a string that specifies the output of each reaction (default
-0023 %     "%rxnID\t(%rxnName):\t%flux\n").
-0024 % metaboliteList : cell
-0025 %     cell array of metabolite names. Only reactions involving any of these
-0026 %     metabolites will be printed.
-0027 %
-0028 % Notes
-0029 % -----
-0030 % The following codes are available for user-defined output strings:
-0031 %
-0032 % - %rxnID : reaction ID
-0033 % - %rxnName : reaction name
-0034 % - %lower : lower bound
-0035 % - %upper : upper bound
-0036 % - %obj : objective coefficient
-0037 % - %eqn : equation
-0038 % - %flux : flux
-0039 % - %element : equation using the metabolite formulas rather than metabolite
-0040 %   names
-0041 % - %unbalanced : "(*)" if the reaction is unbalanced and "(-)" if it could
-0042 %   not be parsed
-0043 % - %lumped : equation where the elemental compositions for the left/right
-0044 %   hand sides are lumped
-0045 %
-0046 % Examples
-0047 % --------
-0048 %     printFluxes(model, fluxes, onlyExchange, cutOffFlux, outputFile, ...
-0049 %                 outputString, metaboliteList);
-0050 
-0051 p=parseRAVENargs(varargin, {'onlyExchange',true; 'cutOffFlux',10^-8; 'outputFile',[]; 'outputString',[]; 'metaboliteList',{}});
-0052 onlyExchange=p.onlyExchange;
-0053 cutOffFlux=p.cutOffFlux;
-0054 if isempty(cutOffFlux)
-0055     cutOffFlux=10^-8;
-0056 end
-0057 outputFile=p.outputFile;
-0058 if ~isempty(outputFile)
-0059     outputFile=char(outputFile);
-0060     fid=fopen(outputFile,'w');
-0061 else
-0062     fid=1;
-0063 end
-0064 outputString=p.outputString;
-0065 if isempty(outputString)
-0066     outputString='%rxnID\t(%rxnName):\t%flux\n';
-0067 else
-0068     outputString=char(outputString);
-0069 end
-0070 metaboliteList=p.metaboliteList;
-0071 if ~isempty(metaboliteList)
-0072     metaboliteList=convertCharArray(metaboliteList);
-0073 end
-0074 if isempty(fluxes)
-0075     EM='Empty vector of fluxes, solveLP possibly returned infeasible';
-0076     error('RAVEN:badInput', '%s', EM);
-0077 elseif size(fluxes,1)~=numel(model.rxns)
-0078     EM='The number of fluxes and the number of reactions must be the same';
-0079     error('RAVEN:badInput', '%s', EM);
-0080 end
-0081 
-0082 %Only keep reactions involving the defined metabolites
-0083 if ~isempty(metaboliteList)
-0084     I=ismember(upper(model.metNames),upper(metaboliteList));
-0085     [~, K]=find(model.S(I,:));
-0086     
-0087     %Delete all other reactions
-0088     toDelete=true(numel(model.rxns),1);
-0089     toDelete(K)=false;
-0090     model=removeReactions(model,toDelete);
-0091     fluxes(toDelete,:)=[];
-0092 end
-0093 
-0094 if onlyExchange==true
-0095     fprintf(fid,'EXCHANGE FLUXES:\n');
-0096 else
-0097     fprintf(fid,'FLUXES:\n');
-0098 end
-0099 
-0100 %Remove reactions which are below the cut off
-0101 toDelete=abs(fluxes)<cutOffFlux;
-0102 toDelete=all(toDelete,2);
-0103 model=removeReactions(model,toDelete,true,true);
-0104 fluxes(toDelete,:)=[];
-0105 
-0106 if any(strfind(outputString,'%eqn'))
-0107     %Construct the equations
-0108     eqn=constructEquations(model);
-0109 else
-0110     eqn=cell(numel(model.rxns),1);
-0111     eqn(:)={''};
-0112 end
-0113 if any(strfind(outputString,'%element'))
-0114     %For printing equations using the composition
-0115     cModel=model;
-0116     cModel.metNames=cModel.metFormulas;
-0117     cModel.metNames(cellfun(@isempty,cModel.metNames))={'?'};
-0118     element=constructEquations(cModel);
-0119 else
-0120     element=cell(numel(model.rxns),1);
-0121     element(:)={''};
-0122 end
-0123 
-0124 if any(strfind(outputString,'%unbalanced')) || any(strfind(outputString,'%lumped'))
-0125     balanceStructure=getElementalBalance(model);
-0126 end
-0127 
-0128 unbalanced=cell(numel(model.rxns),1);
-0129 unbalanced(:)={''};
-0130 if any(strfind(outputString,'%unbalanced'))
-0131     unbalanced(balanceStructure.balanceStatus==0)={'(*)'};
-0132     unbalanced(balanceStructure.balanceStatus<0)={'(-)'};
-0133 end
-0134 
-0135 lumped=cell(numel(model.rxns),1);
-0136 lumped(:)={''};
-0137 if any(strfind(outputString,'%lumped'))
-0138     for i=1:numel(model.rxns)
-0139         leftGroup='';
-0140         rightGroup='';
-0141         for j=1:numel(balanceStructure.elements.names)
-0142             I=balanceStructure.leftComp(i,j);
-0143             if I~=0
-0144                 if I==1
-0145                     leftGroup=[leftGroup balanceStructure.elements.abbrevs{j}];
-0146                 else
-0147                     leftGroup=[leftGroup balanceStructure.elements.abbrevs{j} num2str(I)];
-0148                 end
-0149             end
-0150             I=balanceStructure.rightComp(i,j);
-0151             if I~=0
-0152                 if I==1
-0153                     rightGroup=[rightGroup balanceStructure.elements.abbrevs{j}];
-0154                 else
-0155                     rightGroup=[rightGroup balanceStructure.elements.abbrevs{j} num2str(I)];
-0156                 end
-0157             end
-0158         end
-0159         if model.rev(i)
-0160             lumped{i}=[leftGroup ' <=> ' rightGroup];
-0161         else
-0162             lumped{i}=[leftGroup ' => ' rightGroup];
-0163         end
-0164     end
-0165 end
-0166 
-0167 %Use getExchangeRxns' own definition of "exchange reaction" rather than a
-0168 %local, incomplete re-implementation: a model that still carries explicit
-0169 %boundary metabolites represents an exchange as realMet <=> realMet[b] --
-0170 %one reactant AND one product -- which "no reactants or no products" alone
-0171 %does not recognise, silently printing nothing for such a model.
-0172 isExchRxn=false(numel(model.rxns),1);
-0173 if onlyExchange==true
-0174     [~,exchIdx]=getExchangeRxns(model,'all');
-0175     isExchRxn(exchIdx)=true;
-0176 end
-0177 
-0178 for i=1:numel(model.rxns)
-0179     %Only print if it is an exchange reaction or if all reactions should be
-0180     %printed.
-0181     if (onlyExchange==false || isExchRxn(i))
-0182         printString=outputString;
-0183         
-0184         %Produce the final string
-0185         printString=strrep(printString,'%rxnID',model.rxns{i});
-0186         printString=strrep(printString,'%eqn',eqn{i});
-0187         printString=strrep(printString,'%rxnName',model.rxnNames{i});
-0188         printString=strrep(printString,'%lower',num2str(model.lb(i)));
-0189         printString=strrep(printString,'%upper',num2str(model.ub(i)));
-0190         printString=strrep(printString,'%obj',num2str(model.c(i)));
-0191         printString=strrep(printString,'%flux',num2str(fluxes(i,:)));
-0192         printString=strrep(printString,'%element',element{i});
-0193         printString=strrep(printString,'%unbalanced',unbalanced{i});
-0194         printString=strrep(printString,'%lumped',lumped{i});
-0195         %Substituted model text (rxnID, eqn, rxnName, etc.) may itself
-0196         %contain "%", which fprintf would otherwise reinterpret as a new
-0197         %format directive; print as literal data instead, after manually
-0198         %expanding the template's own \t and \n escapes.
-0199         printString=strrep(printString,'\t',sprintf('\t'));
-0200         printString=strrep(printString,'\n',sprintf('\n'));
-0201         fprintf(fid,'%s',printString);
-0202     end
-0203 end
-0204 
-0205 if fid~=1
-0206     fprintf('File successfully saved.\n');
-0207     fclose(fid);
-0208 end
-0209 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/queries/printModelStats.html b/doc/queries/printModelStats.html deleted file mode 100644 index 37ecfdfd..00000000 --- a/doc/queries/printModelStats.html +++ /dev/null @@ -1,250 +0,0 @@ - - - - Description of printModelStats - - - - - - - - - -
Home > queries > printModelStats.m
- - - -

printModelStats -

- -

PURPOSE ^

-
printModelStats Print some statistics about a model to the screen.
- -

SYNOPSIS ^

-
function printModelStats(model, varargin)
- -

DESCRIPTION ^

-
 printModelStats  Print some statistics about a model to the screen.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- printModelIssues : logical
-     true if information about unconnected reactions/metabolites and
-     elemental balancing should be printed (default false).
- printDetails : logical
-     true if detailed information should be printed about model issues.
-     Only used if printModelIssues is true (default true).
-
- Examples
- --------
-     printModelStats(model, printModelIssues, printDetails);
-
- See also
- --------
- modelSummary : the shorter view, without the per-compartment breakdown, but
-     adding the objective function and - given a flux vector - the objective
-     value and the net uptake and secretion.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function printModelStats(model, varargin)
-0002 % printModelStats  Print some statistics about a model to the screen.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % model : struct
-0007 %     a model structure.
-0008 %
-0009 % Name-Value Arguments
-0010 % --------------------
-0011 % printModelIssues : logical
-0012 %     true if information about unconnected reactions/metabolites and
-0013 %     elemental balancing should be printed (default false).
-0014 % printDetails : logical
-0015 %     true if detailed information should be printed about model issues.
-0016 %     Only used if printModelIssues is true (default true).
-0017 %
-0018 % Examples
-0019 % --------
-0020 %     printModelStats(model, printModelIssues, printDetails);
-0021 %
-0022 % See also
-0023 % --------
-0024 % modelSummary : the shorter view, without the per-compartment breakdown, but
-0025 %     adding the objective function and - given a flux vector - the objective
-0026 %     value and the net uptake and secretion.
-0027 
-0028 p=parseRAVENargs(varargin, {'printModelIssues',false; 'printDetails',true});
-0029 printModelIssues=p.printModelIssues;
-0030 printDetails=p.printDetails;
-0031 
-0032 %Escapes model text before it is spliced into an fprintf template: the
-0033 %assembled string is used as a format string below, so a literal "%" in
-0034 %e.g. a metabolite or compartment name would otherwise be misread as a
-0035 %format directive and swallow the remainder of the line.
-0036 esc=@(s) strrep(s,'%','%%');
-0037 
-0038 fprintf(['Network statistics for ' esc(model.id) ': ' esc(model.name) '\n']);
-0039 
-0040 %Get which reactions are present in each compartment
-0041 rxnComps=sparse(numel(model.rxns),numel(model.comps));
-0042 
-0043 %For each compartment, find the metabolites that are present in that
-0044 %compartment and then the reactions they are involved in
-0045 for i=1:numel(model.comps)
-0046     [~, I]=find(model.S(model.metComps==i,:));
-0047     rxnComps(I,i)=1;
-0048 end
-0049 
-0050 if isfield(model,'eccodes')
-0051     fprintf(['EC-numbers\t\t\t' num2str(numel(unique(model.eccodes))) '\n']);
-0052 end
-0053 
-0054 %Print information about genes
-0055 if isfield(model,'genes')
-0056     fprintf(['Genes*\t\t\t\t' num2str(numel(model.genes)) '\n']);
-0057     %Find the genes in each compartment
-0058     for i=1:numel(model.comps)
-0059         [~, I]=find(model.rxnGeneMat(rxnComps(:,i)==1,:));
-0060         fprintf(['\t' esc(model.compNames{i}) '\t' num2str(numel(unique(I))) '\n']);
-0061     end
-0062 end
-0063 
-0064 %Print information about reactions
-0065 fprintf(['\nReactions*\t\t\t' num2str(numel(model.rxns)) '\n']);
-0066 for i=1:numel(model.comps)
-0067     fprintf(['\t' esc(model.compNames{i}) '\t' num2str(sum(rxnComps(:,i))) '\n']);
-0068 end
-0069 
-0070 %Removes the effect of compartments and removes duplicate reactions
-0071 temp=model;
-0072 temp.comps(:)={'s'}; %Set all compartments to be the same
-0073 equ=constructEquations(sortModel(temp,true,true),temp.rxns,false);
-0074 
-0075 fprintf(['Unique reactions**\t' num2str(numel(unique(equ))) '\n']);
-0076 
-0077 %Print information about metabolites
-0078 fprintf(['\nMetabolites\t\t\t' num2str(numel(model.mets)) '\n']);
-0079 for i=1:numel(model.comps)
-0080     fprintf(['\t' esc(model.compNames{i}) '\t' num2str(sum(model.metComps==i)) '\n']);
-0081 end
-0082 
-0083 fprintf(['Unique metabolites\t' num2str(numel(unique(model.metNames))) '\n']);
-0084 
-0085 fprintf('\n* Genes and reactions are counted for each compartment if any of the corresponding metabolites are in that compartment. The sum may therefore not add up to the total number.\n');
-0086 fprintf('** Unique reactions are defined as being biochemically unique (no compartmentalization)\n');
-0087 
-0088 %Also print some potential problems if there are any
-0089 if printModelIssues==true
-0090     fprintf(['\nShort model quality summary for ' esc(model.id) ': ' esc(model.name) '\n']);
-0091     
-0092     %Check that all the metabolites are being used
-0093     involvedMat=model.S;
-0094     involvedMat(involvedMat~=0)=1;
-0095     usedMets=sum(involvedMat,2);
-0096     notPresent=find(usedMets==0);
-0097     if ~isempty(notPresent)
-0098         errorText=['Non-used metabolites\t' num2str(numel(notPresent)) '\n'];
-0099         if printDetails==true
-0100             for i=1:numel(notPresent)
-0101                 errorText=[errorText '\t(' esc(model.mets{notPresent(i)}) ') ' esc(model.metNames{notPresent(i)}) '\n'];
-0102             end
-0103             errorText=[errorText '\n'];
-0104         end
-0105         fprintf(errorText);
-0106     end
-0107     
-0108     %Check if there are empty reactions
-0109     usedRxns=sum(involvedMat,1);
-0110     notUsed=find(usedRxns==0);
-0111     if ~isempty(notUsed)
-0112         errorText=['Empty reactions\t' num2str(numel(notUsed)) '\n'];
-0113         if printDetails==true
-0114             for i=1:numel(notUsed)
-0115                 errorText=[errorText '\t' esc(model.rxns{notUsed(i)}) '\n'];
-0116             end
-0117             errorText=[errorText '\n'];
-0118         end
-0119         fprintf(errorText);
-0120     end
-0121     
-0122     %Check if there are dead-end reactions/metabolites
-0123     [~, deletedReactions, deletedMetabolites]=simplifyModel(model,true,false,false,true);
-0124     
-0125     if ~isempty(deletedReactions)
-0126         errorText=['Dead-end reactions\t' num2str(numel(deletedReactions)) '\n'];
-0127         if printDetails==true
-0128             for i=1:numel(deletedReactions)
-0129                 errorText=[errorText '\t' esc(deletedReactions{i}) '\n'];
-0130             end
-0131             errorText=[errorText '\n'];
-0132         end
-0133         fprintf(errorText);
-0134     end
-0135     
-0136     %Ignore non-used metabolites
-0137     deletedMetabolites=setdiff(deletedMetabolites,model.mets(notPresent));
-0138     %Must map to indexes in order to print names
-0139     deletedMetabolites=find(ismember(model.mets,deletedMetabolites));
-0140     if ~isempty(deletedMetabolites)
-0141         errorText=['Dead-end metabolites\t' num2str(numel(deletedMetabolites)) '\n'];
-0142         if printDetails==true
-0143             for i=1:numel(deletedMetabolites)
-0144                 errorText=[errorText '\t(' esc(model.mets{deletedMetabolites(i)}) ') ' esc(model.metNames{deletedMetabolites(i)}) '\n'];
-0145             end
-0146             errorText=[errorText '\n'];
-0147         end
-0148         fprintf(errorText);
-0149     end
-0150     
-0151     balanceStructure=getElementalBalance(model);
-0152     
-0153     notParsed=find(balanceStructure.balanceStatus<0);
-0154     notBalanced=find(balanceStructure.balanceStatus==0);
-0155     
-0156     if ~isempty(notParsed)
-0157         errorText=['Reactions which could not be elementally balanced\t' num2str(numel(notParsed)) '\n'];
-0158         if printDetails==true
-0159             for i=1:numel(notParsed)
-0160                 errorText=[errorText '\t' esc(model.rxns{notParsed(i)}) '\n'];
-0161             end
-0162             errorText=[errorText '\n'];
-0163         end
-0164         fprintf(errorText);
-0165     end
-0166     if ~isempty(notBalanced)
-0167         errorText=['Reactions which are elementally unbalanced\t' num2str(numel(notBalanced)) '\n'];
-0168         if printDetails==true
-0169             names=strcat(balanceStructure.elements.names,{', '});
-0170             for i=1:numel(notBalanced)
-0171                 badOnes=sprintf('%s', names{abs(balanceStructure.leftComp(notBalanced(i),:)-balanceStructure.rightComp(notBalanced(i),:))>10^-7});
-0172                 errorText=[errorText '\t' esc(model.rxns{notBalanced(i)}) '\t' esc(badOnes(1:end-2)) '\n'];
-0173             end
-0174             errorText=[errorText '\n'];
-0175         end
-0176         fprintf(errorText);
-0177     end
-0178 end
-0179 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/reconstruction/guessComposition.html b/doc/reconstruction/guessComposition.html deleted file mode 100644 index 8a712cb4..00000000 --- a/doc/reconstruction/guessComposition.html +++ /dev/null @@ -1,257 +0,0 @@ - - - - Description of guessComposition - - - - - - - - - -
Home > reconstruction > guessComposition.m
- - - -

guessComposition -

- -

PURPOSE ^

-
guessComposition Guess the composition of metabolites without one.
- -

SYNOPSIS ^

-
function [model, guessedFor, couldNotGuess]=guessComposition(model, varargin)
- -

DESCRIPTION ^

-
 guessComposition  Guess the composition of metabolites without one.
-
- Attempts to guess the composition of metabolites without information about
- elemental composition.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- printResults : logical
-     true if the output should be printed (default true).
-
- Returns
- -------
- model : struct
-     a model structure with information about elemental composition added.
- guessedFor : double
-     indexes for the metabolites for which a composition could be guessed.
- couldNotGuess : double
-     indexes for the metabolites for which no composition could be
-     assigned.
-
- Examples
- --------
-     [model, guessedFor, couldNotGuess] = guessComposition(model, printResults);
-
- Notes
- -----
- A metabolite's composition can only be resolved from a reaction in which
- every other participating metabolite already has a known composition;
- the function repeats this until no further metabolites can be resolved.
- It requires that the existing compositions and reactions are correct,
- and throws an error if a metabolite would need inconsistent
- compositions in different reactions -- so it can also be used to check
- a model for such errors. Note that successful completion does not
- guarantee correctness if the pre-existing compositions were themselves
- wrong.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [model, guessedFor, couldNotGuess]=guessComposition(model, varargin)
-0002 % guessComposition  Guess the composition of metabolites without one.
-0003 %
-0004 % Attempts to guess the composition of metabolites without information about
-0005 % elemental composition.
-0006 %
-0007 % Parameters
-0008 % ----------
-0009 % model : struct
-0010 %     a model structure.
-0011 %
-0012 % Name-Value Arguments
-0013 % --------------------
-0014 % printResults : logical
-0015 %     true if the output should be printed (default true).
-0016 %
-0017 % Returns
-0018 % -------
-0019 % model : struct
-0020 %     a model structure with information about elemental composition added.
-0021 % guessedFor : double
-0022 %     indexes for the metabolites for which a composition could be guessed.
-0023 % couldNotGuess : double
-0024 %     indexes for the metabolites for which no composition could be
-0025 %     assigned.
-0026 %
-0027 % Examples
-0028 % --------
-0029 %     [model, guessedFor, couldNotGuess] = guessComposition(model, printResults);
-0030 %
-0031 % Notes
-0032 % -----
-0033 % A metabolite's composition can only be resolved from a reaction in which
-0034 % every other participating metabolite already has a known composition;
-0035 % the function repeats this until no further metabolites can be resolved.
-0036 % It requires that the existing compositions and reactions are correct,
-0037 % and throws an error if a metabolite would need inconsistent
-0038 % compositions in different reactions -- so it can also be used to check
-0039 % a model for such errors. Note that successful completion does not
-0040 % guarantee correctness if the pre-existing compositions were themselves
-0041 % wrong.
-0042 
-0043 p=parseRAVENargs(varargin, {'printResults',true});
-0044 printResults=p.printResults;
-0045 
-0046 %The metabolites for which there is no elemental composition
-0047 originalMissing=unique(model.metNames(cellfun(@isempty,model.metFormulas)));
-0048 guessedFor={};
-0049 
-0050 %This is to keep track of if new metabolite compositions were predicted
-0051 predicted=true;
-0052 while predicted==true
-0053     predicted=false;
-0054     
-0055     %Get the unique names (composition should be independent of
-0056     %compartment) for the metabolites without composition
-0057     metNames=unique(model.metNames(cellfun(@isempty,model.metFormulas)));
-0058     
-0059     %Parse the formulas in the model
-0060     [elements, useMat, exitFlag]=parseFormulas(model.metFormulas, true,false);
-0061     
-0062     for i=1:numel(metNames)
-0063         %Get the metabolites with this name. Not so neat, but this is a
-0064         %fast function anyways
-0065         mets=find(ismember(model.metNames,metNames(i)));
-0066         
-0067         currentComp=[];
-0068         
-0069         %Loop through the metabolites -1: Could not assign due to missing
-0070         %info. -2: Could not assign due to contradiction 1: Composition
-0071         %assigned
-0072         metStatus=-1;
-0073         for j=1:numel(mets)
-0074             %Get the reactions that the metabolite participates in
-0075             [~, I]=find(model.S(mets(j),:));
-0076             if any(I)
-0077                 for k=1:numel(I)
-0078                     %Loop through the reactions and check if all other mets
-0079                     %in them have known composition
-0080                     eqn=model.S(:,I(k));
-0081                     %The metabolite's own stoichiometric coefficient in
-0082                     %this reaction, needed to recover its atom counts from
-0083                     %the balance below
-0084                     coeff=eqn(mets(j));
-0085                     eqn(mets(j))=0;
-0086                     if all(exitFlag(eqn~=0)==1)
-0087                         %This means that all other mets had composition.
-0088                         %Calculate the resulting composition for the
-0089                         %unknown one
-0090                         comp=useMat'*eqn;
-0091 
-0092                         %This can result in round off errors if there are
-0093                         %stoichiometries with many decimals. Ignore values
-0094                         %below 10^-12
-0095                         comp(abs(comp)<10^-12)=0;
-0096 
-0097                         %Check if the composition consist of both negative
-0098                         %and positive values. If so, throw an error
-0099                         if all(comp<=0) || all(comp>=0)
-0100                             %Normalize by the metabolite's own coefficient,
-0101                             %so e.g. a coefficient of 2 halves the atom
-0102                             %counts recovered from the balance
-0103                             comp=abs(comp)/abs(coeff);
-0104                             if isempty(currentComp)
-0105                                 currentComp=comp;
-0106                             end
-0107                             %Also to deal with round off errors
-0108                             if all(abs(currentComp-comp)<10^-10)
-0109                                 metStatus=1;
-0110                             else
-0111                                 metStatus=-2;
-0112                                 break;
-0113                                 
-0114                             end
-0115                         else
-0116                             metStatus=-2;
-0117                             break;
-0118                         end
-0119                     end
-0120                 end
-0121                 %If there was contradictions in one compartment, then abort
-0122                 %for all compartments
-0123                 if metStatus==-2
-0124                     break;
-0125                 end
-0126             else
-0127                 %The metabolite does not participate, no composition can be
-0128                 %calculated
-0129                 metStatus=-1;
-0130             end
-0131         end
-0132         %Check status of the metabolite
-0133         switch metStatus
-0134             case -2
-0135                 EM=['Could not predict composition for "' metNames{i} '" due to inconsistencies'];
-0136                 warning('RAVEN:warning', '%s', EM);
-0137             case 1
-0138                 %Calculate and add the composition
-0139                 str=getCompString(elements,comp);
-0140                 model.metFormulas(mets)={str};
-0141                 if printResults==true
-0142                     fprintf(['Predicted composition for "' metNames{i} '" to be ' str '\n']);
-0143                 end
-0144                 
-0145                 %Keep track
-0146                 guessedFor=[guessedFor;metNames(i)];
-0147                 
-0148                 predicted=true; %To loop again
-0149         end
-0150     end
-0151 end
-0152 
-0153 couldNotGuess=setdiff(originalMissing,guessedFor);
-0154 end
-0155 
-0156 %Helper function for getting the composition string
-0157 function str=getCompString(elements,comp)
-0158 str='';
-0159 
-0160 for i=1:numel(comp)
-0161     if comp(i)~=0
-0162         if comp(i)==1
-0163             str=[str elements.abbrevs{i}];
-0164         else
-0165             str=[str elements.abbrevs{i} num2str(comp(i))];
-0166         end
-0167     end
-0168 end
-0169 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/reconstruction/homology/getBlast.html b/doc/reconstruction/homology/getBlast.html deleted file mode 100644 index fe82412e..00000000 --- a/doc/reconstruction/homology/getBlast.html +++ /dev/null @@ -1,310 +0,0 @@ - - - - Description of getBlast - - - - - - - - - -
Home > reconstruction > homology > getBlast.m
- - - -

getBlast -

- -

PURPOSE ^

-
getBlast Bidirectional BLAST between an organism and template organisms.
- -

SYNOPSIS ^

-
function [blastStructure,blastReport]=getBlast(organismID,fastaFile,modelIDs,refFastaFiles,varargin)
- -

DESCRIPTION ^

-
 getBlast  Bidirectional BLAST between an organism and template organisms.
-
- Parameters
- ----------
- organismID : char
-     the id of the organism of interest. This should also match with the
-     id supplied to getModelFromHomology.
- fastaFile : char
-     a FASTA file with the protein sequences for the organism of interest.
- modelIDs : cell
-     a cell array of model ids. These must match the "model.id" fields in
-     the "models" structure if the output is to be used with
-     getModelFromHomology.
- refFastaFiles : cell
-     a cell array with the paths to the corresponding FASTA files.
-
- Name-Value Arguments
- --------------------
- developMode : logical
-     true if blastReport should be generated that is used in the unit
-     testing function for BLAST+ (default false).
- hideVerbose : logical
-     true if no status messages should be printed (default false).
-
- Returns
- -------
- blastStructure : struct
-     structure containing the bidirectional homology measurements that
-     can be used by getModelFromHomology.
- blastReport : struct
-     structure containing MD5 hashes for FASTA database files and
-     non-parsed BLAST output data. Will be blank if developMode is false.
-
- Notes
- -----
- This function calls BLAST+ to perform a bidirectional homology test
- between the organism of interest and a set of other organisms using
- standard settings. The only filtering this function does is the removal
- of hits with an E-value higher than 10e-5.
-
- Examples
- --------
-     [blastStructure,blastReport] = getBlast(organismID,fastaFile,...
-        modelIDs,refFastaFiles,developMode,hideVerbose);
-
- See also
- --------
- getModelFromHomology, getDiamond
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [blastStructure,blastReport]=getBlast(organismID,fastaFile,...
-0002     modelIDs,refFastaFiles,varargin)
-0003 % getBlast  Bidirectional BLAST between an organism and template organisms.
-0004 %
-0005 % Parameters
-0006 % ----------
-0007 % organismID : char
-0008 %     the id of the organism of interest. This should also match with the
-0009 %     id supplied to getModelFromHomology.
-0010 % fastaFile : char
-0011 %     a FASTA file with the protein sequences for the organism of interest.
-0012 % modelIDs : cell
-0013 %     a cell array of model ids. These must match the "model.id" fields in
-0014 %     the "models" structure if the output is to be used with
-0015 %     getModelFromHomology.
-0016 % refFastaFiles : cell
-0017 %     a cell array with the paths to the corresponding FASTA files.
-0018 %
-0019 % Name-Value Arguments
-0020 % --------------------
-0021 % developMode : logical
-0022 %     true if blastReport should be generated that is used in the unit
-0023 %     testing function for BLAST+ (default false).
-0024 % hideVerbose : logical
-0025 %     true if no status messages should be printed (default false).
-0026 %
-0027 % Returns
-0028 % -------
-0029 % blastStructure : struct
-0030 %     structure containing the bidirectional homology measurements that
-0031 %     can be used by getModelFromHomology.
-0032 % blastReport : struct
-0033 %     structure containing MD5 hashes for FASTA database files and
-0034 %     non-parsed BLAST output data. Will be blank if developMode is false.
-0035 %
-0036 % Notes
-0037 % -----
-0038 % This function calls BLAST+ to perform a bidirectional homology test
-0039 % between the organism of interest and a set of other organisms using
-0040 % standard settings. The only filtering this function does is the removal
-0041 % of hits with an E-value higher than 10e-5.
-0042 %
-0043 % Examples
-0044 % --------
-0045 %     [blastStructure,blastReport] = getBlast(organismID,fastaFile,...
-0046 %        modelIDs,refFastaFiles,developMode,hideVerbose);
-0047 %
-0048 % See also
-0049 % --------
-0050 % getModelFromHomology, getDiamond
-0051 
-0052 p=parseRAVENargs(varargin, {'developMode',false; 'hideVerbose',false});
-0053 developMode=p.developMode;
-0054 hideVerbose=p.hideVerbose;
-0055 
-0056 %Everything should be cell arrays
-0057 organismID=convertCharArray(organismID);
-0058 fastaFile=convertCharArray(fastaFile);
-0059 modelIDs=convertCharArray(modelIDs);
-0060 refFastaFiles=convertCharArray(refFastaFiles);
-0061 
-0062 %Create blank structures for results
-0063 blastStructure=[];
-0064 blastReport.dbHashes.phr={};
-0065 blastReport.dbHashes.pot={};
-0066 blastReport.dbHashes.psq={};
-0067 blastReport.dbHashes.pto={};
-0068 blastReport.blastTxtOutput={};
-0069 
-0070 %Get the directory for RAVEN Toolbox
-0071 ravenPath=findRAVENroot();
-0072 
-0073 %Generate temporary names for BLAST databases and output files
-0074 tmpDB=tempname;
-0075 outFile=tempname;
-0076 if ispc && contains(tmpDB,' ')
-0077     warning(['MATLAB assigned ''%s'' as temporary file path, but it '...
-0078              'contains a space character, which is not compatible with '...
-0079              'BLAST. Instead, a temporary folder will be initiated at '...
-0080              '''C:\\tempRAVEN\\'' which should manually be removed when '...
-0081              'finished.'],tmpDB)
-0082     tmpDB=tempname('C:\tempRAVEN');
-0083     outFile=tempname('C:\tempRAVEN');
-0084 end
-0085 
-0086 %Check for existence of files. If no full path is specified for a file,
-0087 %assume that it is in the current folder
-0088 if isrow(refFastaFiles)
-0089     files=horzcat(fastaFile,refFastaFiles);
-0090 else
-0091     files=vertcat(fastaFile,refFastaFiles);
-0092 end
-0093 
-0094 files=checkFileExistence(files,2); %Copy files to temp dir
-0095 fastaFile = files(1);
-0096 refFastaFiles = files(2:end);
-0097 
-0098 %Identify the operating system
-0099 if ispc
-0100     binEnd='.exe';
-0101     setenv('BLASTDB_LMDB_MAP_SIZE','1000000');
-0102 elseif ismac
-0103     binEnd='.mac';
-0104 elseif isunix
-0105     binEnd='';
-0106 else
-0107     error('RAVEN:badInput', '%s', 'Unknown OS, exiting.')
-0108     return
-0109 end
-0110 
-0111 %Fetch the BLAST+ binaries on demand if they are not already present
-0112 if ~exist(fullfile(ravenPath,'software','blast+',['blastp' binEnd]),'file')
-0113     downloadRavenBinaries({'blast+'});
-0114 end
-0115 
-0116 %Run BLAST multi-threaded to use all logical cores assigned to MATLAB
-0117 cores = evalc('feature(''numcores'')');
-0118 cores = strsplit(cores, 'MATLAB was assigned: ');
-0119 cores = regexp(cores{2},'^\d*','match');
-0120 cores = cores{1};
-0121 
-0122 %Create a database for the new organism and blast each of the refFastaFiles
-0123 %against it
-0124 [status, message]=system(['"' fullfile(ravenPath,'software','blast+',['makeblastdb' binEnd]) '" -in "' fastaFile{1} '" -out "' fullfile(tmpDB, 'tmpDB') '" -dbtype prot']);
-0125 if developMode
-0126     blastReport.dbHashes.phr{numel(blastReport.dbHashes.phr)+1}=filemd5(fullfile(tmpDB, 'tmpDB.phr'));
-0127     blastReport.dbHashes.pot{numel(blastReport.dbHashes.pot)+1}=filemd5(fullfile(tmpDB, 'tmpDB.pot'));
-0128     blastReport.dbHashes.psq{numel(blastReport.dbHashes.psq)+1}=filemd5(fullfile(tmpDB, 'tmpDB.psq'));
-0129     blastReport.dbHashes.pto{numel(blastReport.dbHashes.pto)+1}=filemd5(fullfile(tmpDB, 'tmpDB.pto'));
-0130 end
-0131 if status~=0
-0132     error('makeblastdb did not run successfully, error:\n%s',strip(message))
-0133 end
-0134 
-0135 for i=1:numel(refFastaFiles)
-0136     if ~hideVerbose
-0137         fprintf(['BLASTing "' modelIDs{i} '" against "' organismID{1} '"..\n']);
-0138     end
-0139     [status, message]=system(['"' fullfile(ravenPath,'software','blast+',['blastp' binEnd]) '" -query "' refFastaFiles{i} '" -out "' outFile '_' num2str(i) '" -db "' fullfile(tmpDB, 'tmpDB') '" -evalue 10e-5 -outfmt "10 qseqid sseqid evalue pident length bitscore ppos" -num_threads "' cores '"']);
-0140     if developMode
-0141         blastReport.blastTxtOutput{numel(blastReport.blastTxtOutput)+1}=importdata([outFile '_' num2str(i)]);
-0142     end
-0143     if status~=0
-0144         error('blastp did not run successfully, error:\n%s',strip(message))
-0145     end
-0146 end
-0147 delete([tmpDB filesep 'tmpDB*']);
-0148 
-0149 %Then create a database for each of the reference organisms and blast the
-0150 %new organism against them
-0151 for i=1:numel(refFastaFiles)
-0152     if ~hideVerbose
-0153         fprintf(['BLASTing "' organismID{1} '" against "' modelIDs{i} '"..\n']);
-0154     end
-0155     [status, message]=system(['"' fullfile(ravenPath,'software','blast+',['makeblastdb' binEnd]) '" -in "' refFastaFiles{i} '" -out "' fullfile(tmpDB, 'tmpDB') '" -dbtype prot']);
-0156     if status~=0
-0157         error('makeblastdb did not run successfully, error:\n%s',strip(message))
-0158     end
-0159     [status, message]=system(['"' fullfile(ravenPath,'software','blast+',['blastp' binEnd]) '" -query "' fastaFile{1} '" -out "' outFile '_r' num2str(i) '" -db "' fullfile(tmpDB, 'tmpDB') '" -evalue 10e-5 -outfmt "10 qseqid sseqid evalue pident length bitscore ppos" -num_threads "' cores '"']);
-0160     if developMode
-0161         blastReport.dbHashes.phr{numel(blastReport.dbHashes.phr)+1}=filemd5(fullfile(tmpDB, 'tmpDB.phr'));
-0162         blastReport.dbHashes.pot{numel(blastReport.dbHashes.pot)+1}=filemd5(fullfile(tmpDB, 'tmpDB.pot'));
-0163         blastReport.dbHashes.psq{numel(blastReport.dbHashes.psq)+1}=filemd5(fullfile(tmpDB, 'tmpDB.psq'));
-0164         blastReport.dbHashes.pto{numel(blastReport.dbHashes.pto)+1}=filemd5(fullfile(tmpDB, 'tmpDB.pto'));
-0165         blastReport.blastTxtOutput{numel(blastReport.blastTxtOutput)+1}=importdata([outFile '_r' num2str(i)]);
-0166     end    
-0167     if status~=0
-0168         error('blastp did not run successfully, error:\n%s',strip(message))
-0169     end
-0170     delete([tmpDB filesep 'tmpDB*']);
-0171 end
-0172 
-0173 %Done with the BLAST, do the parsing of the text files
-0174 for i=1:numel(refFastaFiles)*2
-0175     tempStruct=[];
-0176     if i<=numel(refFastaFiles)
-0177         tempStruct.fromId=modelIDs{i};
-0178         tempStruct.toId=organismID{1};
-0179         A=readtable([outFile '_' num2str(i)],'Delimiter',',','Format','%s%s%f%f%f%f%f');
-0180     else
-0181         tempStruct.fromId=organismID{1};
-0182         tempStruct.toId=modelIDs{i-numel(refFastaFiles)};
-0183         A=readtable([outFile '_r' num2str(i-numel(refFastaFiles))],'Delimiter',',','Format','%s%s%f%f%f%f%f');
-0184     end
-0185     tempStruct.fromGenes=A{:,1};
-0186     tempStruct.toGenes=A{:,2};
-0187     tempStruct.evalue=table2array(A(:,3));
-0188     tempStruct.identity=table2array(A(:,4));
-0189     tempStruct.aligLen=table2array(A(:,5));
-0190     tempStruct.bitscore=table2array(A(:,6));
-0191     tempStruct.ppos=table2array(A(:,7));
-0192     blastStructure=[blastStructure tempStruct];
-0193 end
-0194 
-0195 %Remove the old tempfiles
-0196 delete([outFile '*']);
-0197 %Remove the temp fasta files
-0198 delete(files{:})
-0199 end
-0200 
-0201 function hash = filemd5(path)
-0202 %Compute MD5 of a file via the Java MessageDigest API (no toolbox required).
-0203 md  = java.security.MessageDigest.getInstance('MD5');
-0204 fis = java.io.FileInputStream(java.io.File(path));
-0205 buf = javaArray('byte', 65536);
-0206 while true
-0207     n = fis.read(buf);
-0208     if n < 0; break; end
-0209     md.update(buf, 0, n);
-0210 end
-0211 fis.close();
-0212 bytes = typecast(md.digest(), 'uint8');
-0213 hash  = lower(sprintf('%02x', bytes));
-0214 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/reconstruction/homology/getDiamond.html b/doc/reconstruction/homology/getDiamond.html deleted file mode 100644 index 7f8fc140..00000000 --- a/doc/reconstruction/homology/getDiamond.html +++ /dev/null @@ -1,278 +0,0 @@ - - - - Description of getDiamond - - - - - - - - - -
Home > reconstruction > homology > getDiamond.m
- - - -

getDiamond -

- -

PURPOSE ^

-
getDiamond Bidirectional BLAST with DIAMOND against template organisms.
- -

SYNOPSIS ^

-
function [blastStructure,diamondReport]=getDiamond(organismID,fastaFile,modelIDs,refFastaFiles,varargin)
- -

DESCRIPTION ^

-
 getDiamond  Bidirectional BLAST with DIAMOND against template organisms.
-
- Parameters
- ----------
- organismID : char
-     the id of the organism of interest. This should also match with the
-     id supplied to getModelFromHomology.
- fastaFile : char
-     a FASTA file with the protein sequences for the organism of interest.
- modelIDs : cell
-     a cell array of model ids. These must match the "model.id" fields in
-     the "models" structure if the output is to be used with
-     getModelFromHomology.
- refFastaFiles : cell
-     a cell array with the paths to the corresponding FASTA files.
-
- Name-Value Arguments
- --------------------
- developMode : logical
-     true if blastReport should be generated that is used in the unit
-     testing function for DIAMOND (default false).
- hideVerbose : logical
-     true if no status messages should be printed (default false).
-
- Returns
- -------
- blastStructure : struct
-     structure containing the bidirectional homology measurements which
-     are used by getModelFromHomology.
- diamondReport : struct
-     structure containing MD5 hashes for FASTA database files and
-     non-parsed BLAST output data. Will be blank if developMode is false.
-
- Notes
- -----
- This function calls DIAMOND to perform a bidirectional homology search
- between the organism of interest and a set of other organisms using the
- "--more-sensitive" setting from DIAMOND. For the most sensitive results,
- the use of getBlast() is adviced, however, getDiamond() is a fast
- alternative (>15x faster). The blastStructure generated is in the same
- format as those obtained from getBlast().
-
- Examples
- --------
-     [blastStructure,diamondReport] = getDiamond(organismID,fastaFile,...
-        modelIDs,refFastaFiles,developMode,hideVerbose);
-
- See also
- --------
- getModelFromHomology, getBlast
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function [blastStructure,diamondReport]=getDiamond(organismID,fastaFile,...
-0002     modelIDs,refFastaFiles,varargin)
-0003 % getDiamond  Bidirectional BLAST with DIAMOND against template organisms.
-0004 %
-0005 % Parameters
-0006 % ----------
-0007 % organismID : char
-0008 %     the id of the organism of interest. This should also match with the
-0009 %     id supplied to getModelFromHomology.
-0010 % fastaFile : char
-0011 %     a FASTA file with the protein sequences for the organism of interest.
-0012 % modelIDs : cell
-0013 %     a cell array of model ids. These must match the "model.id" fields in
-0014 %     the "models" structure if the output is to be used with
-0015 %     getModelFromHomology.
-0016 % refFastaFiles : cell
-0017 %     a cell array with the paths to the corresponding FASTA files.
-0018 %
-0019 % Name-Value Arguments
-0020 % --------------------
-0021 % developMode : logical
-0022 %     true if blastReport should be generated that is used in the unit
-0023 %     testing function for DIAMOND (default false).
-0024 % hideVerbose : logical
-0025 %     true if no status messages should be printed (default false).
-0026 %
-0027 % Returns
-0028 % -------
-0029 % blastStructure : struct
-0030 %     structure containing the bidirectional homology measurements which
-0031 %     are used by getModelFromHomology.
-0032 % diamondReport : struct
-0033 %     structure containing MD5 hashes for FASTA database files and
-0034 %     non-parsed BLAST output data. Will be blank if developMode is false.
-0035 %
-0036 % Notes
-0037 % -----
-0038 % This function calls DIAMOND to perform a bidirectional homology search
-0039 % between the organism of interest and a set of other organisms using the
-0040 % "--more-sensitive" setting from DIAMOND. For the most sensitive results,
-0041 % the use of getBlast() is adviced, however, getDiamond() is a fast
-0042 % alternative (>15x faster). The blastStructure generated is in the same
-0043 % format as those obtained from getBlast().
-0044 %
-0045 % Examples
-0046 % --------
-0047 %     [blastStructure,diamondReport] = getDiamond(organismID,fastaFile,...
-0048 %        modelIDs,refFastaFiles,developMode,hideVerbose);
-0049 %
-0050 % See also
-0051 % --------
-0052 % getModelFromHomology, getBlast
-0053 
-0054 p=parseRAVENargs(varargin, {'developMode',false; 'hideVerbose',false});
-0055 developMode=p.developMode;
-0056 hideVerbose=p.hideVerbose;
-0057 
-0058 %Everything should be cell arrays
-0059 organismID=convertCharArray(organismID);
-0060 fastaFile=convertCharArray(fastaFile);
-0061 modelIDs=convertCharArray(modelIDs);
-0062 refFastaFiles=convertCharArray(refFastaFiles);
-0063 
-0064 %Create blank structures for results
-0065 blastStructure=[];
-0066 diamondReport.dbHashes={};
-0067 diamondReport.diamondTxtOutput={};
-0068 
-0069 %Get the directory for RAVEN Toolbox.
-0070 ravenPath=findRAVENroot();
-0071 
-0072 %Generate temporary names for DIAMOND databases and output files
-0073 tmpDB=tempname;
-0074 outFile=tempname;
-0075 
-0076 %Check for existence of files. If no full path is specified for a file,
-0077 %assume that it is in the current folder
-0078 if isrow(refFastaFiles)
-0079     files=horzcat(fastaFile,refFastaFiles);
-0080 else
-0081     files=vertcat(fastaFile,refFastaFiles);
-0082 end
-0083 
-0084 files=checkFileExistence(files,2); %Copy files to temp dir
-0085 fastaFile = files(1);
-0086 refFastaFiles = files(2:end);
-0087 
-0088 %Identify the operating system
-0089 if ispc
-0090     binEnd='.exe';
-0091 elseif ismac
-0092     binEnd='.mac';
-0093 elseif isunix
-0094     binEnd='';
-0095 else
-0096     error('RAVEN:badInput', '%s', 'Unknown OS, exiting.')
-0097     return
-0098 end
-0099 
-0100 %Fetch the DIAMOND binary on demand if it is not already present
-0101 if ~exist(fullfile(ravenPath,'software','diamond',['diamond' binEnd]),'file')
-0102     downloadRavenBinaries({'diamond'});
-0103 end
-0104 
-0105 %Run DIAMOND multi-threaded to use all logical cores assigned to MATLAB.
-0106 cores = evalc('feature(''numcores'')');
-0107 cores = strsplit(cores, 'MATLAB was assigned: ');
-0108 cores = regexp(cores{2},'^\d*','match');
-0109 cores = cores{1};
-0110 
-0111 %Create a database for the new organism and blast each of the refFastaFiles
-0112 %against it
-0113 [status, message]=system(['"' fullfile(ravenPath,'software','diamond',['diamond' binEnd]) '" makedb --in "' fastaFile{1} '" --db "' fullfile(tmpDB) '"']);
-0114 if developMode
-0115     diamondReport.dbHashes{numel(diamondReport.dbHashes)+1} = char(regexp(message,'[a-f0-9]{32}','match'));
-0116 end
-0117 if status~=0
-0118     error('DIAMOND makedb did not run successfully, error:\n%s',strip(message))
-0119 end
-0120 
-0121 for i=1:numel(refFastaFiles)
-0122     if ~hideVerbose
-0123         fprintf(['Running DIAMOND blastp with "' modelIDs{i} '" against "' organismID{1} '"..\n']);
-0124     end
-0125     [status, message]=system(['"' fullfile(ravenPath,'software','diamond',['diamond' binEnd]) '" blastp --query "' refFastaFiles{i} '" --out "' outFile '_' num2str(i) '" --db "' fullfile(tmpDB) '" --more-sensitive --outfmt 6 qseqid sseqid evalue pident length bitscore ppos --threads ' cores ]);
-0126     if developMode
-0127         diamondReport.diamondTxtOutput{numel(diamondReport.diamondTxtOutput)+1}=importdata([outFile '_' num2str(i)]);
-0128     end
-0129     if status~=0
-0130         error('DIAMOND blastp did not run successfully, error:\n%s',strip(message))
-0131     end
-0132 end
-0133 delete([tmpDB filesep 'tmpDB*']);
-0134 
-0135 %Then create a database for each of the reference organisms and blast the
-0136 %new organism against them
-0137 for i=1:numel(refFastaFiles)
-0138     if ~hideVerbose
-0139         fprintf(['Running DIAMOND blastp with "' organismID{1} '" against "' modelIDs{i} '"..\n']);
-0140     end
-0141     [status, message1]=system(['"' fullfile(ravenPath,'software','diamond',['diamond' binEnd]) '" makedb --in "' refFastaFiles{i} '" --db "' fullfile(tmpDB) '"']);
-0142     if status~=0
-0143         error('DIAMOND makedb did not run successfully, error:\n%s',strip(message1))
-0144     end
-0145     [status, message]=system(['"' fullfile(ravenPath,'software','diamond',['diamond' binEnd]) '" blastp --query "' fastaFile{1} '" --out "' outFile '_r' num2str(i) '" --db "' fullfile(tmpDB) '" --more-sensitive --outfmt 6 qseqid sseqid evalue pident length bitscore ppos --threads ' cores]);
-0146     if developMode
-0147         diamondReport.dbHashes{numel(diamondReport.dbHashes)+1} = char(regexp(message1,'[a-f0-9]{32}','match'));
-0148         diamondReport.diamondTxtOutput{numel(diamondReport.diamondTxtOutput)+1}=importdata([outFile '_r' num2str(i)]);
-0149     end
-0150     if status~=0
-0151         error('DIAMOND blastp did not run successfully, error:\n%s',strip(message))
-0152     end
-0153     delete([tmpDB filesep 'tmpDB*']);
-0154 end
-0155 
-0156 %Done with the DIAMOND blastp, do the parsing of the text files
-0157 for i=1:numel(refFastaFiles)*2
-0158     tempStruct=[];
-0159     if i<=numel(refFastaFiles)
-0160         tempStruct.fromId=modelIDs{i};
-0161         tempStruct.toId=organismID{1};
-0162         A=readtable([outFile '_' num2str(i)],'Delimiter','\t','Format','%s%s%f%f%f%f%f');
-0163     else
-0164         tempStruct.fromId=organismID{1};
-0165         tempStruct.toId=modelIDs{i-numel(refFastaFiles)};
-0166         A=readtable([outFile '_r' num2str(i-numel(refFastaFiles))],'Delimiter','\t','Format','%s%s%f%f%f%f%f');
-0167     end
-0168     tempStruct.fromGenes=A{:,1};
-0169     tempStruct.toGenes=A{:,2};
-0170     tempStruct.evalue=table2array(A(:,3));
-0171     tempStruct.identity=table2array(A(:,4));
-0172     tempStruct.aligLen=table2array(A(:,5));
-0173     tempStruct.bitscore=table2array(A(:,6));
-0174     tempStruct.ppos=table2array(A(:,7));
-0175     blastStructure=[blastStructure tempStruct];
-0176 end
-0177 
-0178 %Remove the old tempfiles
-0179 delete([outFile '*']);
-0180 %Remove the temp fasta files
-0181 delete(files{:})
-0182 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/reconstruction/homology/getModelFromHomology.html b/doc/reconstruction/homology/getModelFromHomology.html deleted file mode 100644 index d1a458c2..00000000 --- a/doc/reconstruction/homology/getModelFromHomology.html +++ /dev/null @@ -1,778 +0,0 @@ - - - - Description of getModelFromHomology - - - - - - - - - -
Home > reconstruction > homology > getModelFromHomology.m
- - - -

getModelFromHomology -

- -

PURPOSE ^

-
getModelFromHomology Construct a new model from existing models and homology.
- -

SYNOPSIS ^

-
function [draftModel, hitGenes]=getModelFromHomology(models,blastStructure,getModelFor,varargin)
- -

DESCRIPTION ^

-
 getModelFromHomology  Construct a new model from existing models and homology.
-
- Constructs a new model from a set of existing models and gene homology
- information.
-
- Parameters
- ----------
- models : cell
-     a cell array of model structures to build the model from. These
-     models must be sorted by importance in decreasing order.
- blastStructure : struct
-     a blastStructure as produced by getBlast or makeFakeBlastStructure.
- getModelFor : char
-     a three-four letter abbreviation of the organism to build a model
-     for. Must have BLASTP hits in both directions to the organisms in
-     "models".
-
- Name-Value Arguments
- --------------------
- preferredOrder : cell
-     the order in which reactions should be added from the models. If not
-     supplied, reactions will be included from all models, otherwise one
-     gene will only result in reactions from one model (default {}).
- strictness : double
-     integer that specifies which reactions should be included (default 1):
-
-     - 1 : map new genes to old for all pairs which have acceptable BLASTP
-       results in both directions.
-     - 2 : map new genes to old for all pairs which have acceptable BLASTP
-       results in the correspondent direction (mapping can be done in the
-       opposite direction, see mapNewGenesToOld below).
-     - 3 : check all BLASTP results and retain only the best hit (by
-       bitscore) for each gene pair in each direction separately, then map
-       new genes to old for all pairs which have acceptable BLASTP results
-       in both directions.
- onlyGenesInModels : logical
-     consider BLASTP results only for genes that exist in the models. This
-     tends to import a larger fraction from the existing models but may
-     give less reliable results. Has effect only if strictness=3
-     (default false).
- maxE : double
-     only look at genes with E-values <= this value (default 10^-30).
- minLen : double
-     only look at genes with alignment length >= this value (default 100).
-     Measured against KEGG and OMA orthology across four organisms: any
-     value at or below 150 performs the same, while higher values discard
-     real orthologs without reducing the number of wrong ones.
- minIde : double
-     only look at genes with identity >= this value (default 40 (%)).
-     The filter that decides the outcome, and 40 is the measured optimum
-     once a wrongly transferred reaction is treated as worse than a missing
-     one. maxE, by contrast, changes nothing between 10^-4 and 10^-50.
- mapNewGenesToOld : logical
-     determines how to match genes if not looking at only 1-1 orthologs.
-     Either map the new genes to the old or old genes to new. The default
-     is to map the new genes (default true).
-
- Returns
- -------
- draftModel : struct
-     a model structure for the new organism.
- hitGenes : struct
-     collect the old and new genes.
-
- Examples
- --------
-     draftModel = getModelFromHomology(models, blastStructure, getModelFor);
-
- Notes
- -----
- The models in the "models" structure should have named the metabolites in
- the same manner, have their reversible reactions in the same direction
- (run sortModel), and use the same compartment names. To avoid keeping
- unneccesary old genes, the models should not have "or"-relations in their
- grRules (use expandModel).
-
- The resulting draft model contains only reactions associated with
- orthologous genes. The old (original) genes involved in "and" relations
- in grRules without any orthologs are still included in the draft model as
- OLD_MODELID_geneName.
-
- "to" and "from" means relative to the new organism.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [draftModel, hitGenes]=getModelFromHomology(models,blastStructure,...
-0002     getModelFor,varargin)
-0003 % getModelFromHomology  Construct a new model from existing models and homology.
-0004 %
-0005 % Constructs a new model from a set of existing models and gene homology
-0006 % information.
-0007 %
-0008 % Parameters
-0009 % ----------
-0010 % models : cell
-0011 %     a cell array of model structures to build the model from. These
-0012 %     models must be sorted by importance in decreasing order.
-0013 % blastStructure : struct
-0014 %     a blastStructure as produced by getBlast or makeFakeBlastStructure.
-0015 % getModelFor : char
-0016 %     a three-four letter abbreviation of the organism to build a model
-0017 %     for. Must have BLASTP hits in both directions to the organisms in
-0018 %     "models".
-0019 %
-0020 % Name-Value Arguments
-0021 % --------------------
-0022 % preferredOrder : cell
-0023 %     the order in which reactions should be added from the models. If not
-0024 %     supplied, reactions will be included from all models, otherwise one
-0025 %     gene will only result in reactions from one model (default {}).
-0026 % strictness : double
-0027 %     integer that specifies which reactions should be included (default 1):
-0028 %
-0029 %     - 1 : map new genes to old for all pairs which have acceptable BLASTP
-0030 %       results in both directions.
-0031 %     - 2 : map new genes to old for all pairs which have acceptable BLASTP
-0032 %       results in the correspondent direction (mapping can be done in the
-0033 %       opposite direction, see mapNewGenesToOld below).
-0034 %     - 3 : check all BLASTP results and retain only the best hit (by
-0035 %       bitscore) for each gene pair in each direction separately, then map
-0036 %       new genes to old for all pairs which have acceptable BLASTP results
-0037 %       in both directions.
-0038 % onlyGenesInModels : logical
-0039 %     consider BLASTP results only for genes that exist in the models. This
-0040 %     tends to import a larger fraction from the existing models but may
-0041 %     give less reliable results. Has effect only if strictness=3
-0042 %     (default false).
-0043 % maxE : double
-0044 %     only look at genes with E-values <= this value (default 10^-30).
-0045 % minLen : double
-0046 %     only look at genes with alignment length >= this value (default 100).
-0047 %     Measured against KEGG and OMA orthology across four organisms: any
-0048 %     value at or below 150 performs the same, while higher values discard
-0049 %     real orthologs without reducing the number of wrong ones.
-0050 % minIde : double
-0051 %     only look at genes with identity >= this value (default 40 (%)).
-0052 %     The filter that decides the outcome, and 40 is the measured optimum
-0053 %     once a wrongly transferred reaction is treated as worse than a missing
-0054 %     one. maxE, by contrast, changes nothing between 10^-4 and 10^-50.
-0055 % mapNewGenesToOld : logical
-0056 %     determines how to match genes if not looking at only 1-1 orthologs.
-0057 %     Either map the new genes to the old or old genes to new. The default
-0058 %     is to map the new genes (default true).
-0059 %
-0060 % Returns
-0061 % -------
-0062 % draftModel : struct
-0063 %     a model structure for the new organism.
-0064 % hitGenes : struct
-0065 %     collect the old and new genes.
-0066 %
-0067 % Examples
-0068 % --------
-0069 %     draftModel = getModelFromHomology(models, blastStructure, getModelFor);
-0070 %
-0071 % Notes
-0072 % -----
-0073 % The models in the "models" structure should have named the metabolites in
-0074 % the same manner, have their reversible reactions in the same direction
-0075 % (run sortModel), and use the same compartment names. To avoid keeping
-0076 % unneccesary old genes, the models should not have "or"-relations in their
-0077 % grRules (use expandModel).
-0078 %
-0079 % The resulting draft model contains only reactions associated with
-0080 % orthologous genes. The old (original) genes involved in "and" relations
-0081 % in grRules without any orthologs are still included in the draft model as
-0082 % OLD_MODELID_geneName.
-0083 %
-0084 % "to" and "from" means relative to the new organism.
-0085 
-0086 hitGenes.oldGenes = [];  % collect the old genes from the template model (organism)
-0087 hitGenes.newGenes = [];  % collect the new genes of the draft model (target organism)
-0088 
-0089 getModelFor=char(getModelFor);
-0090 
-0091 p=parseRAVENargs(varargin, {'preferredOrder',[]; 'strictness',1; ...
-0092     'onlyGenesInModels',false; 'maxE',10^-30; 'minLen',100; 'minIde',40; ...
-0093     'mapNewGenesToOld',true});
-0094 preferredOrder=p.preferredOrder;
-0095 if isempty(preferredOrder)
-0096     preferredOrder=[];
-0097 else
-0098     preferredOrder=convertCharArray(preferredOrder);
-0099     [row,col]=size(preferredOrder);
-0100     if col>row
-0101         preferredOrder=transpose(preferredOrder);
-0102     end
-0103 end
-0104 switch p.strictness
-0105     case 2
-0106         bidirectional=false; bestHitsOnly=false;
-0107     case 3
-0108         bidirectional=true;  bestHitsOnly=true;
-0109     otherwise
-0110         bidirectional=true;  bestHitsOnly=false;
-0111 end
-0112 onlyGenesInModels=p.onlyGenesInModels;
-0113 maxE=p.maxE;
-0114 minLen=p.minLen;
-0115 minIde=p.minIde;
-0116 mapNewGenesToOld=p.mapNewGenesToOld;
-0117 
-0118 if isfield(models,'S')
-0119     models={models};
-0120 end
-0121     
-0122 %Check that all the information is in the blast structure
-0123 modelNames=cell(numel(models),1);
-0124 for i=1:numel(models)
-0125     modelNames{i}=models{i}.id;
-0126     %Gene short names, geneMiriams and proteins are often different
-0127     %between species, safer not to include them
-0128     if isfield(models{i},'geneShortNames')
-0129         models{i}=rmfield(models{i},'geneShortNames');
-0130     end
-0131     if isfield(models{i},'geneMiriams')
-0132         models{i}=rmfield(models{i},'geneMiriams');
-0133     end
-0134     if isfield(models{i},'proteins')
-0135         models{i}=rmfield(models{i},'proteins');
-0136     end
-0137     %The geneFrom field also loses meaning if the genes are replaced by
-0138     %orthologs
-0139     if isfield(models{i},'geneFrom')
-0140         models{i}=rmfield(models{i},'geneFrom');
-0141     end
-0142 end
-0143 
-0144 %Check that genes do not begin with ( or end with ), as this makes problematic grRules
-0145 for i=1:numel(blastStructure)
-0146     problemGenes = startsWith(blastStructure(i).fromGenes,'(') | endsWith(blastStructure(i).fromGenes,')');
-0147     if any(problemGenes)
-0148         error(['One or multiple gene identifiers from ' blastStructure(i).fromId ...
-0149                ' starts with ''('' and/or ends with '')'', which is not allowed'])
-0150     end
-0151 end
-0152 
-0153 %Assume for now that all information is there and that it is correct. This
-0154 %is important to fix since no further checks are being made!
-0155 
-0156 %Check whether provided fasta files use the same gene identifiers as
-0157 %provided template models
-0158 for i=1:numel(blastStructure)
-0159     if ~strcmp(blastStructure(i).fromId,getModelFor)
-0160         j=strcmpi(blastStructure(i).fromId,modelNames);
-0161         if j==0
-0162             error(['While the blastStructure contains sequences from '...
-0163                 'organismID "%s" (as\nprovided in getBlast), none of '...
-0164                 'template models have this id (as model.id)'],...
-0165                 string(blastStructure(i).fromId));
-0166         end
-0167         k=sum(ismember(blastStructure(i).fromGenes,models{j}.genes));
-0168         if k<(numel(models{j}.genes)*0.05)
-0169             error(['Less than 5%% of the genes in the template model '...
-0170                 'with model.id "%s"\ncan be found in the blastStructure. '...
-0171                 'Ensure that the protein FASTA\nused in getBlast and '...
-0172                 'the template model used in getModelFromHomology\nuse '...
-0173                 'the same style of gene identifiers'],models{j}.id)
-0174         end
-0175     end
-0176 end
-0177 
-0178 %Standardize grRules of template models
-0179 for i=1:length(models)
-0180     fprintf('\nStandardizing grRules of template model with ID "%s" ...',models{i}.id);
-0181     [models{i}.grRules,models{i}.rxnGeneMat]=standardizeGrRules(models{i},false);
-0182 end
-0183 fprintf(' done\n');
-0184 
-0185 %Remove all gene matches that are below the cutoffs
-0186 for i=1:numel(blastStructure)
-0187     indexes=blastStructure(i).evalue<maxE & blastStructure(i).aligLen>=minLen & blastStructure(i).identity>=minIde; %Do it in this direction to lose NaNs
-0188     blastStructure(i).fromGenes(~indexes)=[];
-0189     blastStructure(i).toGenes(~indexes)=[];
-0190     blastStructure(i).evalue(~indexes)=[];
-0191     blastStructure(i).identity(~indexes)=[];
-0192     blastStructure(i).aligLen(~indexes)=[];
-0193     blastStructure(i).bitscore(~indexes)=[];
-0194     blastStructure(i).ppos(~indexes)=[];
-0195 end
-0196 
-0197 %Remove all reactions from the models that have no genes encoding for them.
-0198 %Also remove all genes that encode for no reactions. There should not be any
-0199 %but there might be mistakes
-0200 for i=1:numel(models)
-0201     [hasGenes, ~]=find(models{i}.rxnGeneMat);
-0202     hasNoGenes=1:numel(models{i}.rxns);
-0203     hasNoGenes(hasGenes)=[];
-0204     models{i}=removeReactions(models{i},hasNoGenes,true,true);
-0205 end
-0206 
-0207 %Create a structure that contains all genes used in the blasts in any
-0208 %direction for each of the models in "models" and for the new organism. The
-0209 %first cell is for the new organism and then according to the preferred
-0210 %order. If no such order is supplied, then according to the order in
-0211 %"models"
-0212 allGenes=cell(numel(models)+1,1);
-0213 if isempty(preferredOrder)
-0214     useOrder=modelNames;
-0215 else
-0216     useOrder=preferredOrder;
-0217 end
-0218 
-0219 %Get the corresponding indexes for those models in the "models" structure
-0220 useOrderIndexes=zeros(numel(models),1);
-0221 for i=1:numel(models)
-0222     [~, index]=ismember(models{i}.id,useOrder);
-0223     useOrderIndexes(index)=i;
-0224 end
-0225 
-0226 %Remove all genes from the blast structure that have no genes in the models
-0227 if onlyGenesInModels==true
-0228     modelGenes={};
-0229     for i=1:numel(models)
-0230         modelGenes=[modelGenes;models{i}.genes];
-0231     end
-0232     for i=1:numel(blastStructure)
-0233         %Check to see if it should match the toId or fromId
-0234         if strcmpi(blastStructure(i).fromId,getModelFor)
-0235             I=ismember(blastStructure(i).toGenes,modelGenes);
-0236         else
-0237             I=ismember(blastStructure(i).fromGenes,modelGenes);
-0238         end
-0239         blastStructure(i).fromGenes(~I)=[];
-0240         blastStructure(i).toGenes(~I)=[];
-0241         blastStructure(i).evalue(~I)=[];
-0242         blastStructure(i).identity(~I)=[];
-0243         blastStructure(i).aligLen(~I)=[];
-0244         blastStructure(i).bitscore(~I)=[];
-0245         blastStructure(i).ppos(~I)=[];
-0246         
-0247         %Check that no matching in blastStructure is empty. This happens if
-0248         %no genes in the models are present in the corresponding sheet
-0249         if isempty(blastStructure(i).fromGenes)
-0250             EM=['No genes in matching from ' blastStructure(i).fromId ' to ' blastStructure(i).toId ' are present in the corresponding model'];
-0251             error('RAVEN:badInput', '%s', EM);
-0252         end
-0253     end
-0254 end
-0255 
-0256 %If only best orthologs are to be used then all other measurements are
-0257 %deleted from the blastStructure. All code after this stays the same. This
-0258 %means that preferred order can still matter. The best ortholog is selected
-0259 %by bitscore, which unlike E-value does not shift when the search database
-0260 %changes size.
-0261 if bestHitsOnly
-0262     for i=1:numel(blastStructure)
-0263         keep=false(numel(blastStructure(i).toGenes),1);
-0264         [allFromGenes, ~, I]=unique(blastStructure(i).fromGenes);
-0265 
-0266         %It would be nice to get rid of this loop
-0267         for j=1:numel(allFromGenes)
-0268             allMatches=find(I==j);
-0269             bestMatches=allMatches(blastStructure(i).bitscore(allMatches)==max(blastStructure(i).bitscore(allMatches)));
-0270 
-0271             %Keep the best matches
-0272             keep(bestMatches)=true;
-0273         end
-0274 
-0275         %Delete all matches that were not best matches
-0276         blastStructure(i).fromGenes(~keep)=[];
-0277         blastStructure(i).toGenes(~keep)=[];
-0278         blastStructure(i).evalue(~keep)=[];
-0279         blastStructure(i).identity(~keep)=[];
-0280         blastStructure(i).aligLen(~keep)=[];
-0281         blastStructure(i).bitscore(~keep)=[];
-0282         blastStructure(i).ppos(~keep)=[];
-0283     end
-0284 end
-0285 
-0286 useOrder=[{getModelFor};useOrder];
-0287 
-0288 for i=1:numel(blastStructure)
-0289     [~, toIndex]=ismember(blastStructure(i).toId,useOrder);
-0290     [~, fromIndex]=ismember(blastStructure(i).fromId,useOrder);
-0291     
-0292     %Add all genes to the corresponding list in allGenes
-0293     allGenes{toIndex}=[allGenes{toIndex};blastStructure(i).toGenes];
-0294     allGenes{fromIndex}=[allGenes{fromIndex};blastStructure(i).fromGenes];
-0295 end
-0296 
-0297 %Keep only the unique gene names
-0298 maxOtherGeneNr=0; %Determines the dimension of the connectivity matrixes
-0299 for i=1:numel(allGenes)
-0300     allGenes{i}=unique(allGenes{i});
-0301     if i>1
-0302         if numel(allGenes{i})>maxOtherGeneNr
-0303             maxOtherGeneNr=numel(allGenes{i});
-0304         end
-0305     end
-0306 end
-0307 
-0308 %Generate a cell array of matrixes that describes how the genes in the new
-0309 %organism map to the models. Each cell matches to the corresponding model
-0310 %in useOrder (starting at 2 of course). First dimension is gene in new
-0311 %organism, second which gene it is in the other organism. The second matrix
-0312 %describes how they map back.
-0313 
-0314 %As it is now, a significant match is indicated by a 1. This could be
-0315 %expanded to contain some kind of significance level. The first dimension
-0316 %is still the genes in the new model.
-0317 
-0318 allTo=cell(numel(useOrder)-1,1);
-0319 allFrom=cell(numel(useOrder)-1,1);
-0320 
-0321 for i=1:numel(useOrder)-1
-0322     allTo{i}=sparse(numel(allGenes{1}),numel(allGenes{i+1}));
-0323     allFrom{i}=sparse(numel(allGenes{1}),numel(allGenes{i+1}));
-0324 end
-0325 
-0326 %Fill the matches to other species
-0327 for i=1:numel(blastStructure)
-0328     if strcmp(blastStructure(i).toId,getModelFor)
-0329         %This was "to" the new organism. They should all match so no checks
-0330         %are being made
-0331         [~, a]=ismember(blastStructure(i).toGenes,allGenes{1});
-0332         [~, fromModel]=ismember(blastStructure(i).fromId,useOrder);
-0333         [~, b]=ismember(blastStructure(i).fromGenes,allGenes{fromModel});
-0334         idx = sub2ind(size(allTo{fromModel-1}), a, b);
-0335         allTo{fromModel-1}(idx)=1;
-0336     else
-0337         %This was "from" the new organism
-0338         [~, a]=ismember(blastStructure(i).fromGenes,allGenes{1});
-0339         [~, toModel]=ismember(blastStructure(i).toId,useOrder);
-0340         [~, b]=ismember(blastStructure(i).toGenes,allGenes{toModel});
-0341         idx = sub2ind(size(allFrom{toModel-1}), a, b);
-0342         allFrom{toModel-1}(idx)=1;
-0343     end
-0344 end
-0345 
-0346 %Now we have all the gene matches in a convenient way. For all the genes in
-0347 %the new organism get the genes that should be included from other
-0348 %organisms. If all genes should be included this simply means keep the
-0349 %allFrom matrix as it is. If only orthologs which could be mapped in both
-0350 %BLAST directions are to be included then only those elements are kept.
-0351 
-0352 finalMappings=cell(numel(useOrder)-1,1);
-0353 if bidirectional
-0354     for j=1:numel(allFrom)
-0355         finalMappings{j}=allTo{j}~=0 & allFrom{j}~=0;
-0356     end
-0357 else
-0358     if mapNewGenesToOld==true
-0359         finalMappings=allFrom;
-0360     else
-0361         finalMappings=allTo;
-0362     end
-0363 end
-0364 
-0365 %Remove all genes from the mapping that are not in the models. This does not
-0366 %do much if only genes in the models were used for the original mapping.
-0367 %Also simplify the finalMapping and allGenes structures so that they only
-0368 %contain mappings that exist
-0369 usedNewGenes=false(numel(allGenes{1}),1);
-0370 
-0371 for i=1:numel(allGenes)-1
-0372     %First remove mappings for those genes that are not in the model
-0373     if onlyGenesInModels==false
-0374         a=ismember(allGenes{i+1},models{useOrderIndexes(i)}.genes);
-0375         finalMappings{i}(:,~a)=false;
-0376     end
-0377     
-0378     %Then remove unused ones and simplify
-0379     [a, b]=find(finalMappings{i});
-0380     usedGenes=false(numel(allGenes{i+1}),1);
-0381     usedNewGenes(a)=true;
-0382     usedGenes(b)=true;
-0383     finalMappings{i}=finalMappings{i}(:,usedGenes);
-0384     allGenes{i+1}=allGenes{i+1}(usedGenes);
-0385 end
-0386 
-0387 %Remove all new genes that have not been mapped to anything
-0388 allGenes{1}=allGenes{1}(usedNewGenes);
-0389 for i=1:numel(finalMappings)
-0390     finalMappings{i}=finalMappings{i}(usedNewGenes,:);
-0391 end
-0392 
-0393 %Now is it time to choose which reactions should be included from which
-0394 %models. If there is a preferred order specified then each gene can only
-0395 %result in reactions from one model, otherwise they should all be included
-0396 
-0397 %Start by simplifying the models by removing genes/reactions that are not
-0398 %used. This is where it gets weird with complexes, especially "or"
-0399 %complexes. In this step only reactions which are encoded by one single
-0400 %gene, or where all genes should be deleted, are deleted. The info on the
-0401 %full complex is still present in the grRules
-0402 
-0403 for i=1:numel(models)
-0404     a=ismember(models{useOrderIndexes(i)}.genes,allGenes{i+1});
-0405     
-0406     %Remove reactions that are not associated to any of the genes in
-0407     %allGenes, thereby also keeping complexes where only for one of the
-0408     %genes was matched
-0409     [rxnsToKeep,~] = find(models{useOrderIndexes(i)}.rxnGeneMat(:,a));
-0410     rxnsToRemove = repmat(1,numel(models{useOrderIndexes(i)}.rxns),1);
-0411     rxnsToRemove(rxnsToKeep) = 0;
-0412     rxnsToRemove = find(rxnsToRemove);
-0413     models{useOrderIndexes(i)}=removeReactions(models{useOrderIndexes(i)},rxnsToRemove,true,true,true);
-0414 end
-0415 
-0416 %Since mergeModels function will be used in the end, the models are
-0417 %simplified further by deleting genes/reactions in the order specified by
-0418 %preferredOrder. This means that the last model will only contain reactions
-0419 %for genes that mapped only to that model
-0420 
-0421 allUsedGenes=false(numel(allGenes{1}),1);
-0422 
-0423 if ~isempty(preferredOrder) && numel(models)>1
-0424     [usedGenes, ~]=find(finalMappings{1}); %All that are used in the first model in preferredOrder
-0425     allUsedGenes(usedGenes)=true;
-0426     for i=2:numel(finalMappings)
-0427         [usedGenes, ~]=find(finalMappings{i});
-0428         usedGenes=unique(usedGenes);
-0429         a=ismember(usedGenes,find(allUsedGenes));
-0430         
-0431         [~, genesToDelete]=find(finalMappings{i}(usedGenes(a),:)); %IMPORTANT! IS it really correct to remove all genes that map?
-0432         genesToDelete=unique(genesToDelete); %Maybe not needed, but for clarity if nothing else
-0433         
-0434         %Remove all the genes that were already found and add the other
-0435         %ones to allUsedGenes
-0436         models{useOrderIndexes(i)}=removeGenes(models{useOrderIndexes(i)},allGenes{i+1}(genesToDelete),true,true,false);
-0437         allUsedGenes(usedGenes)=true;
-0438         
-0439         %Remove the deleted genes from finalMappings and allGenes.
-0440         finalMappings{i}(:,genesToDelete)=[];
-0441         allGenes{i+1}(genesToDelete)=[];
-0442     end
-0443 end
-0444 
-0445 %Now loop through the models and update the gene associations. Genes not
-0446 %belonging to the new organism will be renamed as "OLD_MODELID_gene"
-0447 for i=1:numel(models)
-0448     %Find all the new genes that should be used for this model
-0449     [newGenes, oldGenes]=find(finalMappings{i});
-0450     
-0451     %Create a new gene list with the genes from the new organism and those
-0452     %genes that could not be removed
-0453     replaceableGenes=allGenes{i+1}(unique(oldGenes));
-0454     nonReplaceableGenes=setdiff(models{useOrderIndexes(i)}.genes,replaceableGenes);
-0455     fullGeneList=[allGenes{1}(unique(newGenes));nonReplaceableGenes];
-0456     
-0457     %Just to save some indexing later. This is the LAST index of
-0458     %replaceable ones
-0459     nonRepStartIndex=numel(unique(newGenes));
-0460     
-0461     %Construct a new rxnGeneMat
-0462     newRxnGeneMat=sparse(numel(models{useOrderIndexes(i)}.rxns),numel(fullGeneList));
-0463     
-0464     %Now update the rxnGeneMat. This is a little tricky and could
-0465     %probably be done in a more efficient way, but I just loop through the
-0466     %reactions and add them one by one
-0467     for j=1:numel(models{useOrderIndexes(i)}.rxns)
-0468         %Get the old genes encoding for this reaction
-0469         [~, oldGeneIds]=find(models{useOrderIndexes(i)}.rxnGeneMat(j,:));
-0470 
-0471         %Tokenize the grRule once; all gene substitutions operate on the
-0472         %token list so that no gene ID can partially match another.
-0473         tokens=gprTokenize(models{useOrderIndexes(i)}.grRules{j});
-0474 
-0475         %Update the matrix for each gene. This includes replacing one gene
-0476         %with several new ones if there were several matches
-0477         for k=1:numel(oldGeneIds)
-0478             %Match the gene to one in the gene list. This is done as a text
-0479             %match. Could probably be done better, but the indexing is
-0480             %complex
-0481 
-0482             geneName=models{useOrderIndexes(i)}.genes(oldGeneIds(k));
-0483 
-0484             %First search in the mappable genes
-0485             mapIndex=find(ismember(allGenes{i+1},geneName));
-0486 
-0487             if ~isempty(mapIndex)
-0488                 % add the old genes
-0489                 hitGenes.oldGenes = [hitGenes.oldGenes, {geneName}];
-0490 
-0491                 %Get the new genes for that gene
-0492                 a=find(finalMappings{i}(:,mapIndex));
-0493 
-0494                 %Find the positions of these genes in the final gene list
-0495                 [~, b]=ismember(allGenes{1}(a),fullGeneList);
-0496 
-0497                 %Update the matrix
-0498                 newRxnGeneMat(j,b)=1;
-0499 
-0500                 %Build repString for hitGenes (display only)
-0501                 repString=fullGeneList{b(1)};
-0502                 if numel(b)>1
-0503                     for l=2:numel(b)
-0504                         repString=[repString ' or ' fullGeneList{b(l)}]; %#ok<AGROW>
-0505                     end
-0506                     repString=['(' repString ')'];
-0507                 end
-0508                 hitGenes.newGenes = [hitGenes.newGenes, {repString}];
-0509 
-0510                 %Replace the old gene token with the new gene(s)
-0511                 tokens=gprReplaceToken(tokens,geneName{1},fullGeneList(b));
-0512             else
-0513                 %Then search in the non-replaceable genes. There could only
-0514                 %be one match here
-0515                 index=find(ismember(nonReplaceableGenes,geneName));
-0516 
-0517                 %Update the matrix
-0518                 newRxnGeneMat(j,nonRepStartIndex+index)=1;
-0519 
-0520                 oldName=strcat('OLD_',models{useOrderIndexes(i)}.id,'_',geneName{1});
-0521                 tokens=gprReplaceToken(tokens,geneName{1},{oldName});
-0522             end
-0523         end
-0524 
-0525         models{useOrderIndexes(i)}.grRules{j}=gprBuildRule(tokens);
-0526     end
-0527     
-0528     %Add the new list of genes
-0529     models{useOrderIndexes(i)}.rxnGeneMat=newRxnGeneMat;
-0530     if ~isempty(nonReplaceableGenes)
-0531         models{useOrderIndexes(i)}.genes=[allGenes{1}(unique(newGenes));strcat('OLD_',models{useOrderIndexes(i)}.id,'_',nonReplaceableGenes)];
-0532     else
-0533         models{useOrderIndexes(i)}.genes=allGenes{1}(unique(newGenes));
-0534     end
-0535     if isfield(models{useOrderIndexes(i)},'geneComps')
-0536         geneComps=models{useOrderIndexes(i)}.geneComps(1);
-0537         models{useOrderIndexes(i)}.geneComps=zeros(numel(models{useOrderIndexes(i)}.genes),1);
-0538         %Assume that all genes are in the same compartment, and this
-0539         %compartment is specified for the first gene
-0540         models{useOrderIndexes(i)}.geneComps(:)=geneComps;
-0541     end
-0542 end
-0543 
-0544 %Now merge the models. All information should be correct except for "or"
-0545 %complexes
-0546 draftModel=mergeModels(models,'metNames');
-0547 
-0548 %Remove OLD_ genes that appear only in OR branches (they were added for
-0549 %AND-complex genes with no orthologs; in OR branches they are redundant).
-0550 %Uses token-level removal to avoid partial-match hazards.
-0551 for oldI=1:numel(draftModel.grRules)
-0552     if ~contains(draftModel.grRules{oldI},'OLD_')
-0553         continue
-0554     end
-0555     tkns=gprTokenize(draftModel.grRules{oldI});
-0556     again=true;
-0557     while again
-0558         again=false;
-0559         for kk=1:numel(tkns)
-0560             if startsWith(tkns{kk},'OLD_')
-0561                 if kk>1 && strcmp(tkns{kk-1},'or')
-0562                     tkns(kk-1:kk)=[];
-0563                     again=true; break
-0564                 elseif kk<numel(tkns) && strcmp(tkns{kk+1},'or')
-0565                     tkns(kk:kk+1)=[];
-0566                     again=true; break
-0567                 end
-0568             end
-0569         end
-0570     end
-0571     draftModel.grRules{oldI}=gprBuildRule(tkns);
-0572 end
-0573 
-0574 %Change name of the resulting model
-0575 draftModel.id=getModelFor;
-0576 name='Generated by getModelFromHomology using ';
-0577 for i=1:numel(models)
-0578     if i<numel(models)
-0579         name=[name models{i}.id ', '];
-0580     else
-0581         name=[name models{i}.id];
-0582     end
-0583 end
-0584 draftModel.name=name;
-0585 draftModel.rxnNotes=cell(length(draftModel.rxns),1);
-0586 draftModel.rxnNotes(:)={'Included by getModelFromHomology'};
-0587 draftModel.rxnConfidenceScores=NaN(length(draftModel.rxns),1);
-0588 draftModel.rxnConfidenceScores(:)=2;
-0589 draftModel=deleteUnusedGenes(draftModel,0);
-0590 %Standardize grRules and notify if problematic grRules are found
-0591 [draftModel.grRules,draftModel.rxnGeneMat]=standardizeGrRules(draftModel,false);
-0592 draftModel=deleteUnusedGenes(draftModel,false);
-0593 end
-0594 
-0595 
-0596 function tokens=gprTokenize(rule)
-0597 % Split a GPR rule into tokens: gene IDs, "and", "or", "(", ")".
-0598 % Brackets are separated from adjacent text by whitespace before splitting.
-0599 if isempty(rule)
-0600     tokens={};
-0601     return
-0602 end
-0603 r=regexprep(rule,'([\(\)])',' $1 ');
-0604 r=regexprep(r,'\s+',' ');
-0605 tokens=strsplit(strtrim(r),' ');
-0606 tokens=tokens(~cellfun(@isempty,tokens));
-0607 end
-0608 
-0609 
-0610 function rule=gprBuildRule(tokens)
-0611 % Reconstruct a GPR rule string from a token list.
-0612 if isempty(tokens)
-0613     rule='';
-0614     return
-0615 end
-0616 rule=strjoin(tokens,' ');
-0617 rule=strrep(rule,'( ','(');
-0618 rule=strrep(rule,' )',')');
-0619 end
-0620 
-0621 
-0622 function tokens=gprReplaceToken(tokens,oldGene,newGenes)
-0623 % Replace each occurrence of token oldGene with the gene(s) in newGenes.
-0624 % newGenes is a cell array; multiple genes are wrapped in parentheses and
-0625 % joined with " or ".
-0626 k=1;
-0627 while k<=numel(tokens)
-0628     if strcmp(tokens{k},oldGene)
-0629         if numel(newGenes)==1
-0630             tokens{k}=newGenes{1};
-0631             k=k+1;
-0632         else
-0633             seq={'('};
-0634             for g=1:numel(newGenes)
-0635                 seq{end+1}=newGenes{g}; %#ok<AGROW>
-0636                 if g<numel(newGenes)
-0637                     seq{end+1}='or'; %#ok<AGROW>
-0638                 end
-0639             end
-0640             seq{end+1}=')';
-0641             tokens=[tokens(1:k-1),seq,tokens(k+1:end)];
-0642             k=k+numel(seq);
-0643         end
-0644     else
-0645         k=k+1;
-0646     end
-0647 end
-0648 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/reconstruction/kegg/getKEGGModelForOrganism.html b/doc/reconstruction/kegg/getKEGGModelForOrganism.html deleted file mode 100644 index cb705488..00000000 --- a/doc/reconstruction/kegg/getKEGGModelForOrganism.html +++ /dev/null @@ -1,706 +0,0 @@ - - - - Description of getKEGGModelForOrganism - - - - - - - - - -
Home > reconstruction > kegg > getKEGGModelForOrganism.m
- - - -

getKEGGModelForOrganism -

- -

PURPOSE ^

-
getKEGGModelForOrganism Reconstruct a model from KEGG protein homology.
- -

SYNOPSIS ^

-
function model=getKEGGModelForOrganism(organismID,varargin)
- -

DESCRIPTION ^

-
 getKEGGModelForOrganism  Reconstruct a model from KEGG protein homology.
-
- Reconstructs a genome-scale metabolic model based on protein homology to
- the orthologies in KEGG. If the target species is not available in KEGG,
- the user must select a closely related species. It is also possible to
- circumvent protein homology search (see fastaFile parameter for more
- details).
-
- Parameters
- ----------
- organismID : char
-     three or four letter abbreviation of the organism (as used in KEGG).
-     If not available, use a closely related species. This is used for
-     determing the phylogenetic distance. Use "eukaryotes" or "prokaryotes"
-     to get a model for the whole domain. Only applicable if fastaFile is
-     empty, i.e. no homology search should be performed.
-
- Name-Value Arguments
- --------------------
- fastaFile : char
-     a FASTA file that contains the protein sequences of the organism for
-     which to reconstruct a model. If no FASTA file is supplied then a
-     model is reconstructed based only on the organism abbreviation. This
-     option ignores all settings except for keepSpontaneous,
-     keepUndefinedStoich, keepIncomplete and keepGeneral.
- dataDir : char
-     directory for which to retrieve the input data, styled as
-     kegg118_prokaryotes or kegg118_eukaryotes, indicating the KEGG version
-     and whether the HMMs were trained on pro- or eukaryotic sequences. The
-     directory name matches the published HMM library it is paired with. The
-     prebuilt concatenated KO HMM library (dataDir.hmm) is downloaded here
-     from the corresponding raven-data release if not already present. This
-     parameter should ALWAYS be provided.
- outDir : char
-     directory to save the results from the quering of the hidden Markov
-     models. The output is specific for the input sequences and the
-     settings used. It is stored in this manner so that the function can
-     continue if interrupted or if it should run in parallel. Be careful
-     not to leave output files from different organisms or runs with
-     different settings in the same folder. They will not be overwritten
-     (default is a temporary dir where all *.out files are deleted before
-     and after doing the reconstruction).
- keepSpontaneous : logical
-     include reactions labeled as "spontaneous" (default true).
- keepUndefinedStoich : logical
-     include reactions in the form n A <=> n+1 A. These will be dealt with
-     as two separate metabolites (default true).
- keepIncomplete : logical
-     include reactions which have been labelled as "incomplete",
-     "erroneous" or "unclear" (default true).
- keepGeneral : logical
-     include reactions which have been labelled as "general reaction".
-     These are reactions on the form "an aldehyde <=> an alcohol", and are
-     therefore unsuited for modelling purposes. Note that not all reactions
-     have this type of annotation, and the script will therefore not be
-     able to remove all such reactions (default false).
- cutOff : double
-     significance score from HMMer needed to assign genes to a KO (default
-     10^-30).
- minScoreRatioKO : double
-     ignore genes in a KO if their score is <log(score)/log(best score in
-     KO). This is to "prune" KOs which have many genes and where some are
-     clearly a better fit (default 0.3, lower is less strict).
- minScoreRatioG : double
-     a gene is only assigned to KOs for which the score is
-     >=log(score)/log(best score) for that gene. This is to prevent that a
-     gene which clearly belongs to one KO is assigned also to KOs with much
-     lower scores (default 0.9, lower is less strict).
- maxPhylDist : double
-     -1 to only use sequences from the same domain (Prokaryota, Eukaryota);
-     any other (positive) value to only use sequences for organisms where
-     the phylogenetic distance is at the most this large (as calculated in
-     getPhylDist). Only used when reconstructing from KEGG annotations (no
-     fastaFile) (default Inf, which means that all sequences will be used).
- globalModel : struct
-     structure containing both model and KOModel structures as generated
-     by getModelFromKEGG. These will otherwise be loaded by via
-     getModelFromKEGG. Providing globalKEGGmodel can speed up model
-     generation if getKEGGModelForOrganism is run multiple times for
-     different strains. Example:
-     [globalModel.model,globalModel.KOModel] = getModelFromKEGG (default
-     empty, global model is loaded by getModelFromKEGG).
-
- Returns
- -------
- model : struct
-     the reconstructed model.
-
- Notes
- -----
- The reconstruction works in one of two modes:
-
-   1. From KEGG annotations (no fastaFile supplied). The reactions
-      associated with organismID in the local KEGG database are kept;
-      maxPhylDist controls which organism annotations are considered.
-   2. From protein homology (fastaFile supplied). The query proteome is
-      searched, in a single hmmsearch, against a prebuilt KEGG-version- and
-      domain-specific concatenated KO HMM library (e.g. kegg118_eukaryotes),
-      downloaded from the corresponding RAVEN release if not already present
-      in dataDir. Hits are filtered by cutOff and the minScoreRatioKO /
-      minScoreRatioG ratios into a KO-gene matrix, from which the model is
-      built (spontaneous reactions without genes are kept if keepSpontaneous
-      is true).
-
- The global KEGG model (reaction-KO relationships) is either supplied via
- globalModel, built from a local KEGG FTP dump in dataDir\keggdb (which
- requires a KEGG FTP license), or loaded from the internal RAVEN version.
-
- Examples
- --------
-     model=getKEGGModelForOrganism(organismID,fastaFile,dataDir,...
-        outDir,keepSpontaneous,keepUndefinedStoich,keepIncomplete,...
-        keepGeneral,cutOff,minScoreRatioKO,minScoreRatioG,maxPhylDist);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • getModelFromKEGG getModelFromKEGG Load the global KEGG model.
  • getPhylDist getPhylDist Load the pre-built KEGG phylogenetic distance matrix.
  • keggDataVersion keggDataVersion The raven-data release tag for KEGG artefacts.
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function model=getKEGGModelForOrganism(organismID,varargin)
-0002 % getKEGGModelForOrganism  Reconstruct a model from KEGG protein homology.
-0003 %
-0004 % Reconstructs a genome-scale metabolic model based on protein homology to
-0005 % the orthologies in KEGG. If the target species is not available in KEGG,
-0006 % the user must select a closely related species. It is also possible to
-0007 % circumvent protein homology search (see fastaFile parameter for more
-0008 % details).
-0009 %
-0010 % Parameters
-0011 % ----------
-0012 % organismID : char
-0013 %     three or four letter abbreviation of the organism (as used in KEGG).
-0014 %     If not available, use a closely related species. This is used for
-0015 %     determing the phylogenetic distance. Use "eukaryotes" or "prokaryotes"
-0016 %     to get a model for the whole domain. Only applicable if fastaFile is
-0017 %     empty, i.e. no homology search should be performed.
-0018 %
-0019 % Name-Value Arguments
-0020 % --------------------
-0021 % fastaFile : char
-0022 %     a FASTA file that contains the protein sequences of the organism for
-0023 %     which to reconstruct a model. If no FASTA file is supplied then a
-0024 %     model is reconstructed based only on the organism abbreviation. This
-0025 %     option ignores all settings except for keepSpontaneous,
-0026 %     keepUndefinedStoich, keepIncomplete and keepGeneral.
-0027 % dataDir : char
-0028 %     directory for which to retrieve the input data, styled as
-0029 %     kegg118_prokaryotes or kegg118_eukaryotes, indicating the KEGG version
-0030 %     and whether the HMMs were trained on pro- or eukaryotic sequences. The
-0031 %     directory name matches the published HMM library it is paired with. The
-0032 %     prebuilt concatenated KO HMM library (dataDir.hmm) is downloaded here
-0033 %     from the corresponding raven-data release if not already present. This
-0034 %     parameter should ALWAYS be provided.
-0035 % outDir : char
-0036 %     directory to save the results from the quering of the hidden Markov
-0037 %     models. The output is specific for the input sequences and the
-0038 %     settings used. It is stored in this manner so that the function can
-0039 %     continue if interrupted or if it should run in parallel. Be careful
-0040 %     not to leave output files from different organisms or runs with
-0041 %     different settings in the same folder. They will not be overwritten
-0042 %     (default is a temporary dir where all *.out files are deleted before
-0043 %     and after doing the reconstruction).
-0044 % keepSpontaneous : logical
-0045 %     include reactions labeled as "spontaneous" (default true).
-0046 % keepUndefinedStoich : logical
-0047 %     include reactions in the form n A <=> n+1 A. These will be dealt with
-0048 %     as two separate metabolites (default true).
-0049 % keepIncomplete : logical
-0050 %     include reactions which have been labelled as "incomplete",
-0051 %     "erroneous" or "unclear" (default true).
-0052 % keepGeneral : logical
-0053 %     include reactions which have been labelled as "general reaction".
-0054 %     These are reactions on the form "an aldehyde <=> an alcohol", and are
-0055 %     therefore unsuited for modelling purposes. Note that not all reactions
-0056 %     have this type of annotation, and the script will therefore not be
-0057 %     able to remove all such reactions (default false).
-0058 % cutOff : double
-0059 %     significance score from HMMer needed to assign genes to a KO (default
-0060 %     10^-30).
-0061 % minScoreRatioKO : double
-0062 %     ignore genes in a KO if their score is <log(score)/log(best score in
-0063 %     KO). This is to "prune" KOs which have many genes and where some are
-0064 %     clearly a better fit (default 0.3, lower is less strict).
-0065 % minScoreRatioG : double
-0066 %     a gene is only assigned to KOs for which the score is
-0067 %     >=log(score)/log(best score) for that gene. This is to prevent that a
-0068 %     gene which clearly belongs to one KO is assigned also to KOs with much
-0069 %     lower scores (default 0.9, lower is less strict).
-0070 % maxPhylDist : double
-0071 %     -1 to only use sequences from the same domain (Prokaryota, Eukaryota);
-0072 %     any other (positive) value to only use sequences for organisms where
-0073 %     the phylogenetic distance is at the most this large (as calculated in
-0074 %     getPhylDist). Only used when reconstructing from KEGG annotations (no
-0075 %     fastaFile) (default Inf, which means that all sequences will be used).
-0076 % globalModel : struct
-0077 %     structure containing both model and KOModel structures as generated
-0078 %     by getModelFromKEGG. These will otherwise be loaded by via
-0079 %     getModelFromKEGG. Providing globalKEGGmodel can speed up model
-0080 %     generation if getKEGGModelForOrganism is run multiple times for
-0081 %     different strains. Example:
-0082 %     [globalModel.model,globalModel.KOModel] = getModelFromKEGG (default
-0083 %     empty, global model is loaded by getModelFromKEGG).
-0084 %
-0085 % Returns
-0086 % -------
-0087 % model : struct
-0088 %     the reconstructed model.
-0089 %
-0090 % Notes
-0091 % -----
-0092 % The reconstruction works in one of two modes:
-0093 %
-0094 %   1. From KEGG annotations (no fastaFile supplied). The reactions
-0095 %      associated with organismID in the local KEGG database are kept;
-0096 %      maxPhylDist controls which organism annotations are considered.
-0097 %   2. From protein homology (fastaFile supplied). The query proteome is
-0098 %      searched, in a single hmmsearch, against a prebuilt KEGG-version- and
-0099 %      domain-specific concatenated KO HMM library (e.g. kegg118_eukaryotes),
-0100 %      downloaded from the corresponding RAVEN release if not already present
-0101 %      in dataDir. Hits are filtered by cutOff and the minScoreRatioKO /
-0102 %      minScoreRatioG ratios into a KO-gene matrix, from which the model is
-0103 %      built (spontaneous reactions without genes are kept if keepSpontaneous
-0104 %      is true).
-0105 %
-0106 % The global KEGG model (reaction-KO relationships) is either supplied via
-0107 % globalModel, built from a local KEGG FTP dump in dataDir\keggdb (which
-0108 % requires a KEGG FTP license), or loaded from the internal RAVEN version.
-0109 %
-0110 % Examples
-0111 % --------
-0112 %     model=getKEGGModelForOrganism(organismID,fastaFile,dataDir,...
-0113 %        outDir,keepSpontaneous,keepUndefinedStoich,keepIncomplete,...
-0114 %        keepGeneral,cutOff,minScoreRatioKO,minScoreRatioG,maxPhylDist);
-0115 
-0116 p=parseRAVENargs(varargin, {'fastaFile',[]; 'dataDir',[]; 'outDir',[]; ...
-0117     'keepSpontaneous',true; 'keepUndefinedStoich',true; ...
-0118     'keepIncomplete',true; 'keepGeneral',false; 'cutOff',10^-30; ...
-0119     'minScoreRatioKO',0.3; 'minScoreRatioG',0.9; 'maxPhylDist',inf; ...
-0120     'globalModel',[]});
-0121 fastaFile=p.fastaFile;
-0122 if isempty(fastaFile)
-0123     fastaFile=[];
-0124 else
-0125     fastaFile=char(fastaFile);
-0126 end
-0127 dataDir=p.dataDir;
-0128 if isempty(dataDir)
-0129     dataDir=[];
-0130 else
-0131     dataDir=char(dataDir);
-0132 end
-0133 outDir=p.outDir;
-0134 if isempty(outDir)
-0135     outDir=tempdir;
-0136     %Delete all *.out files if any exist
-0137     delete(fullfile(outDir,'*.out'));
-0138 else
-0139     outDir=char(outDir);
-0140 end
-0141 keepSpontaneous=p.keepSpontaneous;
-0142 if isempty(keepSpontaneous)
-0143     keepSpontaneous=true;
-0144 end
-0145 keepUndefinedStoich=p.keepUndefinedStoich;
-0146 if isempty(keepUndefinedStoich)
-0147     keepUndefinedStoich=true;
-0148 end
-0149 keepIncomplete=p.keepIncomplete;
-0150 if isempty(keepIncomplete)
-0151     keepIncomplete=true;
-0152 end
-0153 keepGeneral=p.keepGeneral;
-0154 if isempty(keepGeneral)
-0155     keepGeneral=false;
-0156 end
-0157 cutOff=p.cutOff;
-0158 if isempty(cutOff)
-0159     cutOff=10^-30;
-0160 end
-0161 minScoreRatioKO=p.minScoreRatioKO;
-0162 if isempty(minScoreRatioKO)
-0163     minScoreRatioKO=0.3;
-0164 end
-0165 minScoreRatioG=p.minScoreRatioG;
-0166 if isempty(minScoreRatioG)
-0167     minScoreRatioG=0.9;
-0168 end
-0169 maxPhylDist=p.maxPhylDist;
-0170 if isempty(maxPhylDist)
-0171     maxPhylDist=inf;
-0172     %Include all sequences for each reaction
-0173 end
-0174 globalModel=p.globalModel;
-0175 
-0176 if isempty(fastaFile)
-0177     fprintf(['\n*** The model reconstruction from KEGG based on the annotation available for KEGG Species <strong>' organismID '</strong> ***\n\n']);
-0178 else
-0179     fprintf('\n*** The model reconstruction from KEGG based on the protein homology search against KEGG Orthology specific HMMs ***\n\n');
-0180     %Check if query fasta exists
-0181     fastaFile=checkFileExistence(fastaFile,2); %Copy file to temp dir
-0182 end
-0183 
-0184 %Run the external binaries multi-threaded to use all logical cores assigned
-0185 %to MATLAB
-0186 cores = evalc('feature(''numcores'')');
-0187 cores = strsplit(cores, 'MATLAB was assigned: ');
-0188 cores = regexp(cores{2},'^\d*','match');
-0189 cores = cores{1};
-0190 
-0191 %Get the directory for RAVEN Toolbox.
-0192 ravenPath=findRAVENroot();
-0193 
-0194 libraryFile='';
-0195 
-0196 %Checking if dataDir is consistent. It must point to a RAVEN-provided
-0197 %pre-trained HMM set, compatible with the current RAVEN version. The
-0198 %reconstruction uses the concatenated KO HMM library (a single
-0199 %gzip-compressed flatfile, queried in one hmmsearch); if it is not already
-0200 %present it is downloaded from the corresponding raven-data release
-0201 %(https://github.com/SysBioChalmers/raven-data).
-0202 %Only needed for the protein-homology path (fastaFile supplied): the
-0203 %annotation-only path never touches libraryFile, so skip the (100+ MB)
-0204 %download/extraction entirely when there is no FASTA file to search.
-0205 if ~isempty(dataDir) && ~isempty(fastaFile)
-0206     kver=keggDataVersion();
-0207     hmmOptions={[kver '_eukaryotes'],[kver '_prokaryotes']};
-0208     if ~endsWith(dataDir,hmmOptions)
-0209         error(['Pre-trained HMMs set is not recognised. dataDir must match one of: ' strjoin(hmmOptions,' or ')])
-0210     end
-0211     %dataDir points to a RAVEN-provided set. Its name matches the
-0212     %published HMM library asset, so it doubles as the download filename.
-0213     hmmName=hmmOptions{endsWith(dataDir,hmmOptions)};
-0214     libraryFile=[dataDir '.hmm'];
-0215     if isfile(libraryFile)
-0216         fprintf(['NOTE: Found <strong>' libraryFile '</strong> HMM library, it will therefore be used during reconstruction\n']);
-0217     elseif isfile([libraryFile '.gz'])
-0218         fprintf('Extracting the HMM library file... ');
-0219         gunzip([libraryFile '.gz']);
-0220         fprintf('COMPLETE\n');
-0221     else
-0222         fetchRavenDataAsset(fileparts(libraryFile),kver,[hmmName '.hmm.gz']);
-0223         fprintf('Extracting the HMM library file... ');
-0224         gunzip([libraryFile '.gz']);
-0225         fprintf('COMPLETE\n');
-0226     end
-0227     %Check that the HMM library is available
-0228     if ~isfile(libraryFile)
-0229         error(['The HMM library seems improperly extracted and not found at ',strrep(libraryFile,'\','/'),'. Please remove the corresponding .gz file and rerun getKEGGModelForOrganism']);
-0230     end
-0231 end
-0232 
-0233 %Check if the fasta-file contains "/" or "\". If not then it is probably just
-0234 %a file name. Expand to full path.
-0235 if any(fastaFile)
-0236     if ~any(strfind(fastaFile,'\')) && ~any(strfind(fastaFile,'/'))
-0237         fastaFile=which(fastaFile);
-0238     end
-0239     if ~isfolder(outDir)
-0240         mkdir(outDir);
-0241     end
-0242 end
-0243 
-0244 %Load the global KEGG model. Can be provided as input to skip loading
-0245 %when calling getKEGGModelForOrganism multiple times for different strains.
-0246 if ~isempty(globalModel)
-0247     model=globalModel.model;
-0248     KOModel=globalModel.KOModel;
-0249 else
-0250     [model, KOModel]=getModelFromKEGG(keepSpontaneous,keepUndefinedStoich,keepIncomplete,keepGeneral);
-0251 end
-0252 model.id=organismID;
-0253 model.c=zeros(numel(model.rxns),1);
-0254 
-0255 %If no FASTA file is supplied, then just remove all genes which are not for
-0256 %the given organism ID
-0257 if isempty(fastaFile)
-0258     %Check if organismID can be found in KEGG species list or is
-0259     %set to "eukaryotes" or "prokaryotes"
-0260     phylDistsFull=getPhylDist(false);
-0261     if ~ismember(organismID,[phylDistsFull.ids 'eukaryotes' 'prokaryotes'])
-0262         error('Provided organismID is incorrect. Only species abbreviations from KEGG Species List or "eukaryotes"/"prokaryotes" are allowed.');
-0263     end
-0264 
-0265     fprintf(['Pruning the model from <strong>non-' organismID '</strong> genes... ']);
-0266     if ismember(organismID,{'eukaryotes','prokaryotes'})
-0267         phylDists=getPhylDist(maxPhylDist==-1);
-0268         if strcmp(organismID,'eukaryotes')
-0269             proxyid='hsa';
-0270             %Use H. sapiens here
-0271         else
-0272             proxyid='eco';
-0273             %Use E. coli here
-0274         end
-0275         [~, phylDistId]=ismember(proxyid,phylDists.ids);
-0276         idsToKeep=phylDists.ids(~isinf(phylDists.distMat(phylDistId,:)));
-0277         taxIDs=cellfun(@(x) x{1},cellfun(@(x) strsplit(x,':'),model.genes,'UniformOutput',false),'UniformOutput',false);
-0278         I=ismember(upper(taxIDs),upper(idsToKeep));
-0279     else
-0280         %KEGG organism IDs may have three or four letters
-0281         organismID=strcat(organismID,':');
-0282         %Add colon for accurate matching
-0283         if length(organismID)==4
-0284             I=cellfun(@(x) strcmpi(x(1:4),organismID),model.genes);
-0285         elseif length(organismID)==5
-0286             I=cellfun(@(x) strcmpi(x(1:5),organismID),model.genes);
-0287         end
-0288     end
-0289     %Remove those genes
-0290     model.genes=model.genes(I);
-0291     model.rxnGeneMat=model.rxnGeneMat(:,I);
-0292     fprintf('COMPLETE\n');
-0293 end
-0294 
-0295 %First remove all reactions without genes
-0296 if keepSpontaneous==true
-0297     fprintf('Removing non-spontaneous reactions without GPR rules... ');
-0298     load(fullfile(ravenPath,'reconstruction','kegg','keggModel.mat'),'isSpontaneous');
-0299     I=~any(model.rxnGeneMat,2)&~ismember(model.rxns,isSpontaneous);
-0300     spontRxnsWithGenes=model.rxns(any(model.rxnGeneMat,2)&~ismember(model.rxns,isSpontaneous));
-0301 else
-0302     fprintf('Removing reactions without GPR rules... ');
-0303     I=~any(model.rxnGeneMat,2);
-0304 end
-0305 model=removeReactions(model,I,true);
-0306 fprintf('COMPLETE\n');
-0307 
-0308 %Clean gene names
-0309 fprintf('Fixing gene names in the model... ');
-0310 %Get rid of the prefix organism id
-0311 model.genes=regexprep(model.genes,'^\w+?:','');
-0312 fprintf('COMPLETE\n');
-0313 
-0314 %If no FASTA file is supplied, then we are done here
-0315 if isempty(fastaFile)
-0316     %Create grRules
-0317     fprintf('Constructing GPR associations and annotations for the model... ');
-0318     model.grRules=cell(numel(model.rxns),1);
-0319     model.grRules(:)={''};
-0320     %Add the gene associations as "or"
-0321     for i=1:numel(model.rxns)
-0322         %Find the involved genes
-0323         I=find(model.rxnGeneMat(i,:));
-0324         if any(I)
-0325             model.grRules{i}=['(' model.genes{I(1)}];
-0326             for j=2:numel(I)
-0327                 model.grRules{i}=[model.grRules{i} ' or ' model.genes{I(j)}];
-0328             end
-0329             model.grRules{i}=[model.grRules{i} ')'];
-0330         end
-0331     end
-0332     %Fix grRules and reconstruct rxnGeneMat
-0333     [grRules,rxnGeneMat] = standardizeGrRules(model); %Give detailed output
-0334     model.grRules = grRules;
-0335     model.rxnGeneMat = rxnGeneMat;
-0336     %Add geneMiriams, assuming that it follows the syntax
-0337     %kegg.genes/organismID:geneName
-0338     model.geneMiriams='';
-0339     for i=1:numel(model.genes)
-0340         model.geneMiriams{i,1}.name{1,1}='kegg.genes';
-0341         model.geneMiriams{i,1}.value{1,1}=strcat(lower(organismID),model.genes{i,1});
-0342     end
-0343     %Add the description to the reactions
-0344     for i=1:numel(model.rxns)
-0345         if ~isempty(model.rxnNotes{i})
-0346             model.rxnNotes(i)=strcat('Included by getKEGGModelForOrganism (without HMMs).',model.rxnNotes(i));
-0347             model.rxnNotes(i)=strrep(model.rxnNotes(i),'.','. ');
-0348         else
-0349             model.rxnNotes(i)={'Included by getKEGGModelForOrganism (without HMMs)'};
-0350         end
-0351     end
-0352     fprintf('COMPLETE\n\n');
-0353     fprintf('*** Model reconstruction complete ***\n');
-0354     return;
-0355 end
-0356 
-0357 %Query the whole proteome against the concatenated KO HMM library in a
-0358 %single hmmsearch. With the profile library as the query and the
-0359 %proteome as the target sequence database, per-hit E-values stay
-0360 %comparable across KOs (same search direction, same effective database
-0361 %size), and no per-organism phylogenetic-distance subsampling is needed
-0362 %because the library sequence set is already fixed.
-0363 if ispc
-0364     binEnd='.exe';
-0365 elseif ismac
-0366     binEnd='.mac';
-0367 else
-0368     binEnd='';
-0369 end
-0370 %Fetch the HMMER binary on demand if it is not already present
-0371 if ~exist(fullfile(ravenPath,'software','hmmer',['hmmsearch' binEnd]),'file')
-0372     downloadRavenBinaries({'hmmer'});
-0373 end
-0374 tblFile=[tempname '.tblout'];
-0375 fprintf('Querying the user-specified FASTA file against the KEGG Orthology specific HMMs... ');
-0376 [status, output]=system(['"' fullfile(ravenPath,'software','hmmer',['hmmsearch' binEnd]) '" --cpu "' num2str(cores) '" --tblout "' tblFile '" "' libraryFile '" "' fastaFile '"']);
-0377 if status~=0
-0378     EM=['Error when querying the concatenated HMM library:\n' output];
-0379     error('RAVEN:badInput', '%s', EM);
-0380 end
-0381 fprintf('COMPLETE\n');
-0382 
-0383 fprintf('Parsing the HMM search results... ');
-0384 koGeneMat=zeros(numel(KOModel.rxns),3000); %Make room for 3000 genes
-0385 genes=cell(3000,1);
-0386 %Map each KO id to its row in koGeneMat
-0387 koTable=java.util.Hashtable;
-0388 for i=1:numel(KOModel.rxns)
-0389     koTable.put(KOModel.rxns{i},i);
-0390 end
-0391 %Store the column index for each gene in a hash list
-0392 hTable=java.util.Hashtable;
-0393 geneCounter=0;
-0394 fid=fopen(tblFile,'r');
-0395 while 1
-0396     tline=fgetl(fid);
-0397     if ~ischar(tline)
-0398         break;
-0399     end
-0400     %Skip the comment/header lines that hmmsearch --tblout writes
-0401     if isempty(tline) || tline(1)=='#'
-0402         continue;
-0403     end
-0404     elements=regexp(strtrim(tline),'\s+','split');
-0405     if numel(elements)<5
-0406         continue;
-0407     end
-0408     %With the profile library as the query, the target name (col 1) is
-0409     %the proteome gene, the query name (col 3) is the KO, and the
-0410     %full-sequence E-value is in col 5
-0411     gene=elements{1};
-0412     ko=elements{3};
-0413     score=str2double(elements{5});
-0414     koIdx=koTable.get(ko);
-0415     if isempty(koIdx) || isnan(score) || score>cutOff
-0416         continue;
-0417     end
-0418     %If the score is exactly 0, change it to a very small value to avoid
-0419     %NaN
-0420     if score==0
-0421         score=10^-250;
-0422     end
-0423     I=hTable.get(gene);
-0424     if any(I)
-0425         %Keep the best (smallest) E-value for this gene-KO pair
-0426         if koGeneMat(koIdx,I)==0 || score<koGeneMat(koIdx,I)
-0427             koGeneMat(koIdx,I)=score;
-0428         end
-0429     else
-0430         geneCounter=geneCounter+1;
-0431         hTable.put(gene,geneCounter);
-0432         genes{geneCounter}=gene;
-0433         koGeneMat(koIdx,geneCounter)=score;
-0434     end
-0435 end
-0436 fclose(fid);
-0437 delete(tblFile);
-0438 fprintf('COMPLETE\n');
-0439 
-0440 fprintf('Removing gene, KEGG Orthology associations below minScoreRatioKO, minScoreRatioG... ');
-0441 koGeneMat=koGeneMat(:,1:geneCounter);
-0442 
-0443 %Remove the genes for each KO that are below minScoreRatioKO.
-0444 for i=1:size(koGeneMat,1)
-0445     J=find(koGeneMat(i,:));
-0446     if any(J)
-0447         koGeneMat(i,J(log(koGeneMat(i,J))/log(min(koGeneMat(i,J)))<minScoreRatioKO))=0;
-0448     end
-0449 end
-0450 
-0451 %Remove the KOs for each gene that are below minScoreRatioG
-0452 for i=1:size(koGeneMat,2)
-0453     J=find(koGeneMat(:,i));
-0454     if any(J)
-0455         koGeneMat(J(log(koGeneMat(J,i))/log(min(koGeneMat(J,i)))<minScoreRatioG),i)=0;
-0456     end
-0457 end
-0458 fprintf('COMPLETE\n');
-0459 
-0460 fprintf('Adding gene annotations to the model... ');
-0461 %Create the new model
-0462 model.genes=genes(1:geneCounter);
-0463 model.grRules=cell(numel(model.rxns),1);
-0464 model.grRules(:)={''};
-0465 model.rxnGeneMat=sparse(numel(model.rxns),numel(model.genes));
-0466 
-0467 %Loop through the reactions and add the corresponding genes
-0468 for i=1:numel(model.rxns)
-0469     if isstruct(model.rxnMiriams{i})
-0470         %Get all KOs
-0471         I=find(strcmpi(model.rxnMiriams{i}.name,'kegg.orthology'));
-0472         KOs=model.rxnMiriams{i}.value(I);
-0473         %Find the KOs and the corresponding genes
-0474         J=ismember(KOModel.rxns,KOs);
-0475         [~, K]=find(koGeneMat(J,:));
-0476 
-0477         if any(K)
-0478             model.rxnGeneMat(i,K)=1;
-0479             %Also delete KOs for which no genes were found. If no genes at
-0480             %all were matched to the reaction it will be deleted later
-0481             L=sum(koGeneMat(J,:),2)==0;
-0482             model.rxnMiriams{i}.value(I(L))=[];
-0483             model.rxnMiriams{i}.name(I(L))=[];
-0484         end
-0485     end
-0486 end
-0487 fprintf('COMPLETE\n');
-0488 
-0489 %Find and delete all reactions without genes. This also removes genes that
-0490 %are not used (which could happen because minScoreRatioG and
-0491 %minScoreRatioKO). If keepSpontaneous==true, the spontaneous reactions
-0492 %without genes are kept in the model. Spontaneous reactions with original
-0493 %gene associations are treated in the same way, like the rest of the
-0494 %reactions - if gene associations were removed during HMM search, such
-0495 %reactions are deleted from the model
-0496 if keepSpontaneous==true
-0497     %Not the most comprise way to delete reactions without genes, but this
-0498     %makes the code easier to understand. Firstly the non-spontaneous
-0499     %reactions without genes are removed. After that, the second deletion
-0500     %step removes spontaneous reactions, which had gene associations before
-0501     %HMM search, but no longer have after it
-0502     fprintf('Removing non-spontaneous reactions which after HMM search no longer have GPR rules... ');
-0503     I=~any(model.rxnGeneMat,2)&~ismember(model.rxns,isSpontaneous);
-0504     model=removeReactions(model,I,true,true);
-0505     I=~any(model.rxnGeneMat,2)&ismember(model.rxns,spontRxnsWithGenes);
-0506     model=removeReactions(model,I,true,true);
-0507 else
-0508     %Just simply check for any new reactions without genes and remove
-0509     %it
-0510     fprintf('Removing reactions which after HMM search no longer have GPR rules... ');
-0511     I=~any(model.rxnGeneMat,2);
-0512     model=removeReactions(model,I,true,true);
-0513 end
-0514 fprintf('COMPLETE\n');
-0515 
-0516 fprintf('Constructing GPR rules and finalizing the model... ');
-0517 %Add the gene associations as "or"
-0518 for i=1:numel(model.rxns)
-0519     %Find the involved genes
-0520     I=find(model.rxnGeneMat(i,:));
-0521     if any(I)
-0522         model.grRules{i}=['(' model.genes{I(1)}];
-0523         for j=2:numel(I)
-0524             model.grRules{i}=[model.grRules{i} ' or ' model.genes{I(j)}];
-0525         end
-0526         model.grRules{i}=[model.grRules{i} ')'];
-0527     end
-0528 end
-0529 
-0530 %Fix grRules and reconstruct rxnGeneMat
-0531 [grRules,rxnGeneMat] = standardizeGrRules(model,false); %Give detailed output
-0532 model.grRules = grRules;
-0533 model.rxnGeneMat = rxnGeneMat;
-0534 
-0535 %Add the description to the reactions
-0536 for i=1:numel(model.rxns)
-0537     if ~isempty(model.rxnNotes{i})
-0538         model.rxnNotes(i)=strcat('Included by getKEGGModelForOrganism (using HMMs).',model.rxnNotes(i));
-0539         model.rxnNotes(i)=strrep(model.rxnNotes(i),'.','. ');
-0540     else
-0541         model.rxnNotes(i)={'Included by getKEGGModelForOrganism (using HMMs)'};
-0542     end
-0543 end
-0544 %Remove the temp fasta file
-0545 delete(fastaFile)
-0546 fprintf('COMPLETE\n\n*** Model reconstruction complete ***\n');
-0547 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/solver/getMinNrFluxes.html b/doc/solver/getMinNrFluxes.html deleted file mode 100644 index 851663b6..00000000 --- a/doc/solver/getMinNrFluxes.html +++ /dev/null @@ -1,647 +0,0 @@ - - - - Description of getMinNrFluxes - - - - - - - - - -
Home > solver > getMinNrFluxes.m
- - - -

getMinNrFluxes -

- -

PURPOSE ^

-
getMinNrFluxes Find the minimal set of fluxes that satisfy the model.
- -

SYNOPSIS ^

-
function [x,I,exitFlag]=getMinNrFluxes(model, varargin)
- -

DESCRIPTION ^

-
 getMinNrFluxes  Find the minimal set of fluxes that satisfy the model.
-
- Uses mixed integer linear programming to find the minimal set of fluxes
- that satisfy the model.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- toMinimize : cell or logical or double
-     either a cell array of reaction IDs, a logical vector with the same
-     number of elements as reactions in the model, or a vector of indexes
-     for the reactions that should be minimized (default model.rxns).
- params : struct
-     *obsolete option*.
- scores : double
-     vector of weights for the reactions. Negative scores should not have
-     flux. Positive scores are not possible in this implementation, and
-     they are changed to max(scores(scores<0)), or to 0 if scores has no
-     negative entry at all. Must have the same dimension as toMinimize
-     (find(toMinimize) if it is a logical vector) (default -1 for all
-     reactions).
- formulation : char
-     which MILP formulation to build (default 'irrev'):
-
-     - 'irrev' : convert the model to irreversible format and use one
-       binary per irreversible reaction, so a reversible reaction that is
-       minimized costs two binaries. The magnitude of the fluxes is
-       estimated with an LP first, which also provides a start solution.
-     - 'reversible' : keep the model reversible and use one binary per
-       reaction, at the cost of a larger constraint matrix. Fewer binaries
-       makes this the faster formulation on large problems, but it needs a
-       solver that handles MILPs well and no LP estimate of the flux
-       magnitudes is made, so an arbitrary large flux is assumed.
- verbose : logical
-     if true, the MILP progression will be shown (default false).
- resolveTies : logical
-     if true, pin the MILP's degenerate optimum to a canonical answer instead of
-     relying on Seed alone: hold the primary (scores) objective at its optimum,
-     then minimize the count of "on" reactions, then, among the sparsest, minimize
-     their summed reaction-id rank (lowest ids preferred). Two extra MILP solves;
-     best-effort, so a phase that does not converge within TimeLimit is dropped
-     with a warning rather than changing the result. Only implemented for
-     formulation='reversible' -- errors if combined with 'irrev'. Matches
-     raven-toolbox's resolve_ties (raven-gecko-parity#104) (default false).
-
- Returns
- -------
- x : double
-     the corresponding fluxes for the full model.
- I : double
-     the indexes of the reactions in toMinimize that were used in the
-     solution.
- exitFlag : double
-     exit status:
-
-     - 1 : optimal solution found
-     - -1 : no feasible solution found
-     - -2 : a solution was found but is not proven optimal, because the
-       solver stopped before reaching optimality. It is returned, but it
-       may use more fluxes than the minimum
-
- Examples
- --------
-     [x, I, exitFlag] = getMinNrFluxes(model, toMinimize, params, scores);
-
- Notes
- -----
- Uses 1000 mmol/gDW/h as an arbitary large flux. Could possibly cause
- problems if the fluxes in the model are larger than that.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • checkSolution checkSolution Check if a Mosek solution is feasible and optimal.
  • optimizeProb optimizeProb Optimize an LP or MILP formulated in COBRA terms.
  • solveLP solveLP Solve a linear programming problem.
-This function is called by: -
    -
  • solveLP solveLP Solve a linear programming problem.
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function [x,I,exitFlag]=getMinNrFluxes(model, varargin)
-0002 % getMinNrFluxes  Find the minimal set of fluxes that satisfy the model.
-0003 %
-0004 % Uses mixed integer linear programming to find the minimal set of fluxes
-0005 % that satisfy the model.
-0006 %
-0007 % Parameters
-0008 % ----------
-0009 % model : struct
-0010 %     a model structure.
-0011 %
-0012 % Name-Value Arguments
-0013 % --------------------
-0014 % toMinimize : cell or logical or double
-0015 %     either a cell array of reaction IDs, a logical vector with the same
-0016 %     number of elements as reactions in the model, or a vector of indexes
-0017 %     for the reactions that should be minimized (default model.rxns).
-0018 % params : struct
-0019 %     *obsolete option*.
-0020 % scores : double
-0021 %     vector of weights for the reactions. Negative scores should not have
-0022 %     flux. Positive scores are not possible in this implementation, and
-0023 %     they are changed to max(scores(scores<0)), or to 0 if scores has no
-0024 %     negative entry at all. Must have the same dimension as toMinimize
-0025 %     (find(toMinimize) if it is a logical vector) (default -1 for all
-0026 %     reactions).
-0027 % formulation : char
-0028 %     which MILP formulation to build (default 'irrev'):
-0029 %
-0030 %     - 'irrev' : convert the model to irreversible format and use one
-0031 %       binary per irreversible reaction, so a reversible reaction that is
-0032 %       minimized costs two binaries. The magnitude of the fluxes is
-0033 %       estimated with an LP first, which also provides a start solution.
-0034 %     - 'reversible' : keep the model reversible and use one binary per
-0035 %       reaction, at the cost of a larger constraint matrix. Fewer binaries
-0036 %       makes this the faster formulation on large problems, but it needs a
-0037 %       solver that handles MILPs well and no LP estimate of the flux
-0038 %       magnitudes is made, so an arbitrary large flux is assumed.
-0039 % verbose : logical
-0040 %     if true, the MILP progression will be shown (default false).
-0041 % resolveTies : logical
-0042 %     if true, pin the MILP's degenerate optimum to a canonical answer instead of
-0043 %     relying on Seed alone: hold the primary (scores) objective at its optimum,
-0044 %     then minimize the count of "on" reactions, then, among the sparsest, minimize
-0045 %     their summed reaction-id rank (lowest ids preferred). Two extra MILP solves;
-0046 %     best-effort, so a phase that does not converge within TimeLimit is dropped
-0047 %     with a warning rather than changing the result. Only implemented for
-0048 %     formulation='reversible' -- errors if combined with 'irrev'. Matches
-0049 %     raven-toolbox's resolve_ties (raven-gecko-parity#104) (default false).
-0050 %
-0051 % Returns
-0052 % -------
-0053 % x : double
-0054 %     the corresponding fluxes for the full model.
-0055 % I : double
-0056 %     the indexes of the reactions in toMinimize that were used in the
-0057 %     solution.
-0058 % exitFlag : double
-0059 %     exit status:
-0060 %
-0061 %     - 1 : optimal solution found
-0062 %     - -1 : no feasible solution found
-0063 %     - -2 : a solution was found but is not proven optimal, because the
-0064 %       solver stopped before reaching optimality. It is returned, but it
-0065 %       may use more fluxes than the minimum
-0066 %
-0067 % Examples
-0068 % --------
-0069 %     [x, I, exitFlag] = getMinNrFluxes(model, toMinimize, params, scores);
-0070 %
-0071 % Notes
-0072 % -----
-0073 % Uses 1000 mmol/gDW/h as an arbitary large flux. Could possibly cause
-0074 % problems if the fluxes in the model are larger than that.
-0075 
-0076 p=parseRAVENargs(varargin, {'toMinimize',[]; 'params',[]; 'scores',[]; ...
-0077     'formulation','irrev'; 'verbose',false; 'resolveTies',false});
-0078 toMinimize=p.toMinimize;
-0079 params=p.params;
-0080 scores=p.scores;
-0081 formulation=p.formulation;
-0082 verbose=p.verbose;
-0083 resolveTies=p.resolveTies;
-0084 if isempty(toMinimize)
-0085     toMinimize=model.rxns;
-0086 elseif ~islogical(toMinimize) && ~isnumeric(toMinimize)
-0087     toMinimize=convertCharArray(toMinimize);
-0088 else
-0089     toMinimize=model.rxns(toMinimize);
-0090 end
-0091 
-0092 if isempty(formulation)
-0093     formulation='irrev';
-0094 end
-0095 formulation=lower(char(formulation));
-0096 if ~ismember(formulation,{'irrev','reversible'})
-0097     EM='Valid options for formulation are "irrev" or "reversible"';
-0098     error('RAVEN:badInput', '%s', EM);
-0099 end
-0100 
-0101 %For passing parameters to the solver
-0102 if isempty(params)
-0103     params=struct();
-0104 end
-0105 
-0106 if isempty(scores)
-0107     %It says that the default is -1, but that is to fit with other code
-0108     scores=ones(numel(toMinimize),1)*1;
-0109 else
-0110     if numel(scores)~=numel(toMinimize)
-0111         EM='The number of scores must be the same as the number of reactions to minimize';
-0112         error('RAVEN:badInput', '%s', EM);
-0113     end
-0114 
-0115     %Change positive scores to have a small negative weight. This is a
-0116     %temporary solution. If no score is negative there is nothing to clamp
-0117     %to (max([]) errors here rather than filling in a value, since the
-0118     %right-hand side is a computed empty array, not the literal [] MATLAB
-0119     %treats as a delete), so clamp to 0 instead: every reaction is then
-0120     %equally (un)weighted.
-0121     negScores=scores<0;
-0122     if any(negScores)
-0123         scores(~negScores)=max(scores(negScores));
-0124     else
-0125         scores(~negScores)=0;
-0126     end
-0127 
-0128     %It says that the default is -1, but that is to fit with other code
-0129     scores=scores*-1;
-0130 end
-0131 
-0132 if strcmp(formulation,'reversible')
-0133     [x,I,exitFlag]=minNrFluxesReversible(model,toMinimize,scores,params,verbose,resolveTies);
-0134 else
-0135     if resolveTies
-0136         EM='resolveTies is only implemented for formulation=''reversible''';
-0137         error('RAVEN:badInput', '%s', EM);
-0138     end
-0139     [x,I,exitFlag]=minNrFluxesIrrev(model,toMinimize,scores,params,verbose);
-0140 end
-0141 end
-0142 
-0143 function [x,I,exitFlag]=minNrFluxesIrrev(model,toMinimize,scores,params,verbose)
-0144 %One binary per irreversible reaction. Reversible reactions to minimize are
-0145 %split by convertToIrrev and therefore carry two binaries each.
-0146 
-0147 exitFlag=1;
-0148 
-0149 %Check if the model is in irreversible format
-0150 if any(model.rev)
-0151     %Convert the model to irreversible format
-0152     irrevModel=convertToIrrev(model);
-0153 
-0154     %Find the indexes for the reactions in toMinimize
-0155     [indexes, I]=ismember(strrep(irrevModel.rxns,'_REV',''),toMinimize);
-0156 else
-0157     irrevModel=model;
-0158 
-0159     %Find the indexes for the reactions in toMinimize
-0160     [indexes, I]=ismember(irrevModel.rxns,toMinimize);
-0161 end
-0162 
-0163 indexes=find(indexes);
-0164 %Adjust scores to fit with reversible
-0165 scores=scores(I(indexes));
-0166 
-0167 %Add binary constraints in the following manner: -  Add one unique
-0168 %"metabolite" for each integer reaction as a substrate.
-0169 %   These metabolites can have net production
-0170 %-  Add reactions for the production of each of those metabolites. The
-0171 %   amount produced in one reaction unit must be larger than the largest
-0172 %   possible flux in the model (but not too large to avoid bad scaling)
-0173 
-0174 %Calculate a solution to the problem without any constraints. This is to
-0175 %get an estimate about the magnitude of fluxes in the model and to get a
-0176 %feasible start solution.
-0177 sol=solveLP(irrevModel,1);
-0178 
-0179 %Return an empty solution if the non-constrained problem could not be solved
-0180 if isempty(sol.x)
-0181     x=[];
-0182     I=[];
-0183     exitFlag=-1;
-0184     return;
-0185 end
-0186 
-0187 %Take the maximal times 5 to have a safe margin. If it is smaller than 1000,
-0188 %then use 1000 instead.
-0189 maxFlux=max(max(sol.x)*5,1000);
-0190 
-0191 intArray=speye(numel(irrevModel.rxns))*-1;
-0192 intArray=intArray(indexes,:);
-0193 prob.a=[irrevModel.S;intArray];
-0194 a=[sparse(numel(irrevModel.mets),numel(indexes));speye(numel(indexes))*maxFlux];
-0195 prob.a=[prob.a a];
-0196 prob.ints.sub=numel(irrevModel.rxns)+1:numel(irrevModel.rxns)+numel(indexes);
-0197 
-0198 prob.c=[zeros(numel(irrevModel.rxns),1);scores(:);zeros(size(prob.a,1),1)]; %Minimize the number of fluxes
-0199 prob.A=[prob.a -speye(size(prob.a,1))];
-0200 prob.blc=[irrevModel.b(:,1);zeros(numel(indexes),1)];
-0201 if size(irrevModel.b,2)==2
-0202     prob.buc=[irrevModel.b(:,2);inf(numel(indexes),1)];
-0203 else
-0204     prob.buc=[irrevModel.b(:,1);inf(numel(indexes),1)];
-0205 end
-0206 prob.blx=[irrevModel.lb;zeros(numel(indexes),1)];
-0207 prob.bux=[irrevModel.ub;ones(numel(indexes),1)];
-0208 prob.lb = [prob.blx; prob.blc];
-0209 prob.ub = [prob.bux; prob.buc];
-0210 prob.osense=1;
-0211 prob.csense=repmat('E', 1, size(prob.a,1),1);
-0212 prob.b=zeros(size(prob.a,1), 1);
-0213 
-0214 %Use the output from the linear solution as starting point. Only the values
-0215 %for the integer variables will be used, but all are supplied.
-0216 prob.sol.int.xx=zeros(numel(prob.c),1);
-0217 prob.sol.int.xx(prob.ints.sub(sol.x(indexes)>10^-12))=1;
-0218 prob.x0=[];
-0219 prob.vartype=repmat('C', size(prob.A,2), 1);
-0220 prob.vartype(prob.ints.sub) = 'I'; % with .lb = 0 and .ub = 1, they are binary
-0221 % integers (glpk in octave only allows "continuous" or "", not "binary")
-0222 prob=rmfield(prob,{'blx','bux','blc','buc'});
-0223 
-0224 % Optimize the problem
-0225 res = optimizeProb(prob,params,verbose);
-0226 [isFeasible, isOptimal]=checkSolution(res);
-0227 
-0228 if ~isFeasible
-0229     x=[];
-0230     I=[];
-0231     exitFlag=-1;
-0232     return;
-0233 end
-0234 if ~isOptimal
-0235     %A feasible but suboptimal solution, i.e. the solver stopped before
-0236     %reaching optimality. The solution is still returned, but must not be
-0237     %reported as optimal: it may use more fluxes than the minimum.
-0238     exitFlag=-2;
-0239 end
-0240 
-0241 xx=res.full(1:numel(irrevModel.rxns));
-0242 I=res.full(numel(xx)+1:end);
-0243 
-0244 %Map back to original model from irrevModel
-0245 x=xx(1:numel(model.rxns));
-0246 if numel(irrevModel.rxns)>numel(model.rxns)
-0247     x(model.rev~=0)=x(model.rev~=0)-xx(numel(model.rxns)+1:end);
-0248 end
-0249 
-0250 I=ismember(toMinimize,strrep(irrevModel.rxns(indexes(I>10^-12)),'_REV',''));
-0251 end
-0252 
-0253 function [x,I,exitFlag]=minNrFluxesReversible(model,toMinimize,scores,params,verbose,resolveTies)
-0254 %One binary per reaction, on the reversible model. Trades a larger
-0255 %constraint matrix for half the binaries on reversible reactions.
-0256 
-0257 % glpk solver as implemented by COBRA does not work well for MILP.
-0258 global CBT_MILP_SOLVER
-0259 if strcmp(getpref('RAVEN','solver'),'cobra') && strcmp(CBT_MILP_SOLVER,'glpk')
-0260     error('RAVEN:badInput', '%s', 'The current solver is set to ''cobra'', while in COBRA the MILP solver has been set to ''glpk''. The COBRA implementation of glpk is not well suitable for solving MILPs. Please install the Gurobi or an alternative MILP solver.');
-0261 end
-0262 
-0263 exitFlag=1;
-0264 
-0265 %The trick to make this possible using a reversible model is that for
-0266 %reversible reactions, we do the following. Set the flux Vi == Vipos - Vineg.
-0267 %What happens then is that if the flux is positive, Vipos will have a nonzero value,
-0268 %and when the flux is negative, Vineg will have a positive value. In addition
-0269 %we can add an arbitrary constant to both Vipos and Vineg. For example, if the
-0270 %flux Vi is -1, Vineg can be 4 and Vipos 3. This is however not a problem,
-0271 %because we can be sure that Vineg + Vipos >= abs(Vi). Since we are interested
-0272 %in forcing the ints to be on when there is a flux, it does not matter if we overestimate
-0273 %the flux! So, we can simply constrain the boolean Yi to Yi*Maxflux >= Vineg + Vipos.
-0274 
-0275 %The matrix then becomes as this:
-0276 %         S       p n int b     var
-0277 %         SSSSSSSS        -
-0278 %         SSSSSSSS         -
-0279 %         SSSSSSSS          -
-0280 %         SSSSSSSS           -
-0281 %         SSSSSSSS            -
-0282 %         SSSSSSSS             -
-0283 %         -       1 -
-0284 %               -  1 -
-0285 %           -          M         -
-0286 %             -         M         -
-0287 %                 - - M         -
-0288 %                  - -   M         -
-0289 % An example with 8 rxns and 6 metabolites. - means -1, M max flux, and S is the S matrix.
-0290 % 4 rxns are to be minimized(1,3,5,7) and 1,7 are reversible. The p and n
-0291 % are the Vipos and Vineg variables (2 rxns of each). The ints are the Yi for
-0292 % the variables that are to be minimized (the rest of the rxns does not have any).
-0293 % The mets here are the constraints, so right under the S matrix, you have
-0294 % Vi == Vipos - Vineg for the reactions 1 and 7 while the two next rows represent
-0295 % the non-reversible rxns 3 and 5, where we simply say that yi*M >= Vi. The last
-0296 % 2 rows are the reactions yi*M >= Vipos + Vineg. To the right, we first have a
-0297 % -I matrix for setting the b constraints, and under that we have rxns that are
-0298 % just variables (rxns) between 0 and Inf to complete the constraints mentioned above.
-0299 %Ex: yi*M >= Vipos + Vineg is impl. as yi*M - Vipos - Vineg - var == 0, 0 <= var <= Inf.
-0300 %
-0301 %All rows should be equal to zero, so we do not set the b vector in the problem
-0302 %The reactions should be constrained as follows
-0303 %S - as given in model.lb and model.ub
-0304 %pos and neg - between 0 and inf
-0305 %ints - between 0 and 1
-0306 %b - as stated in the model.b vector - if this has one column, that is lb and ub (fixed value), if two columns, that is lb and ub
-0307 %var - between zero and inf
-0308 
-0309 [minLog, I]=ismember(model.rxns,toMinimize);
-0310 indexes=find(minLog);
-0311 revIndexes = find(minLog & (model.rev == 1));
-0312 irrevIndexes = find(minLog & (model.rev == 0));
-0313 revIndexesInInd = find(model.rev(indexes) == 1);
-0314 irrevIndexesInInd = find(model.rev(indexes) == 0);
-0315 
-0316 %The int columns are ordered as indexes, i.e. by position in the model,
-0317 %while the scores arrive ordered as toMinimize
-0318 scores=scores(I(indexes));
-0319 
-0320 %Add binary constraints in the following manner: -  Add one unique
-0321 %"metabolite" for each integer reaction as a substrate.
-0322 %   These metabolites can have net production
-0323 %-  Add reactions for the production of each of those metabolites. The
-0324 %   amount produced in one reaction unit must be larger than the largest
-0325 %   possible flux in the model (but not too large to avoid bad scaling)
-0326 
-0327 maxFlux=1000;
-0328 %we build the total matrix as blocks: [S pos neg int b var]
-0329 
-0330 %s block
-0331 intArray=speye(numel(model.rxns))*-1;
-0332 intArrayRev=intArray(revIndexes,:);
-0333 intArrayIrrev = intArray(irrevIndexes,:);
-0334 sBlock=[model.S;intArrayRev;intArrayIrrev;sparse(numel(revIndexes), numel(model.rxns))]; %the S matrix and what is below
-0335 
-0336 %pos/neg blocks
-0337 revposorneg1 = sparse(numel(model.mets), numel(revIndexes));
-0338 revpos2 = speye(numel(revIndexes));
-0339 revneg2 = -revpos2;
-0340 revposneg3 = sparse(numel(irrevIndexes), numel(revIndexes));
-0341 revposneg4 = revneg2;
-0342 posBlock = [revposorneg1;revpos2;revposneg3;revposneg4];
-0343 negBlock = [revposorneg1;revneg2;revposneg3;revposneg4];
-0344 
-0345 %int block
-0346 int1 = sparse(numel(model.mets), numel(indexes));
-0347 int2 = sparse(numel(revIndexes), numel(indexes));
-0348 tmpEye = speye(numel(indexes))*maxFlux;
-0349 int3 = tmpEye(irrevIndexesInInd,:);%we here select only the irrev indexes from the indexes
-0350 int4 = tmpEye(revIndexesInInd,:);
-0351 intBlock = [int1;int2;int3;int4];
-0352 
-0353 %b block
-0354 b1 = speye(numel(model.mets))*-1;
-0355 b2 = sparse(numel(indexes) + numel(revIndexes), numel(model.mets));
-0356 bBlock = [b1;b2];
-0357 
-0358 %var block
-0359 var1 = sparse(numel(model.mets), numel(indexes));
-0360 var2 = sparse(numel(revIndexes), numel(indexes));
-0361 tmpEye = speye(numel(indexes))*-1;
-0362 var3 = tmpEye(irrevIndexesInInd,:);
-0363 var4 = tmpEye(revIndexesInInd,:);
-0364 varBlock = [var1;var2;var3;var4];
-0365 
-0366 prob.A = [sBlock posBlock negBlock intBlock bBlock varBlock];
-0367 prob.a = prob.A;%I think this is needed as well
-0368 
-0369 prob.c=[zeros(numel(model.rxns),1);zeros(numel(revIndexes)*2,1); scores(:);zeros(numel(model.mets) + numel(indexes),1)]; %Minimize the sum of reaction scores for reactions that are on
-0370 
-0371 %ub and lb, text copied from above
-0372 %S - as given in model.lb and model.ub
-0373 %pos and neg - between 0 and inf
-0374 %ints - between 0 and 1
-0375 %b - as stated in the model.b vector - if this has one column, that is lb and ub (fixed value), if two columns, that is lb and ub
-0376 %var - between zero and inf
-0377 
-0378 if size(model.b,2)==2
-0379     bub = model.b(:,2);
-0380 else
-0381     bub = model.b(:,1);
-0382 end
-0383 
-0384 prob.lb = [model.lb;zeros(numel(revIndexes)*2,1);zeros(numel(indexes),1);model.b(:,1);zeros(numel(indexes),1)];
-0385 prob.ub = [model.ub;inf(numel(revIndexes)*2,1);ones(numel(indexes),1);bub;inf(numel(indexes),1)];
-0386 
-0387 prob.b=zeros(size(prob.a,1), 1);
-0388 
-0389 intsIndexes = find(prob.c ~= 0);
-0390 prob.x0=[];
-0391 prob.vartype=repmat('C', 1, size(prob.A,2));
-0392 prob.vartype(intsIndexes) = 'B';
-0393 prob.csense = repmat('E', 1, size(prob.A,1));
-0394 prob.osense=1; %minimize the objective
-0395 
-0396 %Defaults that suit this formulation, without discarding what the caller set
-0397 if ~isfield(params,'intTol')
-0398     params.intTol = 10^-9;
-0399 end
-0400 if ~isfield(params,'TimeLimit')
-0401     params.TimeLimit = 300;
-0402 end
-0403 if ~isfield(params,'Seed')
-0404     %Although the solver reports "optimal solution found", different seeds
-0405     %can return solutions with noticeably different objective values, so a
-0406     %fixed one is used to keep results reproducible
-0407     params.Seed = 26;
-0408 end
-0409 
-0410 % Optimize the problem
-0411 res = optimizeProb(prob,params,verbose);
-0412 [isFeasible, isOptimal]=checkSolution(res);
-0413 
-0414 if ~isFeasible
-0415     x=[];
-0416     I=[];
-0417     exitFlag=-1;
-0418     return;
-0419 end
-0420 if ~isOptimal
-0421     %A feasible but suboptimal solution, i.e. the solver stopped before
-0422     %reaching optimality. The solution is still returned, but must not be
-0423     %reported as optimal: it may use more fluxes than the minimum.
-0424     exitFlag=-2;
-0425 end
-0426 
-0427 if resolveTies
-0428     %This MILP is highly degenerate: many reaction subsets reach the same
-0429     %score optimum, and the solver returns an arbitrary one -- reproducible
-0430     %for a fixed solver build + Seed, but fragile to a solver version bump
-0431     %or an unrelated change upstream of the MILP (see raven-gecko-parity#104,
-0432     %where this was demonstrated on genome-scale Human-GEM: the same Seed,
-0433     %same problem, but a different MILP construction path flipped a tied
-0434     %pair of reactions). This runs a lexicographic phase 2, holding the
-0435     %score objective at its optimum with a floor constraint: first minimise
-0436     %the count of "on" reactions (the sparsest optimum), then, among the
-0437     %sparsest, minimise their summed reaction-id rank (prefer lower ids).
-0438     %Ports raven-toolbox's _resolve_ties_fill (raven-toolbox#114).
-0439     %
-0440     %intCols identifies the int/binary variable columns directly from the
-0441     %block layout (prob.c = [zeros(rxns); zeros(2*revIndexes); scores(:);
-0442     %zeros(mets+indexes)]) rather than via find(prob.c~=0): a zero-scored
-0443     %reaction would otherwise silently drop out of the column count and
-0444     %misalign every later index into "indexes".
-0445     intCols = numel(model.rxns) + 2*numel(revIndexes) + (1:numel(indexes));
-0446     primaryObj = full(prob.c(intCols)' * res.full(intCols));
-0447     tol = max(abs(primaryObj)*1e-7, 1e-7);
-0448 
-0449     floorRow = sparse(1,size(prob.A,2));
-0450     floorRow(intCols) = prob.c(intCols)';
-0451     tieProb = prob;
-0452     tieProb.A = [prob.A; floorRow];
-0453     tieProb.a = tieProb.A;
-0454     tieProb.b = [prob.b; primaryObj+tol];
-0455     tieProb.csense = [prob.csense 'L'];
-0456 
-0457     %An integer objective needs only a cheap absolute-gap proof, not a tiny
-0458     %relative one -- RAVEN's own MIPGap default (1e-12) can be slow to prove
-0459     %on a genome-scale problem where a gap below 1 already proves optimality
-0460     %exactly (mirrors raven-toolbox's own MIPGap=0/MIPGapAbs=0.4 for the same
-0461     %two phases).
-0462     tieParams = params;
-0463     tieParams.MIPGap = 0;
-0464     tieParams.MIPGapAbs = 0.4;
-0465 
-0466     unproven = {};
-0467 
-0468     %Phase 2a: fewest "on" reactions among the score-optimal solutions.
-0469     countObj = zeros(size(prob.c));
-0470     countObj(intCols) = 1;
-0471     tieProb.c = countObj;
-0472     res2a = optimizeProb(tieProb,tieParams,verbose);
-0473     [isFeasible2a, isOptimal2a] = checkSolution(res2a);
-0474     if ~isOptimal2a
-0475         unproven{end+1} = 'phase2a-parsimony';
-0476     end
-0477     if isFeasible2a
-0478         kmin = full(countObj'*res2a.full);
-0479 
-0480         %Phase 2b: among the sparsest, prefer lower reaction ids -- a
-0481         %deterministic tie-break independent of solver/seed.
-0482         countRow = sparse(1,size(tieProb.A,2));
-0483         countRow(intCols) = 1;
-0484         tieProb2b = tieProb;
-0485         tieProb2b.A = [tieProb.A; countRow];
-0486         tieProb2b.a = tieProb2b.A;
-0487         tieProb2b.b = [tieProb.b; kmin+0.5];
-0488         tieProb2b.csense = [tieProb.csense 'L'];
-0489 
-0490         [~, sortOrder] = sort(model.rxns(indexes));
-0491         ranks = zeros(numel(indexes),1);
-0492         ranks(sortOrder) = (1:numel(indexes))';
-0493         idObj = zeros(size(prob.c));
-0494         idObj(intCols) = ranks;
-0495         tieProb2b.c = idObj;
-0496         res2b = optimizeProb(tieProb2b,tieParams,verbose);
-0497         [isFeasible2b, isOptimal2b] = checkSolution(res2b);
-0498         if ~isOptimal2b
-0499             unproven{end+1} = 'phase2b-idrank';
-0500         end
-0501         if isFeasible2b
-0502             %Adopt the tie-resolved solution; exitFlag still reflects
-0503             %whether the *primary* (scores) objective was proven optimal,
-0504             %same as without resolveTies.
-0505             res = res2b;
-0506         end
-0507     end
-0508     if ~isempty(unproven)
-0509         EM = ['getMinNrFluxes tie resolution did not converge (' strjoin(unproven,', ') '): the selection among equally score-optimal solutions is itself an unproven incumbent, so resolveTies has reduced but not removed the run-to-run spread. Raise params.TimeLimit for a proven tie-break.'];
-0510         warning('RAVEN:warning', '%s', EM);
-0511     end
-0512 end
-0513 
-0514 x=res.full(1:numel(model.rxns));%the fluxes
-0515 
-0516 ints = res.full(intsIndexes);
-0517 %The margin for integers in gurobi is 10^-5; use 10^-3 to have some margin.
-0518 I=ismember(toMinimize, model.rxns(indexes(ints > 10^-3)));
-0519 
-0520 sel = (ints > 10^-12) & (ints < 0.5);
-0521 if sum(sel) > 0
-0522     %This may indicate that there is a problem with the tolerances in the solver
-0523     disp(['getMinNrFluxes: Some variables meant to be boolean in the MILP have intermediate values. Num vars: ' num2str(sum(sel))])
-0524 end
-0525 
-0526 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/solver/optimizeProb.html b/doc/solver/optimizeProb.html deleted file mode 100644 index 67281119..00000000 --- a/doc/solver/optimizeProb.html +++ /dev/null @@ -1,453 +0,0 @@ - - - - Description of optimizeProb - - - - - - - - - -
Home > solver > optimizeProb.m
- - - -

optimizeProb -

- -

PURPOSE ^

-
optimizeProb Optimize an LP or MILP formulated in COBRA terms.
- -

SYNOPSIS ^

-
function res = optimizeProb(prob,varargin)
- -

DESCRIPTION ^

-
 optimizeProb  Optimize an LP or MILP formulated in COBRA terms.
-
- Parameters
- ----------
- prob : struct
-     COBRA-style LP/MILP problem struct to be optimised.
-
- Name-Value Arguments
- --------------------
- params : struct
-     solver-specific parameters. In addition to solver parameters, the
-     field "maxRatio" can be set to a number > 1 to improve numerical
-     conditioning: before solving (LP only) any column whose coefficients
-     span more than maxRatio is split via auxiliary metabolites/variables,
-     preserving the feasible region. See splitProbForConditioning. The
-     field is consumed here and not forwarded to the solver.
- verbose : logical
-     if true MILP progress is shown (default true).
-
- Returns
- -------
- res : struct
-     the output structure from the selected solver RAVENSOLVER (COBRA
-     style).
-
- See also
- --------
- splitProbForConditioning
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
  • getMinNrFluxes getMinNrFluxes Find the minimal set of fluxes that satisfy the model.
  • solveLP solveLP Solve a linear programming problem.
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function res = optimizeProb(prob,varargin)
-0002 % optimizeProb  Optimize an LP or MILP formulated in COBRA terms.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % prob : struct
-0007 %     COBRA-style LP/MILP problem struct to be optimised.
-0008 %
-0009 % Name-Value Arguments
-0010 % --------------------
-0011 % params : struct
-0012 %     solver-specific parameters. In addition to solver parameters, the
-0013 %     field "maxRatio" can be set to a number > 1 to improve numerical
-0014 %     conditioning: before solving (LP only) any column whose coefficients
-0015 %     span more than maxRatio is split via auxiliary metabolites/variables,
-0016 %     preserving the feasible region. See splitProbForConditioning. The
-0017 %     field is consumed here and not forwarded to the solver.
-0018 % verbose : logical
-0019 %     if true MILP progress is shown (default true).
-0020 %
-0021 % Returns
-0022 % -------
-0023 % res : struct
-0024 %     the output structure from the selected solver RAVENSOLVER (COBRA
-0025 %     style).
-0026 %
-0027 % See also
-0028 % --------
-0029 % splitProbForConditioning
-0030 
-0031 p=parseRAVENargs(varargin, {'params',[]; 'verbose',true});
-0032 params=p.params;
-0033 if isempty(params)
-0034     params=struct();
-0035 end
-0036 verbose=p.verbose;
-0037 
-0038 %Set as global variable for speed improvement if optimizeProb is run many times
-0039 global RAVENSOLVER;
-0040 %CBT_MILP_SOLVER is a COBRA Toolbox global, set by changeCobraSolver, and
-0041 %has to be declared as such to be readable in the MILP check below
-0042 global CBT_MILP_SOLVER;
-0043 if isempty(RAVENSOLVER)
-0044     if(~ispref('RAVEN','solver'))
-0045         error('RAVEN:badInput', '%s', 'RAVEN solver not defined or unknown. Try using setRavenSolver(''solver'').');
-0046     else
-0047         RAVENSOLVER = getpref('RAVEN','solver');
-0048     end
-0049 end
-0050 solver=RAVENSOLVER;
-0051 if ~all(lower(prob.vartype) == 'c')
-0052     milp=true;
-0053     errorText = 'glpk is not suitable for solving MILPs, ';
-0054     switch solver
-0055         case 'glpk'
-0056             error([errorText 'select a different solver with setRavenSolver().'])
-0057         case 'cobra'
-0058             if strcmp(CBT_MILP_SOLVER,'glpk')
-0059                 error([errorText 'select a different solver with changeCobraSolver() or setRavenSolver().'])
-0060             end
-0061     end
-0062 else
-0063     milp=false;
-0064 end
-0065 
-0066 %% Define default parameters, which will then be used to make solver-
-0067 % specific solverparams structures
-0068 defaultparams.feasTol        = 1e-9;
-0069 defaultparams.optTol         = 1e-9;
-0070 defaultparams.objTol         = 1e-6;
-0071 defaultparams.timeLimit      = 1000;
-0072 %defaultparams.iterationLimit = 1000;
-0073 defaultparams.intTol         = 1e-12;
-0074 defaultparams.relMipGapTol   = 1e-12;
-0075 defaultparams.absMipGapTol   = 1e-12;
-0076 if milp
-0077     defaultparams.MIPGap     = 1e-12;
-0078     defaultparams.Seed       = 1;
-0079 end
-0080 
-0081 %% Optionally improve numerical conditioning before solving. This is opt-in
-0082 % through params.maxRatio and only applied to LPs. Columns whose coefficients
-0083 % span more than maxRatio are split via auxiliary metabolites/variables, which
-0084 % preserves the feasible region but reduces the spread within each column. The
-0085 % added rows/columns are stripped from the result further down.
-0086 condApplied = false;
-0087 if isfield(params,'maxRatio')
-0088     maxRatio = params.maxRatio;
-0089     params   = rmfield(params,'maxRatio');
-0090     if ~milp && isnumeric(maxRatio) && isscalar(maxRatio) && isfinite(maxRatio) && maxRatio>1
-0091         [prob, condInfo] = splitProbForConditioning(prob, maxRatio);
-0092         condApplied = condInfo.applied;
-0093     end
-0094 end
-0095 
-0096 res.obj=[];
-0097 switch solver
-0098     %% Use whatever solver is set by COBRA Toolbox changeCobraSolver
-0099     case 'cobra'
-0100         if milp
-0101             cparams=struct('timeLimit',1e9,'printLevel',0,'intTol',1e-6,'relMipGapTol',1e-9);
-0102             cparams=structUpdate(cparams,params);
-0103             res=solveCobraMILP(prob,cparams);
-0104         else
-0105             res=solveCobraLP(prob);
-0106         end
-0107         %% Use Gurobi in a MATLAB environment
-0108     case 'gurobi'
-0109         if milp
-0110             if verbose
-0111                 solverparams.OutputFlag = 1;
-0112             else
-0113                 solverparams.OutputFlag = 0;
-0114             end
-0115             solverparams.IntFeasTol = 10^-9; %min val for gurobi
-0116             solverparams.MIPGap = defaultparams.MIPGap;
-0117             solverparams.Seed = defaultparams.Seed;
-0118         else
-0119             solverparams.OutputFlag = 0;
-0120         end
-0121         solverparams.DisplayInterval= 1; % Level of verbosity
-0122         solverparams.TimeLimit      = defaultparams.timeLimit;
-0123         solverparams.FeasibilityTol = defaultparams.feasTol;
-0124         solverparams.OptimalityTol  = defaultparams.optTol;
-0125         solverparams.Presolve       = 2;
-0126         % getCurrentTask requires the Parallel Computing Toolbox; without it
-0127         % the call itself errors, so guard it rather than requiring the
-0128         % toolbox for every gurobi solve.
-0129         if exist('getCurrentTask','file') && ~isempty(getCurrentTask) % If run in parallel, then one thread per gurobi
-0130             solverparams.Threads=1;
-0131         end
-0132         solverparams = structUpdate(solverparams,params);
-0133 
-0134         % Restructering problem according to gurobi format
-0135         if isfield(prob, 'csense')
-0136             prob.sense = renameparams(prob.csense, {'L','G','E'}, {'<','>','='});
-0137             prob = rmfield(prob, {'csense'});
-0138         end
-0139         % gurobi minimises unless told otherwise, so an absent osense means
-0140         % minimisation; osense is also needed below to sign the duals
-0141         osense = 1;
-0142         if isfield(prob, 'osense')
-0143             osense = prob.osense;
-0144             if osense < 0
-0145                 prob.modelsense = 'max';
-0146             else
-0147                 prob.modelsense = 'min';
-0148             end
-0149             prob = rmfield(prob, {'osense'});
-0150         end
-0151         [prob.obj, prob.rhs, prob.vtype] = deal(prob.c, prob.b, prob.vartype);
-0152         prob = rmfield(prob, {'c','b','vartype'});
-0153 
-0154         resG = gurobi(prob,solverparams);
-0155 
-0156         % Name output fields the same as COBRA does. Every field below is
-0157         % optional in the gurobi result: which ones are present depends on
-0158         % the status and on the method gurobi chose (a barrier solve returns
-0159         % no simplex basis, for instance). They are therefore read
-0160         % individually rather than under one try/catch, which would have
-0161         % reported a missing basis on an optimal solve as "infeasible".
-0162         res.origStat = resG.status;
-0163         if isfield(resG,'x')
-0164             res.full = resG.x;
-0165         end
-0166         if isfield(resG,'objval')
-0167             res.obj = resG.objval;
-0168         end
-0169         if isfield(resG,'pi') && isfield(resG,'rc')
-0170             res.dual     = -resG.pi*osense;
-0171             res.rcost    = -resG.rc*osense;
-0172         end
-0173         if milp && strcmp(resG.status, 'TIME_LIMIT') && isfield(resG,'x') && isfield(resG,'objval')
-0174             % An incumbent was found before the time limit. It is usable but
-0175             % not proven optimal, so it is reported as feasible rather than
-0176             % optimal; callers that must not act on a suboptimal MILP
-0177             % solution (getMinNrFluxes, solveLP) key on that distinction.
-0178             resG.status = 'SUBOPTIMAL';
-0179         end
-0180         switch resG.status
-0181             case 'OPTIMAL'
-0182                 res.stat = 1;
-0183             case {'UNBOUNDED','SUBOPTIMAL'}
-0184                 res.stat = 2;
-0185             otherwise
-0186                 res.stat = 0;
-0187         end
-0188         if ~milp
-0189             if isfield(resG,'vbasis')
-0190                 res.vbasis = resG.vbasis;
-0191             end
-0192             if isfield(resG,'cbasis')
-0193                 res.cbasis = resG.cbasis;
-0194             end
-0195         elseif isfield(resG,'mipgap')
-0196             res.mipgap = resG.mipgap;
-0197         end
-0198         if res.stat>0 && ~isfield(res,'full')
-0199             % A status that claims a solution without returning one
-0200             res.stat = 0;
-0201         end
-0202         %% Use GLPK using RAVEN-provided binary
-0203     case 'glpk'
-0204         solverparams.scale   = 1; % Auto scaling
-0205         %solverparams.tmlim   = defaultparams.timeLimit;
-0206         solverparams.tolbnd  = defaultparams.feasTol;
-0207         solverparams.toldj   = defaultparams.optTol;
-0208         solverparams.tolint  = defaultparams.intTol;
-0209         solverparams.tolobj  = defaultparams.objTol;
-0210         solverparams.msglev  = 0; % Level of verbosity
-0211         solverparams = structUpdate(solverparams,params);
-0212 
-0213         prob.csense = renameparams(prob.csense, {'L','G','E'}, {'U','L','S'});
-0214 
-0215         if milp
-0216             solverparams.tmlim   = solverparams.tmlim*10;
-0217             solverparams.msglev  = 1; % Level of verbosity
-0218             disp('Issues have been observed when using GLPK for MILP solving. Be advised to carefully observe the results, or us another solver.')
-0219         end
-0220 
-0221         % Ensure that RAVEN glpk binary is used, return to original
-0222         % directory afterwards
-0223         [ravenDir,currDir]=findRAVENroot();
-0224         cd(fullfile(ravenDir,'software','GLPKmex'))
-0225         [xopt, fmin, errnum, extra] = glpk(prob.c, prob.A, prob.b, prob.lb, prob.ub, prob.csense, prob.vartype, prob.osense, solverparams);
-0226         cd(currDir)
-0227 
-0228         switch errnum % 1 = undefined; 2 = feasible; 3 = infeasible; 4 = no feasible solution; 5 = optimal; 6 = no unbounded solution
-0229             case 5
-0230                 res.stat = 1; % Optimal
-0231             case 2
-0232                 res.stat = 2; % Feasible, but not optimal
-0233             otherwise
-0234                 res.stat = 0;
-0235         end
-0236         res.origStat = errnum;
-0237         res.full     = xopt;
-0238         res.obj      = fmin;
-0239         res.dual     = -extra.lambda*prob.osense;
-0240         res.rcost    = -extra.redcosts*prob.osense;
-0241         %% Use scip
-0242     case {'soplex','scip'} % Old 'soplex' option also allowed
-0243         % scip takes a row range [rl,ru] rather than a right-hand side plus
-0244         % a sense, so csense has to be translated into that range. Passing
-0245         % [-b,b] is only the same problem when every row is an equality with
-0246         % b = 0, which is the case for solveLP but for none of the MILPs
-0247         % built elsewhere in RAVEN.
-0248         rl = full(prob.b(:));
-0249         ru = rl;
-0250         csense = prob.csense(:);
-0251         rl(csense=='L') = -inf;   % A*x <= b
-0252         ru(csense=='G') = inf;    % A*x >= b
-0253         [xopt,fval,exitflag,scipStats] = scip([], prob.c, prob.A, rl, ru, prob.lb, prob.ub, prob.vartype);
-0254 
-0255         %   [x,fval,exitflag,stats] = scip(H, f, A, rl, ru, lb, ub, xtype, sos, qc, nl, x0, opts)
-0256         %
-0257         %   Input arguments*:
-0258         %       H - quadratic objective matrix (sparse, optional [NOT TRIL / TRIU])
-0259         %       f - linear objective vector
-0260         %       A - linear constraint matrix (sparse)
-0261         %       rl - linear constraint lhs
-0262         %       ru - linear constraint rhs
-0263         %       lb - decision variable lower bounds
-0264         %       ub - decision variable upper bounds
-0265         %       xtype - string of variable integrality ("c" continuous, "i" integer, "b" binary)
-0266         %       sos - SOS structure with fields type, index and weight (see below)
-0267         %       qc - Quadratic Constraints structure with fields Q, l, qrl and qru (see below)
-0268         %       nl - Nonlinear Objective and Constraints structure (see below)
-0269         %       x0 - primal solution
-0270         %       opts - solver options (see below)
-0271         %
-0272         %   Return arguments:
-0273         %       x - solution vector
-0274         %       fval - objective value at the solution
-0275         %       exitflag - exit status (see below)
-0276         %       stats - statistics structure
-0277         %
-0278         %   Option Fields (all optional, see also optiset for a list):
-0279         %       solverOpts - specific SCIP options (list of pairs of parameter names and values)
-0280         %       maxiter - maximum LP solver iterations
-0281         %       maxnodes - maximum nodes to explore
-0282         %       maxtime - maximum execution time [s]
-0283         %       tolrfun - primal feasibility tolerance
-0284         %       display - solver display level [0-5]
-0285         %       probfile - write problem to given file
-0286         %       presolvedfile - write presolved problem to file
-0287         %
-0288         %   Return Status, as returned by the MatlabSCIPInterface build that
-0289         %   RAVEN provides and that setRavenSolver points at
-0290         %   (github.com/scipopt/MatlabSCIPInterface). Verified against that
-0291         %   binary with a bounded, an infeasible and an unbounded problem:
-0292         %       1 - Problem Solved to Optimality
-0293         %       2 - Problem is Infeasible
-0294         %       3 - Problem is Unbounded
-0295         %
-0296         %   The OPTI Toolbox build of scip uses a different, longer set in
-0297         %   which optimality is 11, infeasibility 12 and unboundedness 13,
-0298         %   with the lower numbers meaning "a limit was reached". Those codes
-0299         %   are accepted below as well, so either build reports optimality
-0300         %   correctly; only OPTI's 1 ("User Interrupted") is ambiguous, and
-0301         %   reading it as optimal is no worse than the previous mapping,
-0302         %   which recognised no status from the shipped build at all.
-0303         
-0304         res.origStat = exitflag;
-0305         res.full = xopt;
-0306         res.obj  = fval;
-0307 
-0308         switch exitflag
-0309             case {1, 11}                  % solved to optimality
-0310                 res.stat = 1;
-0311             case {3, 13}                  % unbounded
-0312                 res.stat = 2;
-0313             case {5, 6, 7, 8, 9, 10}      % OPTI: a limit was reached, so a
-0314                 res.stat = 2;             % solution may exist but is not proven optimal
-0315             otherwise                     % 2/12 infeasible, 0 unknown, 4, 14
-0316                 res.stat = 0;
-0317         end
-0318 
-0319         if milp
-0320             % Callers such as ftINIT compare this against their own
-0321             % params.MIPGap, so it has to be the quantity the gurobi branch
-0322             % reports there: a relative gap. It is derived from the bounds
-0323             % rather than read from scipStats.BBgap, whose units are not
-0324             % documented. inf where no gap can be established, so that a
-0325             % caller testing "gap small enough" does not accept the solution.
-0326             if res.stat == 1
-0327                 res.mipgap = 0;
-0328             else
-0329                 res.mipgap = inf;
-0330                 primal = scipStats.PrimalBound;
-0331                 if isfinite(primal) && abs(primal) > 1e-10
-0332                     res.mipgap = abs(primal-scipStats.DualBound)/abs(primal);
-0333                 end
-0334             end
-0335         end
-0336     otherwise
-0337         error('RAVEN solver not defined or unknown. Try using setRavenSolver(''solver'').');
-0338 end
-0339 if res.stat>0
-0340     res.full=res.full(1:size(prob.a,2));
-0341 end
-0342 %Strip the auxiliary rows/columns that were added for conditioning, so that
-0343 %the result matches the dimensions of the original problem. res.full is
-0344 %already truncated to the original variables above (prob.a is left untouched).
-0345 if condApplied
-0346     if isfield(res,'dual') && numel(res.dual)>condInfo.nOrigRows
-0347         res.dual=res.dual(1:condInfo.nOrigRows);
-0348     end
-0349     if isfield(res,'rcost') && numel(res.rcost)>condInfo.nOrigCols
-0350         res.rcost=res.rcost(1:condInfo.nOrigCols);
-0351     end
-0352 end
-0353 end
-0354 
-0355 function s_merged=structUpdate(s_old,s_new)
-0356 %Remove overlapping fields from first struct;
-0357 %Obtain all unique names of remaining fields;
-0358 %Merge both structs
-0359 s_merged = rmfield(s_old, intersect(fieldnames(s_old), fieldnames(s_new)));
-0360 names = [fieldnames(s_merged); fieldnames(s_new)];
-0361 s_merged = cell2struct([struct2cell(s_merged); struct2cell(s_new)], names, 1);
-0362 end
-0363 
-0364 function paramlist = renameparams(paramlist,old,new)
-0365 if ~iscell(paramlist)
-0366     wasNoCell = true;
-0367     paramlist={paramlist};
-0368 else
-0369     wasNoCell = false;
-0370 end
-0371 for i=1:numel(old)
-0372     paramlist = regexprep(paramlist,old{i},new{i});
-0373 end
-0374 if wasNoCell
-0375     paramlist=paramlist{1};
-0376 end
-0377 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/solver/setRavenSolver.html b/doc/solver/setRavenSolver.html deleted file mode 100644 index 97a7ca0e..00000000 --- a/doc/solver/setRavenSolver.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - Description of setRavenSolver - - - - - - - - - -
Home > solver > setRavenSolver.m
- - - -

setRavenSolver -

- -

PURPOSE ^

-
setRavenSolver Set the solver for RAVEN.
- -

SYNOPSIS ^

-
function setRavenSolver(solver)
- -

DESCRIPTION ^

-
 setRavenSolver  Set the solver for RAVEN.
-
- Sets the solver for RAVEN and optionally saves a default value to MATLAB
- startup.
-
- Parameters
- ----------
- solver : char
-     string with solver name. The following options are available:
-
-     - glpk : uses RAVEN-provided binaries.
-     - gurobi : requires a working Gurobi installation.
-     - cobra : requires a working COBRA Toolbox installation. RAVEN
-       parses the problem to COBRA and uses the solver that is set by
-       changeCobraSolver.
-
- Examples
- --------
-     setRavenSolver(solver);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function setRavenSolver(solver)
-0002 % setRavenSolver  Set the solver for RAVEN.
-0003 %
-0004 % Sets the solver for RAVEN and optionally saves a default value to MATLAB
-0005 % startup.
-0006 %
-0007 % Parameters
-0008 % ----------
-0009 % solver : char
-0010 %     string with solver name. The following options are available:
-0011 %
-0012 %     - glpk : uses RAVEN-provided binaries.
-0013 %     - gurobi : requires a working Gurobi installation.
-0014 %     - cobra : requires a working COBRA Toolbox installation. RAVEN
-0015 %       parses the problem to COBRA and uses the solver that is set by
-0016 %       changeCobraSolver.
-0017 %
-0018 % Examples
-0019 % --------
-0020 %     setRavenSolver(solver);
-0021 solver=char(solver);
-0022 switch solver
-0023     case 'cobra'
-0024         global CBT_LP_SOLVER;
-0025         if isempty(CBT_LP_SOLVER)
-0026             error('COBRA toolbox is not initialized, run ''initCobraToolbox()''')
-0027         else
-0028             setpref('RAVEN','solver','cobra');
-0029         end
-0030     case 'mosek'
-0031         error('MOSEK support has been discontinued since RAVEN 2.3.0.')
-0032     case {'glpk','gurobi'}
-0033         setpref('RAVEN','solver',solver)
-0034     case 'soplex'
-0035         error('The ''soplex'' solver is replaced by ''scip''.')
-0036     case 'scip'
-0037         if ~ispc
-0038             try
-0039                 scip; % The user might have installed SCIP manually
-0040                 setpref('RAVEN','solver','scip')
-0041             catch
-0042                 error('SCIP not found. RAVEN only provides the precompiled SCIP MEX binary for Windows. Instructions on how to compile the SCIP MEX file are found at https://github.com/scipopt/MatlabSCIPInterface')
-0043             end
-0044         else
-0045             ravenDir = findRAVENroot();
-0046             if ~exist(fullfile(ravenDir,'software','scip','scip.mexw64'),'file')
-0047                 try
-0048                     disp('Downloading and installing RAVEN-provided SCIP MEX binary...')
-0049                     websave(fullfile(ravenDir,'software','scip_mex_win.zip'),'https://github.com/SysBioChalmers/RAVEN/releases/download/v2.8.6/scip_mex_win.zip'); % Should be updated to release with SCIP functionality
-0050                 catch
-0051                     error('Unable to download SCIP MEX binary from the RAVEN GitHub page.')
-0052                 end
-0053                 unzip(fullfile(ravenDir,'software','scip_mex_win.zip'),fullfile(ravenDir,'software','scip'));
-0054                 delete(fullfile(ravenDir,'software','scip_mex_win.zip'));
-0055             end
-0056             try
-0057                 scip; % The pre-compiled MEX file might fail
-0058                 setpref('RAVEN','solver','scip')
-0059             catch
-0060                 error('Unable to use the RAVEN-provided precompiled SCIP MEX binary. Instructions on how to compile the SCIP MEX file are found at https://github.com/scipopt/MatlabSCIPInterface')
-0061             end
-0062         end
-0063     otherwise
-0064         error('Invalid solver defined')
-0065 end
-0066 global RAVENSOLVER
-0067 RAVENSOLVER = solver;
-0068 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/solver/solveLP.html b/doc/solver/solveLP.html deleted file mode 100644 index 01898bc7..00000000 --- a/doc/solver/solveLP.html +++ /dev/null @@ -1,338 +0,0 @@ - - - - Description of solveLP - - - - - - - - - -
Home > solver > solveLP.m
- - - -

solveLP -

- -

PURPOSE ^

-
solveLP Solve a linear programming problem.
- -

SYNOPSIS ^

-
function [solution, hsSolOut]=solveLP(model,varargin)
- -

DESCRIPTION ^

-
 solveLP  Solve a linear programming problem.
-
- Parameters
- ----------
- model : struct
-     a model structure.
-
- Name-Value Arguments
- --------------------
- minFlux : double
-     determines if a second optimization should be performed in order to
-     get rid of loops in the flux distribution (default 0):
-
-     - 0 : no such optimization is performed
-     - 1 : the sum of abs(fluxes) is minimized. This is the fastest way of
-       getting rid of loops
-     - 2 : *option is deprecated*
-     - 3 : the number of fluxes is minimized. This can result in the flux
-       distributions that are the easiest to interpret. Note that this
-       optimization can be very slow
- params : struct
-     solver parameters, forwarded to optimizeProb. Mostly obsolete, but the
-     field "maxRatio" (a number > 1) can be set to improve numerical
-     conditioning of ill-scaled models: any reaction whose stoichiometric
-     coefficients span more than maxRatio is transiently split via
-     auxiliary metabolites before solving, which preserves the feasible
-     region (and thus the flux distribution of the original reactions). A
-     common value is 1e6 (no splitting by default).
- hsSol : struct
-     hot-start solution for the LP solver. This can significantly speed up
-     the process if many similar optimization problems are solved
-     iteratively. Only used if minFlux is 0 or 1.
-
- Returns
- -------
- solution : struct
-     LP solution with fields:
-
-     - f : objective value
-     - x : primal (flux distribution)
-     - stat : exit flag (1 = optimization terminated successfully, 0 =
-       solution is feasible but not necessarily optimal, -1 = no feasible
-       solution found, -2 = solution found but flux minimization failed)
-     - msg : string describing the status of the optimization
-     - sPrice : shadow price (only reported if minFlux was 0)
-     - rCost : reduced cost (only reported if minFlux was 0)
- hsSolOut : struct
-     solution to be used as hot-start solution (see the input parameters).
-     Only used if minFlux is 0 or 1.
-
- Examples
- --------
-     [solution, hsSolOut] = solveLP(model, minFlux, params, hsSol);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
  • checkSolution checkSolution Check if a Mosek solution is feasible and optimal.
  • getMinNrFluxes getMinNrFluxes Find the minimal set of fluxes that satisfy the model.
  • optimizeProb optimizeProb Optimize an LP or MILP formulated in COBRA terms.
  • solveLP solveLP Solve a linear programming problem.
-This function is called by: -
    -
  • getMinNrFluxes getMinNrFluxes Find the minimal set of fluxes that satisfy the model.
  • solveLP solveLP Solve a linear programming problem.
- - - - -

SOURCE CODE ^

-
0001 function [solution, hsSolOut]=solveLP(model,varargin)
-0002 % solveLP  Solve a linear programming problem.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % model : struct
-0007 %     a model structure.
-0008 %
-0009 % Name-Value Arguments
-0010 % --------------------
-0011 % minFlux : double
-0012 %     determines if a second optimization should be performed in order to
-0013 %     get rid of loops in the flux distribution (default 0):
-0014 %
-0015 %     - 0 : no such optimization is performed
-0016 %     - 1 : the sum of abs(fluxes) is minimized. This is the fastest way of
-0017 %       getting rid of loops
-0018 %     - 2 : *option is deprecated*
-0019 %     - 3 : the number of fluxes is minimized. This can result in the flux
-0020 %       distributions that are the easiest to interpret. Note that this
-0021 %       optimization can be very slow
-0022 % params : struct
-0023 %     solver parameters, forwarded to optimizeProb. Mostly obsolete, but the
-0024 %     field "maxRatio" (a number > 1) can be set to improve numerical
-0025 %     conditioning of ill-scaled models: any reaction whose stoichiometric
-0026 %     coefficients span more than maxRatio is transiently split via
-0027 %     auxiliary metabolites before solving, which preserves the feasible
-0028 %     region (and thus the flux distribution of the original reactions). A
-0029 %     common value is 1e6 (no splitting by default).
-0030 % hsSol : struct
-0031 %     hot-start solution for the LP solver. This can significantly speed up
-0032 %     the process if many similar optimization problems are solved
-0033 %     iteratively. Only used if minFlux is 0 or 1.
-0034 %
-0035 % Returns
-0036 % -------
-0037 % solution : struct
-0038 %     LP solution with fields:
-0039 %
-0040 %     - f : objective value
-0041 %     - x : primal (flux distribution)
-0042 %     - stat : exit flag (1 = optimization terminated successfully, 0 =
-0043 %       solution is feasible but not necessarily optimal, -1 = no feasible
-0044 %       solution found, -2 = solution found but flux minimization failed)
-0045 %     - msg : string describing the status of the optimization
-0046 %     - sPrice : shadow price (only reported if minFlux was 0)
-0047 %     - rCost : reduced cost (only reported if minFlux was 0)
-0048 % hsSolOut : struct
-0049 %     solution to be used as hot-start solution (see the input parameters).
-0050 %     Only used if minFlux is 0 or 1.
-0051 %
-0052 % Examples
-0053 % --------
-0054 %     [solution, hsSolOut] = solveLP(model, minFlux, params, hsSol);
-0055 
-0056 p=parseRAVENargs(varargin, {'minFlux',0; 'params',[]; 'hsSol',[]});
-0057 minFlux=p.minFlux;
-0058 params=p.params;
-0059 if isempty(params)
-0060     params.relGap=0.4;
-0061 end
-0062 hsSol=p.hsSol;
-0063 
-0064 %Default return values
-0065 hsSolOut=[];
-0066 solution.x=[];
-0067 solution.f=[];
-0068 solution.stat=-1;
-0069 
-0070 %Check for valid lb and ub
-0071 if ~isnumeric([model.lb,model.ub])
-0072     invalidBound = true(numel(model.lb),1);
-0073 else
-0074     invalidBound = false(numel(model.lb),1);
-0075 end
-0076 invalidBound = invalidBound | model.lb>model.ub;
-0077 invalidBound = invalidBound | logical(sum(isnan([model.lb,model.ub]),2));
-0078 if any(invalidBound)
-0079     error(['Invalid set of bounds for reaction(s): ', strjoin(model.rxns(invalidBound),', '), '.'])
-0080 end
-0081 %Check for valid objective
-0082 if ~isnumeric(model.c) || any(isnan(model.c))
-0083     error('Invalid defintion of objective function in model.c.')
-0084 end
-0085 %Check for valid S-matrix
-0086 if ~allfinite(model.S)
-0087     invalidS = ~isfinite(model.S);
-0088     error(['Invalid coefficients defined for reaction(s): ', strjoin(model.rxns(any(invalidS)),', '), '.'])
-0089 end
-0090 
-0091 %Ignore the hot-start if the previous solution was not feasible
-0092 if isfield(hsSol,'stat')
-0093     if hsSol.stat<1
-0094         hsSol=[];
-0095     end
-0096 end
-0097 
-0098 % Setup the linear problem for glpk
-0099 prob        = [];
-0100 prob.c      = [model.c*-1;zeros(size(model.S,1),1)];
-0101 prob.b      = zeros(size(model.S,1), 1);
-0102 prob.lb     = [model.lb; model.b(:,1)];
-0103 prob.ub     = [model.ub; model.b(:,min(size(model.b,2),2))];
-0104 prob.csense = repmat('E', 1, length(prob.b)); % Equality constraints
-0105 prob.osense = 1; 
-0106 prob.vartype = repmat('C', 1, length(prob.c)); % Fluxes are continuous values
-0107 prob.A = [model.S -speye(size(model.S,1))];
-0108 prob.a = model.S;
-0109 
-0110 %If hot-start should be used
-0111 if ~isempty(hsSol)
-0112     prob.vbasis=hsSol.vbasis;
-0113     prob.cbasis=hsSol.cbasis;
-0114 end
-0115 
-0116 % Parse the problem to the LP solver
-0117 res = optimizeProb(prob,params);
-0118 % Swap the sign of the obj-value
-0119 res.obj = -res.obj;
-0120 
-0121 %Check if the problem was feasible and that the solution was optimal
-0122 [isFeasible, isOptimal]=checkSolution(res);
-0123 
-0124 %If the problem was infeasible using hot-start it is often possible to
-0125 %re-solve it without hot-start and get a feasible solution
-0126 if ~isFeasible && ~isempty(hsSol)
-0127     prob=rmfield(prob,{'vbasis','cbasis'});
-0128     res=optimizeProb(prob,params);
-0129     [isFeasible, isOptimal]=checkSolution(res);
-0130 end
-0131 
-0132 %Return without solution if the problem was infeasible
-0133 if ~isFeasible
-0134     solution.msg='The problem is infeasible';
-0135     return;
-0136 end
-0137 if ~isOptimal
-0138     solution.msg='The problem is feasible, but not necessarily optimal';
-0139     solution.stat=0;
-0140 else
-0141     %All is well
-0142     solution.stat=1;
-0143     solution.msg='Optimal solution found';
-0144 end
-0145 
-0146 %Construct the output structure
-0147 if isfield(res,'full')
-0148     solution.x=res.full;
-0149     if minFlux<=1
-0150         if(isfield(res,'vbasis')) % gurobi uses vbasis and cbasis as hotstart
-0151             hsSolOut.vbasis=res.vbasis;
-0152             hsSolOut.cbasis=res.cbasis;
-0153         end
-0154     end
-0155     solution.f=res.obj;
-0156 if isfield(res,'dual')
-0157     solution.sPrice=res.dual;
-0158 else
-0159     solution.sPrice=[];
-0160 end
-0161 if isfield(res,'rcost')
-0162     solution.rCost=res.rcost(1:numel(model.rxns));
-0163 else
-0164     solution.rCost=[];
-0165 end
-0166 
-0167 %If either the square, the number, or the sum of fluxes should be minimized
-0168 %then the objective function value should be fixed before another
-0169 %optimization. It is not correct to fix the reactions which participate in
-0170 %the objective function to their values in solution.x, as there can be
-0171 %multiple solutions with the same objective function value. In addition,
-0172 %this approach could result in numerical issues when several fluxes are
-0173 %fixed. Instead a new "fake metabolite" is added to the problem. This
-0174 %metabolite is produced by each reaction with the stoichiometry that
-0175 %reaction has in the objective function. The equality constraint of that
-0176 %"fake metabolite" is then set to be at least as good as the objective
-0177 %function value.
-0178 if minFlux~=0
-0179     model.S=[model.S;model.c'];
-0180     model.mets=[model.mets;'TEMP'];
-0181     
-0182     %If the constraint on the objective function value is exact there is a
-0183     %larger risk of numerical errors. However for the quadratic fitting
-0184     %intervals are not allowed
-0185     if minFlux~=2
-0186         if size(model.b,2)==1
-0187             model.b=[model.b model.b];
-0188         end
-0189         if solution.f>0
-0190             model.b=[model.b;[solution.f*0.999999 inf]];
-0191         else
-0192             model.b=[model.b;[solution.f*1.000001 inf]];
-0193         end
-0194     else
-0195         model.b=[model.b;ones(1,size(model.b,2))*solution.f];
-0196     end
-0197     
-0198     switch minFlux
-0199         %The sum of fluxes should be minimized
-0200         case 1
-0201             %Convert the model to the irreversible format
-0202             revRxns=find(model.rev);
-0203             if ~isempty(revRxns)
-0204                 iModel=convertToIrrev(model);
-0205             else
-0206                 iModel=model;
-0207             end
-0208             %Minimize all fluxes
-0209             iModel.c(:)=-1;
-0210             sol=solveLP(iModel,0,params);
-0211             
-0212             %Map back to reversible fluxes
-0213             if sol.stat>=0
-0214                 solution.x=sol.x(1:numel(model.c));
-0215                 solution.x(revRxns)=solution.x(revRxns)-sol.x(numel(model.c)+1:end);
-0216             else
-0217                 EM='Could not solve the problem of minimizing the sum of fluxes. Uses output from original problem';
-0218                 warning('RAVEN:warning', '%s', EM);
-0219                 solution.stat=-2;
-0220             end
-0221             
-0222         case 2
-0223             error('%Minimization of square of fluxes is deprecated')
-0224             
-0225             %The number of fluxes should be minimized
-0226         case 3
-0227             [qx,I]=getMinNrFluxes(model,model.rxns,params);
-0228             qx(~I)=0;
-0229             if any(I)
-0230                 solution.x=qx;
-0231             else
-0232                 EM='Could not solve the problem of minimizing the number of fluxes. Uses output from linear program';
-0233                 warning('RAVEN:warning', '%s', EM);
-0234                 solution.stat=-2;
-0235             end
-0236     end
-0237     solution=rmfield(solution,{'sPrice','rCost'});
-0238 end
-0239 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/solver/splitProbForConditioning.html b/doc/solver/splitProbForConditioning.html deleted file mode 100644 index a5db5270..00000000 --- a/doc/solver/splitProbForConditioning.html +++ /dev/null @@ -1,238 +0,0 @@ - - - - Description of splitProbForConditioning - - - - - - - - - -
Home > solver > splitProbForConditioning.m
- - - -

splitProbForConditioning -

- -

PURPOSE ^

-
splitProbForConditioning Improve LP conditioning by splitting wide columns.
- -

SYNOPSIS ^

-
function [prob, condInfo] = splitProbForConditioning(prob, maxRatio)
- -

DESCRIPTION ^

-
 splitProbForConditioning  Improve LP conditioning by splitting wide columns.
-
- Reformulates an LP problem to improve its numerical conditioning by
- splitting columns whose coefficients span more than a given ratio. In a
- metabolic model a column corresponds to a reaction, and a wide spread of
- stoichiometric coefficients within one reaction (e.g. a biomass reaction
- with both major substrates and trace cofactors) cannot be removed by the
- diagonal row/column scaling that LP solvers apply, and may lead to
- unreliable solutions or spurious infeasibility.
-
- Splitting is done via auxiliary metabolites and variables that preserve
- the feasible region of the original problem exactly, so the values of all
- original variables are unchanged.
-
- The function is meant to be called inside optimizeProb, on the COBRA-style
- problem struct, immediately before the problem is passed to the solver.
- Auxiliary rows/columns are appended after the original ones, so the
- augmentation can be stripped from the solver output afterwards (see
- condInfo).
-
- Parameters
- ----------
- prob : struct
-     COBRA-style LP problem struct, with at least the fields A, b, c, lb,
-     ub, csense and vartype.
- maxRatio : double
-     maximum allowed ratio between the largest and smallest absolute
-     coefficient within any single column.
-
- Returns
- -------
- prob : struct
-     the (possibly) augmented problem struct. Identical to the input if no
-     column needed splitting.
- condInfo : struct
-     struct describing the augmentation, with fields:
-
-     - applied : true if any column was split
-     - nSplits : number of auxiliary metabolite/conversion pairs added
-     - nOrigRows : number of constraint rows before augmentation
-     - nOrigCols : number of variables (columns) before augmentation
-
- Examples
- --------
-     [prob, condInfo] = splitProbForConditioning(prob, maxRatio);
-
- See also
- --------
- optimizeProb
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • optimizeProb optimizeProb Optimize an LP or MILP formulated in COBRA terms.
- - - - -

SOURCE CODE ^

-
0001 function [prob, condInfo] = splitProbForConditioning(prob, maxRatio)
-0002 % splitProbForConditioning  Improve LP conditioning by splitting wide columns.
-0003 %
-0004 % Reformulates an LP problem to improve its numerical conditioning by
-0005 % splitting columns whose coefficients span more than a given ratio. In a
-0006 % metabolic model a column corresponds to a reaction, and a wide spread of
-0007 % stoichiometric coefficients within one reaction (e.g. a biomass reaction
-0008 % with both major substrates and trace cofactors) cannot be removed by the
-0009 % diagonal row/column scaling that LP solvers apply, and may lead to
-0010 % unreliable solutions or spurious infeasibility.
-0011 %
-0012 % Splitting is done via auxiliary metabolites and variables that preserve
-0013 % the feasible region of the original problem exactly, so the values of all
-0014 % original variables are unchanged.
-0015 %
-0016 % The function is meant to be called inside optimizeProb, on the COBRA-style
-0017 % problem struct, immediately before the problem is passed to the solver.
-0018 % Auxiliary rows/columns are appended after the original ones, so the
-0019 % augmentation can be stripped from the solver output afterwards (see
-0020 % condInfo).
-0021 %
-0022 % Parameters
-0023 % ----------
-0024 % prob : struct
-0025 %     COBRA-style LP problem struct, with at least the fields A, b, c, lb,
-0026 %     ub, csense and vartype.
-0027 % maxRatio : double
-0028 %     maximum allowed ratio between the largest and smallest absolute
-0029 %     coefficient within any single column.
-0030 %
-0031 % Returns
-0032 % -------
-0033 % prob : struct
-0034 %     the (possibly) augmented problem struct. Identical to the input if no
-0035 %     column needed splitting.
-0036 % condInfo : struct
-0037 %     struct describing the augmentation, with fields:
-0038 %
-0039 %     - applied : true if any column was split
-0040 %     - nSplits : number of auxiliary metabolite/conversion pairs added
-0041 %     - nOrigRows : number of constraint rows before augmentation
-0042 %     - nOrigCols : number of variables (columns) before augmentation
-0043 %
-0044 % Examples
-0045 % --------
-0046 %     [prob, condInfo] = splitProbForConditioning(prob, maxRatio);
-0047 %
-0048 % See also
-0049 % --------
-0050 % optimizeProb
-0051 
-0052 [nOrigRows, nOrigCols] = size(prob.A);
-0053 condInfo.applied   = false;
-0054 condInfo.nSplits   = 0;
-0055 condInfo.nOrigRows = nOrigRows;
-0056 condInfo.nOrigCols = nOrigCols;
-0057 
-0058 %Detection uses a small slack so that reactions that are only marginally over
-0059 %the threshold are left alone. This also makes the transformation idempotent:
-0060 %a freshly split column sits exactly at maxRatio and is not split again.
-0061 detectRatio = maxRatio * 1.01;
-0062 
-0063 %Work on triplets to avoid repeated resizing of the sparse matrix
-0064 [I, J, V] = find(prob.A);
-0065 absV = abs(V);
-0066 
-0067 %Largest/smallest absolute coefficient and number of nonzeros per column
-0068 cMax   = accumarray(J, absV, [nOrigCols 1], @max, 0);
-0069 cMin   = accumarray(J, absV, [nOrigCols 1], @min, inf);
-0070 nnzCol = accumarray(J, 1,    [nOrigCols 1], @sum, 0);
-0071 
-0072 %Single-coefficient columns (e.g. exchange reactions or slack variables) have
-0073 %an undefined ratio and are never split
-0074 badCols = find(nnzCol >= 2 & (cMax ./ cMin) > detectRatio);
-0075 if isempty(badCols)
-0076     return
-0077 end
-0078 
-0079 %Flag the entries that are too small within their (bad) column. These are
-0080 %removed from the original matrix and re-introduced via the auxiliary chain.
-0081 smallThresh = cMax(J) / maxRatio;
-0082 removeEntry = ismember(J, badCols) & (absV < smallThresh);
-0083 
-0084 %Keep all entries that are not removed
-0085 keep = ~removeEntry;
-0086 newI = I(keep);
-0087 newJ = J(keep);
-0088 newV = V(keep);
-0089 
-0090 %Auxiliary entries are accumulated here
-0091 addI = zeros(0,1); addJ = zeros(0,1); addV = zeros(0,1);
-0092 convLb = zeros(0,1); convUb = zeros(0,1);
-0093 nStar = 0;   %auxiliary metabolite rows added so far
-0094 nConv = 0;   %conversion variable columns added so far
-0095 
-0096 remIdx = find(removeEntry);
-0097 for k = 1:numel(remIdx)
-0098     e         = remIdx(k);
-0099     metRow    = I(e);
-0100     parentCol = J(e);
-0101     coef      = V(e);
-0102     curCmax   = cMax(parentCol);
-0103     %Deliver the (signed) coefficient "coef" of metabolite "metRow" into
-0104     %column "parentCol", splitting recursively until it is within range
-0105     while true
-0106         small = curCmax / maxRatio;
-0107         if abs(coef) >= small
-0108             %Coefficient is acceptable; place it directly
-0109             addI(end+1,1) = metRow;    addJ(end+1,1) = parentCol; addV(end+1,1) = coef; %#ok<AGROW>
-0110             break
-0111         end
-0112         %Otherwise introduce a star metabolite and a conversion variable
-0113         beta    = sign(coef) * small;
-0114         nStar   = nStar + 1; starRow = nOrigRows + nStar;
-0115         nConv   = nConv + 1; convCol = nOrigCols + nConv;
-0116         %The parent column now consumes the star metabolite (coefficient beta)
-0117         addI(end+1,1) = starRow; addJ(end+1,1) = parentCol; addV(end+1,1) = beta; %#ok<AGROW>
-0118         %The conversion variable produces one unit of the star metabolite
-0119         addI(end+1,1) = starRow; addJ(end+1,1) = convCol;   addV(end+1,1) = 1;    %#ok<AGROW>
-0120         %Conversion variables are determined by the equality and left unbounded
-0121         convLb(end+1,1) = -inf; convUb(end+1,1) = inf; %#ok<AGROW>
-0122         %Deliver the residual coefficient into the new conversion column. Its
-0123         %reference scale is 1 (the production coefficient of the star met).
-0124         coef      = -coef / beta;
-0125         parentCol = convCol;
-0126         curCmax   = 1;
-0127     end
-0128 end
-0129 
-0130 condInfo.applied = true;
-0131 condInfo.nSplits = nStar;
-0132 
-0133 %Assemble the augmented matrix and extend the problem vectors. New rows are
-0134 %appended at the bottom, new columns at the right.
-0135 prob.A = sparse([newI; addI], [newJ; addJ], [newV; addV], ...
-0136     nOrigRows + nStar, nOrigCols + nConv);
-0137 prob.b       = [prob.b(:);       zeros(nStar,1)];
-0138 prob.csense  = [prob.csense(:).', repmat('E',1,nStar)];
-0139 prob.c       = [prob.c(:);       zeros(nConv,1)];
-0140 prob.lb      = [prob.lb(:);      convLb];
-0141 prob.ub      = [prob.ub(:);      convUb];
-0142 prob.vartype = [prob.vartype(:).', repmat('C',1,nConv)];
-0143 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/tasks/checkTasks.html b/doc/tasks/checkTasks.html deleted file mode 100644 index 8de57471..00000000 --- a/doc/tasks/checkTasks.html +++ /dev/null @@ -1,406 +0,0 @@ - - - - Description of checkTasks - - - - - - - - - -
Home > tasks > checkTasks.m
- - - -

checkTasks -

- -

PURPOSE ^

-
checkTasks Perform a set of simulations as defined in a task file.
- -

SYNOPSIS ^

-
function [taskReport, essentialRxns, taskStructure, essentialFluxes]=checkTasks(model,inputFile,varargin)
- -

DESCRIPTION ^

-
 checkTasks  Perform a set of simulations as defined in a task file.
-
- This function is used for defining a set of tasks for a model to perform.
- The tasks are defined by defining constraints on the model, and if the
- problem is feasible, then the task is considered successful. In general,
- each row can contain one constraint on uptakes, one constraint on outputs,
- one new equation, and one change of reaction bounds. If more bounds are
- needed to define the task, then several rows can be used for each task.
-
- Parameters
- ----------
- model : struct
-     a model structure.
- inputFile : char
-     a task list in Excel format. See the function parseTaskList for
-     details (optional if taskStructure is supplied).
-
- Name-Value Arguments
- --------------------
- printOutput : logical
-     true if the results of the test should be displayed (default true).
- printOnlyFailed : logical
-     true if only tasks that failed should be displayed (default false).
- getEssential : logical
-     true if the essential reactions should be calculated for all the
-     tasks. This option is used with runINIT (default false).
- taskStructure : struct
-     structure with the tasks, as from parseTaskList. If this is supplied
-     then inputFile is ignored.
- runParallel : logical
-     true to evaluate tasks in parallel workers, since each task is
-     independent of the others (see parallelWorkersRAVEN). Gurobi is
-     automatically pinned to one thread per worker by optimizeProb, so
-     this does not oversubscribe cores. Default false, matching the
-     previous (serial) behaviour, since checkTasks is often called on
-     small task lists where starting a pool would only add overhead.
-
- Returns
- -------
- taskReport : struct
-     structure with the results, with fields:
-
-     - id : cell array with the id of the task
-     - description : cell array with the description of the task
-     - ok : boolean array with true if the task was successful
- essentialRxns : logical
-     MxN matrix with the essential reactions (M) for each task (N). An
-     element is true if the corresponding reaction is essential in the
-     corresponding task. Failed tasks and SHOULD FAIL tasks are ignored.
-     This is used by the INIT algorithm (if tasks are supplied). If
-     getEssential=false then essentialRxns=false(nRxns,nTasks).
- taskStructure : struct
-     structure with the tasks, as from parseTaskList.
- essentialFluxes : double
-     the fluxes of the essential rxns - same structure as essentialRxns.
-
- Examples
- --------
-     [taskReport, essentialRxns, taskStructure] = checkTasks(model, inputFile, ...
-         printOutput, printOnlyFailed, getEssential, taskStructure);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function [taskReport, essentialRxns, taskStructure, essentialFluxes]=checkTasks(model,inputFile,varargin)
-0002 % checkTasks  Perform a set of simulations as defined in a task file.
-0003 %
-0004 % This function is used for defining a set of tasks for a model to perform.
-0005 % The tasks are defined by defining constraints on the model, and if the
-0006 % problem is feasible, then the task is considered successful. In general,
-0007 % each row can contain one constraint on uptakes, one constraint on outputs,
-0008 % one new equation, and one change of reaction bounds. If more bounds are
-0009 % needed to define the task, then several rows can be used for each task.
-0010 %
-0011 % Parameters
-0012 % ----------
-0013 % model : struct
-0014 %     a model structure.
-0015 % inputFile : char
-0016 %     a task list in Excel format. See the function parseTaskList for
-0017 %     details (optional if taskStructure is supplied).
-0018 %
-0019 % Name-Value Arguments
-0020 % --------------------
-0021 % printOutput : logical
-0022 %     true if the results of the test should be displayed (default true).
-0023 % printOnlyFailed : logical
-0024 %     true if only tasks that failed should be displayed (default false).
-0025 % getEssential : logical
-0026 %     true if the essential reactions should be calculated for all the
-0027 %     tasks. This option is used with runINIT (default false).
-0028 % taskStructure : struct
-0029 %     structure with the tasks, as from parseTaskList. If this is supplied
-0030 %     then inputFile is ignored.
-0031 % runParallel : logical
-0032 %     true to evaluate tasks in parallel workers, since each task is
-0033 %     independent of the others (see parallelWorkersRAVEN). Gurobi is
-0034 %     automatically pinned to one thread per worker by optimizeProb, so
-0035 %     this does not oversubscribe cores. Default false, matching the
-0036 %     previous (serial) behaviour, since checkTasks is often called on
-0037 %     small task lists where starting a pool would only add overhead.
-0038 %
-0039 % Returns
-0040 % -------
-0041 % taskReport : struct
-0042 %     structure with the results, with fields:
-0043 %
-0044 %     - id : cell array with the id of the task
-0045 %     - description : cell array with the description of the task
-0046 %     - ok : boolean array with true if the task was successful
-0047 % essentialRxns : logical
-0048 %     MxN matrix with the essential reactions (M) for each task (N). An
-0049 %     element is true if the corresponding reaction is essential in the
-0050 %     corresponding task. Failed tasks and SHOULD FAIL tasks are ignored.
-0051 %     This is used by the INIT algorithm (if tasks are supplied). If
-0052 %     getEssential=false then essentialRxns=false(nRxns,nTasks).
-0053 % taskStructure : struct
-0054 %     structure with the tasks, as from parseTaskList.
-0055 % essentialFluxes : double
-0056 %     the fluxes of the essential rxns - same structure as essentialRxns.
-0057 %
-0058 % Examples
-0059 % --------
-0060 %     [taskReport, essentialRxns, taskStructure] = checkTasks(model, inputFile, ...
-0061 %         printOutput, printOnlyFailed, getEssential, taskStructure);
-0062 
-0063 p=parseRAVENargs(varargin, {'printOutput',true; 'printOnlyFailed',false; 'getEssential',false; 'taskStructure',[]; 'runParallel',false});
-0064 printOutput=p.printOutput;
-0065 printOnlyFailed=p.printOnlyFailed;
-0066 getEssential=p.getEssential;
-0067 taskStructure=p.taskStructure;
-0068 runParallel=p.runParallel;
-0069 
-0070 %Prepare the input model a little
-0071 model.b=zeros(numel(model.mets),2);
-0072 
-0073 modelMets=upper(strcat(model.metNames,'[',model.comps(model.metComps),']'));
-0074 if ~isfield(model,'unconstrained')
-0075     EM='Exchange metabolites should normally not be removed from the model when using checkTasks. Inputs and outputs are defined in the task file instead. Use importModel(file,false) to import a model with exchange metabolites remaining';
-0076     warning('RAVEN:warning', '%s', EM);
-0077 end
-0078 
-0079 %Parse the task file
-0080 if isempty(taskStructure)
-0081     taskStructure=parseTaskList(inputFile);
-0082 end
-0083 
-0084 nTasks=numel(taskStructure);
-0085 essentialRxns=false(numel(model.rxns),nTasks);
-0086 essentialFluxes = NaN(numel(model.rxns),nTasks);
-0087 
-0088 %Each task is independent (tModel is rebuilt from model at the top of
-0089 %every iteration, nothing carries over), so this loop can run in
-0090 %parallel. ids/descriptions/ok are plain arrays sliced by i and only
-0091 %assembled into taskReport once the loop is done, because parfor's
-0092 %classifier rejects slicing a struct field that is itself indexed with
-0093 %cell/array subscripts (taskReport.id{i,1}=... is not a supported sliced
-0094 %form). runParallel=false (the default) makes this run exactly like the
-0095 %previous for loop, in the client, serially.
-0096 ids=cell(nTasks,1);
-0097 descriptions=cell(nTasks,1);
-0098 ok=false(nTasks,1);
-0099 
-0100 nW=parallelWorkersRAVEN(runParallel);
-0101 parfor (i=1:nTasks, nW)
-0102     tModel=model;
-0103     ids{i}=taskStructure(i).id;
-0104     descriptions{i}=taskStructure(i).description;
-0105     %Set the inputs
-0106     if ~isempty(taskStructure(i).inputs)
-0107         [I, J]=ismember(upper(taskStructure(i).inputs),modelMets);
-0108         J=J(I); %Only keep the ones with matches
-0109         K=ismember(upper(taskStructure(i).inputs),'ALLMETS');
-0110         L=~cellfun('isempty',strfind(upper(taskStructure(i).inputs),'ALLMETSIN'));
-0111         %Check that all metabolites are either real metabolites or
-0112         %ALLMETS/ALLMETSIN
-0113         if ~all(I|K|L)
-0114             fprintf(['ERROR: Could not find all inputs in "[' taskStructure(i).id '] ' taskStructure(i).description '"\n']);
-0115             ok(i)=false;
-0116             continue;
-0117         end
-0118         if numel(J)~=numel(unique(J))
-0119             EM=['The constraints on some input(s) in "[' taskStructure(i).id '] ' taskStructure(i).description '" are defined more than one time'];
-0120             error('RAVEN:badInput', '%s', EM);
-0121         end
-0122         %If all metabolites should be added
-0123         if any(K)
-0124             %Check if ALLMETS is the first metabolite. Otherwise print a
-0125             %warning since it will write over any other constraints that
-0126             %are set
-0127             if K(1)==0
-0128                 EM=['ALLMETS is used as an input in "[' taskStructure(i).id '] ' taskStructure(i).description '" but it it not the first metabolite in the list. Constraints defined for the metabolites before it will be over-written'];
-0129                 warning('RAVEN:warning', '%s', EM);
-0130             end
-0131             %Use the first match of ALLMETS. There should only be one, but
-0132             %still..
-0133             tModel.b(:,1)=taskStructure(i).UBin(find(K,1))*-1;
-0134         end
-0135         %If metabolites in a specific compartment should be used
-0136         if any(L)
-0137             L=find(L);
-0138             for j=1:numel(L)
-0139                 %The compartment defined
-0140                 compartment=upper(taskStructure(i).inputs{L(j)}(11:end-1));
-0141                 %Check if it exists in the model
-0142                 C=find(ismember(upper(model.comps),compartment));
-0143                 if any(C)
-0144                     %Match to metabolites
-0145                     tModel.b(model.metComps==C,1)=taskStructure(i).UBin(L(j))*-1;
-0146                 else
-0147                     EM=['The compartment defined for ALLMETSIN in "[' taskStructure(i).id '] ' taskStructure(i).description '" does not exist'];
-0148                     error('RAVEN:badInput', '%s', EM);
-0149                 end
-0150             end
-0151         end
-0152         %Then add the normal constraints
-0153         if any(J)
-0154             tModel.b(J,1)=taskStructure(i).UBin(I)*-1;
-0155             tModel.b(J,2)=taskStructure(i).LBin(I)*-1;
-0156         end
-0157     end
-0158     %Set the outputs
-0159     if ~isempty(taskStructure(i).outputs)
-0160         [I, J]=ismember(upper(taskStructure(i).outputs),modelMets);
-0161         J=J(I); %Only keep the ones with matches
-0162         K=ismember(upper(taskStructure(i).outputs),'ALLMETS');
-0163         L=~cellfun('isempty',strfind(upper(taskStructure(i).outputs),'ALLMETSIN'));
-0164         %Check that all metabolites are either real metabolites or
-0165         %ALLMETS/ALLMETSIN
-0166         if ~all(I|K|L)
-0167             fprintf(['ERROR: Could not find all outputs in "[' taskStructure(i).id '] ' taskStructure(i).description '"\n']);
-0168             ok(i)=false;
-0169             continue;
-0170         end
-0171         if numel(J)~=numel(unique(J))
-0172             EM=['The constraints on some output(s) in "[' taskStructure(i).id '] ' taskStructure(i).description '" are defined more than one time'];
-0173             error('RAVEN:badInput', '%s', EM);
-0174         end
-0175         %If all metabolites should be added
-0176         if any(K)
-0177             %Check if ALLMETS is the first metabolite. Otherwise print a
-0178             %warning since it will write over any other constraints that
-0179             %are set
-0180             if K(1)==0
-0181                 EM=['ALLMETS is used as an output in "[' taskStructure(i).id '] ' taskStructure(i).description '" but it it not the first metabolite in the list. Constraints defined for the metabolites before it will be over-written'];
-0182                 warning('RAVEN:warning', '%s', EM);
-0183             end
-0184             %Use the first match of ALLMETS. There should only be one, but
-0185             %still..
-0186             tModel.b(:,2)=taskStructure(i).UBout(find(K,1));
-0187         end
-0188         %If metabolites in a specific compartment should be used
-0189         if any(L)
-0190             L=find(L);
-0191             for j=1:numel(L)
-0192                 %The compartment defined
-0193                 compartment=upper(taskStructure(i).outputs{L(j)}(11:end-1));
-0194                 %Check if it exists in the model
-0195                 C=find(ismember(upper(model.comps),compartment));
-0196                 if any(C)
-0197                     %Match to metabolites
-0198                     tModel.b(model.metComps==C,2)=taskStructure(i).UBout(L(j));
-0199                 else
-0200                     EM=['The compartment defined for ALLMETSIN in "[' taskStructure(i).id '] ' taskStructure(i).description '" does not exist'];
-0201                     error('RAVEN:badInput', '%s', EM);
-0202                 end
-0203             end
-0204         end
-0205         %Then add the normal constraints
-0206         if any(J)
-0207             %Verify that IN and OUT bounds are consistent. Cannot require
-0208             %that a metabolite is simultaneously input AND output at some
-0209             %nonzero flux.
-0210             I = find(I);  % otherwise indexing becomes confusing
-0211             nonzero_LBin = tModel.b(J,2) < 0;
-0212             nonzero_LBout = taskStructure(i).LBout(I) > 0;
-0213             if any(nonzero_LBin & nonzero_LBout)
-0214                 EM=['The IN LB and OUT LB in "[' taskStructure(i).id '] ' taskStructure(i).description '" cannot be nonzero for the same metabolite'];
-0215                 error('RAVEN:badInput', '%s', EM);
-0216             end
-0217             tModel.b(J(nonzero_LBout),1)=taskStructure(i).LBout(I(nonzero_LBout));
-0218             tModel.b(J,2)=taskStructure(i).UBout(I);
-0219         end
-0220     end
-0221     %Add new rxns
-0222     if ~isempty(taskStructure(i).equations)
-0223         % num2str on the whole column right-aligns every row to a common
-0224         % width, embedding a leading space in "TEMPORARY_ 1" once any id
-0225         % reaches two digits ("TEMPORARY_10"); format each one independently.
-0226         rxnIds=arrayfun(@(x) sprintf('TEMPORARY_%d',x), ...
-0227             (1:numel(taskStructure(i).equations))', 'UniformOutput', false);
-0228         % Built as a single struct() call, not incremental rxn.field=...
-0229         % assignments: parfor's variable classifier cannot establish that a
-0230         % variable built up via several dot-indexed assignments is a
-0231         % same-shape temporary in every iteration, and rejects it outright.
-0232         rxn=struct('equations',{taskStructure(i).equations}, ...
-0233             'lb',taskStructure(i).LBequ, 'ub',taskStructure(i).UBequ, ...
-0234             'rxns',{rxnIds});
-0235         %Allow for new metabolites to be added. This is because it should
-0236         %be possible to add, say, a whole new pathway
-0237         tModel=addRxns(tModel,rxn,3,[],true);
-0238     end
-0239     %Add changed bounds
-0240     if ~isempty(taskStructure(i).changed)
-0241         tModel=setParam(tModel,'lb',taskStructure(i).changed,taskStructure(i).LBrxn);
-0242         tModel=setParam(tModel,'ub',taskStructure(i).changed,taskStructure(i).UBrxn);
-0243     end
-0244     
-0245     %Solve and print
-0246     sol=solveLP(tModel);
-0247     if ~isempty(sol.x)
-0248         %assign the fluxes
-0249         essentialFluxes(:,i) = sol.x(1:numel(model.rxns));
-0250         
-0251         if ~taskStructure(i).shouldFail
-0252             ok(i)=true;
-0253             if printOnlyFailed==false && printOutput==true
-0254                 fprintf(['PASS: [' taskStructure(i).id '] ' taskStructure(i).description '\n']);
-0255             end
-0256             %Calculate the essential reactions
-0257             if getEssential==true
-0258                 [~, taskEssential]=getEssentialRxns(tModel);
-0259                 %This is because there could be more reactions in tModel
-0260                 %than in model. Built as a full local column and written
-0261                 %with a plain ':' row index: parfor only accepts a sliced
-0262                 %output write when every subscript besides the loop
-0263                 %variable is ':', not a data-dependent index vector.
-0264                 essentialCol=false(numel(model.rxns),1);
-0265                 essentialCol(taskEssential(taskEssential<=numel(model.rxns)))=true;
-0266                 essentialRxns(:,i)=essentialCol;
-0267             end
-0268         else
-0269             ok(i)=false;
-0270             if printOutput==true
-0271                 fprintf(['PASS (should fail): [' taskStructure(i).id '] ' taskStructure(i).description '\n']);
-0272             end
-0273         end
-0274     else
-0275         if ~taskStructure(i).shouldFail
-0276             ok(i)=false;
-0277             if printOutput==true
-0278                 fprintf(['FAIL: [' taskStructure(i).id '] ' taskStructure(i).description '\n']);
-0279             end
-0280         else
-0281             ok(i)=true;
-0282             if printOnlyFailed==false && printOutput==true
-0283                 fprintf(['FAIL (should fail): [' taskStructure(i).id '] ' taskStructure(i).description '\n']);
-0284             end
-0285         end
-0286     end
-0287     if taskStructure(i).printFluxes && ~isempty(sol.x)
-0288         sol=solveLP(tModel,1);
-0289         if ~isempty(sol.x)
-0290             printFluxes(tModel,sol.x,false,10^-6,[],'%rxnID (%eqn):%flux\n');
-0291             fprintf('\n');
-0292         end
-0293     end
-0294 end
-0295 
-0296 taskReport.id=ids;
-0297 taskReport.description=descriptions;
-0298 taskReport.ok=ok;
-0299 
-0300 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/tasks/parseTaskList.html b/doc/tasks/parseTaskList.html deleted file mode 100644 index b664cac9..00000000 --- a/doc/tasks/parseTaskList.html +++ /dev/null @@ -1,436 +0,0 @@ - - - - Description of parseTaskList - - - - - - - - - -
Home > tasks > parseTaskList.m
- - - -

parseTaskList -

- -

PURPOSE ^

-
parseTaskList Parse a task list file.
- -

SYNOPSIS ^

-
function taskStruct=parseTaskList(inputFile)
- -

DESCRIPTION ^

-
 parseTaskList  Parse a task list file.
-
- Parameters
- ----------
- inputFile : char
-     a task list in either Excel (*.xlsx, with a sheet named TASKS with
-     all relevant content) or tab-delimited (*.txt) format. The file may
-     contain the following column headers (note, all rows starting with a
-     non-empty cell are removed; the first row after that is considered
-     the headers):
-
-     - ID : the only required header. Each task must have a unique id
-       (string or numeric). Tasks can span multiple rows, only the first
-       row in each task should have an id
-     - DESCRIPTION : description of the task
-     - IN : allowed input(s) for the task. Metabolite names should be on
-       the form "model.metName[model.comps]". Several inputs can be
-       delimited by ";". If so, then the same bounds are used for all
-       inputs. If that is not wanted, then use several rows for the task
-     - IN LB : lower bound for the uptake of the metabolites in the row
-       (optional, default 0 which corresponds to a minimal uptake of 0
-       units)
-     - IN UB : upper bound for the uptake of the metabolites in the row
-       (optional, default 1000 which corresponds to a maximal uptake of
-       1000 units)
-     - OUT : allowed output(s) for the task (see IN)
-     - OUT LB : lower bound for the production of the metabolites in the
-       row (optional, default 0 which corresponds to a minimal production
-       of 0 units)
-     - OUT UB : upper bound for the production of the metabolites in the
-       row (optional, default 1000 which corresponds to a maximal
-       production of 1000 units)
-     - EQU : equation to add. The equation should be on the form
-       "0.4 A + 2 B <=> (or =>) C" and the metabolites should be on the
-       form "model.metName[model.comps]" (optional)
-     - EQU LB : lower bound for the equation (optional, default -1000 for
-       reversible and 0 for irreversible)
-     - EQU UB : upper bound for the equation (optional, default 1000)
-     - CHANGED RXN : reaction ID for which to change the bounds for.
-       Several IDs can be delimited by ";". If so, then the same bounds
-       are used for all reactions. If that is not wanted, then use several
-       rows for the task
-     - CHANGED LB : lower bound for the reaction
-     - CHANGED UB : upper bound for the reaction
-     - SHOULD FAIL : true if the correct behavior of the model is to not
-       have a feasible solution given the constraints (optional, default
-       false)
-     - PRINT FLUX : true if the function should print the corresponding
-       flux distribution for a task. Can be useful for testing (optional,
-       default false)
-
- Returns
- -------
- taskStruct : struct
-     array of structures with the following fields:
-
-     - id : the id of the task
-     - description : the description of the task
-     - shouldFail : true if the task should fail
-     - printFluxes : true if the fluxes should be printed
-     - comments : string with comments
-     - inputs : cell array with input metabolites (in the form metName[comps])
-     - LBin : array with lower bounds on inputs (default, 0)
-     - UBin : array with upper bounds on inputs (default, 1000)
-     - outputs : cell array with output metabolites (in the form metName[comps])
-     - LBout : array with lower bounds on outputs (default, 0)
-     - UBout : array with upper bounds on outputs (default, 1000)
-     - equations : cell array with equations (with mets in the form metName[comps])
-     - LBequ : array with lower bounds on equations (default, -1000 for
-       reversible and 0 for irreversible)
-     - UBequ : array with upper bounds on equations (default, 1000)
-     - changed : cell array with reactions to change bounds for
-     - LBrxn : array with lower bounds on changed reactions
-     - UBrxn : array with upper bounds on changed reactions
-
- Examples
- --------
-     taskStruct = parseTaskList(inputFile);
-
- Notes
- -----
- This function is used for defining a set of tasks for a model to perform.
- The tasks are defined by defining constraints on the model, and if the
- problem is feasible, then the task is considered successful. In general,
- each row can contain one constraint on uptakes, one constraint on
- outputs, one new equation, and one change of reaction bounds. If more
- bounds are needed to define the task, then several rows can be used for
- each task. To perform the task use checkTasks or fitTasks.
-
- The general metabolites "ALLMETS" and "ALLMETSIN[comps]" can be used as
- inputs or outputs in the similar manner to normal metabolites. This is a
- convenient way to, for example, allow excretion of all metabolites to
- check whether it is the synthesis of some metabolite that is limiting or
- whether it is the degradation of some byproduct. One important difference
- is that only the upper bounds are used for these general metabolites.
- That is, you can only say that uptake or excretion is allowed, not that
- it is required. This is to avoid conflicts where the constraints for the
- general metabolites overwrite those of the real ones.
-
- See also
- --------
- checkTasks, fitTasks
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • checkTasks checkTasks Perform a set of simulations as defined in a task file.
- - - - -

SOURCE CODE ^

-
0001 function taskStruct=parseTaskList(inputFile)
-0002 % parseTaskList  Parse a task list file.
-0003 %
-0004 % Parameters
-0005 % ----------
-0006 % inputFile : char
-0007 %     a task list in either Excel (*.xlsx, with a sheet named TASKS with
-0008 %     all relevant content) or tab-delimited (*.txt) format. The file may
-0009 %     contain the following column headers (note, all rows starting with a
-0010 %     non-empty cell are removed; the first row after that is considered
-0011 %     the headers):
-0012 %
-0013 %     - ID : the only required header. Each task must have a unique id
-0014 %       (string or numeric). Tasks can span multiple rows, only the first
-0015 %       row in each task should have an id
-0016 %     - DESCRIPTION : description of the task
-0017 %     - IN : allowed input(s) for the task. Metabolite names should be on
-0018 %       the form "model.metName[model.comps]". Several inputs can be
-0019 %       delimited by ";". If so, then the same bounds are used for all
-0020 %       inputs. If that is not wanted, then use several rows for the task
-0021 %     - IN LB : lower bound for the uptake of the metabolites in the row
-0022 %       (optional, default 0 which corresponds to a minimal uptake of 0
-0023 %       units)
-0024 %     - IN UB : upper bound for the uptake of the metabolites in the row
-0025 %       (optional, default 1000 which corresponds to a maximal uptake of
-0026 %       1000 units)
-0027 %     - OUT : allowed output(s) for the task (see IN)
-0028 %     - OUT LB : lower bound for the production of the metabolites in the
-0029 %       row (optional, default 0 which corresponds to a minimal production
-0030 %       of 0 units)
-0031 %     - OUT UB : upper bound for the production of the metabolites in the
-0032 %       row (optional, default 1000 which corresponds to a maximal
-0033 %       production of 1000 units)
-0034 %     - EQU : equation to add. The equation should be on the form
-0035 %       "0.4 A + 2 B <=> (or =>) C" and the metabolites should be on the
-0036 %       form "model.metName[model.comps]" (optional)
-0037 %     - EQU LB : lower bound for the equation (optional, default -1000 for
-0038 %       reversible and 0 for irreversible)
-0039 %     - EQU UB : upper bound for the equation (optional, default 1000)
-0040 %     - CHANGED RXN : reaction ID for which to change the bounds for.
-0041 %       Several IDs can be delimited by ";". If so, then the same bounds
-0042 %       are used for all reactions. If that is not wanted, then use several
-0043 %       rows for the task
-0044 %     - CHANGED LB : lower bound for the reaction
-0045 %     - CHANGED UB : upper bound for the reaction
-0046 %     - SHOULD FAIL : true if the correct behavior of the model is to not
-0047 %       have a feasible solution given the constraints (optional, default
-0048 %       false)
-0049 %     - PRINT FLUX : true if the function should print the corresponding
-0050 %       flux distribution for a task. Can be useful for testing (optional,
-0051 %       default false)
-0052 %
-0053 % Returns
-0054 % -------
-0055 % taskStruct : struct
-0056 %     array of structures with the following fields:
-0057 %
-0058 %     - id : the id of the task
-0059 %     - description : the description of the task
-0060 %     - shouldFail : true if the task should fail
-0061 %     - printFluxes : true if the fluxes should be printed
-0062 %     - comments : string with comments
-0063 %     - inputs : cell array with input metabolites (in the form metName[comps])
-0064 %     - LBin : array with lower bounds on inputs (default, 0)
-0065 %     - UBin : array with upper bounds on inputs (default, 1000)
-0066 %     - outputs : cell array with output metabolites (in the form metName[comps])
-0067 %     - LBout : array with lower bounds on outputs (default, 0)
-0068 %     - UBout : array with upper bounds on outputs (default, 1000)
-0069 %     - equations : cell array with equations (with mets in the form metName[comps])
-0070 %     - LBequ : array with lower bounds on equations (default, -1000 for
-0071 %       reversible and 0 for irreversible)
-0072 %     - UBequ : array with upper bounds on equations (default, 1000)
-0073 %     - changed : cell array with reactions to change bounds for
-0074 %     - LBrxn : array with lower bounds on changed reactions
-0075 %     - UBrxn : array with upper bounds on changed reactions
-0076 %
-0077 % Examples
-0078 % --------
-0079 %     taskStruct = parseTaskList(inputFile);
-0080 %
-0081 % Notes
-0082 % -----
-0083 % This function is used for defining a set of tasks for a model to perform.
-0084 % The tasks are defined by defining constraints on the model, and if the
-0085 % problem is feasible, then the task is considered successful. In general,
-0086 % each row can contain one constraint on uptakes, one constraint on
-0087 % outputs, one new equation, and one change of reaction bounds. If more
-0088 % bounds are needed to define the task, then several rows can be used for
-0089 % each task. To perform the task use checkTasks or fitTasks.
-0090 %
-0091 % The general metabolites "ALLMETS" and "ALLMETSIN[comps]" can be used as
-0092 % inputs or outputs in the similar manner to normal metabolites. This is a
-0093 % convenient way to, for example, allow excretion of all metabolites to
-0094 % check whether it is the synthesis of some metabolite that is limiting or
-0095 % whether it is the degradation of some byproduct. One important difference
-0096 % is that only the upper bounds are used for these general metabolites.
-0097 % That is, you can only say that uptake or excretion is allowed, not that
-0098 % it is required. This is to avoid conflicts where the constraints for the
-0099 % general metabolites overwrite those of the real ones.
-0100 %
-0101 % See also
-0102 % --------
-0103 % checkTasks, fitTasks
-0104 
-0105 if ~isfile(inputFile)
-0106     error('Task list %s cannot be found',string(inputFile));
-0107 end
-0108 
-0109 %Load the tasks file
-0110 convNumeric = false;
-0111 if strcmp(extractAfter(inputFile,strlength(inputFile) - 4), '.txt')
-0112     %load from tab delimited text file
-0113     fid = fopen(inputFile);
-0114     %Need to read numeric columns as strings, this is converted further
-0115     %down. If not, the titles would be lost.
-0116     convNumeric = true;
-0117     C = textscan(fid,'%q%q%q%q%q%q%q%q%q%q%q%q%q%q%q%q%q%q%q%q%*[^\n]', 'Delimiter', '\t');
-0118     fclose(fid);
-0119     raw = [C{:}];%unnest the cell array of cell arrays into a 2-dim cell array
-0120 else
-0121     %Read the TASKS sheet from an Excel file (readcell handles both .xls
-0122     %and .xlsx).
-0123     try
-0124         raw=readcell(inputFile,'Sheet','TASKS');
-0125     catch
-0126         error('RAVEN:badInput', '%s', ['Could not load sheet "TASKS" from ' inputFile]);
-0127     end
-0128     %Normalise blank cells (which readcell returns as "missing") to [], as
-0129     %expected by the downstream cleanSheet/parsing.
-0130     raw(cellfun(@(x) all(ismissing(x)),raw))={[]};
-0131 end
-0132 
-0133 %Remove all lines starting with "#" (or actually any character) and all
-0134 %empty columns
-0135 raw=cleanSheet(raw);
-0136 
-0137 %Captions
-0138 columns={'ID';'DESCRIPTION';'IN';'IN LB';'IN UB';'OUT';'OUT LB';'OUT UB';'EQU';'EQU LB';'EQU UB';'CHANGED RXN';'CHANGED LB';'CHANGED UB';'SHOULD FAIL';'PRINT FLUX';'COMMENTS'};
-0139 %Match the columns
-0140 [I, colI]=ismember(columns,raw(1,:));
-0141 
-0142 %Only ID is required. Columns that are absent get index 0 from ismember, which
-0143 %cannot be used to index raw, so point them at a spare empty column instead:
-0144 %every optional field then reads as empty and its default applies.
-0145 raw(:,end+1)={[]};
-0146 colI(colI==0)=size(raw,2);
-0147 
-0148 %If read from  a text file, the numbers will be strings - fix that
-0149 if convNumeric % safeguard; a no-op if the columns are already numeric
-0150     numericColumns = [0 0 0 1 1 0 1 1 0 1 1 0 1 1 0 0 0] == 1;
-0151     cols = colI(numericColumns);
-0152     numeric = cellfun(@isnumeric,raw(:,cols));
-0153     %trick to avoid messing up the title row:
-0154     numeric(1,:) = 1;
-0155     for colind = 1:numel(cols)
-0156         col = cols(colind);
-0157         raw(~numeric(:,colind),col) = cellfun(@str2num, raw(~numeric(:,colind),col), 'UniformOutput', false);
-0158     end
-0159 end
-0160 
-0161 %Check that the ID field is present
-0162 if I(1)==0
-0163     EM='The TASKS sheet must have a column named ID';
-0164     error('RAVEN:badInput', '%s', EM);
-0165 end
-0166 
-0167 %make sure numerical fields are converted from strings
-0168 
-0169 %Add default bounds where needed
-0170 for i=[4 5 7 8]
-0171     I=cellfun(@isempty,raw(:,colI(i)));
-0172     if i==5 || i==8
-0173         raw(I,colI(i))={1000};
-0174     else
-0175         raw(I,colI(i))={0};
-0176     end
-0177 end
-0178 
-0179 %Create an empty task structure
-0180 eTask.id='';
-0181 eTask.description='';
-0182 eTask.shouldFail=false;
-0183 eTask.printFluxes=false;
-0184 eTask.comments='';
-0185 eTask.inputs={};
-0186 eTask.LBin=[];
-0187 eTask.UBin=[];
-0188 eTask.outputs={};
-0189 eTask.LBout=[];
-0190 eTask.UBout=[];
-0191 eTask.equations={};
-0192 eTask.LBequ=[];
-0193 eTask.UBequ=[];
-0194 eTask.changed={};
-0195 eTask.LBrxn=[];
-0196 eTask.UBrxn=[];
-0197 
-0198 %Main loop
-0199 taskStruct=[];
-0200 task=eTask;
-0201 if isnumeric(raw{2,colI(1)})
-0202     task.id=num2str(raw{2,colI(1)});
-0203 else
-0204     task.id=raw{2,colI(1)};
-0205 end
-0206 task.description=raw{2,colI(2)};
-0207 if ~isempty(raw{2,colI(15)})
-0208     task.shouldFail=true;
-0209 end
-0210 if ~isempty(raw{2,colI(16)})
-0211     task.printFluxes=true;
-0212 end
-0213 if ~isempty(raw{2,colI(17)})
-0214     task.comments=raw{2,colI(17)};
-0215 end
-0216 
-0217 for i=2:size(raw,1)
-0218     %Set the inputs
-0219     if ischar(raw{i,colI(3)})
-0220         inputs=regexp(raw{i,colI(3)},';','split');
-0221         task.inputs=[task.inputs;inputs(:)];
-0222         task.LBin=[task.LBin;ones(numel(inputs),1)*raw{i,colI(4)}];
-0223         task.UBin=[task.UBin;ones(numel(inputs),1)*raw{i,colI(5)}];
-0224     end
-0225     %Set the outputs
-0226     if ischar(raw{i,colI(6)})
-0227         outputs=regexp(raw{i,colI(6)},';','split');
-0228         task.outputs=[task.outputs;outputs(:)];
-0229         task.LBout=[task.LBout;ones(numel(outputs),1)*raw{i,colI(7)}];
-0230         task.UBout=[task.UBout;ones(numel(outputs),1)*raw{i,colI(8)}];
-0231     end
-0232     %Add new rxns
-0233     if ischar(raw{i,colI(9)})
-0234         task.equations=[task.equations;raw{i,colI(9)}];
-0235         if ~isempty(raw{i,colI(10)})
-0236             task.LBequ=[task.LBequ;raw{i,colI(10)}];
-0237         else
-0238             if any(strfind(raw{i,colI(9)},'<=>'))
-0239                 task.LBequ=[task.LBequ;-1000];
-0240             else
-0241                 task.LBequ=[task.LBequ;0];
-0242             end
-0243         end
-0244         if ~isempty(raw{i,colI(11)})
-0245             task.UBequ=[task.UBequ;raw{i,colI(11)}];
-0246         else
-0247             task.UBequ=[task.UBequ;1000];
-0248         end
-0249     end
-0250     %Add changed bounds
-0251     if ischar(raw{i,colI(12)})
-0252         changed=regexp(raw{i,colI(12)},';','split');
-0253         task.changed=[task.changed;changed(:)];
-0254         task.LBrxn=[task.LBrxn;ones(numel(changed),1)*raw{i,colI(13)}];
-0255         task.UBrxn=[task.UBrxn;ones(numel(changed),1)*raw{i,colI(14)}];
-0256     end
-0257     
-0258     %Check if it should add more constraints
-0259     if i<size(raw,1)
-0260         if isempty(raw{i+1,colI(1)})
-0261             continue;
-0262         end
-0263     end
-0264     
-0265     taskStruct=[taskStruct;task];
-0266     task=eTask;
-0267     if i<size(raw,1)
-0268         if isnumeric(raw{i+1,colI(1)})
-0269             task.id=num2str(raw{i+1,colI(1)});
-0270         else
-0271             task.id=raw{i+1,colI(1)};
-0272         end
-0273         task.description=raw{i+1,colI(2)};
-0274         if ~isempty(raw{i+1,colI(15)})
-0275             task.shouldFail=true;
-0276         end
-0277         if ~isempty(raw{i+1,colI(16)})
-0278             task.printFluxes=true;
-0279         end
-0280         if ~isempty(raw{i+1,colI(17)})
-0281             task.comments=raw{i+1,colI(17)};
-0282         end
-0283     end
-0284 end
-0285 
-0286 %Should add more checks, such as unique IDs and missing headers
-0287 
-0288 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/RavenTestCase.html b/doc/testing/function_tests/RavenTestCase.html deleted file mode 100644 index 79e8cb43..00000000 --- a/doc/testing/function_tests/RavenTestCase.html +++ /dev/null @@ -1,171 +0,0 @@ - - - - Description of RavenTestCase - - - - - - - - - -
Home > testing > function_tests > RavenTestCase.m
- - - -

RavenTestCase -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef (Abstract) RavenTestCase < matlab.unittest.TestCase
-0002 % RavenTestCase  Shared base class for the RAVEN function test suite.
-0003 %
-0004 %   Provides, once per test class:
-0005 %     - ravenRoot : the RAVEN installation root.
-0006 %     - model     : a small E. coli textbook model in RAVEN format, reloaded
-0007 %                   fresh for every test method so mutations cannot leak
-0008 %                   between tests.
-0009 %     - a deterministic LP solver (GLPK, which ships with RAVEN).
-0010 %
-0011 %   It also offers assume* helpers that mark a test as filtered (not failed)
-0012 %   when an optional dependency is missing, so the suite stays green in any
-0013 %   environment. Concrete test classes subclass this and add a methods(Test)
-0014 %   block.
-0015 
-0016     properties
-0017         ravenRoot char
-0018         model struct          % reset before every test method
-0019     end
-0020 
-0021     properties (Access = protected)
-0022         modelTemplate struct  % pristine copy, loaded once per class
-0023     end
-0024 
-0025     methods (TestClassSetup)
-0026         function setupRaven(testCase)
-0027             testCase.ravenRoot = findRAVENroot();
-0028             loaded = load(fullfile(testCase.ravenRoot,'testing','function_tests', ...
-0029                 'test_data','ecoli_textbook.mat'),'model');
-0030             testCase.modelTemplate = loaded.model;
-0031             try
-0032                 setRavenSolver('glpk');
-0033             catch
-0034                 % leave whatever solver is configured; solver tests assume below
-0035             end
-0036 
-0037             % Silence progress reporting for the duration of the test class,
-0038             % restoring whatever the user had configured afterwards.
-0039             origProgress = getpref('RAVEN','progressBar','auto');
-0040             setpref('RAVEN','progressBar','none');
-0041             testCase.addTeardown(@() setpref('RAVEN','progressBar',origProgress));
-0042         end
-0043 
-0044         function suppressFigures(testCase)
-0045             % Keep plotting functions from popping windows or leaking figures.
-0046             orig = get(groot, 'defaultFigureVisible');
-0047             set(groot, 'defaultFigureVisible', 'off');
-0048             testCase.addTeardown(@() set(groot, 'defaultFigureVisible', orig));
-0049             testCase.addTeardown(@() close('all'));
-0050         end
-0051     end
-0052 
-0053     methods (TestMethodSetup)
-0054         function freshModel(testCase)
-0055             % Hand each test an untouched copy of the model.
-0056             testCase.model = testCase.modelTemplate;
-0057         end
-0058     end
-0059 
-0060     methods (Access = protected)
-0061         function assumeSolver(testCase, solverName)
-0062             % Skip unless the named solver is on the MATLAB path.
-0063             testCase.assumeNotEmpty(which(solverName), ...
-0064                 sprintf('Solver "%s" not available; test skipped.', solverName));
-0065         end
-0066 
-0067         function assumeDependency(testCase, isAvailable, label)
-0068             % Skip when an external binary / network / GUI dependency is absent.
-0069             testCase.assumeTrue(logical(isAvailable), ...
-0070                 sprintf('Dependency "%s" not available; test skipped.', label));
-0071         end
-0072 
-0073         function assumeMILPSolver(testCase)
-0074             % Switch to a MILP-capable solver for this test, or skip if none.
-0075             % GLPK (the default) cannot solve MILPs in RAVEN.
-0076             if ~isempty(which('gurobi'))
-0077                 evalc('setRavenSolver(''gurobi'')');
-0078             else
-0079                 ok = false;
-0080                 try, evalc('setRavenSolver(''scip'')'); ok = true; catch, end %#ok<NOCOM>
-0081                 testCase.assumeTrue(ok, 'No MILP solver (gurobi/scip) available; test skipped.');
-0082             end
-0083             testCase.addTeardown(@() evalc('setRavenSolver(''glpk'')'));
-0084         end
-0085 
-0086         function m = taskTestModel(~)
-0087             % Small synthetic model used by task/gap-filling tests.
-0088             m = struct();
-0089             m.id = 'testModel'; m.rxns = {}; m.S = []; m.rev = [];
-0090             m.mets = {'as';'ac';'bc';'cc';'dc';'ec';'es';'fc'};
-0091             m.metNames = {'a';'a';'b';'c';'d';'e';'e';'f'};
-0092             m.comps = {'s';'c'}; m.compNames = m.comps;
-0093             m.metComps = [1;2;2;2;2;2;1;2];
-0094             m.genes = {'G1';'G2';'G3';'G4';'G5';'G6';'G7';'G8';'G9';'G10'};
-0095             m.grRules = {}; m.rxnGeneMat = [];
-0096             r = struct();
-0097             r.rxns = {'R1';'R2';'R3';'R4';'R5';'R6';'R7';'R8';'R9';'R10'};
-0098             r.equations = {'=> a[s]';'a[s] <=> a[c]';'a[c] <=> b[c] + c[c]'; ...
-0099                 'a[c] <=> 2 d[c]';'b[c] + c[c] => e[c]';'2 d[c] => e[c]'; ...
-0100                 'e[c] => e[s]';'e[s] =>';'a[c] <=> f[c]';'f[c] <=> e[c]'};
-0101             r.grRules = {'';'';'G3';'G4';'G5';'G6';'G7';'';'G9';'G10'};
-0102             evalc('m = addRxns(m, r, 3);');
-0103             m.c = [0;0;0;0;0;0;0;1;0;0];
-0104             m.ub = repmat(1000,10,1);
-0105             m.lb = [0;-1000;-1000;-1000;0;0;0;0;-1000;-1000];
-0106             m.rxnNames = m.rxns;
-0107             m.b = repmat(0,8,1);
-0108             m = closeModel(m);
-0109         end
-0110 
-0111         function t = taskTestStruct(~)
-0112             % Task that the model above can satisfy: make e[s] from a[s].
-0113             t = struct();
-0114             t.id = 'Gen e[s] from a[s]'; t.description = t.id;
-0115             t.shouldFail = false; t.printFluxes = false; t.comments = '';
-0116             t.inputs = {'a[s]'}; t.LBin = 0; t.UBin = inf;
-0117             t.outputs = {'e[s]'}; t.LBout = 1; t.UBout = 1;
-0118             t.equations = {}; t.LBequ = []; t.UBequ = [];
-0119             t.changed = {}; t.LBrxn = {}; t.UBrxn = {};
-0120         end
-0121     end
-0122 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/index.html b/doc/testing/function_tests/index.html deleted file mode 100644 index 14515ef8..00000000 --- a/doc/testing/function_tests/index.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - Index for Directory testing/function_tests - - - - - - - - - - -
< Master indexIndex for testing/function_tests >
- -

Index for testing/function_tests

- -

Matlab files in this directory:

- -
 RavenTestCase
 tAnalysis
 tAnnotation
 tAssignCompartments
 tBinaries
 tBiomass
 tComparison
 tConditions
 tConversion
 tCuration
 tGapfilling
 tGenomeData
 tINIT
 tIO
 tInstallation
 tLocalization
 tManipulation
 tNamedArgs
 tOmics
 tParseRAVENargs
 tQueries
 tReconstruction
 tSampling
 tSolver
 tSyntax
 tTasks
 tUtils
- - -

Subsequent directories:

- - -
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/private/getExprForRxnScore.html b/doc/testing/function_tests/private/getExprForRxnScore.html deleted file mode 100644 index 5c8b6d55..00000000 --- a/doc/testing/function_tests/private/getExprForRxnScore.html +++ /dev/null @@ -1,114 +0,0 @@ - - - - Description of getExprForRxnScore - - - - - - - - - -
Home > testing > function_tests > private > getExprForRxnScore.m
- - - -

getExprForRxnScore -

- -

PURPOSE ^

-
getExprForRxnScore Convert a reaction score to the required gene expression.
- -

SYNOPSIS ^

-
function expr = getExprForRxnScore(scores, varargin)
- -

DESCRIPTION ^

-
 getExprForRxnScore  Convert a reaction score to the required gene expression.
-
- Converts a reaction score to the gene expression (CPM or TPM) required to
- get that reaction score, if the GPR is only a single gene. Useful function
- primarily in test cases, where you want to be able to define the reaction
- scores of rxns, but need to send in gene expression.
-
- Parameters
- ----------
- scores : double
-     vector of scores to convert.
-
- Name-Value Arguments
- --------------------
- threshold : double
-     gene expression threshold, matching the one used to originally
-     calculate the reaction score; scales the resulting expression value
-     (default 1).
-
- Returns
- -------
- expr : double
-     the resulting gene expression vector.
-
- Examples
- --------
-     expr = getExprForRxnScore(scores, threshold);
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function expr = getExprForRxnScore(scores, varargin)
-0002 % getExprForRxnScore  Convert a reaction score to the required gene expression.
-0003 %
-0004 % Converts a reaction score to the gene expression (CPM or TPM) required to
-0005 % get that reaction score, if the GPR is only a single gene. Useful function
-0006 % primarily in test cases, where you want to be able to define the reaction
-0007 % scores of rxns, but need to send in gene expression.
-0008 %
-0009 % Parameters
-0010 % ----------
-0011 % scores : double
-0012 %     vector of scores to convert.
-0013 %
-0014 % Name-Value Arguments
-0015 % --------------------
-0016 % threshold : double
-0017 %     gene expression threshold, matching the one used to originally
-0018 %     calculate the reaction score; scales the resulting expression value
-0019 %     (default 1).
-0020 %
-0021 % Returns
-0022 % -------
-0023 % expr : double
-0024 %     the resulting gene expression vector.
-0025 %
-0026 % Examples
-0027 % --------
-0028 %     expr = getExprForRxnScore(scores, threshold);
-0029 
-0030 p=parseRAVENargs(varargin, {'threshold',1});
-0031 threshold=p.threshold;
-0032 
-0033 %This is how the score is calculated: 5*log(expression./threshold)
-0034 %expression = threshold*10.^(scores/5)
-0035 %This is a bit confusing - it seems that it is threshold*e.^(scores/5)
-0036 %This is probably what is being used in scoreModel, this code
-0037 %negates that perfectly, see the T0009 test case.
-0038 
-0039 expr = threshold*exp(scores/5);
-0040 
-0041 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/private/index.html b/doc/testing/function_tests/private/index.html deleted file mode 100644 index 5549866d..00000000 --- a/doc/testing/function_tests/private/index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - Index for Directory testing/function_tests/private - - - - - - - - - - -
< Master indexIndex for testing/function_tests/private >
- -

Index for testing/function_tests/private

- -

Matlab files in this directory:

- -
 getExprForRxnScoregetExprForRxnScore Convert a reaction score to the required gene expression.
- - - - -
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tAnalysis.html b/doc/testing/function_tests/tAnalysis.html deleted file mode 100644 index 049e282a..00000000 --- a/doc/testing/function_tests/tAnalysis.html +++ /dev/null @@ -1,399 +0,0 @@ - - - - Description of tAnalysis - - - - - - - - - -
Home > testing > function_tests > tAnalysis.m
- - - -

tAnalysis -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tAnalysis < RavenTestCase
-0002 % tAnalysis  Tests for the flux-analysis and simulation functions in analysis/.
-0003 
-0004     methods (Test)
-0005 
-0006         function getAllowedBoundsSizes(testCase)
-0007             [mn, mx] = getAllowedBounds(testCase.model, 1:10);
-0008             testCase.verifyNumElements(mn, 10);
-0009             testCase.verifyNumElements(mx, 10);
-0010             testCase.verifyTrue(all(mx >= mn - 1e-9));
-0011         end
-0012 
-0013         function getEssentialRxnsReturnsList(testCase)
-0014             evalc('[ess, idx] = getEssentialRxns(testCase.model);');
-0015             testCase.verifyClass(ess, 'cell');
-0016             testCase.verifyTrue(all(ismember(ess, testCase.model.rxns)));
-0017         end
-0018 
-0019         function haveFluxReturnsLogical(testCase)
-0020             I = haveFlux(testCase.model);
-0021             testCase.verifyNumElements(I, numel(testCase.model.rxns));
-0022         end
-0023 
-0024         function haveFluxSeedIsReproducible(testCase)
-0025             % The order reactions are tested in is randomised; a given
-0026             % seed must make that order (and so results) reproducible.
-0027             I1 = haveFlux(testCase.model, 'seed', 42);
-0028             I2 = haveFlux(testCase.model, 'seed', 42);
-0029             testCase.verifyEqual(I1, I2);
-0030         end
-0031 
-0032         function getMinNrFluxesReturnsFlux(testCase)
-0033             testCase.assumeMILPSolver();
-0034             evalc('[x, I, exitFlag] = getMinNrFluxes(testCase.model, testCase.model.rxns);');
-0035             testCase.verifyNotEmpty(x);
-0036         end
-0037 
-0038         function getMinNrFluxesFormulationsAgree(testCase)
-0039             % The two formulations build different MILPs for the same
-0040             % problem, so they may pick different reactions, but both
-0041             % minimise the same objective and must therefore pick the same
-0042             % number of them.
-0043             testCase.assumeMILPSolver();
-0044             evalc('[xIrrev, Iirrev, flagIrrev] = getMinNrFluxes(testCase.model, testCase.model.rxns, [], [], ''irrev'');');
-0045             evalc('[xRev, Irev, flagRev] = getMinNrFluxes(testCase.model, testCase.model.rxns, [], [], ''reversible'');');
-0046             testCase.verifyEqual(flagIrrev, 1);
-0047             testCase.verifyEqual(flagRev, 1);
-0048             testCase.verifyNumElements(xRev, numel(testCase.model.rxns));
-0049             testCase.verifyNumElements(Irev, numel(testCase.model.rxns));
-0050             testCase.verifyEqual(sum(Irev), sum(Iirrev));
-0051         end
-0052 
-0053         function getMinNrFluxesResolveTiesIsDeterministic(testCase)
-0054             % The reversible-formulation MILP is degenerate on a real model
-0055             % (see getMinNrFluxesFormulationsAgree above): resolveTies pins
-0056             % it to a canonical answer instead of leaving the choice to the
-0057             % solver/seed, so three independent runs must all agree
-0058             % (raven-gecko-parity#104).
-0059             testCase.assumeMILPSolver();
-0060             evalc(['[~, I1, flag1] = getMinNrFluxes(testCase.model, testCase.model.rxns, ' ...
-0061                 '[], [], ''reversible'', false, ''resolveTies'', true);']);
-0062             evalc(['[~, I2, flag2] = getMinNrFluxes(testCase.model, testCase.model.rxns, ' ...
-0063                 '[], [], ''reversible'', false, ''resolveTies'', true);']);
-0064             evalc(['[~, I3, flag3] = getMinNrFluxes(testCase.model, testCase.model.rxns, ' ...
-0065                 '[], [], ''reversible'', false, ''resolveTies'', true);']);
-0066             testCase.verifyEqual(flag1, 1);
-0067             testCase.verifyEqual(flag2, 1);
-0068             testCase.verifyEqual(flag3, 1);
-0069             testCase.verifyEqual(I1, I2);
-0070             testCase.verifyEqual(I2, I3);
-0071         end
-0072 
-0073         function getMinNrFluxesResolveTiesOnlySupportsReversible(testCase)
-0074             testCase.verifyError( ...
-0075                 @() getMinNrFluxes(testCase.model, testCase.model.rxns, [], [], 'irrev', ...
-0076                     false, 'resolveTies', true), ...
-0077                 'RAVEN:badInput');
-0078         end
-0079 
-0080         function getMinNrFluxesRejectsUnknownFormulation(testCase)
-0081             testCase.verifyError( ...
-0082                 @() getMinNrFluxes(testCase.model, testCase.model.rxns, [], [], 'both'), ...
-0083                 'RAVEN:badInput');
-0084         end
-0085 
-0086         function getAllSubGraphsReturnsResult(testCase)
-0087             sg = getAllSubGraphs(testCase.model);
-0088             testCase.verifyNotEmpty(sg);
-0089         end
-0090 
-0091         function findGeneDeletionsRuns(testCase)
-0092             evalc('[genes, fluxes] = findGeneDeletions(testCase.model, ''sgd'');');
-0093             testCase.verifyNotEmpty(genes);
-0094         end
-0095 
-0096         function findGeneDeletionsComputesGrRatio(testCase)
-0097             evalc(['[genes, ~, ~, ~, grRatioMuts] = findGeneDeletions(testCase.model, ' ...
-0098                 '''sgd'');']);
-0099             testCase.verifyNotEmpty(genes);
-0100             testCase.verifyEqual(numel(grRatioMuts), numel(genes));
-0101             testCase.verifyGreaterThan(max(grRatioMuts), 0);
-0102         end
-0103 
-0104         function traceFluxPathReturnsCells(testCase)
-0105             testCase.assumeSolver('solveLP');
-0106             sol = solveLP(testCase.model);
-0107             % Use two reactions that must be metabolically connected
-0108             biomassRxn = testCase.model.rxns{find(testCase.model.c == 1, 1)};
-0109             [~, exchIdx] = getExchangeRxns(testCase.model);
-0110             % Pick an exchange reaction with non-zero flux
-0111             exchFlux = sol.x(exchIdx);
-0112             active   = exchIdx(abs(exchFlux) > 1e-8);
-0113             if isempty(active), testCase.assumeTrue(false); end
-0114             targetRxn = testCase.model.rxns{active(1)};
-0115             evalc('[p, m, f] = traceFluxPath(testCase.model, sol.x, biomassRxn, targetRxn, ''verbose'', false);');
-0116             % Either a path is found (cell + double) or not — both are valid
-0117             testCase.verifyClass(f, 'double');
-0118             testCase.verifyGreaterThanOrEqual(f, 0);
-0119             testCase.verifyLessThanOrEqual(f, 1);
-0120         end
-0121 
-0122         function traceFluxPathSameReactionIsIdentity(testCase)
-0123             testCase.assumeSolver('solveLP');
-0124             sol  = solveLP(testCase.model);
-0125             rxn1 = testCase.model.rxns{find(testCase.model.c == 1, 1)};
-0126             [p, m, f] = traceFluxPath(testCase.model, sol.x, rxn1, rxn1, 'verbose', false);
-0127             testCase.verifyEqual(f, 1.0);
-0128             testCase.verifyNumElements(p, 1);
-0129             testCase.verifyEmpty(m);
-0130         end
-0131 
-0132         function walkFluxesRefusesNonInteractive(testCase)
-0133             % The test suite itself runs under -batch, so this exercises the
-0134             % real guard rather than a mocked one.
-0135             testCase.assumeTrue(batchStartupOptionUsed, ...
-0136                 'Test runner is not using -batch; the non-interactive guard cannot be exercised here.');
-0137             testCase.assumeSolver('solveLP');
-0138             sol = solveLP(testCase.model);
-0139             biomassRxn = testCase.model.rxns{find(testCase.model.c == 1, 1)};
-0140             testCase.verifyError(@() walkFluxes(testCase.model, sol.x, biomassRxn), ...
-0141                 'walkFluxes:nonInteractive');
-0142         end
-0143 
-0144         function compareFluxesReturnsResult(testCase)
-0145             testCase.assumeSolver('solveLP');
-0146             solA = solveLP(testCase.model);
-0147             o2exch = find(strcmp(testCase.model.rxnNames, 'O2 exchange'), 1);
-0148             modelAna = setParam(testCase.model, 'eq', testCase.model.rxns(o2exch), 0);
-0149             solB = solveLP(modelAna);
-0150             result = compareFluxes(testCase.model, solA.x, solB.x, 'verbose', false);
-0151             testCase.verifyTrue(isfield(result, 'turnedOn'));
-0152             testCase.verifyTrue(isfield(result, 'turnedOff'));
-0153             testCase.verifyTrue(isfield(result, 'flipped'));
-0154             testCase.verifyTrue(isfield(result, 'changed'));
-0155             testCase.verifyClass(result.changed.rxn, 'cell');
-0156             testCase.verifyNotEmpty(result.changed.rxn);
-0157             testCase.verifyEqual(numel(result.changed.flux1), numel(result.changed.rxn));
-0158             testCase.verifyEqual(numel(result.changed.absDelta), numel(result.changed.rxn));
-0159             % sorted descending
-0160             testCase.verifyTrue(all(diff(result.changed.absDelta) <= 0));
-0161         end
-0162 
-0163         function compareFluxesIdenticalIsEmpty(testCase)
-0164             testCase.assumeSolver('solveLP');
-0165             sol = solveLP(testCase.model);
-0166             result = compareFluxes(testCase.model, sol.x, sol.x, 'verbose', false);
-0167             testCase.verifyEmpty(result.changed.rxn);
-0168             testCase.verifyEmpty(result.turnedOn);
-0169             testCase.verifyEmpty(result.turnedOff);
-0170         end
-0171 
-0172         function compareFluxesMetaboliteListRestrictsResult(testCase)
-0173             testCase.assumeSolver('solveLP');
-0174             solA = solveLP(testCase.model);
-0175             o2exch = find(strcmp(testCase.model.rxnNames, 'O2 exchange'), 1);
-0176             modelAna = setParam(testCase.model, 'eq', testCase.model.rxns(o2exch), 0);
-0177             solB = solveLP(modelAna);
-0178             unfiltered = compareFluxes(testCase.model, solA.x, solB.x, 'verbose', false);
-0179             testCase.assumeNotEmpty(unfiltered.changed.rxn);
-0180 
-0181             % Name a metabolite of the largest-changing reaction, so the
-0182             % filtered result is guaranteed to be non-empty and the subset
-0183             % checks below are not satisfied vacuously.
-0184             topRxn = strcmp(testCase.model.rxns, unfiltered.changed.rxn{1});
-0185             met = testCase.model.metNames{find(testCase.model.S(:,topRxn) ~= 0, 1)};
-0186             filtered = compareFluxes(testCase.model, solA.x, solB.x, ...
-0187                 'metaboliteList', {met}, 'verbose', false);
-0188             testCase.verifyNotEmpty(filtered.changed.rxn);
-0189 
-0190             % Every kept reaction must involve the named metabolite, and the
-0191             % filtered result can only be a subset of the unfiltered one.
-0192             metIdx = strcmpi(met, testCase.model.metNames);
-0193             withMet = testCase.model.rxns(any(testCase.model.S(metIdx,:) ~= 0, 1));
-0194             testCase.verifyTrue(all(ismember(filtered.changed.rxn, withMet)));
-0195             testCase.verifyTrue(all(ismember(filtered.changed.rxn, unfiltered.changed.rxn)));
-0196         end
-0197 
-0198         function compareFluxesUnknownMetaboliteWarns(testCase)
-0199             testCase.assumeSolver('solveLP');
-0200             sol = solveLP(testCase.model);
-0201             testCase.verifyWarning(@() compareFluxes(testCase.model, sol.x, sol.x, ...
-0202                 'metaboliteList', {'no such metabolite'}, 'verbose', false), ...
-0203                 'RAVEN:warning');
-0204         end
-0205 
-0206         function getFluxZZeroVarianceSignMatchesGeneralCase(testCase)
-0207             % The zero-variance branch's sign must agree with the general
-0208             % branch: positive when flux increased from A to B, negative
-0209             % when it decreased.
-0210             solA = [1 1 1; 5 5 5];    % rxn1 constant at 1, rxn2 constant at 5
-0211             solB = [5 5 5; 1 1 1];    % rxn1 increased to 5, rxn2 decreased to 1
-0212             Z = getFluxZ(solA, solB);
-0213             testCase.verifyEqual(Z(1), 100);
-0214             testCase.verifyEqual(Z(2), -100);
-0215         end
-0216 
-0217         function getFluxZComputesScores(testCase)
-0218             n = numel(testCase.model.rxns);
-0219             Z = getFluxZ(rand(n, 20), rand(n, 20));
-0220             testCase.verifyNumElements(Z, n);
-0221         end
-0222 
-0223         function analyzeSamplingRuns(testCase)
-0224             n = numel(testCase.model.rxns);
-0225             Tex = randn(n, 1);           % per-reaction expression t-scores
-0226             solutionsA = rand(n, 20);
-0227             solutionsB = rand(n, 20);
-0228             evalc('scores = analyzeSampling(Tex, 18, solutionsA, solutionsB, false);');
-0229             testCase.verifySize(scores, [n 3]);
-0230         end
-0231 
-0232         function randomSamplingRuns(testCase)
-0233             % Bare call exercises the default method (ACHR).
-0234             evalc('sols = randomSampling(testCase.model, 5);');
-0235             testCase.verifyEqual(size(sols, 1), numel(testCase.model.rxns));
-0236         end
-0237 
-0238         function randomSamplingDeterministicWithSeed(testCase)
-0239             % goodRxns reuse is specific to the random-objective method.
-0240             evalc('[~, gR] = randomSampling(testCase.model, 0, ''method'', ''randomObjective'', ''runParallel'', false);');
-0241             evalc('s1 = randomSampling(testCase.model, 5, ''method'', ''randomObjective'', ''seed'', 42, ''runParallel'', false, ''goodRxns'', gR);');
-0242             evalc('s2 = randomSampling(testCase.model, 5, ''method'', ''randomObjective'', ''seed'', 42, ''runParallel'', false, ''goodRxns'', gR);');
-0243             testCase.verifyEqual(s1, s2);
-0244         end
-0245 
-0246         function reporterMetabolitesRuns(testCase)
-0247             pvals = rand(numel(testCase.model.genes), 1);
-0248             rm = reporterMetabolites(testCase.model, testCase.model.genes, pvals);
-0249             testCase.verifyClass(rm, 'struct');
-0250         end
-0251 
-0252         function reporterMetabolitesKeepsPercentInMetNames(testCase)
-0253             % A metNames entry containing "%" must survive intact in the
-0254             % outputFile report, not be truncated by fprintf misreading it
-0255             % as a format directive.
-0256             m = testCase.model;
-0257             m.metNames{1} = 'metabolite 30% pure';
-0258             pvals = rand(numel(m.genes), 1);
-0259             outFile = [tempname '.txt'];
-0260             c = onCleanup(@() delete(outFile));
-0261             evalc('reporterMetabolites(m, m.genes, pvals, ''outputFile'', outFile);');
-0262             content = fileread(outFile);
-0263             testCase.verifySubstring(content, 'metabolite 30% pure');
-0264         end
-0265 
-0266         function reporterMetabolitesIsDeterministic(testCase)
-0267             % Closed-form background correction (RM1) must produce identical
-0268             % Z-scores on repeated calls with identical inputs.
-0269             pvals = rand(numel(testCase.model.genes), 1);
-0270             rm1 = reporterMetabolites(testCase.model, testCase.model.genes, pvals);
-0271             rm2 = reporterMetabolites(testCase.model, testCase.model.genes, pvals);
-0272             testCase.verifyEqual(rm1.metZScores, rm2.metZScores);
-0273         end
-0274 
-0275         function FSEOFRuns(testCase)
-0276             biomassRxn = testCase.model.rxns{find(testCase.model.c == 1, 1)};
-0277             [~, exchIdx] = getExchangeRxns(testCase.model);
-0278             targetRxn = testCase.model.rxns{exchIdx(1)};
-0279             outFile = [tempname '.txt'];
-0280             evalc('targets = FSEOF(testCase.model, biomassRxn, targetRxn, 5, 0.9, outFile);');
-0281             testCase.verifyClass(targets, 'struct');
-0282         end
-0283 
-0284         function runRobustnessAnalysisRuns(testCase)
-0285             [~, exchIdx] = getExchangeRxns(testCase.model);
-0286             controlRxn = testCase.model.rxns{exchIdx(1)};
-0287             evalc('[cFlux, oFlux] = runRobustnessAnalysis(testCase.model, controlRxn, 5);');
-0288             testCase.verifyNumElements(cFlux, 5);
-0289         end
-0290 
-0291         function runProductionEnvelopeRuns(testCase)
-0292             biomassRxn = testCase.model.rxns{find(testCase.model.c == 1, 1)};
-0293             [~, exchIdx] = getExchangeRxns(testCase.model);
-0294             targetRxn = testCase.model.rxns{exchIdx(1)};
-0295             evalc('[bio, tgt] = runProductionEnvelope(testCase.model, targetRxn, biomassRxn, 5);');
-0296             testCase.verifyNotEmpty(bio);
-0297         end
-0298 
-0299         function runPhenotypePhasePlaneRuns(testCase)
-0300             [~, exchIdx] = getExchangeRxns(testCase.model);
-0301             r1 = testCase.model.rxns{exchIdx(1)};
-0302             r2 = testCase.model.rxns{exchIdx(2)};
-0303             evalc('g = runPhenotypePhasePlane(testCase.model, r1, r2, 3, 3);');
-0304             testCase.verifyNotEmpty(g);
-0305         end
-0306 
-0307         function runDynamicFBARuns(testCase)
-0308             % Use glucose (consumed) as substrate and plot target; the function
-0309             % always plots, so the plotted reaction must carry non-zero flux.
-0310             [~, exchIdx] = getExchangeRxns(testCase.model);
-0311             glcMet = find(strcmp(testCase.model.metNames, 'D-Glucose'), 1);
-0312             glcExch = exchIdx(any(testCase.model.S(glcMet, exchIdx) ~= 0, 1));
-0313             subRxn = testCase.model.rxns(glcExch);
-0314             evalc(['c = runDynamicFBA(testCase.model, subRxn, 10, 0.1, 0.1, 2, ' ...
-0315                 'subRxn, {});']);
-0316             testCase.verifyNotEmpty(c);
-0317         end
-0318 
-0319         function getMinimalMediumReturnsMedium(testCase)
-0320             testCase.assumeMILPSolver();
-0321             evalc('[med, idx] = getMinimalMedium(testCase.model, ''verbose'', false);');
-0322             testCase.verifyClass(med, 'cell');
-0323             testCase.verifyClass(idx, 'double');
-0324             % Every returned reaction must have lb < 0 (it is an uptake exchange)
-0325             [~, exchIdx] = getExchangeRxns(testCase.model);
-0326             testCase.verifyTrue(all(ismember(idx, exchIdx)));
-0327             testCase.verifyTrue(all(testCase.model.lb(idx) < 0));
-0328         end
-0329 
-0330         function getMinimalMediumExplicitGrowth(testCase)
-0331             testCase.assumeMILPSolver();
-0332             sol = solveLP(testCase.model);
-0333             minG = 0.05 * sol.f;
-0334             evalc('[med, idx] = getMinimalMedium(testCase.model, ''minGrowth'', minG, ''verbose'', false);');
-0335             testCase.verifyClass(med, 'cell');
-0336             testCase.verifyNotEmpty(med);
-0337         end
-0338 
-0339         function runSimpleOptKnockRuns(testCase)
-0340             testCase.assumeMILPSolver();
-0341             biomassRxn = testCase.model.rxns{find(testCase.model.c == 1, 1)};
-0342             [~, exchIdx] = getExchangeRxns(testCase.model);
-0343             targetRxn = testCase.model.rxns{exchIdx(1)};
-0344             evalc(['out = runSimpleOptKnock(testCase.model, targetRxn, biomassRxn, ' ...
-0345                 'testCase.model.rxns(1:5), ''rxns'', 1);']);
-0346             testCase.verifyClass(out, 'struct');
-0347         end
-0348 
-0349     end
-0350 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tAnnotation.html b/doc/testing/function_tests/tAnnotation.html deleted file mode 100644 index befc22c5..00000000 --- a/doc/testing/function_tests/tAnnotation.html +++ /dev/null @@ -1,110 +0,0 @@ - - - - Description of tAnnotation - - - - - - - - - -
Home > testing > function_tests > tAnnotation.m
- - - -

tAnnotation -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tAnnotation < RavenTestCase
-0002 % tAnnotation  Tests for the annotation/metadata functions in annotation/.
-0003 
-0004     methods (Test)
-0005 
-0006         function editMiriamAddsAnnotation(testCase)
-0007             m2 = editMiriam(testCase.model, 'met', 1, 'bigg.metabolite', 'testval', 'add');
-0008             testCase.verifyClass(m2, 'struct');
-0009             testCase.verifyEqual(numel(m2.mets), numel(testCase.model.mets));
-0010         end
-0011 
-0012         function editMiriamRemovesAnnotation(testCase)
-0013             m2 = editMiriam(testCase.model, 'met', 1, 'bigg.metabolite', 'testval', 'add');
-0014             m3 = editMiriam(m2, 'met', 1, 'bigg.metabolite', '', 'remove');
-0015             testCase.verifyClass(m3, 'struct');
-0016             [~, names] = extractMiriam(m3.metMiriams(1));
-0017             testCase.verifyFalse(any(strcmp('bigg.metabolite', names)));
-0018         end
-0019 
-0020         function extractMiriamReturnsNames(testCase)
-0021             [miriams, names] = extractMiriam(testCase.model.metMiriams); %#ok<ASGLU>
-0022             testCase.verifyNotEmpty(names);
-0023         end
-0024 
-0025         function assignSBOtermsRuns(testCase)
-0026             evalc('m2 = assignSBOterms(testCase.model);');
-0027             testCase.verifyClass(m2, 'struct');
-0028         end
-0029 
-0030         function deltaGCsvRoundTrip(testCase)
-0031             m = testCase.model;
-0032             m.metDeltaG = (1:numel(m.mets))';
-0033             m.rxnDeltaG = (1:numel(m.rxns))';
-0034             metCsv = [tempname '.csv'];
-0035             rxnCsv = [tempname '.csv'];
-0036             testCase.addTeardown(@() delete(metCsv));
-0037             testCase.addTeardown(@() delete(rxnCsv));
-0038             evalc('saveDeltaGCSV(m, metCsv, rxnCsv);');
-0039             base = testCase.model;
-0040             evalc('m2 = loadDeltaGCSV(base, metCsv, rxnCsv);');
-0041             testCase.verifyEqual(m2.metDeltaG, m.metDeltaG, 'AbsTol', 1e-6);
-0042             testCase.verifyEqual(m2.rxnDeltaG, m.rxnDeltaG, 'AbsTol', 1e-6);
-0043         end
-0044 
-0045         function loadDeltaGCsvStoresValuesLiterally(testCase)
-0046             % No filtering of any kind: every matched value, including
-0047             % yeast-GEM's own "no measurement" placeholder (10000000.0), is
-0048             % stored exactly as the CSV states it.
-0049             m = testCase.model;
-0050             metCsv = [tempname '.csv'];
-0051             testCase.addTeardown(@() delete(metCsv));
-0052             fid = fopen(metCsv, 'w');
-0053             fprintf(fid, 'Var1,Var2\n%s,-42.5\n%s,10000000.0\n', m.mets{1}, m.mets{2});
-0054             fclose(fid);
-0055             evalc('md = loadDeltaGCSV(m, ''metCsv'', metCsv);');
-0056             testCase.verifyEqual(md.metDeltaG(1), -42.5, 'AbsTol', 1e-9);
-0057             testCase.verifyEqual(md.metDeltaG(2), 1e7, 'AbsTol', 1e-3);
-0058         end
-0059 
-0060     end
-0061 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tAssignCompartments.html b/doc/testing/function_tests/tAssignCompartments.html deleted file mode 100644 index ac954454..00000000 --- a/doc/testing/function_tests/tAssignCompartments.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - Description of tAssignCompartments - - - - - - - - - -
Home > testing > function_tests > tAssignCompartments.m
- - - -

tAssignCompartments -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tAssignCompartments < RavenTestCase
-0002 % tAssignCompartments  Tests for the functionality-constrained compartment-assignment MILP.
-0003 %
-0004 %   Uses a tiny single-compartment toy: EX_A (uptake) -> r1: A->B -> bio: B-> (objective), with
-0005 %   gene g1 (on r1) scored higher for a hypothetical compartment 'm' than for the cytosol 'c'.
-0006 %   The decisive checks are that the functionality constraint can override the localization score
-0007 %   and that the returned model stays able to grow. Requires a MILP solver.
-0008 
-0009     methods (Test)
-0010 
-0011         function functionalityOverridesScore(testCase)
-0012             testCase.assumeMILPSolver();
-0013             model = tAssignCompartments.toy();
-0014             GSS = tAssignCompartments.gss();
-0015             % B is non-transportable, so placing r1 in 'm' would strand B where biomass (in 'c')
-0016             % cannot reach it -> r1 must stay in 'c', against g1's higher 'm' score.
-0017             evalc(['[oModel, place, ~, eFlag] = assignCompartments(model, GSS, {''r1''}, ' ...
-0018                    '''defaultCompartment'', ''c'', ''transportable'', {}, ''verbose'', false);']);
-0019             testCase.verifyEqual(eFlag, 1);
-0020             testCase.verifyEqual(place.compartment{1}, 'c');
-0021             sol = solveLP(oModel);
-0022             testCase.verifyGreaterThan(sol.f, 1e-6);          % applied model still grows
-0023         end
-0024 
-0025         function scoreRespectedWhenFunctional(testCase)
-0026             testCase.assumeMILPSolver();
-0027             model = tAssignCompartments.toy();
-0028             GSS = tAssignCompartments.gss();
-0029             % Transports allowed and cheap -> r1 can follow its score to 'm' and stay functional.
-0030             evalc(['[oModel, place, trans, eFlag] = assignCompartments(model, GSS, {''r1''}, ' ...
-0031                    '''defaultCompartment'', ''c'', ''transportCost'', 0.1, ''verbose'', false);']);
-0032             testCase.verifyEqual(eFlag, 1);
-0033             testCase.verifyEqual(place.compartment{1}, 'm');
-0034             testCase.verifyGreaterThan(numel(trans.mets), 0); % transports were added
-0035             sol = solveLP(oModel);
-0036             testCase.verifyGreaterThan(sol.f, 1e-6);
-0037         end
-0038 
-0039         function infeasibleGrowthFloorReported(testCase)
-0040             testCase.assumeMILPSolver();
-0041             model = tAssignCompartments.toy();
-0042             GSS = tAssignCompartments.gss();
-0043             evalc(['[~, ~, ~, eFlag] = assignCompartments(model, GSS, {''r1''}, ' ...
-0044                    '''defaultCompartment'', ''c'', ''minGrowth'', 1e6, ''verbose'', false);']);
-0045             testCase.verifyEqual(eFlag, -1);                  % growth floor unreachable
-0046         end
-0047 
-0048     end
-0049 
-0050     methods (Static)
-0051         function model = toy()
-0052             model.id='toy'; model.comps={'c'}; model.compNames={'cytoplasm'};
-0053             model.mets={'A_c';'B_c'}; model.metNames={'A';'B'}; model.metComps=[1;1];
-0054             model.S=sparse([-1 -1 0; 0 1 -1]);     % [A;B] x [EX_A r1 bio]
-0055             model.rxns={'EX_A';'r1';'bio'}; model.rxnNames=model.rxns;
-0056             model.lb=[-10;0;0]; model.ub=[1000;1000;1000]; model.rev=[1;0;0];
-0057             model.c=[0;0;1]; model.b=zeros(2,1);
-0058             model.genes={'g1'}; model.grRules={'';'g1';''}; model.rxnGeneMat=sparse([0;1;0]);
-0059         end
-0060         function GSS = gss()
-0061             GSS.genes={'g1'}; GSS.compartments={'c';'m'}; GSS.scores=[0.4 0.9];
-0062         end
-0063     end
-0064 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tBinaries.html b/doc/testing/function_tests/tBinaries.html deleted file mode 100644 index b119a8eb..00000000 --- a/doc/testing/function_tests/tBinaries.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - Description of tBinaries - - - - - - - - - -
Home > testing > function_tests > tBinaries.m
- - - -

tBinaries -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tBinaries < matlab.unittest.TestCase
-0002 % tBinaries  On-demand download of RAVEN's external binaries from raven-data.
-0003 %
-0004 %   Validates downloadRavenBinaries on the current platform: each tool is
-0005 %   fetched into software/<tool>/, the executable is present, and (for the CLI
-0006 %   tools) it actually runs. On macOS, running the downloaded binary also
-0007 %   confirms the Gatekeeper quarantine attribute was cleared.
-0008 %
-0009 %   Exercised on Linux and macOS by the "binary-tests" CI job. Requires network
-0010 %   access to https://github.com/SysBioChalmers/raven-data .
-0011 
-0012     methods (Test)
-0013         function blast(testCase)
-0014             testCase.fetchAndRun('blast+', 'blastp', '-version');
-0015             testCase.verifyTrue(isfile(testCase.binPath('blast+','makeblastdb')), ...
-0016                 'makeblastdb not found after download');
-0017         end
-0018 
-0019         function diamond(testCase)
-0020             testCase.fetchAndRun('diamond', 'diamond', 'version');
-0021         end
-0022 
-0023         function hmmer(testCase)
-0024             testCase.fetchAndRun('hmmer', 'hmmsearch', '-h');
-0025         end
-0026     end
-0027 
-0028     methods (Access = private)
-0029         function p = binPath(~, tool, name)
-0030             if ispc; ext = '.exe'; elseif ismac; ext = '.mac'; else; ext = ''; end
-0031             p = fullfile(findRAVENroot(),'software',tool,[name ext]);
-0032         end
-0033 
-0034         function fetchAndRun(testCase, tool, name, versionArg)
-0035             % Download the tool, then confirm the executable exists and runs.
-0036             downloadRavenBinaries({tool});
-0037             exe = testCase.binPath(tool, name);
-0038             testCase.verifyTrue(isfile(exe), ...
-0039                 sprintf('%s not found after download', name));
-0040             [status, ~] = system(['"' exe '" ' versionArg]);
-0041             testCase.verifyEqual(status, 0, ...
-0042                 sprintf('%s did not execute (exit status %d)', name, status));
-0043         end
-0044     end
-0045 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tBiomass.html b/doc/testing/function_tests/tBiomass.html deleted file mode 100644 index d9cd0d9f..00000000 --- a/doc/testing/function_tests/tBiomass.html +++ /dev/null @@ -1,180 +0,0 @@ - - - - Description of tBiomass - - - - - - - - - -
Home > testing > function_tests > tBiomass.m
- - - -

tBiomass -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tBiomass < RavenTestCase
-0002 % tBiomass  Tests for the biomass-composition functions in biomass/.
-0003 %
-0004 %   The textbook E. coli model has a single lumped biomass reaction rather
-0005 %   than per-component pseudoreactions, so component-scaling functions are
-0006 %   tested on their missing-pseudoreaction error path.
-0007 
-0008     methods (Test)
-0009 
-0010         function getBiomassFractionsHandlesMissingComponents(testCase)
-0011             cfg = testCase.biomassConfig();
-0012             fractions = getBiomassFractions(testCase.model, cfg);
-0013             testCase.verifyTrue(isfield(fractions, 'total'));
-0014         end
-0015 
-0016         function setGAMReturnsModel(testCase)
-0017             biomassRxn = testCase.model.rxns{find(testCase.model.c == 1, 1)};
-0018             evalc(['m2 = setGAM(testCase.model, 50, biomassRxn, ' ...
-0019                 '{''ATP'',''ADP'',''H2O'',''H+'',''Phosphate''});']);
-0020             testCase.verifyClass(m2, 'struct');
-0021         end
-0022 
-0023         function scaleBiomassFractionErrorsOnMissingComponent(testCase)
-0024             cfg = testCase.biomassConfig();
-0025             testCase.verifyError(@() scaleBiomassFraction(testCase.model, cfg, ...
-0026                 'protein', 0.5), ?MException);
-0027         end
-0028 
-0029         function scaleBiomassPseudoreactionErrorsOnMissingComponent(testCase)
-0030             cfg = testCase.biomassConfig();
-0031             testCase.verifyError(@() scaleBiomassPseudoreaction(testCase.model, cfg, ...
-0032                 'protein', 0.9), ?MException);
-0033         end
-0034 
-0035         function scaleBiomassPseudoreactionRebalancesProton(testCase)
-0036             % Rescaling the substrates rebalances H+ so the pseudoreaction
-0037             % stays charge neutral.
-0038             [m, cfg] = tBiomass.protonToyModel();
-0039             out = scaleBiomassPseudoreaction(m, cfg, 'protein', 0.5);
-0040             % x_c: -0.5 * charge -2 = +1, so H+ must come in at -1
-0041             testCase.verifyEqual(full(out.S(strcmp(out.mets,'x_c'), 1)), -0.5, 'AbsTol', 1e-9);
-0042             testCase.verifyEqual(full(out.S(strcmp(out.mets,'h_c'), 1)), -1, 'AbsTol', 1e-9);
-0043         end
-0044 
-0045         function scaleBiomassPseudoreactionRefusesUnknownCharge(testCase)
-0046             % An unset charge on a participating metabolite leaves the charge
-0047             % balance unknown. Treating it as neutral would silently write the
-0048             % wrong proton coefficient, so the rescale must refuse instead.
-0049             [m, cfg] = tBiomass.protonToyModel();
-0050             m.metCharges(strcmp(m.mets,'x_c')) = NaN;
-0051             testCase.verifyError(@() scaleBiomassPseudoreaction(m, cfg, 'protein', 0.5), ...
-0052                 'scaleBiomassPseudoreaction:unknownCharge');
-0053         end
-0054 
-0055         function fitParametersSetsUpperBoundPerXRxn(testCase)
-0056             % model.ub for a second xRxn must come from its own column of
-0057             % xValues, not the first column broadcast to every xRxn (which
-0058             % also inverts that reaction's bounds whenever its value is
-0059             % smaller than the first column's).
-0060             m = struct();
-0061             m.id='t'; m.rxns={'R1';'R2';'R3';'R4'}; m.rxnNames=m.rxns;
-0062             m.mets={'m1';'m2'}; m.metNames=m.mets; m.metComps=[1;1];
-0063             m.comps={'c'}; m.compNames={'c'};
-0064             m.S=sparse([1 0 -1 0; 0 1 0 -1]); % R1:=>m1  R2:=>m2  R3:m1=>  R4:m2=>
-0065             m.lb=[0;0;0;0]; m.ub=[1000;1000;1000;1000]; m.rev=[0;0;0;0];
-0066             m.c=[0;0;0;0]; m.b=zeros(2,1);
-0067             m.genes={}; m.grRules={'';'';'';''}; m.rxnGeneMat=sparse(4,0);
-0068             pos.position={[]}; pos.isNegative={[]};
-0069             evalc(['[~,fitnessScore] = fitParameters(m, {''R1'';''R2''}, [5 7], ' ...
-0070                 '{''R3'';''R4''}, [5 7], pos, ''fitToRatio'', false);']);
-0071             % R3/R4 are only reachable via mass balance from R1/R2's fixed
-0072             % (lb=ub) values, so a correct fit has exactly zero residual.
-0073             testCase.verifyEqual(fitnessScore, 0, 'AbsTol', 1e-9);
-0074         end
-0075 
-0076         function fitParametersRuns(testCase)
-0077             % Minimal single-parameter fit of an exchange flux. fitParameters
-0078             % uses fminsearch (base MATLAB), not quadprog, so no toolbox guard.
-0079             biomassRxnIdx = find(testCase.model.c == 1, 1);
-0080             biomassRxn = testCase.model.rxns{biomassRxnIdx};
-0081             % parameterPositions.position/isNegative are cell arrays, one cell
-0082             % per fitted parameter, of linear S-matrix indices (see
-0083             % fitParameters' docstring) -- fit the coefficient of the first
-0084             % reactant in the biomass reaction.
-0085             metIdx = find(testCase.model.S(:,biomassRxnIdx) < 0, 1);
-0086             pos.position = {sub2ind(size(testCase.model.S), metIdx, biomassRxnIdx)};
-0087             pos.isNegative = {true};
-0088             evalc(['p = fitParameters(testCase.model, {biomassRxn}, 0.5, ' ...
-0089                 '{biomassRxn}, 0.5, pos);']);
-0090             testCase.verifyNotEmpty(p);
-0091         end
-0092 
-0093     end
-0094 
-0095     methods (Static, Access = private)
-0096         function [m, cfg] = protonToyModel()
-0097             % One pseudoreaction: x_c -> protein, with h_c available to carry
-0098             % the charge balance.
-0099             m = struct();
-0100             m.id        = 'toy';
-0101             m.rxns      = {'R1'};
-0102             m.rxnNames  = {'protein pseudoreaction'};
-0103             m.mets      = {'x_c';'h_c';'prot_c'};
-0104             m.metNames  = {'x';'h';'protein'};
-0105             m.metComps  = [1;1;1];
-0106             m.metCharges= [-2;1;0];
-0107             m.comps     = {'c'};
-0108             m.compNames = {'cytosol'};
-0109             m.S         = sparse([-1;0;1]);
-0110             m.lb = 0; m.ub = 1000; m.rev = 0; m.c = 1; m.b = zeros(3,1);
-0111             m.grRules = {''}; m.genes = {}; m.rxnGeneMat = sparse(1,0);
-0112 
-0113             cfg = struct();
-0114             cfg.biomass_rxn = 'R1';
-0115             cfg.proton_met  = 'h_c';
-0116             cfg.components  = {struct('name','protein', ...
-0117                 'pseudoreaction_name','protein pseudoreaction', ...
-0118                 'mass_strategy','mw')};
-0119         end
-0120     end
-0121 
-0122     methods (Access = private)
-0123         function cfg = biomassConfig(testCase)
-0124             biomassRxn = testCase.model.rxns{find(testCase.model.c == 1, 1)};
-0125             cfg.biomass_rxn = biomassRxn;
-0126             cfg.proton_met = 'h_c';
-0127             cfg.components = {struct('name','protein', ...
-0128                 'pseudoreaction_name','no_such_pseudoreaction','mass_strategy','mw')};
-0129         end
-0130     end
-0131 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tComparison.html b/doc/testing/function_tests/tComparison.html deleted file mode 100644 index c4777044..00000000 --- a/doc/testing/function_tests/tComparison.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - Description of tComparison - - - - - - - - - -
Home > testing > function_tests > tComparison.m
- - - -

tComparison -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tComparison < RavenTestCase
-0002 % tComparison  Tests for the multi-model comparison functions in comparison/.
-0003 
-0004     methods (Test)
-0005 
-0006         function compareMultipleModelsRuns(testCase)
-0007             modelA = testCase.model; modelA.id = 'modelA';
-0008             modelB = removeReactions(testCase.model, testCase.model.rxns(1:5), ...
-0009                 true, true, true); modelB.id = 'modelB';
-0010             evalc('cs = compareMultipleModels({modelA, modelB});');
-0011             testCase.verifyClass(cs, 'struct');
-0012         end
-0013 
-0014         function compareMultipleModelsReportsIdentityOverlap(testCase)
-0015             % Covers the overlap comparison merged in from the now-removed
-0016             % compareRxnsGenesMetsComps: rxns/mets/genes/eccodes/metNames/equ/uEqu.
-0017             modelA = testCase.model; modelA.id = 'modelA';
-0018             removedRxn = testCase.model.rxns{1};
-0019             modelB = removeReactions(testCase.model, testCase.model.rxns(1), ...
-0020                 true, true, true); modelB.id = 'modelB';
-0021             evalc('cs = compareMultipleModels({modelA, modelB});');
-0022             for field = {'rxns','mets','genes','eccodes','metNames','equ','uEqu'}
-0023                 testCase.verifyTrue(isfield(cs, field{1}));
-0024                 testCase.verifyTrue(isfield(cs.(field{1}), 'comparison'));
-0025                 testCase.verifyTrue(isfield(cs.(field{1}), 'nElements'));
-0026             end
-0027             % modelA has one more reaction than modelB, so the rxns overlap must
-0028             % show a combination where only modelA is included.
-0029             onlyA = cs.rxns.comparison(:,1) & ~cs.rxns.comparison(:,2);
-0030             testCase.verifyTrue(any(cs.rxns.nElements(onlyA) > 0));
-0031             testCase.verifyTrue(any(strcmp(modelA.rxns, removedRxn)));
-0032         end
-0033 
-0034         function diffModelsEqualToItself(testCase)
-0035             r = diffModels(testCase.model, testCase.model);
-0036             testCase.verifyTrue(r.equal);
-0037             testCase.verifyEmpty(r.differences);
-0038         end
-0039 
-0040         function diffModelsDetectsDroppedReaction(testCase)
-0041             modelB = removeReactions(testCase.model, testCase.model.rxns(1), ...
-0042                 true, true, true);
-0043             r = diffModels(testCase.model, modelB);
-0044             testCase.verifyFalse(r.equal);
-0045             testCase.verifyTrue(ismember(testCase.model.rxns{1}, r.rxnsOnlyInA));
-0046             testCase.verifyTrue(any(contains(r.differences, 'reactions only in A')));
-0047         end
-0048 
-0049         function diffModelsDetectsBoundDifference(testCase)
-0050             modelB = testCase.model;
-0051             modelB.lb(1) = modelB.lb(1) - 42;
-0052             r = diffModels(testCase.model, modelB);
-0053             testCase.verifyFalse(r.equal);
-0054             testCase.verifyTrue(any(contains(r.differences, 'bounds')));
-0055         end
-0056 
-0057         function diffModelsStoichWithinToleranceIsEqual(testCase)
-0058             modelB = testCase.model;
-0059             nz = find(modelB.S(:,1), 1);
-0060             modelB.S(nz,1) = modelB.S(nz,1) + 1e-12;
-0061             r = diffModels(testCase.model, modelB, 'stoichTol', 1e-9);
-0062             testCase.verifyTrue(r.equal);
-0063         end
-0064 
-0065         function diffModelsStoichOutsideToleranceIsNotEqual(testCase)
-0066             modelB = testCase.model;
-0067             nz = find(modelB.S(:,1), 1);
-0068             modelB.S(nz,1) = modelB.S(nz,1) + 5;
-0069             r = diffModels(testCase.model, modelB);
-0070             testCase.verifyFalse(r.equal);
-0071             testCase.verifyTrue(any(contains(r.differences, 'coef[')));
-0072         end
-0073 
-0074         function diffModelsGrRuleIsOrderInsensitive(testCase)
-0075             % The improvement over a string compare: "a and b" == "b and a".
-0076             withGpr = find(cellfun(@(r) contains(r,' and '), testCase.model.grRules), 1);
-0077             testCase.assumeNotEmpty(withGpr, 'model has an AND grRule');
-0078             modelB = testCase.model;
-0079             genes = strtrim(strsplit(modelB.grRules{withGpr}, ' and '));
-0080             modelB.grRules{withGpr} = strjoin(fliplr(genes), ' and ');
-0081             r = diffModels(testCase.model, modelB);
-0082             % this reaction must not be reported as a grRule difference
-0083             testCase.verifyFalse(any(contains(r.differences, ...
-0084                 [testCase.model.rxns{withGpr} ': grRule'])));
-0085         end
-0086 
-0087         function diffModelsGrRuleLogicalChangeIsDetected(testCase)
-0088             withGpr = find(cellfun(@(r) contains(r,' and '), testCase.model.grRules), 1);
-0089             testCase.assumeNotEmpty(withGpr, 'model has an AND grRule');
-0090             modelB = testCase.model;
-0091             modelB.grRules{withGpr} = strrep(modelB.grRules{withGpr}, ' and ', ' or ');
-0092             r = diffModels(testCase.model, modelB);
-0093             testCase.verifyFalse(r.equal);
-0094             testCase.verifyTrue(any(contains(r.differences, ...
-0095                 [testCase.model.rxns{withGpr} ': grRule'])));
-0096         end
-0097 
-0098         function diffModelsTruncatesPerCategory(testCase)
-0099             modelB = testCase.model;
-0100             modelB.lb = modelB.lb - 1; % a bound diff on every reaction
-0101             r = diffModels(testCase.model, modelB, 'maxPerCategory', 5);
-0102             testCase.verifyFalse(r.equal);
-0103             testCase.verifyTrue(any(contains(r.differences, 'truncated at 5')));
-0104         end
-0105 
-0106     end
-0107 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tConditions.html b/doc/testing/function_tests/tConditions.html deleted file mode 100644 index 94c738d2..00000000 --- a/doc/testing/function_tests/tConditions.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - Description of tConditions - - - - - - - - - -
Home > testing > function_tests > tConditions.m
- - - -

tConditions -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tConditions < RavenTestCase
-0002 % tConditions  Tests for the condition-application functions in conditions/.
-0003 
-0004     methods (Test)
-0005 
-0006         function applyConditionSetsBounds(testCase)
-0007             rxn = testCase.model.rxns{1};
-0008             condition.bounds = {struct('rxn', rxn, 'lb', 0, 'ub', 0)};
-0009             evalc('m2 = applyCondition(testCase.model, condition);');
-0010             idx = strcmp(m2.rxns, rxn);
-0011             testCase.verifyEqual(m2.ub(idx), 0);
-0012             testCase.verifyEqual(m2.lb(idx), 0);
-0013         end
-0014 
-0015         function applyConditionResetExchangesZeroesUptake(testCase)
-0016             % The prelude reopens exchanges to (0, 1000), i.e. no uptake.
-0017             m = testCase.model;
-0018             [~, exchangeRxns] = getExchangeRxns(m);
-0019             m.lb(exchangeRxns) = -37;
-0020             m.ub(exchangeRxns) = 42;
-0021             condition.prelude.reset_exchanges = 'both';
-0022             evalc('m2 = applyCondition(m, condition);');
-0023             testCase.verifyEqual(unique(m2.lb(exchangeRxns)), 0);
-0024             testCase.verifyEqual(unique(m2.ub(exchangeRxns)), 1000);
-0025         end
-0026 
-0027         function applyConditionRemoveMetZeroesCoefficient(testCase)
-0028             % remove_mets drops a metabolite from the cofactor pseudoreaction.
-0029             m = tConditions.cofactorModel();
-0030             condition.cofactor_pseudoreaction = struct('rxn_id', 'R_cofactor', ...
-0031                 'remove_mets', {{struct('met', 'nadh_c')}});
-0032             evalc('m2 = applyCondition(m, condition);');
-0033             testCase.verifyEqual(full(m2.S(strcmp(m2.mets,'nadh_c'), 1)), 0);
-0034         end
-0035 
-0036         function applyConditionRecomputesChargeBalance(testCase)
-0037             % charge_balance_met takes whatever coefficient makes the
-0038             % pseudoreaction charge neutral, recomputed after the removals.
-0039             m = tConditions.cofactorModel();
-0040             condition.cofactor_pseudoreaction = struct('rxn_id', 'R_cofactor', ...
-0041                 'remove_mets', {{struct('met', 'nadh_c')}}, ...
-0042                 'charge_balance_met', 'h_c');
-0043             evalc('m2 = applyCondition(m, condition);');
-0044             % Left after removing NADH: atp_c (-1, charge -4) and amp_c
-0045             % (+1, charge -2), a residual of +2, so H+ must enter at -2.
-0046             testCase.verifyEqual(full(m2.S(strcmp(m2.mets,'h_c'), 1)), -2);
-0047             % which is to say the reaction ends up charge neutral
-0048             idx = find(m2.S(:,1));
-0049             testCase.verifyEqual(sum(full(m2.S(idx,1)) .* m2.metCharges(idx)), 0);
-0050         end
-0051 
-0052         function applyConditionBiomassDeltaAddsToCoefficient(testCase)
-0053             % A stoichiometry delta adds to the existing coefficient rather
-0054             % than replacing it.
-0055             m = tConditions.cofactorModel();
-0056             m.rxns{2} = 'R_biomass'; m.rxnNames{2} = 'R_biomass';
-0057             before = full(m.S(strcmp(m.mets,'atp_c'), 2));
-0058             condition.biomass_stoichiometry_delta = struct('rxn_id', 'R_biomass', ...
-0059                 'add', {{struct('met', 'atp_c', 'coef', -0.5)}});
-0060             evalc('m2 = applyCondition(m, condition);');
-0061             testCase.verifyEqual(full(m2.S(strcmp(m2.mets,'atp_c'), 2)), before - 0.5);
-0062         end
-0063 
-0064     end
-0065 
-0066     methods (Static, Access = private)
-0067 
-0068         function m = cofactorModel()
-0069             % R_cofactor consumes ATP and NADH; R_other consumes ATP.
-0070             m = struct();
-0071             m.id        = 'cofac';
-0072             m.rxns      = {'R_cofactor';'R_other'};
-0073             m.rxnNames  = {'R_cofactor';'R_other'};
-0074             m.mets      = {'atp_c';'nadh_c';'amp_c';'h_c'};
-0075             m.metNames  = {'ATP';'NADH';'AMP';'H+'};
-0076             m.metComps  = [1;1;1;1];
-0077             m.metCharges= [-4;-2;-2;1];
-0078             m.comps     = {'c'};
-0079             m.compNames = {'cytosol'};
-0080             %              R_cofactor R_other
-0081             m.S = sparse([  -1        -1;    % atp_c
-0082                             -1         0;    % nadh_c
-0083                              1         0;    % amp_c
-0084                              0         0]);  % h_c
-0085             m.lb = [0;0]; m.ub = [1000;1000]; m.rev = [0;0]; m.c = [0;0];
-0086             m.b = zeros(4,1);
-0087             m.genes = {}; m.grRules = {'';''}; m.rxnGeneMat = sparse(2,0);
-0088             m.metFormulas = {'C10';'C21';'C10';'H'};
-0089         end
-0090 
-0091     end
-0092 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tConversion.html b/doc/testing/function_tests/tConversion.html deleted file mode 100644 index 0cad470b..00000000 --- a/doc/testing/function_tests/tConversion.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - Description of tConversion - - - - - - - - - -
Home > testing > function_tests > tConversion.m
- - - -

tConversion -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tConversion < RavenTestCase
-0002 % tConversion  Tests for the model format-conversion functions in conversion/.
-0003 
-0004     methods (Test)
-0005 
-0006         function identifierPrefixRoundTrip(testCase)
-0007             % Adding then removing SBML identifier prefixes is the identity.
-0008             [mp, ~] = addIdentifierPrefix(testCase.model);
-0009             [mr, ~] = removeIdentifierPrefix(mp);
-0010             testCase.verifyEqual(mr.rxns, testCase.model.rxns);
-0011             testCase.verifyEqual(mr.mets, testCase.model.mets);
-0012             testCase.verifyEqual(mr.genes, testCase.model.genes);
-0013         end
-0014 
-0015         function removeIdentifierPrefixSkipsMissingFields(testCase)
-0016             % A model missing an optional field (genes, metNames, rxnNames,
-0017             % id) must not make the default field list throw; it should
-0018             % just be skipped.
-0019             m = rmfield(testCase.model, {'genes','metNames','rxnNames','id'});
-0020             m.rxns = strcat('R_', m.rxns);
-0021             m2 = removeIdentifierPrefix(m);
-0022             testCase.verifyFalse(any(startsWith(m2.rxns, 'R_')));
-0023         end
-0024 
-0025         function ravenCobraWrapperMarksCobra(testCase)
-0026             cobra = ravenCobraWrapper(testCase.model);
-0027             testCase.verifyTrue(isfield(cobra, 'rules'));   % COBRA-only field
-0028         end
-0029 
-0030         function ravenCobraWrapperRoundTrip(testCase)
-0031             cobra = ravenCobraWrapper(testCase.model);
-0032             back = ravenCobraWrapper(cobra);
-0033             testCase.verifyEqual(back.rxns, testCase.model.rxns);
-0034             testCase.verifyNumElements(back.mets, numel(testCase.model.mets));
-0035             testCase.verifyEqual(size(back.S), size(testCase.model.S));
-0036         end
-0037 
-0038         function ravenCobraWrapperGeneFieldsUseGeneMiriams(testCase)
-0039             % Gene annotation fields must come from geneMiriams, not from
-0040             % whatever extractMiriam last returned for metMiriams.
-0041             m = testCase.model;
-0042             m.metMiriams = cell(numel(m.mets),1);
-0043             m.metMiriams{1}.name  = {'hmdb'};
-0044             m.metMiriams{1}.value = {'HMDB00001'};
-0045             m.geneMiriams = cell(numel(m.genes),1);
-0046             m.geneMiriams{1}.name  = {'ncbigene'};
-0047             m.geneMiriams{1}.value = {'12345'};
-0048             evalc('newModel = ravenCobraWrapper(m);');
-0049             testCase.verifyEqual(numel(newModel.geneEntrezID), numel(newModel.genes));
-0050             testCase.verifyEqual(newModel.geneEntrezID{1}, '12345');
-0051         end
-0052 
-0053         function ravenCobraWrapperEscapesRegexGeneNames(testCase)
-0054             % A gene id containing a regex metacharacter ('.') must be
-0055             % matched literally in grRules, not as a wildcard that could
-0056             % also match an unrelated gene id (e.g. 'G.1' vs 'GX1').
-0057             m = testCase.model;
-0058             m.genes{1} = 'G.1';
-0059             m.genes{2} = 'GX1';
-0060             m.grRules{3} = 'GX1';
-0061             evalc('cobra = ravenCobraWrapper(m);');
-0062             testCase.verifyEqual(cobra.rules{3}, 'x(2)');
-0063         end
-0064 
-0065         function standardizeFieldOrderPreservesFields(testCase)
-0066             m2 = standardizeModelFieldOrder(testCase.model);
-0067             testCase.verifyEqual(sort(fieldnames(m2)), sort(fieldnames(testCase.model)));
-0068         end
-0069 
-0070         function standardizeFieldOrderIsIdempotent(testCase)
-0071             m2 = standardizeModelFieldOrder(testCase.model);
-0072             m3 = standardizeModelFieldOrder(m2);
-0073             testCase.verifyEqual(fieldnames(m3), fieldnames(m2));
-0074         end
-0075 
-0076     end
-0077 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tCuration.html b/doc/testing/function_tests/tCuration.html deleted file mode 100644 index a1e64a22..00000000 --- a/doc/testing/function_tests/tCuration.html +++ /dev/null @@ -1,145 +0,0 @@ - - - - Description of tCuration - - - - - - - - - -
Home > testing > function_tests > tCuration.m
- - - -

tCuration -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tCuration < RavenTestCase
-0002 % tCuration  Tests for the table-driven curation functions in curation/.
-0003 %
-0004 %   curateModelFromTables reads its mets/genes/rxns input from tab-delimited
-0005 %   files; the tests below write those files to a temporary directory.
-0006 
-0007     methods (Test)
-0008 
-0009         function curateModelFromTablesKeepsSupersetReaction(testCase)
-0010             % A model reaction that uses the candidate's metabolites *and*
-0011             % others is a different reaction, not a duplicate: adding
-0012             % A + B -> C to a model holding A + B -> C + H must leave the
-0013             % existing reaction alone and add the candidate separately.
-0014             model = tCuration.toyModel();
-0015             [coeffsF, infoF] = tCuration.writeRxnTables(testCase, ...
-0016                 {'A','B','C'}, [-1 -1 1]);
-0017             evalc(['out = curateModelFromTables(model,''none'',''rxnsCoeffs'',' ...
-0018                 'coeffsF,''rxnsInfo'',infoF);']);
-0019 
-0020             testCase.verifyNumElements(out.rxns, 2);
-0021             % The existing reaction keeps its name and its fourth metabolite
-0022             testCase.verifyEqual(out.rxnNames{1}, 'ATP hydrolysis with proton');
-0023             testCase.verifyNumElements(find(out.S(:,1)), 4);
-0024             % The candidate is added as a new reaction
-0025             testCase.verifyEqual(out.rxnNames{2}, 'newRxn');
-0026             testCase.verifyNumElements(find(out.S(:,2)), 3);
-0027         end
-0028 
-0029         function curateModelFromTablesOverwritesExactDuplicate(testCase)
-0030             % An exact stoichiometric match is still treated as the same
-0031             % reaction and has its annotation overwritten.
-0032             model = tCuration.toyModel();
-0033             [coeffsF, infoF] = tCuration.writeRxnTables(testCase, ...
-0034                 {'A','B','C','H'}, [-1 -1 1 1]);
-0035             evalc(['out = curateModelFromTables(model,''none'',''rxnsCoeffs'',' ...
-0036                 'coeffsF,''rxnsInfo'',infoF);']);
-0037 
-0038             testCase.verifyNumElements(out.rxns, 1);
-0039             testCase.verifyEqual(out.rxnNames{1}, 'newRxn');
-0040         end
-0041 
-0042     end
-0043 
-0044     methods (Static, Access = private)
-0045 
-0046         function model = toyModel()
-0047             % Single reaction: A + B -> C + H, all in one compartment.
-0048             model = struct();
-0049             model.id        = 'toy';
-0050             model.rxns      = {'R1'};
-0051             model.rxnNames  = {'ATP hydrolysis with proton'};
-0052             model.mets      = {'m1';'m2';'m3';'m4'};
-0053             model.metNames  = {'A';'B';'C';'H'};
-0054             model.comps     = {'c'};
-0055             model.compNames = {'cytosol'};
-0056             model.metComps  = [1;1;1;1];
-0057             model.S         = sparse([-1;-1;1;1]);
-0058             model.lb        = 0;
-0059             model.ub        = 1000;
-0060             model.rev       = 0;
-0061             model.c         = 0;
-0062             model.b         = zeros(4,1);
-0063             model.genes     = {};
-0064             model.grRules   = {''};
-0065             model.rxnGeneMat= sparse(1,0);
-0066             model.metFormulas = {'';'';'';''};
-0067             model.subSystems  = {{''}};
-0068             model.eccodes     = {''};
-0069             model.rxnNotes    = {''};
-0070             model.rxnReferences = {''};
-0071             model.rxnConfidenceScores = 0;
-0072         end
-0073 
-0074         function [coeffsF, infoF] = writeRxnTables(testCase, metNames, coeffs)
-0075             % Write a one-reaction rxnsCoeffs/rxnsInfo pair for 'newRxn'.
-0076             d = tempname; mkdir(d);
-0077             testCase.addTeardown(@() rmdir(d,'s'));
-0078             coeffsF = fullfile(d,'coeffs.tsv');
-0079             infoF   = fullfile(d,'info.tsv');
-0080 
-0081             fid = fopen(coeffsF,'w');
-0082             fprintf(fid,'rxnIdx\trxnNames\tmetNames\tcomps\tcoefficient\n');
-0083             for i=1:numel(metNames)
-0084                 fprintf(fid,'1\tnewRxn\t%s\tc\t%d\n', metNames{i}, coeffs(i));
-0085             end
-0086             fclose(fid);
-0087 
-0088             fid = fopen(infoF,'w');
-0089             fprintf(fid,['rxnIdx\trxnNames\tgrRules\tlb\tub\trev\tsubSystems\t' ...
-0090                 'eccodes\trxnNotes\trxnReferences\trxnConfidenceScores\n']);
-0091             fprintf(fid,'1\tnewRxn\t\t0\t1000\t0\t\t\t\t\t\n');
-0092             fclose(fid);
-0093         end
-0094 
-0095     end
-0096 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tGapfilling.html b/doc/testing/function_tests/tGapfilling.html deleted file mode 100644 index 3021a5fe..00000000 --- a/doc/testing/function_tests/tGapfilling.html +++ /dev/null @@ -1,488 +0,0 @@ - - - - Description of tGapfilling - - - - - - - - - -
Home > testing > function_tests > tGapfilling.m
- - - -

tGapfilling -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tGapfilling < RavenTestCase
-0002 % tGapfilling  Tests for the gap-analysis and gap-filling functions in gapfilling/.
-0003 
-0004     methods (Test)
-0005 
-0006         function canExchangeConsumeReturnsLogical(testCase)
-0007             out = canExchange(testCase.model, 'consume', testCase.model.mets(1:3));
-0008             testCase.verifyClass(out, 'logical');
-0009             testCase.verifyNumElements(out, 3);
-0010         end
-0011 
-0012         function canExchangeProduceReturnsLogical(testCase)
-0013             out = canExchange(testCase.model, 'produce', testCase.model.mets(1:3));
-0014             testCase.verifyClass(out, 'logical');
-0015             testCase.verifyNumElements(out, 3);
-0016         end
-0017 
-0018         function canExchangeInvalidDirectionErrors(testCase)
-0019             testCase.verifyError( ...
-0020                 @() canExchange(testCase.model, 'neither'), ...
-0021                 'RAVEN:badInput');
-0022         end
-0023 
-0024         function checkProductionReturnsIndices(testCase)
-0025             evalc('notProduced = checkProduction(testCase.model);');
-0026             testCase.verifyClass(notProduced, 'double');
-0027         end
-0028 
-0029         function findLeakMetaboliteProduceReturnsSolution(testCase)
-0030             evalc('[sol, metabolite] = findLeakMetabolite(testCase.model, ''produce'');');
-0031             testCase.verifyNotEmpty(sol);
-0032         end
-0033 
-0034         function findLeakMetaboliteConsumeReturnsSolution(testCase)
-0035             evalc('[sol, metabolite] = findLeakMetabolite(testCase.model, ''consume'');');
-0036             testCase.verifyNotEmpty(sol);
-0037         end
-0038 
-0039         function findLeakMetaboliteInvalidDirectionErrors(testCase)
-0040             testCase.verifyError( ...
-0041                 @() findLeakMetabolite(testCase.model, 'neither'), ...
-0042                 'RAVEN:badInput');
-0043         end
-0044 
-0045         function deprecatedMakeSomethingStillWorks(testCase)
-0046             % The deprecated/ wrapper must keep returning what it always did.
-0047             % Its warning is once-per-session, so it is not asserted here
-0048             % (ordering across tests would decide whether it fires); the
-0049             % warning itself is covered by tUtils/deprecationWarning*.
-0050             evalc('[sol, metabolite] = makeSomething(testCase.model);');
-0051             testCase.verifyNotEmpty(sol);
-0052         end
-0053 
-0054         function deprecatedConsumeSomethingStillWorks(testCase)
-0055             evalc('[sol, metabolite] = consumeSomething(testCase.model);');
-0056             testCase.verifyNotEmpty(sol);
-0057         end
-0058 
-0059         function deprecatedConsumeSomethingKeepsItsOwnArgumentOrder(testCase)
-0060             % consumeSomething's positional order has no allowExcretion,
-0061             % unlike findLeakMetabolite's. Passing its 5th argument
-0062             % (ignoreIntBounds) must reach ignoreIntBounds, not params --
-0063             % forwarding varargin verbatim used to shift it silently.
-0064             evalc(['[sol, metabolite] = consumeSomething(testCase.model, ' ...
-0065                 '[], false, false, [], true);']);
-0066             testCase.verifyNotEmpty(sol);
-0067         end
-0068 
-0069         function fillGapsProducesModel(testCase)
-0070             testCase.assumeMILPSolver();
-0071             modelDB = testCase.model; modelDB.id = 'DB';   % template
-0072             gapModel = removeReactions(modelDB, (1:10));
-0073             gapModel.id = 'gapModel';                      % must differ from template
-0074             evalc('[~,~,~,newModel] = fillGaps(gapModel, modelDB);');
-0075             testCase.verifyClass(newModel, 'struct');
-0076         end
-0077 
-0078         function gapReportRuns(testCase)
-0079             evalc('noFluxRxns = gapReport(testCase.model);');
-0080             testCase.verifyClass(noFluxRxns, 'cell');
-0081         end
-0082 
-0083         function gapReportKeepsPercentInModelName(testCase)
-0084             % A model.name containing "%" must survive intact in the
-0085             % report header, not be truncated by fprintf misreading it as
-0086             % a format directive.
-0087             m = testCase.model;
-0088             m.name = 'ecoli 50% subset';
-0089             out = evalc('gapReport(m);');
-0090             testCase.verifySubstring(out, 'ecoli 50% subset');
-0091         end
-0092 
-0093         function fitTasksShouldFailWarningHasRealNewlineAndPercent(testCase)
-0094             % The SHOULD FAIL warning embeds the task id/description; a
-0095             % literal "\n" must become a real newline (not print as the
-0096             % two characters backslash-n), and a "%" in the task id must
-0097             % survive intact rather than being read as a format directive.
-0098             refModel = testCase.taskTestModel(); refModel.id = 'DB';
-0099             task = testCase.taskTestStruct();
-0100             task.shouldFail = true;
-0101             task.id = 'Task 50% done'; task.description = task.id;
-0102             lastwarn('');
-0103             evalc('fitTasks(refModel, refModel, [], true, [], task);');
-0104             msg = lastwarn();
-0105             testCase.verifySubstring(msg, 'Task 50% done');
-0106             testCase.verifyFalse(contains(msg, '\n'));
-0107         end
-0108 
-0109         function fitTasksProducesModel(testCase)
-0110             testCase.assumeMILPSolver();
-0111             refModel = testCase.taskTestModel(); refModel.id = 'DB';
-0112             gapModel = removeReactions(refModel, {'R2'}); gapModel.id = 'testModel';
-0113             task = testCase.taskTestStruct();
-0114             evalc('[outModel, addedRxns] = fitTasks(gapModel, refModel, [], true, [], task);');
-0115             testCase.verifyClass(outModel, 'struct');
-0116             testCase.verifyTrue(ismember('R2', outModel.rxns));
-0117         end
-0118 
-0119         function fitTasksPrintOutputKeepsPercentInTaskId(testCase)
-0120             % The per-task "Added N reaction(s)" notice embeds the task
-0121             % id/description; a "%" there must survive intact, not be
-0122             % read as an fprintf format directive.
-0123             testCase.assumeMILPSolver();
-0124             refModel = testCase.taskTestModel(); refModel.id = 'DB';
-0125             gapModel = removeReactions(refModel, {'R2'}); gapModel.id = 'testModel';
-0126             task = testCase.taskTestStruct();
-0127             task.id = 'Task 50% done'; task.description = task.id;
-0128             out = evalc('fitTasks(gapModel, refModel, [], true, [], task);');
-0129             testCase.verifySubstring(out, 'Task 50% done');
-0130         end
-0131 
-0132         function fitTasksAcceptsAllMetsShorthands(testCase)
-0133             % ALLMETS and ALLMETSIN[comp] open uptake for a whole model or a
-0134             % whole compartment rather than for a listed metabolite, and
-0135             % write the input bounds along a different route than a named
-0136             % metabolite does. The task needs e[s] out of a[s], and the model
-0137             % is missing R2, the only route from a[s] into the cell.
-0138             testCase.assumeMILPSolver();
-0139             refModel = testCase.taskTestModel(); refModel.id = 'DB';
-0140             gapModel = removeReactions(refModel, {'R2'}); gapModel.id = 'testModel';
-0141 
-0142             % Opening the extracellular compartment is the same thing as
-0143             % naming a[s], so R2 is still the reaction that has to be added
-0144             task = testCase.taskTestStruct();
-0145             task.inputs = {'ALLMETSIN[s]'};
-0146             evalc('outModel = fitTasks(gapModel, refModel, [], false, [], task);');
-0147             testCase.verifyTrue(ismember('R2', outModel.rxns));
-0148 
-0149             % ALLMETS also opens uptake of the cytosolic metabolites, e[c]
-0150             % among them, so the task is satisfiable without crossing the
-0151             % membrane at all and nothing needs to be added
-0152             task.inputs = {'ALLMETS'};
-0153             evalc('outModel = fitTasks(gapModel, refModel, [], false, [], task);');
-0154             testCase.verifyFalse(ismember('R2', outModel.rxns));
-0155         end
-0156 
-0157         function fitTasksRejectsUnknownAllMetsInCompartment(testCase)
-0158             % The compartment named in ALLMETSIN has to exist, otherwise the
-0159             % task silently constrains nothing.
-0160             refModel = testCase.taskTestModel(); refModel.id = 'DB';
-0161             gapModel = removeReactions(refModel, {'R2'}); gapModel.id = 'testModel';
-0162             task = testCase.taskTestStruct();
-0163             task.inputs = {'ALLMETSIN[z]'};
-0164             testCase.verifyError( ...
-0165                 @() fitTasks(gapModel, refModel, [], false, [], task), ...
-0166                 'RAVEN:badInput');
-0167         end
-0168 
-0169         function gapFillFastCoreReturnsLogical(testCase)
-0170             % gapFillFastCore should return a logical vector the same length as model.rxns.
-0171             model = testCase.model;
-0172             coreIdx = 1;   % any single reaction as the core
-0173             active = gapFillFastCore(model, coreIdx, 1e-4);
-0174             testCase.verifyClass(active, 'logical');
-0175             testCase.verifyNumElements(active, numel(model.rxns));
-0176             testCase.verifyTrue(active(coreIdx));  % core reaction must be active
-0177         end
-0178 
-0179         function gapFillFastCoreRejectsTriviallySelfCancelingReversibleCore(testCase)
-0180             % A reversible core reaction touching only its own,
-0181             % otherwise-unused metabolites cannot carry any real
-0182             % steady-state flux (mass balance on those metabolites forces
-0183             % v=0). Forcing both its forward and reverse irreversible
-0184             % copies to >= epsilon must not let them satisfy the core
-0185             % requirement by canceling each other out.
-0186             model = testCase.model;
-0187             r.rxns = {'isolatedRev'};
-0188             r.equations = {'newA[c] <=> newB[c]'};
-0189             evalc('model = addRxns(model, r, 3, ''c'', true);');
-0190             coreIdx = getIndexes(model,'isolatedRev','rxns');
-0191             active = gapFillFastCore(model, coreIdx, 1e-4);
-0192             testCase.verifyFalse(active(coreIdx));
-0193         end
-0194 
-0195         function gapFillSwiftCoreReturnsLogical(testCase)
-0196             % gapFillSwiftCore returns same shape as gapFillFastCore.
-0197             model = testCase.model;
-0198             coreIdx = 1;
-0199             active = gapFillSwiftCore(model, coreIdx, 1e-4);
-0200             testCase.verifyClass(active, 'logical');
-0201             testCase.verifyNumElements(active, numel(model.rxns));
-0202             testCase.verifyTrue(active(coreIdx));
-0203         end
-0204 
-0205         function gapFillSwiftCoreRejectsTriviallySelfCancelingReversibleCore(testCase)
-0206             % Same issue as gapFillFastCore's identically-named test: a
-0207             % reversible core reaction touching only its own,
-0208             % otherwise-unused metabolites cannot carry any real
-0209             % steady-state flux, so forcing both its forward and reverse
-0210             % irreversible copies to >= epsilon must not let them satisfy
-0211             % the core requirement by canceling each other out.
-0212             model = testCase.model;
-0213             r.rxns = {'isolatedRev'};
-0214             r.equations = {'newA[c] <=> newB[c]'};
-0215             evalc('model = addRxns(model, r, 3, ''c'', true);');
-0216             coreIdx = getIndexes(model,'isolatedRev','rxns');
-0217             active = gapFillSwiftCore(model, coreIdx, 1e-4);
-0218             testCase.verifyFalse(active(coreIdx));
-0219         end
-0220 
-0221         function fillGapsIdentifiesOwnRxnsWhenRxnFromPreset(testCase)
-0222             % model.rxnFrom, as e.g. getModelFromHomology output already
-0223             % carries it, must not stop fillGaps from recognising the
-0224             % model's own reactions when checking which of them regain
-0225             % flux via the template.
-0226             testCase.assumeMILPSolver();
-0227             modelDB = testCase.model; modelDB.id = 'DB';
-0228             gapModel = removeReactions(modelDB, (1:10));
-0229             gapModel.id = 'gapModel';
-0230             gapModel.rxnFrom = repmat({'someTemplate'}, numel(gapModel.rxns), 1);
-0231             evalc('[newConnected,~,~,newModel] = fillGaps(gapModel, modelDB);');
-0232             testCase.verifyNotEmpty(newConnected);
-0233             testCase.verifyTrue(all(ismember(gapModel.rxns, newModel.rxns)));
-0234         end
-0235 
-0236         function gapFillFastLPReturnsAddedRxns(testCase)
-0237             % gapFillFastLP should identify database reactions that rescue blocked draft reactions.
-0238             modelDB = testCase.model; modelDB.id = 'DB';
-0239             gapModel = removeReactions(modelDB, modelDB.rxns(1:5));
-0240             gapModel.id = 'gapModel';
-0241             evalc('[addedRxns, newModel, cannotConnect] = gapFillFastLP(gapModel, modelDB, ''verbose'', false);');
-0242             testCase.verifyClass(addedRxns, 'cell');
-0243             testCase.verifyClass(newModel, 'struct');
-0244             testCase.verifyClass(cannotConnect, 'cell');
-0245             % Added reactions must all be present in the returned model and be
-0246             % universal-derived (not original draft reactions). mergeModels
-0247             % appends the template model id to deduplicate reaction IDs shared by
-0248             % draft and DB, so added IDs may carry a '_DB' suffix rather than
-0249             % matching modelDB.rxns verbatim.
-0250             if ~isempty(addedRxns)
-0251                 testCase.verifyTrue(all(ismember(addedRxns, newModel.rxns)));
-0252                 testCase.verifyFalse(any(ismember(addedRxns, gapModel.rxns)));
-0253             end
-0254         end
-0255 
-0256         function gapFillSwiftLPReturnsAddedRxns(testCase)
-0257             % swiftLP variant should produce consistent results with fastLP.
-0258             modelDB = testCase.model; modelDB.id = 'DB';
-0259             gapModel = removeReactions(modelDB, modelDB.rxns(1:5));
-0260             gapModel.id = 'gapModel';
-0261             evalc('[addedRxns, newModel, ~] = gapFillFastLP(gapModel, modelDB, ''variant'', ''swift'', ''verbose'', false);');
-0262             testCase.verifyClass(addedRxns, 'cell');
-0263             testCase.verifyClass(newModel, 'struct');
-0264         end
-0265 
-0266         function gapFillTopologicalIdentifiesGaps(testCase)
-0267             % gapFillTopological should return a struct with the expected fields.
-0268             modelDB = testCase.model; modelDB.id = 'DB';
-0269             gapModel = removeReactions(modelDB, modelDB.rxns(1:5));
-0270             gapModel.id = 'gapModel';
-0271             evalc('result = gapFillTopological(gapModel, modelDB, ''verbose'', false);');
-0272             testCase.verifyClass(result, 'struct');
-0273             testCase.verifyTrue(isfield(result, 'reachableMets'));
-0274             testCase.verifyTrue(isfield(result, 'blockedMets'));
-0275             testCase.verifyTrue(isfield(result, 'candidateRxns'));
-0276             testCase.verifyTrue(isfield(result, 'pruningFraction'));
-0277             testCase.verifyClass(result.reachableMets, 'logical');
-0278             testCase.verifyNumElements(result.reachableMets, numel(gapModel.mets));
-0279         end
-0280 
-0281         function gapFillTopologicalReachesThroughInExchange(testCase)
-0282             % A -> B -> C fed by an 'in' exchange on A. addExchangeRxns writes
-0283             % 'in' as "=> A", which has lb = 0, so a seed test of lb < 0 finds
-0284             % no uptake at all and every metabolite comes back blocked.
-0285             m = testCase.chainModel();
-0286             m = addExchangeRxns(m, 'in', {'A'});
-0287             evalc(['result = gapFillTopological(m, m, ''targets'', ' ...
-0288                 '{''A'',''B'',''C''}, ''verbose'', false);']);
-0289             testCase.verifyTrue(all(result.reachableMets));
-0290             testCase.verifyEmpty(result.blockedMets);
-0291         end
-0292 
-0293         function gapFillTopologicalReachesThroughReversibleExchange(testCase)
-0294             % "A <=>" supplies A too, but is 'reverse' rather than 'uptake'.
-0295             m = testCase.chainModel();
-0296             m = addExchangeRxns(m, 'both', {'A'});
-0297             evalc(['result = gapFillTopological(m, m, ''targets'', ' ...
-0298                 '{''A'',''B'',''C''}, ''verbose'', false);']);
-0299             testCase.verifyTrue(all(result.reachableMets));
-0300         end
-0301 
-0302         function gapFillTopologicalBlocksWithoutExchange(testCase)
-0303             % Without any exchange nothing is producible: the positive control
-0304             % for the two tests above.
-0305             m = testCase.chainModel();
-0306             evalc(['result = gapFillTopological(m, m, ''seeds'', {}, ''targets'', ' ...
-0307                 '{''A'',''B'',''C''}, ''verbose'', false);']);
-0308             testCase.verifyFalse(any(result.reachableMets));
-0309             testCase.verifyNumElements(result.blockedMets, 3);
-0310         end
-0311 
-0312         function gapFillMILPRepairsGrowth(testCase)
-0313             testCase.assumeMILPSolver();
-0314             modelDB = testCase.model; modelDB.id = 'DB';
-0315             gapModel = removeReactions(modelDB, modelDB.rxns(1:5));
-0316             gapModel.id = 'gapModel';
-0317             evalc('[addedRxns, reversedRxns, newModel, exitFlag] = gapFillMILP(gapModel, modelDB, ''verbose'', false);');
-0318             testCase.verifyClass(addedRxns, 'cell');
-0319             testCase.verifyClass(reversedRxns, 'cell');
-0320             testCase.verifyClass(newModel, 'struct');
-0321             testCase.verifyEqual(exitFlag, 1);
-0322             % The repaired model should be able to produce objective flux.
-0323             sol = solveLP(newModel);
-0324             testCase.verifyNotEmpty(sol.f);
-0325             testCase.verifyGreaterThan(sol.f, 0);
-0326         end
-0327 
-0328         function gapFillMILPHandlesUnboundedUniversalReaction(testCase)
-0329             % A universal reaction with ub=Inf (RAVEN's own default when no
-0330             % upper-bound annotation is set) must not put an Inf
-0331             % coefficient into the coupling constraint matrix -- SCIP
-0332             % rejects that outright, and Inf*0 is NaN even where it
-0333             % wouldn't be rejected.
-0334             testCase.assumeMILPSolver();
-0335             gapModel = struct();
-0336             gapModel.id='gapModel'; gapModel.rxns={'R1';'ExB'}; gapModel.rxnNames=gapModel.rxns;
-0337             gapModel.mets={'a';'b'}; gapModel.metNames=gapModel.mets; gapModel.metComps=[1;1];
-0338             gapModel.comps={'c'}; gapModel.compNames={'c'};
-0339             gapModel.S=sparse([-1 0; 1 -1]); % R1: a=>b   ExB: b=>
-0340             gapModel.lb=[0;0]; gapModel.ub=[1000;1000]; gapModel.rev=[0;0];
-0341             gapModel.c=[1;0]; gapModel.b=zeros(2,1);
-0342             gapModel.genes={}; gapModel.grRules={'';''}; gapModel.rxnGeneMat=sparse(2,0);
-0343 
-0344             modelDB = struct();
-0345             modelDB.id='DB'; modelDB.rxns={'ExA'}; modelDB.rxnNames=modelDB.rxns;
-0346             modelDB.mets={'a'}; modelDB.metNames=modelDB.mets; modelDB.metComps=1;
-0347             modelDB.comps={'c'}; modelDB.compNames={'c'};
-0348             modelDB.S=sparse(1,1); modelDB.S(1,1)=1;
-0349             modelDB.lb=0; modelDB.ub=Inf; modelDB.rev=0; modelDB.c=0; modelDB.b=0;
-0350             modelDB.genes={}; modelDB.grRules={''}; modelDB.rxnGeneMat=sparse(1,0);
-0351 
-0352             evalc(['[addedRxns,~,newModel,exitFlag] = gapFillMILP(gapModel, modelDB, ' ...
-0353                 '''verbose'', false);']);
-0354             testCase.verifyEqual(exitFlag, 1);
-0355             testCase.verifyEqual(addedRxns, {'ExA'});
-0356             sol = solveLP(newModel);
-0357             testCase.verifyGreaterThan(sol.f, 0);
-0358         end
-0359 
-0360         function gapFillMILPKeepsRenamedUniversalReaction(testCase)
-0361             % A universal reaction whose id collides with an unrelated
-0362             % draft reaction gets renamed by mergeModels; that renamed
-0363             % reaction must still end up in addedRxns and in newModel, not
-0364             % be silently dropped by a lookup keyed on its original name.
-0365             testCase.assumeMILPSolver();
-0366             gapModel = struct();
-0367             gapModel.id='gapModel'; gapModel.rxns={'R1';'Rname'}; gapModel.rxnNames=gapModel.rxns;
-0368             gapModel.mets={'a';'b'}; gapModel.metNames=gapModel.mets; gapModel.metComps=[1;1];
-0369             gapModel.comps={'c'}; gapModel.compNames={'c'};
-0370             gapModel.S=sparse([-1 0; 1 -1]); % R1: a=>b   Rname (draft): b=>, irrelevant to growth
-0371             gapModel.lb=[0;0]; gapModel.ub=[1000;1000]; gapModel.rev=[0;0];
-0372             gapModel.c=[1;0]; gapModel.b=zeros(2,1);
-0373             gapModel.genes={}; gapModel.grRules={'';''}; gapModel.rxnGeneMat=sparse(2,0);
-0374 
-0375             modelDB = struct();
-0376             % Same id "Rname" as the draft's, but a completely different
-0377             % reaction: this is the one actually needed to enable growth.
-0378             modelDB.id='DB'; modelDB.rxns={'Rname'}; modelDB.rxnNames=modelDB.rxns;
-0379             modelDB.mets={'a'}; modelDB.metNames=modelDB.mets; modelDB.metComps=1;
-0380             modelDB.comps={'c'}; modelDB.compNames={'c'};
-0381             modelDB.S=sparse(1,1); modelDB.S(1,1)=1; % =>a
-0382             modelDB.lb=0; modelDB.ub=1000; modelDB.rev=0; modelDB.c=0; modelDB.b=0;
-0383             modelDB.genes={}; modelDB.grRules={''}; modelDB.rxnGeneMat=sparse(1,0);
-0384 
-0385             evalc(['[addedRxns,~,newModel,exitFlag] = gapFillMILP(gapModel, modelDB, ' ...
-0386                 '''verbose'', false);']);
-0387             testCase.verifyEqual(exitFlag, 1);
-0388             testCase.verifyNotEmpty(addedRxns);
-0389             sol = solveLP(newModel);
-0390             testCase.verifyGreaterThan(sol.f, 0);
-0391         end
-0392 
-0393         function gapFillMILPReversesDirectionality(testCase)
-0394             % gapFillMILP should reverse a reaction whose directionality is wrong.
-0395             testCase.assumeMILPSolver();
-0396             model = testCase.model;
-0397             modelDB = model; modelDB.id = 'DB';
-0398             gapModel = removeReactions(model, model.rxns(1:3));
-0399             gapModel.id = 'gapModel';
-0400             evalc('[~, reversedRxns, ~, ~] = gapFillMILP(gapModel, modelDB, ''verbose'', false);');
-0401             testCase.verifyClass(reversedRxns, 'cell');
-0402         end
-0403 
-0404         function fillGapsDispatchesFastLP(testCase)
-0405             % fillGaps with 'algorithm','fastLP' should reach gapFillFastLP.
-0406             modelDB = testCase.model; modelDB.id = 'DB';
-0407             gapModel = removeReactions(modelDB, modelDB.rxns(1:5));
-0408             gapModel.id = 'gapModel';
-0409             evalc('[~,cannotConnect,addedRxns,newModel,exitFlag] = fillGaps(gapModel, modelDB, ''algorithm'', ''fastLP'', ''verbose'', false);');
-0410             testCase.verifyClass(newModel, 'struct');
-0411             testCase.verifyEqual(exitFlag, 1);
-0412         end
-0413 
-0414         function fillGapsDispatchesGapfillMILP(testCase)
-0415             testCase.assumeMILPSolver();
-0416             modelDB = testCase.model; modelDB.id = 'DB';
-0417             gapModel = removeReactions(modelDB, modelDB.rxns(1:5));
-0418             gapModel.id = 'gapModel';
-0419             evalc('[~,~,addedRxns,newModel,exitFlag] = fillGaps(gapModel, modelDB, ''algorithm'', ''gapfillMILP'', ''verbose'', false);');
-0420             testCase.verifyClass(newModel, 'struct');
-0421             testCase.verifyEqual(exitFlag, 1);
-0422         end
-0423 
-0424     end
-0425 
-0426     methods (Access = private)
-0427         function m = chainModel(~)
-0428             % A -> B -> C, no exchanges; the caller adds the ones it needs.
-0429             m = struct();
-0430             m.rxns = {'R1';'R2'}; m.rxnNames = {'R1';'R2'};
-0431             m.mets = {'A';'B';'C'}; m.metNames = {'A';'B';'C'}; m.metComps = [1;1;1];
-0432             m.comps = {'c'}; m.compNames = {'c'};
-0433             m.S = sparse([-1 0; 1 -1; 0 1]);
-0434             m.lb = [0;0]; m.ub = [1000;1000]; m.rev = [0;0]; m.c = [0;1];
-0435             m.b = [0;0;0];
-0436             m.genes = {}; m.grRules = {'';''}; m.rxnGeneMat = sparse(2,0);
-0437         end
-0438     end
-0439 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tGenomeData.html b/doc/testing/function_tests/tGenomeData.html deleted file mode 100644 index 8eb275ef..00000000 --- a/doc/testing/function_tests/tGenomeData.html +++ /dev/null @@ -1,145 +0,0 @@ - - - - Description of tGenomeData - - - - - - - - - -
Home > testing > function_tests > tGenomeData.m
- - - -

tGenomeData -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tGenomeData < RavenTestCase
-0002 % tGenomeData  Tests for the genome-data utilities used in homology-based
-0003 %              reconstruction (downloadGenomeData, getGeneData,
-0004 %              processProteinFastaFile, renameModelGenes).
-0005 %
-0006 %   Parsing and renaming are tested offline with small GFF3/FASTA fixtures
-0007 %   (one eukaryote, one prokaryote). The NCBI download is guarded and skips
-0008 %   when there is no internet connection.
-0009 
-0010     methods (Access = protected)
-0011         function d = fixtureDir(testCase)
-0012             d = fullfile(testCase.ravenRoot,'testing','function_tests', ...
-0013                 'test_data','genomeData');
-0014         end
-0015     end
-0016 
-0017     methods (Test)
-0018 
-0019         function getGeneDataReadsEukaryote(testCase)
-0020             % gene -> mRNA -> CDS: the protein accession must come from the
-0021             % CDS protein_id (NP_), not the mRNA transcript (NM_), and the
-0022             % two CDS lines of one protein collapse to a single row.
-0023             T = getGeneData(fullfile(testCase.fixtureDir(),'euk.gff'));
-0024             testCase.verifyEqual(height(T), 2);
-0025             i1 = find(strcmp(T.locus_tag,'LT1'),1);
-0026             testCase.verifyEqual(T.old_locus_tag{i1}, 'OLD1');
-0027             testCase.verifyEqual(T.GeneID{i1}, '111');
-0028             testCase.verifyEqual(T.gene_name{i1}, 'AAA');
-0029             testCase.verifyEqual(T.GenBank_protein{i1}, 'NP_111.1');
-0030             i2 = find(strcmp(T.locus_tag,'LT2'),1);
-0031             testCase.verifyEqual(T.GenBank_protein{i2}, 'NP_222.1');
-0032         end
-0033 
-0034         function getGeneDataReadsProkaryote(testCase)
-0035             % gene -> CDS directly.
-0036             T = getGeneData(fullfile(testCase.fixtureDir(),'prok.gff'));
-0037             testCase.verifyEqual(height(T), 1);
-0038             testCase.verifyEqual(T.locus_tag{1}, 'b0001');
-0039             testCase.verifyEqual(T.GeneID{1}, '333');
-0040             testCase.verifyEqual(T.gene_name{1}, 'ccc');
-0041             testCase.verifyEqual(T.GenBank_protein{1}, 'WP_333.1');
-0042         end
-0043 
-0044         function getGeneDataWritesNoFileByDefault(testCase)
-0045             prevFiles = dir('geneData.tsv');
-0046             getGeneData(fullfile(testCase.fixtureDir(),'prok.gff'));
-0047             testCase.verifyEqual(numel(dir('geneData.tsv')), numel(prevFiles));
-0048         end
-0049 
-0050         function processProteinFastaFileRenamesHeaders(testCase)
-0051             T   = getGeneData(fullfile(testCase.fixtureDir(),'euk.gff'));
-0052             out = processProteinFastaFile(fullfile(testCase.fixtureDir(), ...
-0053                 'euk_protein.faa'), T, 'locus_tag', tempdir);
-0054             testCase.addTeardown(@() delete(out));
-0055             fa = readFasta(out);
-0056             headers = {fa.Header};
-0057             % NP_111.1 -> LT1, NP_222.1 -> LT2, NP_999.9 has no match and is kept
-0058             testCase.verifyTrue(ismember('LT1', headers));
-0059             testCase.verifyTrue(ismember('LT2', headers));
-0060             testCase.verifyTrue(any(startsWith(headers, 'NP_999.9')));
-0061         end
-0062 
-0063         function renameModelGenesRenamesGenesAndGrRules(testCase)
-0064             model.id         = 'test';
-0065             model.rxns       = {'r1'; 'r2'};
-0066             model.genes      = {'LT1'; 'LT2'};
-0067             model.grRules    = {'LT1'; 'LT1 or LT2'};
-0068             model.rxnGeneMat = sparse([1 0; 1 1]);
-0069 
-0070             T = getGeneData(fullfile(testCase.fixtureDir(),'euk.gff'));
-0071             evalc('m2 = renameModelGenes(model, T, ''locus_tag'', ''gene_name'');');
-0072 
-0073             testCase.verifyEqual(sort(m2.genes), {'AAA'; 'BBB'});
-0074             testCase.verifyFalse(any(contains(m2.grRules, 'LT1')));
-0075             testCase.verifyTrue(any(contains(m2.grRules, 'AAA')));
-0076             testCase.verifyTrue(any(contains(m2.grRules, 'BBB')));
-0077             testCase.verifyEqual(size(m2.rxnGeneMat,2), numel(m2.genes));
-0078         end
-0079 
-0080         function downloadGenomeDataDownloads(testCase)
-0081             % Network test: download a small genome from NCBI Datasets.
-0082             % Skipped when there is no internet connection.
-0083             d = tempname; mkdir(d);
-0084             testCase.addTeardown(@() rmdir(d,'s'));
-0085             try
-0086                 [gff, faa] = downloadGenomeData('GCF_000005845.2', d, false);
-0087             catch ME
-0088                 testCase.assumeFail(['Skipped, likely no internet connection: ' ME.message]);
-0089                 return
-0090             end
-0091             testCase.verifyTrue(isfile(gff));
-0092             testCase.verifyTrue(isfile(faa));
-0093         end
-0094 
-0095     end
-0096 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tINIT.html b/doc/testing/function_tests/tINIT.html deleted file mode 100644 index 9a325284..00000000 --- a/doc/testing/function_tests/tINIT.html +++ /dev/null @@ -1,959 +0,0 @@ - - - - Description of tINIT - - - - - - - - - - - - - -

tINIT -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tINIT < RavenTestCase
-0002 % tINIT  Tests for the ftINIT context-specific modelling functions in INIT/.
-0003 %
-0004 %   The MILP-based pipeline is exercised end-to-end on small synthetic models
-0005 %   (built by the local getTstModel* helpers, illustrated in tINITtestInfo/):
-0006 %   ftINITPipelineRuns / ftINITWithTaskRuns / ftINITMetabolomicsRuns /
-0007 %   ftINITFullVsThreeStepRuns cover prepINITModel + ftINIT and the internal
-0008 %   ftINITInternalAlg / groupRxnScores helpers, fitTasksPreMerged* covers the
-0009 %   gap-filling step (fitTasks with gapFillMode 'preMerged', ftINIT's mode),
-0010 %   all guarded on a MILP solver (Gurobi/SCIP). The self-contained helpers
-0011 %   (mergeLinear, groupRxnScores, reverseRxns, rescaleModelForINIT,
-0012 %   scoreModel, getExprForRxnScore) are tested directly against known
-0013 %   results.
-0014 
-0015     methods (Test)
-0016 
-0017         function getINITStepsReturnsSteps(testCase)
-0018             steps = getINITSteps([], '1+1');
-0019             testCase.verifyNotEmpty(steps);
-0020         end
-0021 
-0022         function getExprForRxnScoreRuns(testCase)
-0023             expr = getExprForRxnScore(rand(10, 1), 1);
-0024             testCase.verifyNotEmpty(expr);
-0025         end
-0026 
-0027         function removeLowScoreGenesRuns(testCase)
-0028             geneScores = randn(numel(testCase.model.genes), 1);
-0029             evalc('m2 = removeLowScoreGenes(testCase.model, geneScores);');
-0030             testCase.verifyClass(m2, 'struct');
-0031         end
-0032 
-0033         function removeLowScoreGenesKeepsFieldsAligned(testCase)
-0034             % getGenesFromGrRules returns a sorted gene list, but the
-0035             % annotation fields are trimmed with a mask in the original gene
-0036             % order. If newModel.genes took the sorted order, every annotation
-0037             % field would shift relative to it. Use deliberately unsorted genes
-0038             % so sorted != original, and drop one isozyme gene.
-0039             m = struct();
-0040             m.id = 'test';
-0041             m.rxns = {'R1'}; m.rxnNames = {'R1'};
-0042             m.mets = {'A';'B'}; m.metNames = {'A';'B'}; m.metComps = [1;1];
-0043             m.comps = {'c'}; m.compNames = {'c'};
-0044             m.S = sparse([-1;1]); m.lb = 0; m.ub = 1000; m.rev = 0; m.c = 0;
-0045             m.b = [0;0];
-0046             m.genes = {'G3';'G1';'G2'};                      % unsorted
-0047             m.geneShortNames = {'short3';'short1';'short2'}; % aligned to genes
-0048             m.grRules = {'G3 or G1 or G2'};
-0049             m.rxnGeneMat = sparse([1 1 1]);
-0050             scores = [1; -1; 1];   % G1 (negative) is dropped from the isozyme
-0051             evalc('[mm, removed] = removeLowScoreGenes(m, scores);');
-0052 
-0053             testCase.verifyEqual(removed, {'G1'});
-0054             % Each surviving gene keeps its own short name.
-0055             for k = 1:numel(mm.genes)
-0056                 want = strrep(mm.genes{k}, 'G', 'short');
-0057                 testCase.verifyEqual(mm.geneShortNames{k}, want);
-0058             end
-0059             % rxnGeneMat columns must correspond to mm.genes, and R1 still uses
-0060             % both surviving genes.
-0061             testCase.verifyEqual(numel(mm.genes), size(mm.rxnGeneMat, 2));
-0062             testCase.verifyEqual(full(mm.rxnGeneMat), ones(1, numel(mm.genes)));
-0063         end
-0064 
-0065         function reverseRxnsRuns(testCase)
-0066             % R1 = '=> a[s]'; R3 = 'a[c] <=> b[c] + c[c]'
-0067             testModel = getTstModel();
-0068             tmpModel  = reverseRxns(testModel, {'R1';'R3'});
-0069             res       = constructEquations(tmpModel, {'R1';'R3'});
-0070             expRes    = {'a[s] => ';'b[c] + c[c] <=> a[c]'};
-0071             testCase.verifyTrue(all(strcmp(res, expRes)));
-0072         end
-0073 
-0074         function rescaleModelForINITRuns(testCase)
-0075             miniModel      = struct();
-0076             miniModel.S    = [1,1000;-1,-40];
-0077             miniModel.rxns = {'1';'2'};
-0078             miniModel.mets = {'1';'2'};
-0079             res = rescaleModelForINIT(miniModel, 10);
-0080             testCase.verifyTrue(abs(res.S(1,2) - res.S(2,2)*-10) < 10^-6);
-0081             testCase.verifyTrue(abs((abs(res.S(1,2)) + abs(res.S(2,2)))/2) - 1 < 10^-6);
-0082         end
-0083 
-0084         function mergeLinearGroupsAndScores(testCase)
-0085             % mergeLinear merges linear reaction chains; groupRxnScores then
-0086             % aggregates the per-reaction scores onto the merged groups.
-0087             testModel     = getTstModel();
-0088             testRxnScores = getTstModelRxnScores();
-0089             evalc('[reducedModel,origRxnIds,groupIds,reversedRxns] = mergeLinear(testModel, {});');
-0090             % {R1,R2}, {R3,R5}, {R4,R6}, {R7,R8}, {R9,R10} are merged
-0091             testCase.verifyTrue(all(groupIds == [1;1;2;3;2;3;4;4;5;5]));
-0092             % R1, R3, R4, R7 irreversible, R9 reversible
-0093             testCase.verifyTrue(all(reducedModel.rev == [0;0;0;0;1]));
-0094             testCase.verifyTrue(all(reducedModel.lb == [0;0;0;0;-1000]));
-0095 
-0096             newRxnScores = groupRxnScores(reducedModel, testRxnScores, ...
-0097                 origRxnIds, groupIds, ismember(origRxnIds, {'R1';'R2';'R8'}));
-0098             testCase.verifyTrue(all(newRxnScores == [0;-0.5;7.5;-1;0.5]));
-0099 
-0100             % testModel4 has reactions that are not all linearly merged
-0101             testModel4 = getTstModel4();
-0102             evalc('[reducedModel,origRxnIds,groupIds,reversedRxns] = mergeLinear(testModel4, {});');
-0103             % {R5,R6}, {R7,R8}, {R9,R10} are merged
-0104             testCase.verifyTrue(all(groupIds == [0;0;0;0;1;1;2;2;3;3;0]));
-0105             testCase.verifyTrue(all(reducedModel.rev == [1;0;1;0;1;1;0;0]));
-0106             % some reactions flip direction when turned irreversible
-0107             testCase.verifyTrue(strcmp(constructEquations(reducedModel, 'R9'), 'g[s] => '));
-0108             testCase.verifyTrue(all(find(reversedRxns) == [6;9]));
-0109         end
-0110 
-0111         function scoreModelRuns(testCase)
-0112             arrayData.genes   = testCase.model.genes;
-0113             arrayData.tissues = {'t1'};
-0114             arrayData.levels  = abs(randn(numel(testCase.model.genes), 1)) + 1;
-0115             arrayData.threshold = 1;
-0116             evalc('rxnScores = scoreModel(testCase.model, [], arrayData, ''t1'', []);');
-0117             testCase.verifyNumElements(rxnScores, numel(testCase.model.rxns));
-0118         end
-0119 
-0120         function scoreModelHpaUnmeasuredCellTypeIsNotZero(testCase)
-0121             % A gene not detected in one cell type of a tissue and not
-0122             % measured in the other must keep its 'Not detected' score. An
-0123             % unmeasured cell type is not a measurement of zero, and zero
-0124             % ranks above 'Not detected' (-8), so scoring it as such would
-0125             % also keep the gene from ever being pruned.
-0126             m = testCase.model;
-0127             hpaData.genes      = m.genes(1);
-0128             hpaData.tissues    = {'liver';'liver'};
-0129             hpaData.celltypes  = {'hepatocyte';'kupffer'};
-0130             hpaData.levels     = {'Not detected'};
-0131             hpaData.gene2Level = sparse(1,2);
-0132             hpaData.gene2Level(1,1) = 1;   % not detected in hepatocyte
-0133                                            % not measured in kupffer
-0134 
-0135             evalc(['[~,~,hpaScores] = scoreModel(m, hpaData, [], ' ...
-0136                 '''liver'', ''multipleCellScoring'', ''max'');']);
-0137             testCase.verifyEqual(hpaScores(1), -8, 'AbsTol', 1e-9);
-0138 
-0139             % The average is likewise taken over the measurements that exist
-0140             evalc(['[~,~,hpaScores] = scoreModel(m, hpaData, [], ' ...
-0141                 '''liver'', ''multipleCellScoring'', ''average'');']);
-0142             testCase.verifyEqual(hpaScores(1), -8, 'AbsTol', 1e-9);
-0143         end
-0144 
-0145         function scoreModelExactScores(testCase)
-0146             % scoreModel must reproduce the known reaction scores for
-0147             % the reference model prepared by prepINITModel.
-0148             testCase.assumeMILPSolver();
-0149             testModel = getTstModel();
-0150             evalc('prepData = prepINITModel(testModel, {}, {}, false, {}, ''s'');');
-0151             arrayData.genes     = testModel.genes;
-0152             arrayData.tissues   = {'a'};
-0153             arrayData.levels    = getExprForRxnScore(getTstModelRxnScores());
-0154             arrayData.threshold = 1;
-0155             rxnScores = scoreModel(prepData.refModel, [], arrayData, arrayData.tissues{1}, []);
-0156             testCase.verifyTrue(all(abs(rxnScores - getTstModelRxnScores()) < 10^-10));
-0157         end
-0158 
-0159         function getINITModelRuns(testCase)
-0160             % The tINIT path is legacy but supported, and until now nothing
-0161             % exercised it: a refactor of the machinery it shares with ftINIT
-0162             % (checkTasks, simplifyModel, solveLP, removeReactions) could
-0163             % break it without any test noticing.
-0164             testCase.assumeMILPSolver();
-0165             refModel = getTstModel();
-0166             % getINITModel wants the closed form, which this fixture is not
-0167             % built in; its own documentation gives this as the way to add it
-0168             refModel.unconstrained = false(numel(refModel.mets),1);
-0169 
-0170             arrayData.genes     = refModel.genes;
-0171             arrayData.tissues   = {'a'};
-0172             arrayData.levels    = getExprForRxnScore(getTstModelRxnScores());
-0173             arrayData.threshold = 1;
-0174 
-0175             evalc(['m = getINITModel(refModel, arrayData.tissues{1}, ' ...
-0176                 '''arrayData'', arrayData, ''printReport'', false);']);
-0177             testCase.verifyClass(m, 'struct');
-0178             testCase.verifyNotEmpty(m.rxns);
-0179             testCase.verifyTrue(all(ismember(m.rxns, refModel.rxns)));
-0180             % R4 and R10 carry the highest scores, so neither should be cut
-0181             testCase.verifyTrue(all(ismember({'R4';'R10'}, m.rxns)));
-0182         end
-0183 
-0184         function ftINITPipelineRuns(testCase)
-0185             % prepINITModel + ftINIT end-to-end on testModel without tasks.
-0186             testCase.assumeMILPSolver();
-0187             testModel  = getTstModel();
-0188             testParams = struct();
-0189             evalc('prepData = prepINITModel(testModel, {}, {}, false, {}, ''s'');');
-0190             % R1 and R8 are exchange reactions without a GPR
-0191             testCase.verifyTrue(all(strcmp( ...
-0192                 prepData.refModel.rxns(prepData.toIgnoreExch), {'R1';'R8'})));
-0193 
-0194             arrayData.genes     = testModel.genes;
-0195             arrayData.tissues   = {'a'};
-0196             arrayData.levels    = getExprForRxnScore(getTstModelRxnScores());
-0197             arrayData.threshold = 1;
-0198 
-0199             evalc(['resModel = ftINIT(prepData,arrayData.tissues{1},[],[],' ...
-0200                 'arrayData,[],getINITSteps(),true,true,testParams,false);']);
-0201             % R1 and R8 are added (no GPR, exchange); R2 (transport, no GPR) is
-0202             % removed by the standard third step; R7 is not added (it has a GPR).
-0203             testCase.verifyTrue(all(strcmp(resModel.rxns, ...
-0204                 {'R1';'R4';'R6';'R8';'R9';'R10'})));
-0205 
-0206             % make R7 and R10 spontaneous
-0207             evalc('prepData = prepINITModel(testModel, {}, {''R7'';''R10''}, false, {}, ''s'');');
-0208             testCase.verifyTrue(all(strcmp( ...
-0209                 prepData.refModel.rxns(prepData.toIgnoreExch | prepData.toIgnoreSpont), ...
-0210                 {'R1';'R7';'R8';'R10'})));
-0211             evalc(['resModel = ftINIT(prepData,arrayData.tissues{1},[],[],' ...
-0212                 'arrayData,[],getINITSteps(),true,true,testParams,false);']);
-0213             % the model now takes the "correct" path (incl. R2), skipping R9/R10
-0214             testCase.verifyTrue(all(strcmp(resModel.rxns, ...
-0215                 {'R1';'R2';'R4';'R6';'R7';'R8'})));
-0216         end
-0217 
-0218         function ftINITResolveTiesAndProveAbsGapStillRunCorrectly(testCase)
-0219             % resolveTies and proveAbsGap are threaded through ftINIT ->
-0220             % ftINITInternalAlg (main extraction) and, for resolveTies, also
-0221             % into fitTasks' gap-filling (raven-gecko-parity#104). This
-0222             % fixture (same as ftINITPipelineRuns above) is too small to carry
-0223             % a genuine tie or to expose the escalation's near-zero-objective
-0224             % gap defect -- both are covered at genome scale in the PR
-0225             % description -- so this only checks that turning either or both
-0226             % on does not change the correct result or throw.
-0227             % proveAbsGap is deliberately set to 0.05 here, not the
-0228             % genome-scale-recommended 1.0: this fixture's reaction scores
-0229             % (getTstModelRxnScores, as small as 0.5) make 1.0 genuinely loose
-0230             % enough to accept a worse, different incumbent by design -- a
-0231             % real property of a fixed absolute gap, not a bug -- so a value
-0232             % below the model's own score granularity is what actually proves
-0233             % the true optimum here.
-0234             testCase.assumeMILPSolver();
-0235             testModel  = getTstModel();
-0236             testParams = struct();
-0237             evalc('prepData = prepINITModel(testModel, {}, {}, false, {}, ''s'');');
-0238 
-0239             arrayData.genes     = testModel.genes;
-0240             arrayData.tissues   = {'a'};
-0241             arrayData.levels    = getExprForRxnScore(getTstModelRxnScores());
-0242             arrayData.threshold = 1;
-0243 
-0244             expected = {'R1';'R4';'R6';'R8';'R9';'R10'};
-0245 
-0246             evalc(['resTies = ftINIT(prepData,arrayData.tissues{1},[],[],' ...
-0247                 'arrayData,[],getINITSteps(),true,true,testParams,false,' ...
-0248                 '''resolveTies'',true);']);
-0249             testCase.verifyTrue(all(strcmp(resTies.rxns, expected)));
-0250 
-0251             evalc(['resGap = ftINIT(prepData,arrayData.tissues{1},[],[],' ...
-0252                 'arrayData,[],getINITSteps(),true,true,testParams,false,' ...
-0253                 '''proveAbsGap'',0.05);']);
-0254             testCase.verifyTrue(all(strcmp(resGap.rxns, expected)));
-0255 
-0256             evalc(['resBoth = ftINIT(prepData,arrayData.tissues{1},[],[],' ...
-0257                 'arrayData,[],getINITSteps(),true,true,testParams,false,' ...
-0258                 '''resolveTies'',true,''proveAbsGap'',0.05);']);
-0259             testCase.verifyTrue(all(strcmp(resBoth.rxns, expected)));
-0260         end
-0261 
-0262         function ftINITWithTaskRuns(testCase)
-0263             % A task requiring e[s] from a[s] forces R2 and R7 to be essential.
-0264             testCase.assumeMILPSolver();
-0265             testModel      = getTstModel();
-0266             testModelTasks = getTstModelTasks();
-0267             testParams     = struct();
-0268             evalc('prepData = prepINITModel(testModel, testModelTasks, {}, false, {}, ''s'');');
-0269             % essential rxns are reported on the linearly merged model (R1, R7)
-0270             testCase.verifyTrue(all(strcmp(prepData.essentialRxns, {'R1';'R7'})));
-0271 
-0272             arrayData.genes     = testModel.genes;
-0273             arrayData.tissues   = {'a'};
-0274             arrayData.levels    = getExprForRxnScore(getTstModelRxnScores());
-0275             arrayData.threshold = 1;
-0276             evalc(['resModel = ftINIT(prepData,arrayData.tissues{1},[],[],' ...
-0277                 'arrayData,[],getINITSteps(),true,true,testParams,false);']);
-0278             % R2 and R7 essential => all rxns on except R3 and R5 (negative
-0279             % score, not needed for the task)
-0280             testCase.verifyTrue(all(strcmp(resModel.rxns, ...
-0281                 {'R1';'R2';'R4';'R6';'R7';'R8';'R9';'R10'})));
-0282         end
-0283 
-0284         function fitTasksPreMergedRuns(testCase)
-0285             % Remove exchange rxns (required for gap filling) and create a gap
-0286             % by removing R7; fitTasks with gapFillMode 'preMerged' must add
-0287             % R7 back.
-0288             testCase.assumeMILPSolver();
-0289             testModel      = getTstModel();
-0290             testModelTasks = getTstModelTasks();
-0291             testRxnScores  = getTstModelRxnScores();
-0292 
-0293             mTempRef = closeModel(testModel);
-0294             mTempRef = removeReactions(mTempRef, {'R1';'R8'});
-0295             mTemp    = removeReactions(mTempRef, {'R7'});
-0296             mTemp.id = 'tmp';
-0297             tmpRxnScores = testRxnScores([2;3;4;5;6;7;9;10]);
-0298             evalc(['[~,addedRxnMat] = fitTasks(mTemp,mTempRef,[],' ...
-0299                 '''printOutput'',false,''rxnScores'',min(tmpRxnScores,-0.1),' ...
-0300                 '''taskStructure'',testModelTasks,''gapFillMode'',''preMerged'',' ...
-0301                 '''params'',struct(),''verbose'',false);']);
-0302             % An equality rather than all(strcmp(...)): an empty selection
-0303             % makes strcmp return an empty logical, which all() reports as
-0304             % true, so a run that added nothing would pass silently.
-0305             testCase.verifyEqual(mTempRef.rxns(any(addedRxnMat,2)), {'R7'});
-0306         end
-0307 
-0308         function fitTasksPreMergedResolveTiesStillFillsCorrectly(testCase)
-0309             % resolveTies is threaded through fitTasks (gapFillMode preMerged)
-0310             % -> ftINITFillGaps -> getMinNrFluxes (raven-gecko-parity#104); this
-0311             % exercises that whole chain end to end. Same fixture as
-0312             % fitTasksPreMergedRuns above -- correctness (R7 still gets added
-0313             % back) is what this checks, since that fixture has a unique fill
-0314             % (no tie to actually break); getMinNrFluxesResolveTiesIsDeterministic
-0315             % in tAnalysis.m covers the tie-breaking property itself.
-0316             testCase.assumeMILPSolver();
-0317             testModel      = getTstModel();
-0318             testModelTasks = getTstModelTasks();
-0319             testRxnScores  = getTstModelRxnScores();
-0320 
-0321             mTempRef = closeModel(testModel);
-0322             mTempRef = removeReactions(mTempRef, {'R1';'R8'});
-0323             mTemp    = removeReactions(mTempRef, {'R7'});
-0324             mTemp.id = 'tmp';
-0325             tmpRxnScores = testRxnScores([2;3;4;5;6;7;9;10]);
-0326             evalc(['[~,addedRxnMat] = fitTasks(mTemp,mTempRef,[],' ...
-0327                 '''printOutput'',false,''rxnScores'',min(tmpRxnScores,-0.1),' ...
-0328                 '''taskStructure'',testModelTasks,''gapFillMode'',''preMerged'',' ...
-0329                 '''params'',struct(),''verbose'',false,''resolveTies'',true);']);
-0330             testCase.verifyEqual(mTempRef.rxns(any(addedRxnMat,2)), {'R7'});
-0331         end
-0332 
-0333         function fitTasksPreMergedReportsUnfillableTask(testCase)
-0334             % R7 is the only producer of e[s], so removing it from the
-0335             % reference model as well leaves the task unfillable. R8 is kept
-0336             % so that e[s] still takes part in a reaction, and R9 is withheld
-0337             % from the model so the reference has something to offer: the
-0338             % MILP is then the thing that has to report the task as
-0339             % unfillable rather than as "Added 0 reaction(s)".
-0340             testCase.assumeMILPSolver();
-0341             testModel      = getTstModel();
-0342             testModelTasks = getTstModelTasks();
-0343             testRxnScores  = getTstModelRxnScores();
-0344 
-0345             mTempRef = closeModel(testModel);
-0346             mTempRef = removeReactions(mTempRef, {'R1';'R7'});
-0347             mTemp    = removeReactions(mTempRef, {'R9'});
-0348             mTemp.id = 'tmp';
-0349             tmpRxnScores = testRxnScores([2;3;4;5;6;8;9;10]);
-0350             lastwarn('');
-0351             evalc(['[~,addedRxnMat,failedTasks] = fitTasks(mTemp,mTempRef,[],' ...
-0352                 '''printOutput'',false,''rxnScores'',min(tmpRxnScores,-0.1),' ...
-0353                 '''taskStructure'',testModelTasks,''gapFillMode'',''preMerged'');']);
-0354             testCase.verifyTrue(failedTasks(1));
-0355             testCase.verifyFalse(any(addedRxnMat(:)));
-0356             % Every way of failing sets failedTasks, so the reason has to be
-0357             % checked too: without this the test would also pass if the
-0358             % gap-filling attempt threw before it reached the solver.
-0359             testCase.verifySubstring(lastwarn, 'no feasible solution exists');
-0360         end
-0361 
-0362         function fitTasksPreMergedHandlesEmptyReferenceSet(testCase)
-0363             % The reference model holds nothing the model does not already
-0364             % have, so no reaction can be added. An empty reaction list
-0365             % means "all reactions" to the MILP, which would otherwise
-0366             % report the model itself as a solution to an unfillable task.
-0367             testCase.assumeMILPSolver();
-0368             testModel      = getTstModel();
-0369             testModelTasks = getTstModelTasks();
-0370             testRxnScores  = getTstModelRxnScores();
-0371 
-0372             mTempRef = closeModel(testModel);
-0373             mTempRef = removeReactions(mTempRef, {'R1';'R7';'R8'});
-0374             mTemp    = mTempRef;
-0375             mTemp.id = 'tmp';
-0376             tmpRxnScores = testRxnScores([2;3;4;5;6;9;10]);
-0377             lastwarn('');
-0378             evalc(['[~,addedRxnMat,failedTasks] = fitTasks(mTemp,mTempRef,[],' ...
-0379                 '''printOutput'',false,''rxnScores'',min(tmpRxnScores,-0.1),' ...
-0380                 '''taskStructure'',testModelTasks,''gapFillMode'',''preMerged'',' ...
-0381                 '''params'',struct(),''verbose'',false);']);
-0382             testCase.verifyTrue(failedTasks(1));
-0383             testCase.verifyFalse(any(addedRxnMat(:)));
-0384             testCase.verifySubstring(lastwarn, 'no feasible solution exists');
-0385         end
-0386 
-0387         function fitTasksPreMergedWarningHasRealNewlineAndPercent(testCase)
-0388             % The "could not be gap-filled" warning embeds the task
-0389             % id/description; a literal "\n" must become a real newline
-0390             % (not print as the two characters backslash-n), and a "%" in
-0391             % the task id must survive intact.
-0392             testCase.assumeMILPSolver();
-0393             testModel      = getTstModel();
-0394             testModelTasks = getTstModelTasks();
-0395             testModelTasks.id = 'Task 50% test';
-0396             testModelTasks.description = testModelTasks.id;
-0397             testRxnScores  = getTstModelRxnScores();
-0398 
-0399             mTempRef = closeModel(testModel);
-0400             mTempRef = removeReactions(mTempRef, {'R1';'R7';'R8'});
-0401             mTemp    = mTempRef;
-0402             mTemp.id = 'tmp';
-0403             tmpRxnScores = testRxnScores([2;3;4;5;6;9;10]);
-0404             lastwarn('');
-0405             evalc(['fitTasks(mTemp,mTempRef,[],' ...
-0406                 '''printOutput'',false,''rxnScores'',min(tmpRxnScores,-0.1),' ...
-0407                 '''taskStructure'',testModelTasks,''gapFillMode'',''preMerged'',' ...
-0408                 '''params'',struct(),''verbose'',false);']);
-0409             msg = lastwarn();
-0410             testCase.verifySubstring(msg, 'Task 50% test');
-0411             testCase.verifyFalse(contains(msg, '\n'));
-0412         end
-0413 
-0414         function ftINITFillGapsReportsTaskNeedingAnOrphanMet(testCase)
-0415             % e[s] takes part only in R7 and R8. With both gone from the
-0416             % reference model no reaction touches e[s] at all, so a task
-0417             % requiring net production of it cannot be filled from that
-0418             % reference. That requirement is carried in b, and simplifyModel
-0419             % must leave the metabolite in place: without its row the MILP
-0420             % solves a problem that no longer asks for e[s], and reports the
-0421             % task as satisfiable.
-0422             testCase.assumeMILPSolver();
-0423             testModel     = getTstModel();
-0424             testRxnScores = getTstModelRxnScores();
-0425 
-0426             mTempRef = closeModel(testModel);
-0427             mTempRef = removeReactions(mTempRef, {'R1';'R7';'R8'});
-0428             % R5 is dropped from the model only so that the reference offers
-0429             % a candidate reaction to consider; adding it back does not make
-0430             % e[s] reachable.
-0431             mTemp    = removeReactions(mTempRef, {'R5'});
-0432             mTemp.id = 'tmp';
-0433             tmpRxnScores = min(testRxnScores([2;3;4;5;6;9;10]), -0.1);
-0434 
-0435             tModel = setTaskBounds(mTemp);
-0436             tRef   = setTaskBounds(mTempRef);
-0437             sol = solveLP(tModel);
-0438             testCase.assertEmpty(sol.x, 'the task must start out infeasible');
-0439 
-0440             evalc(['[addedRxns,~,exitFlag] = ftINITFillGaps(tModel,mTemp,tRef,' ...
-0441                 'false,true,tmpRxnScores,struct(),false);']);
-0442             testCase.verifyEqual(exitFlag, -1);
-0443             testCase.verifyEmpty(addedRxns);
-0444         end
-0445 
-0446         function ftINITFillGapsReportsNoCandidateReactions(testCase)
-0447             % The reference model holds no reaction the model does not
-0448             % already have, so nothing can be added to satisfy the task. An
-0449             % empty reaction list means "all reactions" to the MILP, and its
-0450             % solution is then sized for every reaction rather than for the
-0451             % empty set of candidates it is indexed against.
-0452             testCase.assumeMILPSolver();
-0453             testModel     = getTstModel();
-0454             testRxnScores = getTstModelRxnScores();
-0455 
-0456             m = closeModel(testModel);
-0457             m = removeReactions(m, {'R1'});   % R7/R8 stay, so e[s] is reachable
-0458             mTemp = m;
-0459             mTemp.id = 'tmp';
-0460             tmpRxnScores = min(testRxnScores(2:10), -0.1);
-0461 
-0462             % Both sides hold the same reactions, so there is no candidate
-0463             % to add. Only the reference may take up a[s], so only it can
-0464             % satisfy the task.
-0465             tRef   = setTaskBounds(m);
-0466             tModel = setTaskBounds(mTemp);
-0467             tModel.b(strcmp(tModel.mets, 'as'), 1) = 0;
-0468             sol = solveLP(tModel);
-0469             testCase.assertEmpty(sol.x, 'the task must start out infeasible');
-0470 
-0471             evalc(['[addedRxns,~,exitFlag] = ftINITFillGaps(tModel,mTemp,tRef,' ...
-0472                 'false,true,tmpRxnScores,struct(),false);']);
-0473             testCase.verifyEqual(exitFlag, -1);
-0474             testCase.verifyEmpty(addedRxns);
-0475         end
-0476 
-0477         function ftINITMetabolomicsRuns(testCase)
-0478             % Detected metabolites steer ftINIT towards alternative pathways.
-0479             testCase.assumeMILPSolver();
-0480             testModel  = getTstModel();
-0481             testParams = struct();
-0482             evalc('prepData = prepINITModel(testModel, {}, {''R7'';''R10''}, false, {}, ''s'');');
-0483 
-0484             arrayData.genes     = testModel.genes;
-0485             arrayData.tissues   = {'a'};
-0486             arrayData.levels    = getExprForRxnScore(getTstModelRxnScores());
-0487             arrayData.threshold = 1;
-0488 
-0489             % adding metabolite f favours R9/R10 again (presence of R2 is random)
-0490             evalc(['resModel = ftINIT(prepData,arrayData.tissues{1},[],[],' ...
-0491                 'arrayData,{''f''},getINITSteps(),true,true,testParams,false);']);
-0492             if length(resModel.rxns) == 7
-0493                 testCase.verifyTrue(all(strcmp(resModel.rxns, ...
-0494                     {'R1';'R4';'R6';'R7';'R8';'R9';'R10'})));
-0495             else
-0496                 testCase.verifyTrue(all(strcmp(resModel.rxns, ...
-0497                     {'R1';'R2';'R4';'R6';'R7';'R8';'R9';'R10'})));
-0498             end
-0499 
-0500             % metabolites a, e, f give the same result
-0501             evalc(['resModel = ftINIT(prepData,arrayData.tissues{1},[],[],' ...
-0502                 'arrayData,{''f'';''a'';''e''},getINITSteps(),true,true,testParams,false);']);
-0503             if length(resModel.rxns) == 7
-0504                 testCase.verifyTrue(all(strcmp(resModel.rxns, ...
-0505                     {'R1';'R4';'R6';'R7';'R8';'R9';'R10'})));
-0506             else
-0507                 testCase.verifyTrue(all(strcmp(resModel.rxns, ...
-0508                     {'R1';'R2';'R4';'R6';'R7';'R8';'R9';'R10'})));
-0509             end
-0510 
-0511             % metabolite b turns on R2 and R3/R5 and turns off R9
-0512             evalc(['resModel = ftINIT(prepData,arrayData.tissues{1},[],[],' ...
-0513                 'arrayData,{''b''},getINITSteps(),true,true,testParams,false);']);
-0514             testCase.verifyTrue(all(strcmp(resModel.rxns, ...
-0515                 {'R1';'R2';'R3';'R4';'R5';'R6';'R7';'R8'})));
-0516 
-0517             % testModel5 contains reactions that are not linearly merged
-0518             testModel5 = getTstModel5();
-0519             arrayData.genes  = testModel5.genes;
-0520             arrayData.levels = getExprForRxnScore(getTstModel5RxnScores());
-0521 
-0522             evalc('prepData5 = prepINITModel(testModel5, {}, {''R7'';''R10''}, false, {}, ''s'');');
-0523             evalc(['resModel = ftINIT(prepData5,arrayData.tissues{1},[],[],' ...
-0524                 'arrayData,{},getINITSteps(),true,true,testParams,false);']);
-0525             % a->g->e via R11/R13 (score -2) ties the R2 path (score -2), so
-0526             % which of the two is returned is a tie-break that differs between
-0527             % solvers. Neither route uses R9/R10. isequal rather than strcmp,
-0528             % which errors on two lists of different length.
-0529             testCase.verifyTrue( ...
-0530                 isequal(resModel.rxns, {'R1';'R4';'R6';'R7';'R8';'R11';'R13'}) || ...
-0531                 isequal(resModel.rxns, {'R1';'R2';'R4';'R6';'R7';'R8'}));
-0532 
-0533             % adding metabolite g drops R7
-0534             evalc('prepData5 = prepINITModel(testModel5, {}, {''R10''}, false, {}, ''s'');');
-0535             arrayData.levels(7) = getExprForRxnScore(-1.1); % avoid randomness
-0536             evalc(['resModel = ftINIT(prepData5,arrayData.tissues{1},[],[],' ...
-0537                 'arrayData,{''g''},getINITSteps(),true,true,testParams,false);']);
-0538             testCase.verifyTrue(all(strcmp(resModel.rxns, ...
-0539                 {'R1';'R4';'R6';'R8';'R11';'R13'})));
-0540         end
-0541 
-0542         function ftINITFullVsThreeStepRuns(testCase)
-0543             % On a larger model the three-step and "full" variants must give
-0544             % consistent results.
-0545             testCase.assumeMILPSolver();
-0546             testModelL          = getTstModelL();
-0547             testModelLGeneScores = getTstModelLGeneScores();
-0548             testParams          = struct();
-0549 
-0550             arrayDataL.genes     = testModelL.genes;
-0551             arrayDataL.tissues   = {'t1'};
-0552             arrayDataL.levels    = getExprForRxnScore(testModelLGeneScores, 1);
-0553             arrayDataL.threshold = 1;
-0554 
-0555             evalc('prepDataL = prepINITModel(testModelL, [], {}, false, {}, ''s'');');
-0556             evalc(['mres = ftINIT(prepDataL,arrayDataL.tissues{1},[],[],' ...
-0557                 'arrayDataL,[],getINITSteps(),true,true,testParams,false);']);
-0558             evalc(['mres2 = ftINIT(prepDataL,arrayDataL.tissues{1},[],[],' ...
-0559                 'arrayDataL,[],getINITSteps([], ''full''),true,true,testParams,false);']);
-0560 
-0561             expResult = {'S1';'S2';'S3';'S4';'S5';'S6';'S7';'S8';'S9';'E1';'E2'; ...
-0562                 'E3';'E4';'E5';'E6';'E8';'E9';'R1';'R2';'R3';'R4';'R5';'R6';'R7'; ...
-0563                 'R8';'R9';'R12';'R13';'R14';'R15'};
-0564             testCase.verifyTrue(all(contains(mres.rxns,  expResult)));
-0565             testCase.verifyTrue(all(contains(mres2.rxns, expResult)));
-0566         end
-0567 
-0568         function ftINITSeriesVariantsRun(testCase)
-0569             % Exercises the 2-step series from the paper, including the
-0570             % allowExcretion constraint it relies on.
-0571             testCase.assumeMILPSolver();
-0572             testModel  = getTstModel();
-0573             testParams = struct();
-0574             evalc('prepData = prepINITModel(testModel, {}, {}, false, {}, ''s'');');
-0575             arrayData.genes     = testModel.genes;
-0576             arrayData.tissues   = {'a'};
-0577             arrayData.levels    = getExprForRxnScore(getTstModelRxnScores());
-0578             arrayData.threshold = 1;
-0579 
-0580             steps = getINITSteps([], '2+1');
-0581             evalc(['resModel = ftINIT(prepData,arrayData.tissues{1},[],[],' ...
-0582                 'arrayData,[],steps,true,true,testParams,false);']);
-0583             % Same answer as the '1+1' series in ftINITPipelineRuns.
-0584             testCase.verifyEqual(resModel.rxns, {'R1';'R4';'R6';'R8';'R9';'R10'});
-0585 
-0586             steps = getINITSteps([], '2+0');
-0587             evalc(['resModel = ftINIT(prepData,arrayData.tissues{1},[],[],' ...
-0588                 'arrayData,[],steps,true,true,testParams,false);']);
-0589             % '2+0' skips step 3, so the GPR-less transport R2 survives.
-0590             testCase.verifyEqual(resModel.rxns, {'R1';'R2';'R4';'R6';'R8';'R9';'R10'});
-0591         end
-0592 
-0593         function scoreModelDataPrecedenceReactionPrefersHpaWholeRule(testCase)
-0594             % dataPrecedence 'reaction': any gene of a reaction having HPA
-0595             % data means array data is ignored for the whole reaction, not
-0596             % just that gene. R7 is "G1 or G4", G1 has (low) HPA data and G4
-0597             % only array data; per-gene scoring would let G4's higher array
-0598             % score win, per-reaction scoring must not.
-0599             m = scoringTestModel();
-0600             a = scoringArrayData();
-0601             h = scoringHpaDataLowG1();
-0602             perGene = scoreModel(m, h, a, 't1', ...
-0603                 'isozymeScoring', 'max', 'complexScoring', 'max');
-0604             perRxn  = scoreModel(m, h, a, 't1', ...
-0605                 'isozymeScoring', 'max', 'complexScoring', 'max', ...
-0606                 'dataPrecedence', 'reaction');
-0607             r7 = strcmp(m.rxns, 'R7');
-0608 
-0609             testCase.verifyEqual(perRxn(r7), -8, 'AbsTol', 1e-12);
-0610             % Per gene, G4's array score is seen too, and it is higher
-0611             testCase.verifyEqual(perGene(r7), 5*log(3), 'AbsTol', 1e-12);
-0612             testCase.verifyNotEqual(perRxn(r7), perGene(r7));
-0613         end
-0614 
-0615         function scoreModelAverageReducesDownTheRule(testCase)
-0616             % multipleGeneScoring 'average' averages down the grRule, so a
-0617             % complex counts once against its isozymes rather than once per
-0618             % subunit. R5 is "(G1 and G2) or G3".
-0619             m = scoringTestModel();
-0620             a = scoringArrayData();
-0621             scores = scoreModel(m, [], a, 't1', ...
-0622                 'isozymeScoring', 'average', 'complexScoring', 'average');
-0623             r5 = strcmp(m.rxns, 'R5');
-0624 
-0625             g = 5*log([1.5; 2; 2.5]);          % scores of G1, G2, G3
-0626             testCase.verifyEqual(scores(r5), mean([mean(g(1:2)); g(3)]), ...
-0627                 'AbsTol', 1e-12);
-0628             testCase.verifyNotEqual(scores(r5), mean(g));   % not a flat mean
-0629         end
-0630 
-0631         function scoreModelReadsGrRulesNotRxnGeneMat(testCase)
-0632             % A model carrying its gene associations only in grRules (no
-0633             % rxnGeneMat) must still score every reaction correctly.
-0634             m = scoringTestModel();
-0635             a = scoringArrayData();
-0636             noMat = m;
-0637             noMat.rxnGeneMat = [];
-0638             withMat = scoreModel(m, [], a, 't1');
-0639             withoutMat = scoreModel(noMat, [], a, 't1');
-0640             testCase.verifyEqual(withoutMat, withMat, 'AbsTol', 1e-12);
-0641         end
-0642 
-0643         function scoreModelRejectsUnknownDataPrecedence(testCase)
-0644             m = scoringTestModel();
-0645             a = scoringArrayData();
-0646             testCase.verifyError( ...
-0647                 @() scoreModel(m, [], a, 't1', 'dataPrecedence', 'model'), ...
-0648                 'RAVEN:badInput');
-0649         end
-0650 
-0651     end
-0652 end
-0653 
-0654 %==========================================================================
-0655 % Synthetic test models, illustrated in tINITtestInfo/06.svg and 07.svg.
-0656 %==========================================================================
-0657 
-0658 function testModel = getTstModel()
-0659 testModel = struct();
-0660 testModel.id = 'testModel';
-0661 testModel.rxns = {};
-0662 testModel.S = [];
-0663 testModel.rev = [];
-0664 testModel.mets = {'as';'ac';'bc';'cc';'dc';'ec';'es';'fc'};
-0665 testModel.metNames = {'a';'a';'b';'c';'d';'e';'e';'f'};
-0666 testModel.comps = {'s';'c'};
-0667 testModel.compNames = testModel.comps;
-0668 testModel.metComps = [1;2;2;2;2;2;1;2];
-0669 testModel.genes = {'G1';'G2';'G3';'G4';'G5';'G6';'G7';'G8';'G9';'G10'};
-0670 testModel.grRules = {};
-0671 testModel.rxnGeneMat = [];
-0672 
-0673 rxnsToAdd = struct();
-0674 rxnsToAdd.rxns = {'R1';'R2';'R3';'R4';'R5';'R6';'R7';'R8';'R9';'R10'};
-0675 rxnsToAdd.equations = {'=> a[s]';...
-0676     'a[s] <=> a[c]';...
-0677     'a[c] <=> b[c] + c[c]';...
-0678     'a[c] <=> 2 d[c]';...
-0679     'b[c] + c[c] => e[c]';...
-0680     '2 d[c] => e[c]';...
-0681     'e[c] => e[s]';...
-0682     'e[s] =>';...
-0683     'a[c] <=> f[c]';...
-0684     'f[c] <=> e[c]'};
-0685 rxnsToAdd.grRules = {'';'';'G3';'G4';'G5';'G6';'G7';'';'G9';'G10'};
-0686 evalc('testModel = addRxns(testModel,rxnsToAdd, 3);');
-0687 testModel.c = [0;0;0;0;0;0;0;1;0;0];
-0688 testModel.ub = repmat(1000,10,1);
-0689 testModel.lb = [0;-1000;-1000;-1000;0;0;0;0;-1000;-1000];
-0690 testModel.rxnNames = testModel.rxns;
-0691 testModel.b = repmat(0,8,1);
-0692 end
-0693 
-0694 function testModelRxnScores = getTstModelRxnScores()
-0695 testModelRxnScores = [-2;-2;-1;7;0.5;0.5;-1;-2;-3;3.5];
-0696 end
-0697 
-0698 function model = setTaskBounds(model)
-0699 % The b bounds that fitTasks (gapFillMode 'preMerged') derives from getTstModelTasks:
-0700 % a[s] may be taken up freely, e[s] has to leave at exactly one unit.
-0701 model.b = zeros(numel(model.mets), 2);
-0702 model.b(strcmp(model.mets, 'as'), :) = [-inf 0];
-0703 model.b(strcmp(model.mets, 'es'), :) = [1 1];
-0704 end
-0705 
-0706 function testModelTasks = getTstModelTasks()
-0707 testModelTasks = struct();
-0708 testModelTasks.id = 'Gen e[s] from a[s]';
-0709 testModelTasks.description = 'Gen e[s] from a[s]';
-0710 testModelTasks.shouldFail = false;
-0711 testModelTasks.printFluxes = false;
-0712 testModelTasks.comments = '';
-0713 testModelTasks.inputs = {'a[s]'};
-0714 testModelTasks.LBin = 0;
-0715 testModelTasks.UBin = inf;
-0716 testModelTasks.outputs = {'e[s]'};
-0717 testModelTasks.LBout = 1;
-0718 testModelTasks.UBout = 1;
-0719 testModelTasks.equations = {};
-0720 testModelTasks.LBequ = [];
-0721 testModelTasks.UBequ = [];
-0722 testModelTasks.changed = {};
-0723 testModelTasks.LBrxn = {};
-0724 testModelTasks.UBrxn = {};
-0725 end
-0726 
-0727 function testModel2 = getTstModel2()
-0728 testModel2 = struct();
-0729 testModel2.id = 'testModel2';
-0730 testModel2.rxns = {};
-0731 testModel2.S = [];
-0732 testModel2.rev = [];
-0733 testModel2.mets = {'a';'b'};
-0734 testModel2.metNames = {'a';'b'};
-0735 testModel2.comps = {'s'};
-0736 testModel2.compNames = testModel2.comps;
-0737 testModel2.metComps = [1;1];
-0738 testModel2.genes = {'G1';'G2';'G3';'G4'};
-0739 testModel2.grRules = {};
-0740 testModel2.rxnGeneMat = [];
-0741 
-0742 rxnsToAdd = struct();
-0743 rxnsToAdd.rxns = {'R1';'R2';'R3';'R4'};
-0744 rxnsToAdd.equations = {'a[s] <=>';...
-0745     'a[s] => b[s]';...
-0746     'a[s] <=> b[s]';...
-0747     'b[s] =>'};
-0748 rxnsToAdd.grRules = testModel2.genes;
-0749 evalc('testModel2 = addRxns(testModel2,rxnsToAdd,3,'''',true,true);');
-0750 testModel2.c = [0;0;0;1];
-0751 testModel2.ub = repmat(1000,4,1);
-0752 testModel2.lb = [-1000;0;-1000;0];
-0753 testModel2.rxnNames = testModel2.rxns;
-0754 testModel2.b = zeros(2,1);
-0755 end
-0756 
-0757 function testModel4 = getTstModel4()
-0758 testModel4 = getTstModel2();
-0759 
-0760 rxnsToAdd = struct();
-0761 rxnsToAdd.rxns = {'R5';'R6';'R7';'R8';'R9';'R10';'R11'};
-0762 rxnsToAdd.equations = {'5 a[s] <=> 5 d[s]';...
-0763     'e[s] <=> d[s]';
-0764     'f[s] + g[s] <=> e[s]';
-0765     'b[s] <=> f[s]';
-0766     'h[s] <=> g[s]';
-0767     'h[s] =>';
-0768     'e[s] => g[s]'};
-0769 rxnsToAdd.grRules = {'G5';'G6';'G7';'G8';'G9';'G10';'G11'};
-0770 [~,testModel4] = evalc("addRxns(testModel4,rxnsToAdd, 3, [], true, true);");
-0771 end
-0772 
-0773 function testModel5 = getTstModel5()
-0774 testModel = getTstModel();
-0775 
-0776 rxnsToAdd = struct();
-0777 rxnsToAdd.rxns = {'R11';'R12';'R13';'R14'};
-0778 rxnsToAdd.equations = {'a[c] <=> g[c]';...
-0779     'a[c] <=> g[c]';...
-0780     'g[c] <=> e[c]';...
-0781     'g[c] <=> e[c]'};
-0782 rxnsToAdd.grRules = {'G11';'G12';'G13';'G14'};
-0783 [~,testModel5] = evalc("addRxns(testModel,rxnsToAdd, 3, [], true, true);");
-0784 end
-0785 
-0786 function testModel5RxnScores = getTstModel5RxnScores()
-0787 testModel5RxnScores = [getTstModelRxnScores();-1;-1.5;-1;-1.5];
-0788 end
-0789 
-0790 function testModelL = getTstModelL()
-0791 testModelL = struct();
-0792 testModelL.id = 'testModel';
-0793 testModelL.rxns = {};
-0794 testModelL.S = [];
-0795 testModelL.rev = [];
-0796 testModelL.metNames = {'e1';'e2';'e3';'e4';'e5';'e6';'e7';'e8';'e9';'e1';'e2';'e3';'e4';'e5';'e6';'e7';'e8';'e9';'x1';'x2';'x3';'x4';'x5';'x6';'x7';'x8';'x9';'x10';'x11'};
-0797 testModelL.comps = {'s';'c'};
-0798 testModelL.compNames = testModelL.comps;
-0799 testModelL.metComps = [1;1;1;1;1;1;1;1;1;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2];
-0800 testModelL.mets = strcat(testModelL.metNames, testModelL.comps(testModelL.metComps));
-0801 
-0802 testModelL.grRules = {};
-0803 testModelL.rxnGeneMat = [];
-0804 
-0805 testModelL.genes = {'Ge1';'Ge2';'Ge4';'Ge5';'Ge7';'Ge9'; 'Gr1';'Gr2';'Gr3';'Gr5';'Gr6';'Gr7';'Gr8';'Gr9';'Gr10';'Gr11';'Gr12';'Gr14';'Gr15'};
-0806 
-0807 testModelL.ub = [];
-0808 testModelL.lb = [];
-0809 
-0810 rxnsToAdd = struct();
-0811 rxnsToAdd.rxns = {  'S1';'S2';'S3';'S4';'S5';'S6';'S7';'S8';'S9';'E1';'E2';'E2b';'E3';'E4';'E5';'E6';'E7';'E8';'E9';'R1';'R2';'R3';'R4';'R5';'R6';'R7';'R8';'R9';'R10';'R11';'R12';'R13';'R14';'R15'};
-0812 rxnsToAdd.grRules = {'';  '';  '';  '';  '';  '';  '';  '';  ''; 'Ge1';'Ge2';'';'';'Ge4';'Ge5';'';'Ge7';'';'Ge9'; 'Gr1';'Gr2';'Gr3';'';'Gr5';'Gr6';'Gr7';'Gr8';'Gr9';'Gr10';'Gr11';'Gr12';'';'Gr14';'Gr15'};
-0813 rxnsToAdd.equations = {'e1[s] <=>';...
-0814     'e2[s] <=>';...
-0815     'e3[s] <=>';...
-0816     'e4[s] <=>';...
-0817     'e5[s] <=>';...
-0818     'e6[s] <=>';...
-0819     'e7[s] <=>';...
-0820     'e8[s] <=>';...
-0821     'e9[s] <=>';...
-0822     'e1[s] <=> e1[c]';...
-0823     'e2[s] <=> e2[c]';...
-0824     'e2[s] <=> e2[c]';... %b variant
-0825     'e3[s] <=> e3[c]';...
-0826     'e4[s] <=> e4[c]';...
-0827     'e5[s] <=> e5[c]';...
-0828     'e6[s] <=> e6[c]';...
-0829     'e7[s] <=> e7[c]';...
-0830     'e8[s] <=> e8[c]';...
-0831     'e9[s] <=> e9[c]';...
-0832     'e1[c] + e2[c] <=> x1[c]';... %R1
-0833     'e1[c] + e3[c] => x2[c] + x3[c]';... %R2
-0834     'e4[c] + x3[c] => x4[c] + x5[c]';... %R3
-0835     'e5[c] + e6[c] + x4[c] => 2 x2[c] + x6[c]';... %R4
-0836     'x1[c] + x2[c] <=> x7[c] + 2 x8[c]';... %R5
-0837     'x2[c] + x8[c] => x3[c] + x9[c]';... %R6
-0838     'x4[c] <=> x9[c]';... %R7
-0839     'x5[c] <=> x9[c]';... %R8
-0840     'x6[c] <=> x10[c]';... %R9
-0841     'x6[c] <=> x11[c]';... %R10
-0842     'x10[c] + 2 x11[c] => e7[c]';... %R11
-0843     'x9[c] + x10[c] <=> e8[c]';... %R12
-0844     'x7[c] + x8[c] + x9[c] => e9[c]';... %R13
-0845     'x6[c] => x9[c]';... %R14
-0846     'x3[c] => x9[c]'... %R15
-0847     };
-0848 evalc('testModelL = addRxns(testModelL,rxnsToAdd, 3);');
-0849 testModelL.c = [0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0];
-0850 testModelL.rxnNames = testModelL.rxns;
-0851 testModelL.b = repmat(0,length(testModelL.mets),1);
-0852 end
-0853 
-0854 function testModelLGeneScores = getTstModelLGeneScores()
-0855 %testModelL.genes = {'Ge1';'Ge2';'Ge4';'Ge5';'Ge7';'Ge9'; 'Gr1';'Gr2';'Gr3';'Gr5';'Gr6';'Gr7';'Gr8';'Gr9';'Gr10';'Gr11';'Gr12';'Gr14';'Gr15'};
-0856 testModelLGeneScores = [3; -1;   8;    6;    -5;    5;     4;    5;    2;    3;    6;    1;    3;    1;    -3;    1;     3;      1;    2];
-0857 end
-0858 
-0859 %==========================================================================
-0860 % Fixtures for the scoreModel* tests: a small model whose grRules cover
-0861 % every rule shape the scoring treats differently (none, single, OR, AND,
-0862 % nested, mixed-data-source OR), and matching array/HPA data.
-0863 %==========================================================================
-0864 
-0865 function m = scoringTestModel()
-0866 m = struct();
-0867 m.id = 'scoringTest';
-0868 m.rxns = {}; m.S = []; m.rev = [];
-0869 m.mets     = {'ac'; 'bc'; 'cc'; 'dc'; 'ec'};
-0870 m.metNames = {'a'; 'b'; 'c'; 'd'; 'e'};
-0871 m.comps = {'c'}; m.compNames = m.comps;
-0872 m.metComps = [1; 1; 1; 1; 1];
-0873 m.genes = {'G1'; 'G2'; 'G3'; 'G4'};
-0874 m.grRules = {}; m.rxnGeneMat = [];
-0875 r = struct();
-0876 r.rxns = {'R1'; 'R2'; 'R3'; 'R4'; 'R5'; 'R6'; 'R7'};
-0877 r.equations = {'=> a[c]'; 'a[c] => b[c]'; 'b[c] => c[c]'; ...
-0878     'c[c] => d[c]'; 'a[c] => d[c]'; 'd[c] => e[c]'; 'e[c] =>'};
-0879 r.grRules = {''; 'G1'; 'G1 or G2'; 'G1 and G2'; ...
-0880     '(G1 and G2) or G3'; 'G4'; 'G1 or G4'};
-0881 evalc('m = addRxns(m, r, 3);');
-0882 m.c = zeros(7, 1);
-0883 m.lb = zeros(7, 1);
-0884 m.ub = repmat(1000, 7, 1);
-0885 m.rxnNames = m.rxns;
-0886 m.b = zeros(5, 1);
-0887 evalc('[m.grRules, m.rxnGeneMat] = standardizeGrRules(m, true);');
-0888 end
-0889 
-0890 function a = scoringArrayData()
-0891 % Distinct, uncapped scores: with a threshold of 1 the score of each gene
-0892 % is 5*log(level), i.e. 2.03, 3.47, 4.58 and 5.49.
-0893 a = struct();
-0894 a.genes     = {'G1'; 'G2'; 'G3'; 'G4'};
-0895 a.tissues   = {'t1'; 't2'};
-0896 a.celltypes = {'ct1'; 'ct2'};
-0897 a.levels    = [1.5 1; 2 1; 2.5 1; 3 1];
-0898 a.threshold = ones(4, 1);
-0899 end
-0900 
-0901 function h = scoringHpaDataLowG1()
-0902 % Only G1 has HPA data, and its level is the lowest one, so any array
-0903 % score for the other genes outranks it.
-0904 h = struct();
-0905 h.genes      = {'G1'};
-0906 h.tissues    = {'t1'};
-0907 h.celltypes  = {'ct1'};
-0908 h.levels     = {'High', 'Medium', 'Low', 'None'};
-0909 h.gene2Level = 4;
-0910 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tIO.html b/doc/testing/function_tests/tIO.html deleted file mode 100644 index f6f7a480..00000000 --- a/doc/testing/function_tests/tIO.html +++ /dev/null @@ -1,343 +0,0 @@ - - - - Description of tIO - - - - - - - - - - - - - -

tIO -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tIO < RavenTestCase
-0002 % tIO  Tests for the import/export and file-utility functions in io/.
-0003 %
-0004 %   Format support is exercised mainly via export->import round-trips on the
-0005 %   test model and via importing the tutorial 'empty' files.
-0006 
-0007     methods (Test)
-0008 
-0009         function exportForGitWritesDependencies(testCase)
-0010             % Exercises the toolbox-version lookup in exportForGit via the
-0011             % dependencies.txt it writes
-0012             f = fullfile(testCase.ravenRoot,'tutorial','empty.xml');
-0013             evalc('model = importModel(f);');
-0014             outDir = tempname; mkdir(outDir);
-0015             c = onCleanup(@() rmdir(outDir,'s'));
-0016             evalc("exportForGit(model,'path',outDir,'formats',{'xml'},'subDirs',false)");
-0017             dep = fileread(fullfile(outDir,'dependencies.txt'));
-0018             testCase.verifySubstring(dep, 'RAVEN_toolbox');
-0019         end
-0020 
-0021         function exportForGitMainBranchFlagRejectsNonMain(testCase)
-0022             % mainBranchFlag should error when RAVEN is *not* on main/master,
-0023             % which this test, run from a feature branch, always satisfies.
-0024             outDir = tempname; mkdir(outDir);
-0025             c = onCleanup(@() rmdir(outDir,'s'));
-0026             testCase.verifyError(@() evalc(['exportForGit(testCase.model,''path'',outDir,' ...
-0027                 '''formats'',{''xml''},''subDirs'',false,''mainBranchFlag'',true);']), ...
-0028                 ?MException);
-0029         end
-0030 
-0031         function importModelHandlesSpeciesAndReactionEdgeCases(testCase)
-0032             % A model where: some species have no SBO term while others do
-0033             % (and disagree with each other); a multi-reaction objective;
-0034             % a reaction with more than one SUBSYSTEM note; and species
-0035             % whose charge is genuinely (not just by default) zero.
-0036             f = fullfile(testCase.ravenRoot,'testing','function_tests','test_data', ...
-0037                 'importModelEdgeCases.xml');
-0038             evalc('m = importModel(f);');
-0039 
-0040             % SBO terms must land on the metabolite that actually has them,
-0041             % not be shifted onto the wrong one because one species (m2)
-0042             % has none.
-0043             testCase.verifyTrue(isfield(m,'metMiriams'));
-0044             testCase.verifyEmpty(m.metMiriams{strcmp(m.mets,'m2')});
-0045             testCase.verifyNotEmpty(m.metMiriams{strcmp(m.mets,'m3')});
-0046 
-0047             % Both reactions in the combined objective must be captured.
-0048             testCase.verifyEqual(m.c(strcmp(m.rxns,'r1')), 1);
-0049             testCase.verifyEqual(m.c(strcmp(m.rxns,'r2')), 2);
-0050 
-0051             % Two SUBSYSTEM notes on one reaction must stay two entries.
-0052             testCase.verifyEqual(numel(m.subSystems{strcmp(m.rxns,'r1')}), 2);
-0053 
-0054             % All-genuinely-neutral charges must not be dropped.
-0055             testCase.verifyTrue(isfield(m,'metCharges'));
-0056             testCase.verifyEqual(m.metCharges, zeros(4,1));
-0057         end
-0058 
-0059         function importModelStripsRegexSpecialCompartmentName(testCase)
-0060             % A compartment name containing regex metacharacters ("Golgi
-0061             % (cis)") must still be recognized and stripped from the
-0062             % metabolite name suffix, not corrupt the matching pattern.
-0063             f = fullfile(testCase.ravenRoot,'testing','function_tests','test_data', ...
-0064                 'importModelSpecialCompName.xml');
-0065             evalc('m = importModel(f);');
-0066             testCase.verifyEqual(m.metNames{1}, 'metabolite one');
-0067         end
-0068 
-0069         function importModelReadsSBML(testCase)
-0070             f = fullfile(testCase.ravenRoot,'tutorial','empty.xml');
-0071             evalc('m = importModel(f);');
-0072             testCase.verifyTrue(isfield(m, 'rxns'));
-0073         end
-0074 
-0075         function readYAMLmodelReadsYml(testCase)
-0076             f = fullfile(testCase.ravenRoot,'tutorial','empty.yml');
-0077             evalc('m = readYAMLmodel(f);');
-0078             testCase.verifyTrue(isfield(m, 'rxns'));
-0079         end
-0080 
-0081         function parseYAMLReturnsNestedTree(testCase)
-0082             f = [tempname '.yml'];
-0083             testCase.addTeardown(@() delete(f));
-0084             fid = fopen(f,'w');
-0085             fprintf(fid, [ ...
-0086                 '# a full-line comment\n' ...
-0087                 'prelude:\n' ...
-0088                 '  reset_exchanges: out  # trailing comment\n' ...
-0089                 'cofactor_pseudoreaction:\n' ...
-0090                 '  rxn_id: r_4598\n' ...
-0091                 '  remove_mets:\n' ...
-0092                 '    - { met: s_3714 }\n' ...
-0093                 'bounds:\n' ...
-0094                 '  - { rxn: r_1654, lb: -1000 }\n' ...
-0095                 '  - { rxn: r_1663, lb: 0, ub: 0 }\n' ...
-0096                 'expected_uptake_count: 15\n']);
-0097             fclose(fid);
-0098 
-0099             out = parseYAML(f);
-0100             testCase.verifyEqual(out.prelude.reset_exchanges, 'out')
-0101             testCase.verifyEqual(out.cofactor_pseudoreaction.rxn_id, 'r_4598')
-0102             testCase.verifyEqual(out.cofactor_pseudoreaction.remove_mets{1}.met, 's_3714')
-0103             testCase.verifyEqual(numel(out.bounds), 2)
-0104             testCase.verifyEqual(out.bounds{1}.rxn, 'r_1654')
-0105             testCase.verifyEqual(out.bounds{1}.lb, -1000)
-0106             testCase.verifyFalse(isfield(out.bounds{1}, 'ub'))
-0107             testCase.verifyEqual(out.bounds{2}.ub, 0)
-0108             testCase.verifyEqual(out.expected_uptake_count, 15)
-0109         end
-0110 
-0111         function exportImportSBMLRoundTrip(testCase)
-0112             f = [tempname '.xml'];
-0113             testCase.addTeardown(@() delete(f));
-0114             evalc('exportModel(testCase.model, f);');
-0115             evalc('m2 = importModel(f);');
-0116             testCase.verifyEqual(numel(m2.rxns), numel(testCase.model.rxns));
-0117         end
-0118 
-0119         function exportImportSBMLRoundTripPreservesDeltaG(testCase)
-0120             % rxnDeltaG/metDeltaG round-trip through a <p>deltaG: VALUE</p>
-0121             % notes entry -- the same key and format raven-toolbox reads
-0122             % and writes, so either toolbox's SBML file is a complete
-0123             % round trip for the other (RAVEN#724). An untouched reaction
-0124             % or metabolite stays NaN, and the field is omitted entirely
-0125             % if nothing in the model has a value. Values are the kind of
-0126             % magnitude a real thermodynamic estimate has, not contrived
-0127             % high-precision ones: num2str's default precision (no digits
-0128             % argument) is exact for these, same as Confidence Level.
-0129             m = testCase.model;
-0130             m.rxnDeltaG = NaN(numel(m.rxns), 1);
-0131             m.rxnDeltaG(1) = -8.7;
-0132             m.metDeltaG = NaN(numel(m.mets), 1);
-0133             m.metDeltaG(1) = 7.5;
-0134 
-0135             f = [tempname '.xml'];
-0136             testCase.addTeardown(@() delete(f));
-0137             evalc('exportModel(m, f);');
-0138             evalc('m2 = importModel(f);');
-0139 
-0140             testCase.verifyEqual(m2.rxnDeltaG(1), -8.7, 'AbsTol', 1e-9);
-0141             testCase.verifyTrue(isnan(m2.rxnDeltaG(2)));
-0142             testCase.verifyEqual(m2.metDeltaG(1), 7.5, 'AbsTol', 1e-9);
-0143             testCase.verifyTrue(isnan(m2.metDeltaG(2)));
-0144         end
-0145 
-0146         function exportModelOmitsDeltaGFieldWhenAllNaN(testCase)
-0147             f = [tempname '.xml'];
-0148             testCase.addTeardown(@() delete(f));
-0149             evalc('exportModel(testCase.model, f);');
-0150             evalc('m2 = importModel(f);');
-0151             testCase.verifyFalse(isfield(m2, 'rxnDeltaG'));
-0152             testCase.verifyFalse(isfield(m2, 'metDeltaG'));
-0153         end
-0154 
-0155         function exportToExcelFormatWritesFile(testCase)
-0156             f = [tempname '.xlsx'];
-0157             testCase.addTeardown(@() delete(f));
-0158             evalc('exportToExcelFormat(testCase.model, f);');
-0159             testCase.verifyTrue(exist(f,'file')==2);
-0160         end
-0161 
-0162         function exportToExcelFormatWritesEcSheets(testCase)
-0163             % Enzyme-constrained (GECKO) models get two extra export-only
-0164             % sheets, ENZYMES and ENZRXNS, holding the model.ec contents.
-0165             model = testCase.model;
-0166             model.ec.geckoLight = false;
-0167             model.ec.rxns     = model.rxns(1:2);
-0168             model.ec.kcat     = [13.7; 0];
-0169             model.ec.source   = {'brenda'; ''};
-0170             model.ec.notes    = {'note1'; ''};
-0171             model.ec.eccodes  = {'1.1.1.1'; '2.7.1.1;2.7.1.2'};
-0172             model.ec.genes    = model.genes(1:2);
-0173             model.ec.enzymes  = {'P0A1'; 'P0A2'};
-0174             model.ec.mw       = [51000; NaN];
-0175             model.ec.sequence = {'MABC'; 'MDEF'};
-0176             model.ec.concs    = [NaN; 0.5];
-0177             model.ec.rxnEnzMat = [1 2; 0 1];   % R1: P0A1 x1, P0A2 x2; R2: P0A2 x1
-0178 
-0179             f = [tempname '.xlsx'];
-0180             testCase.addTeardown(@() delete(f));
-0181             evalc('exportToExcelFormat(model, f);');
-0182 
-0183             sheets = sheetnames(f);
-0184             testCase.verifyTrue(any(strcmp(sheets,'ENZYMES')));
-0185             testCase.verifyTrue(any(strcmp(sheets,'ENZRXNS')));
-0186 
-0187             % NaN mw/conc are written as blanks; the kcat 0 sentinel is kept.
-0188             enz = readcell(f,'Sheet','ENZYMES');
-0189             testCase.verifyEqual(string(enz(1,2:6)),["ID","GENE","MW","SEQUENCE","CONC"]);
-0190             testCase.verifyEqual(string(enz{2,2}),"P0A1");
-0191             testCase.verifyEqual(enz{2,4},51000,'AbsTol',1e-9);
-0192 
-0193             ecrxn = readcell(f,'Sheet','ENZRXNS');
-0194             testCase.verifyEqual(string(ecrxn(1,2:7)),["ID","KCAT","SOURCE","NOTE","EC-NUMBER","ENZYMES"]);
-0195             testCase.verifyEqual(ecrxn{2,3},13.7,'AbsTol',1e-9);
-0196             testCase.verifyEqual(string(ecrxn{3,6}),"2.7.1.1;2.7.1.2");
-0197             % ENZYMES column: 'enzyme:count' subunit stoichiometry from rxnEnzMat
-0198             testCase.verifyEqual(string(ecrxn{2,7}),"P0A1:1;P0A2:2");
-0199             testCase.verifyEqual(string(ecrxn{3,7}),"P0A2:1");
-0200         end
-0201 
-0202         function writeReadYAMLRoundTrip(testCase)
-0203             f = [tempname '.yml'];
-0204             testCase.addTeardown(@() delete(f));
-0205             evalc('writeYAMLmodel(testCase.model, f);');
-0206             evalc('m2 = readYAMLmodel(f);');
-0207             testCase.verifyEqual(numel(m2.rxns), numel(testCase.model.rxns));
-0208         end
-0209 
-0210         function writeReadYAMLPreservesCompartmentAnnotations(testCase)
-0211             % Compartment annotations must round-trip, and must not be read
-0212             % back as additional compartments.
-0213             m = testCase.model;
-0214             m.compMiriams = cell(numel(m.comps),1);
-0215             m.compMiriams{1}.name  = {'go'};
-0216             m.compMiriams{1}.value = {'GO:0005737'};
-0217             f = [tempname '.yml'];
-0218             testCase.addTeardown(@() delete(f));
-0219             evalc('writeYAMLmodel(m, f);');
-0220             evalc('m2 = readYAMLmodel(f);');
-0221             testCase.verifyEqual(m2.comps, m.comps);
-0222             testCase.verifyEqual(m2.compNames, m.compNames);
-0223             testCase.verifyEqual(m2.compMiriams{1}.name, {'go'});
-0224             testCase.verifyEqual(m2.compMiriams{1}.value, {'GO:0005737'});
-0225         end
-0226 
-0227         function writeReadYAMLPreservesUnknownAnnotationFields(testCase)
-0228             % A caller-specific metaData field with no RAVEN-defined slot
-0229             % (e.g. a `geckopy_version` field) must survive a write/read
-0230             % round trip instead of being silently dropped, same as the
-0231             % fixed annoFields set (taxonomy here).
-0232             m = testCase.model;
-0233             m.annotation.taxonomy = 'taxonomy/4932';
-0234             m.annotation.geckopyVersion = '0.2.1';
-0235             f = [tempname '.yml'];
-0236             testCase.addTeardown(@() delete(f));
-0237             evalc('writeYAMLmodel(m, f);');
-0238             evalc('m2 = readYAMLmodel(f);');
-0239             testCase.verifyEqual(m2.annotation.taxonomy, 'taxonomy/4932');
-0240             testCase.verifyEqual(m2.annotation.geckopyVersion, '0.2.1');
-0241         end
-0242 
-0243         function exportForGitWritesRepo(testCase)
-0244             d = [tempname filesep];
-0245             mkdir(d);
-0246             testCase.addTeardown(@() rmdir(d, 's'));
-0247             evalc('exportForGit(testCase.model, ''ec'', d, {''yml''});');
-0248             testCase.verifyNotEmpty(dir(fullfile(d,'**','*.yml')));
-0249         end
-0250 
-0251         function checkFileExistenceFindsFile(testCase)
-0252             f = fullfile(testCase.ravenRoot,'testing','function_tests', ...
-0253                 'test_data','ecoli_textbook.mat');
-0254             out = checkFileExistence(f, 1, false, true);
-0255             testCase.verifyNotEmpty(out);
-0256         end
-0257 
-0258         function checkFileExistenceAcceptsForwardSlashes(testCase)
-0259             % An absolute Windows path may be written with forward slashes:
-0260             % MATLAB accepts them everywhere, and anything handing RAVEN a path
-0261             % built by another tool is likely to use them. Treating such a path
-0262             % as relative appends the working directory and turns
-0263             % "C:/data/model.mat" into "C:\somewhere\C:\data\model.mat".
-0264             f = fullfile(testCase.ravenRoot,'testing','function_tests', ...
-0265                 'test_data','ecoli_textbook.mat');
-0266             out = checkFileExistence(strrep(f,'\','/'), 1, false, true);
-0267             testCase.verifyNotEmpty(out);
-0268         end
-0269 
-0270         function cleanSheetTrimsComments(testCase)
-0271             raw = {'#comment', '#comment'; 'a', 'b'; '', ''};
-0272             out = cleanSheet(raw);
-0273             testCase.verifyClass(out, 'cell');
-0274         end
-0275 
-0276         function writeExcelRoundTrips(testCase)
-0277             % writeExcel generates the .xlsx (Office Open XML) directly, with
-0278             % no external library; read it back with base MATLAB to confirm
-0279             % the sheets, values and types survive.
-0280             f = [tempname '.xlsx'];
-0281             testCase.addTeardown(@() delete(f));
-0282             s(1).name='RXNS'; s(1).header={'ID','VALUE'};
-0283             s(1).data={'r1',1.5; 'r2',-3};
-0284             s(2).name='METS'; s(2).header={'ID'}; s(2).data={'m1'};
-0285             writeExcel(f, s);
-0286             testCase.verifyEqual(sort(string(sheetnames(f))),["METS";"RXNS"]);
-0287             raw = readcell(f,'Sheet','RXNS');
-0288             testCase.verifyEqual(string(raw(1,:)),["ID","VALUE"]);
-0289             testCase.verifyEqual(string(raw{2,1}),"r1");
-0290             testCase.verifyEqual(raw{2,2},1.5,'AbsTol',1e-9);
-0291         end
-0292 
-0293     end
-0294 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tInstallation.html b/doc/testing/function_tests/tInstallation.html deleted file mode 100644 index 6ae79b2a..00000000 --- a/doc/testing/function_tests/tInstallation.html +++ /dev/null @@ -1,114 +0,0 @@ - - - - Description of tInstallation - - - - - - - - - -
Home > testing > function_tests > tInstallation.m
- - - -

tInstallation -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tInstallation < RavenTestCase
-0002 % tInstallation  Tests for the setup/installation helpers in installation/.
-0003 %
-0004 %   addRavenToUserPath, removeRavenFromPath and updateDocumentation have
-0005 %   destructive / irreversible side effects (rewrite the user's startup.m,
-0006 %   remove RAVEN from the live path, regenerate the whole doc tree). They are
-0007 %   therefore only checked for existence here, not executed.
-0008 
-0009     methods (Test)
-0010 
-0011         function findRAVENrootReturnsInstallFolder(testCase)
-0012             p = findRAVENroot();
-0013             testCase.verifyTrue(isfolder(p));
-0014             testCase.verifyTrue(exist(fullfile(p,'installation','findRAVENroot.m'),'file')==2);
-0015         end
-0016 
-0017         function findRAVENrootSurvivesClearAndRepeatCalls(testCase)
-0018             % The resolved path is cached for the session (findRAVENroot is
-0019             % called on every solver invocation, so re-reading the
-0020             % preference from disk every time is wasteful). Repeated calls
-0021             % must keep returning the same root, and it must still resolve
-0022             % correctly after being cleared, which is what checkRaven
-0023             % does when the RAVEN.ravenPath preference changes.
-0024             p1 = findRAVENroot();
-0025             p2 = findRAVENroot();
-0026             testCase.verifyEqual(p2, p1);
-0027             clear('findRAVENroot');
-0028             p3 = findRAVENroot();
-0029             testCase.verifyEqual(p3, p1);
-0030         end
-0031 
-0032         function checkRavenReturnsVersion(testCase)
-0033             [~, currVer] = evalc('checkRaven(false, false)');
-0034             testCase.verifyNotEmpty(currVer);
-0035         end
-0036 
-0037         function checkInstallationWarnsAndForwards(testCase)
-0038             % checkRaven itself issues other warnings during its checks (e.g.
-0039             % SBML id-prefixing), which would overwrite lastwarn before it
-0040             % could be inspected here. verifyWarning tracks every warning
-0041             % issued during the call, not just the most recent one.
-0042             testCase.verifyWarning(@() evalc('checkInstallation(false, false)'), 'RAVEN:deprecated');
-0043             [~, currVer] = evalc('checkInstallation(false, false)');
-0044             testCase.verifyNotEmpty(currVer);
-0045         end
-0046 
-0047         function checkFunctionUniquenessRuns(testCase)
-0048             [~, status] = evalc('checkFunctionUniqueness()');
-0049             testCase.verifyTrue(islogical(status) || isnumeric(status));
-0050         end
-0051 
-0052         function addRavenToUserPathExists(testCase)
-0053             testCase.verifyEqual(exist('addRavenToUserPath','file'), 2);
-0054         end
-0055 
-0056         function removeRavenFromPathExists(testCase)
-0057             testCase.verifyEqual(exist('removeRavenFromPath','file'), 2);
-0058         end
-0059 
-0060         function updateDocumentationExists(testCase)
-0061             testCase.verifyEqual(exist('updateDocumentation','file'), 2);
-0062         end
-0063 
-0064     end
-0065 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tLocalization.html b/doc/testing/function_tests/tLocalization.html deleted file mode 100644 index 19ffd845..00000000 --- a/doc/testing/function_tests/tLocalization.html +++ /dev/null @@ -1,188 +0,0 @@ - - - - Description of tLocalization - - - - - - - - - -
Home > testing > function_tests > tLocalization.m
- - - -

tLocalization -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tLocalization < RavenTestCase
-0002 % tLocalization  Tests for the subcellular-localization score parsers in localization/.
-0003 %
-0004 %   Exercises parseScores for each supported source (DeepLoc 2, MULocDeep, the COMPARTMENTS
-0005 %   database and UniProt) plus defaultCompartmentMap, using small fixture files written to a
-0006 %   temporary location. No external predictor or network access is needed.
-0007 
-0008     methods (Test)
-0009 
-0010         function deeploc(testCase)
-0011             f = i_tmp(testCase, '.csv');
-0012             writelines([
-0013                 "Protein_ID,Localizations,Signals,Cytoplasm,Nucleus,Mitochondrion"
-0014                 "G1,Cytoplasm,,0.8,0.1,0.05"
-0015                 "G2,Mitochondrion,SP,0.05,0.15,0.9"], f);
-0016             GSS = parseScores(f, 'predictor', 'deeploc');
-0017             testCase.verifyEqual(sort(string(GSS.compartments(:)')), ...
-0018                 ["Cytoplasm" "Mitochondrion" "Nucleus"]);
-0019             testCase.verifyEqual(i_score(GSS,'G1','Cytoplasm'), 1, 'AbsTol', 1e-9);
-0020             testCase.verifyEqual(i_score(GSS,'G2','Mitochondrion'), 1, 'AbsTol', 1e-9);
-0021         end
-0022 
-0023         function deeplocWithCompartmentMap(testCase)
-0024             f = i_tmp(testCase, '.csv');
-0025             writelines([
-0026                 "Protein_ID,Localizations,Signals,Cytoplasm,Nucleus,Mitochondrion,Plastid"
-0027                 "G1,Cytoplasm,,0.8,0.1,0.05,0.4"], f);
-0028             GSS = parseScores(f, 'predictor', 'deeploc', 'compartmentMap', defaultCompartmentMap());
-0029             % labels mapped to model ids; Plastid (no fungal equivalent) dropped
-0030             testCase.verifyEqual(sort(string(GSS.compartments(:)')), ["c" "m" "n"]);
-0031             testCase.verifyEqual(i_score(GSS,'G1','c'), 1, 'AbsTol', 1e-9);
-0032         end
-0033 
-0034         function mulocdeep(testCase)
-0035             f = i_tmp(testCase, '.tsv');
-0036             t = char(9);
-0037             writelines([
-0038                 string(['protein' t 'Cytoplasm' t 'Mitochondrion' t 'Peroxisome'])
-0039                 string(['G1' t '0.2' t '0.7' t '0.1'])
-0040                 string(['G2' t '0.9' t '0.05' t '0.05'])], f);
-0041             GSS = parseScores(f, 'predictor', 'mulocdeep', 'compartmentMap', defaultCompartmentMap());
-0042             testCase.verifyEqual(sort(string(GSS.compartments(:)')), ["c" "m" "p"]);
-0043             testCase.verifyEqual(i_score(GSS,'G1','m'), 1, 'AbsTol', 1e-9);
-0044             testCase.verifyEqual(i_score(GSS,'G2','c'), 1, 'AbsTol', 1e-9);
-0045         end
-0046 
-0047         function compartments(testCase)
-0048             f = i_tmp(testCase, '.tsv');
-0049             t = char(9);
-0050             writelines([
-0051                 string(['YGL001C' t 'ERG26' t 'GO:0005739' t 'Mitochondrion' t '4.5'])
-0052                 string(['YGL001C' t 'ERG26' t 'GO:0005829' t 'Cytosol'       t '2.0'])
-0053                 string(['YGL002W' t 'ABC1'  t 'GO:0005777' t 'Peroxisome'    t '3.0'])
-0054                 string(['YGL002W' t 'ABC1'  t 'GO:0099999' t 'Plastid'       t '5.0'])], f);
-0055             GSS = parseScores(f, 'predictor', 'compartments', ...
-0056                               'compartmentMap', defaultCompartmentMap());
-0057             testCase.verifyEqual(sort(string(GSS.compartments(:)')), ["c" "m" "p"]);
-0058             testCase.verifyEqual(i_score(GSS,'YGL001C','m'), 1, 'AbsTol', 1e-9);          % 4.5 → 1
-0059             testCase.verifyEqual(i_score(GSS,'YGL001C','c'), 2.0/4.5, 'AbsTol', 1e-9);
-0060             % minConfidence drops the weak cytosol annotation
-0061             GSS2 = parseScores(f, 'predictor', 'compartments', ...
-0062                                'compartmentMap', defaultCompartmentMap(), 'minConfidence', 3.0);
-0063             testCase.verifyFalse(any(strcmp(GSS2.compartments,'c')));
-0064         end
-0065 
-0066         function uniprot(testCase)
-0067             f = i_tmp(testCase, '.tsv');
-0068             t = char(9);
-0069             writelines([
-0070                 string(['Entry' t 'Gene Names (primary)' t 'Gene Names (ordered locus)' t ...
-0071                         'Subcellular location [CC]'])
-0072                 string(['P00890' t 'CIT1' t 'YNR001C' t ...
-0073                         'SUBCELLULAR LOCATION: Mitochondrion matrix {ECO:1}.'])
-0074                 string(['P12345' t 'GENEX' t 'YAL001C' t ...
-0075                         'SUBCELLULAR LOCATION: Cytoplasm. Nucleus {ECO:2}. ' ...
-0076                         'Note=Shuttles to the mitochondrion under stress.'])], f);
-0077             GSS = parseScores(f, 'predictor', 'uniprot', 'idColumn', 'Gene Names (ordered locus)');
-0078             testCase.verifyEqual(i_score(GSS,'YNR001C','m'), 1, 'AbsTol', 1e-9);
-0079             testCase.verifyEqual(i_score(GSS,'YAL001C','c'), 1, 'AbsTol', 1e-9);
-0080             testCase.verifyEqual(i_score(GSS,'YAL001C','n'), 1, 'AbsTol', 1e-9);
-0081             % the "mitochondrion" mention inside Note=… is ignored
-0082             testCase.verifyEqual(i_score(GSS,'YAL001C','m'), 0, 'AbsTol', 1e-9);
-0083         end
-0084 
-0085         function uniprotWholeWordMatch(testCase)
-0086             % 'Cytoplasmic vesicle' must NOT be mis-read as cytosol ('c') via substring match
-0087             f = i_tmp(testCase, '.tsv');
-0088             t = char(9);
-0089             writelines([
-0090                 string(['Entry' t 'Subcellular location [CC]'])
-0091                 string(['YAA' t 'SUBCELLULAR LOCATION: Cytoplasmic vesicle.'])
-0092                 string(['YBB' t 'SUBCELLULAR LOCATION: Cytoplasm.'])], f);
-0093             GSS = parseScores(f, 'predictor', 'uniprot');
-0094             testCase.verifyEqual(i_score(GSS,'YBB','c'), 1, 'AbsTol', 1e-9);
-0095             testCase.verifyFalse(any(strcmp(GSS.genes,'YAA')));   % no real location -> dropped
-0096         end
-0097 
-0098         function uniprotMultiIsoformNote(testCase)
-0099             % a Note= between isoform blocks must not swallow the later isoform's location
-0100             f = i_tmp(testCase, '.tsv');
-0101             t = char(9);
-0102             writelines([
-0103                 string(['Entry' t 'Subcellular location [CC]'])
-0104                 string(['YCC' t 'SUBCELLULAR LOCATION: [Isoform 1]: Mitochondrion {ECO:1}. ' ...
-0105                         'Note=Regulated under stress. [Isoform 2]: Cytoplasm {ECO:2}.'])], f);
-0106             GSS = parseScores(f, 'predictor', 'uniprot');
-0107             testCase.verifyEqual(i_score(GSS,'YCC','m'), 1, 'AbsTol', 1e-9);
-0108             testCase.verifyEqual(i_score(GSS,'YCC','c'), 1, 'AbsTol', 1e-9);   % isoform 2 kept
-0109         end
-0110 
-0111         function wolfRemoved(testCase)
-0112             f = i_tmp(testCase, '.txt');
-0113             writelines("anything", f);
-0114             testCase.verifyError(@() parseScores(f, 'predictor', 'wolf'), 'RAVEN:badInput');
-0115         end
-0116 
-0117     end
-0118 end
-0119 
-0120 % ------------------------------------------------------------------------- helpers
-0121 
-0122 function f = i_tmp(testCase, ext)
-0123 f = [tempname ext];
-0124 testCase.addTeardown(@() i_delete(f));
-0125 end
-0126 
-0127 function i_delete(f)
-0128 if isfile(f); delete(f); end
-0129 end
-0130 
-0131 function s = i_score(GSS, gene, comp)
-0132 gi = strcmp(GSS.genes, gene);
-0133 ci = strcmp(GSS.compartments, comp);
-0134 if ~any(gi) || ~any(ci)
-0135     s = 0;
-0136 else
-0137     s = GSS.scores(gi, ci);
-0138 end
-0139 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tManipulation.html b/doc/testing/function_tests/tManipulation.html deleted file mode 100644 index fdfc2df7..00000000 --- a/doc/testing/function_tests/tManipulation.html +++ /dev/null @@ -1,1005 +0,0 @@ - - - - Description of tManipulation - - - - - - - - - -
Home > testing > function_tests > tManipulation.m
- - - -

tManipulation -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tManipulation < RavenTestCase
-0002 % tManipulation  Tests for the structural model-editing functions in manipulation/.
-0003 %
-0004 %   Assertions favour structural invariants (counts, membership, round-trips,
-0005 %   idempotence) over full golden-model comparisons, so they stay robust while
-0006 %   still exercising each function's main path. evalc is used to suppress the
-0007 %   informational printing some functions do.
-0008 
-0009     methods (Test)
-0010 
-0011         function addExchangeRxnsAddsOne(testCase)
-0012             [m2, added] = addExchangeRxns(testCase.model, 'both', '6pgl_c');
-0013             testCase.verifyEqual(numel(m2.rxns), numel(testCase.model.rxns) + 1);
-0014             testCase.verifyNumElements(added, 1);
-0015         end
-0016 
-0017         function addGenesRavenAddsGenes(testCase)
-0018             g.genes = {'testgene1','testgene2'};
-0019             g.geneShortNames = {'s1','s2'};
-0020             m2 = addGenesRaven(testCase.model, g);
-0021             testCase.verifyEqual(numel(m2.genes), numel(testCase.model.genes) + 2);
-0022             testCase.verifyTrue(all(ismember(g.genes, m2.genes)));
-0023         end
-0024 
-0025         function addGenesRavenPartialOverlapAddsRemainder(testCase)
-0026             % When some genesToAdd.genes already exist in the model, the
-0027             % others must still be added, and every parallel field must stay
-0028             % aligned with the (trimmed) gene list.
-0029             g.genes = {testCase.model.genes{1}; 'newgene1'};
-0030             g.geneShortNames = {'existing','s1'};
-0031             evalc('m2 = addGenesRaven(testCase.model, g);');
-0032             testCase.verifyEqual(numel(m2.genes), numel(testCase.model.genes) + 1);
-0033             testCase.verifyTrue(ismember('newgene1', m2.genes));
-0034             testCase.verifyEqual(numel(m2.geneShortNames), numel(m2.genes));
-0035         end
-0036 
-0037         function addMetsAddsMets(testCase)
-0038             mta.metNames = {'newMetA','newMetB'};
-0039             mta.compartments = {'c','e'};
-0040             evalc('m2 = addMets(testCase.model, mta);');
-0041             testCase.verifyEqual(numel(m2.mets), numel(testCase.model.mets) + 2);
-0042         end
-0043 
-0044         function addRxnsAllowNewGenesKeepsGeneIdsIntact(testCase)
-0045             % A gene id that merely contains "and"/"or" as a substring
-0046             % (e.g. "band1") must not be shredded by the grRule parser used
-0047             % to discover new genes.
-0048             r.rxns = 'newRxn1';
-0049             r.equations = '2-Oxoglutarate => TEST';
-0050             r.grRules = 'band1 and orfeo2';
-0051             evalc('m2 = addRxns(testCase.model, r, 2, ''c'', true, true);');
-0052             testCase.verifyTrue(all(ismember({'band1','orfeo2'}, m2.genes)));
-0053         end
-0054 
-0055         function addRxnsPrintsNewGeneIdWithPercent(testCase)
-0056             % A new gene id containing "%" must not be truncated when
-0057             % printed to the "New genes added" notice.
-0058             r.rxns = 'newRxn1';
-0059             r.equations = '2-Oxoglutarate => TEST';
-0060             r.grRules = 'NEWGENE_50%_test';
-0061             out = evalc('addRxns(testCase.model, r, 2, ''c'', true, true);');
-0062             testCase.verifySubstring(out, 'NEWGENE_50%_test');
-0063         end
-0064 
-0065         function addRxnsAddsRxn(testCase)
-0066             r.rxns = 'newRxn1';
-0067             r.equations = '2-Oxoglutarate => TEST';
-0068             evalc('m2 = addRxns(testCase.model, r, 2, ''c'', true);');
-0069             testCase.verifyEqual(numel(m2.rxns), numel(testCase.model.rxns) + 1);
-0070             testCase.verifyTrue(ismember('newRxn1', m2.rxns));
-0071         end
-0072 
-0073         function addRxnsKeepsSpontaneousAligned(testCase)
-0074             % A model that already tracks spontaneous must keep it aligned
-0075             % with rxns after adding reactions that don't specify it, and
-0076             % accept an explicit spontaneous value for the new ones too.
-0077             m = testCase.model;
-0078             m.spontaneous = false(numel(m.rxns), 1);
-0079             r.rxns = 'newRxn1';
-0080             r.equations = '2-Oxoglutarate => TEST';
-0081             evalc('m2 = addRxns(m, r, 2, ''c'', true);');
-0082             testCase.verifyEqual(numel(m2.spontaneous), numel(m2.rxns));
-0083             testCase.verifyFalse(m2.spontaneous(end));
-0084 
-0085             r2.rxns = 'newRxn2';
-0086             r2.equations = '2-Oxoglutarate => TEST2';
-0087             r2.spontaneous = true;
-0088             evalc('m3 = addRxns(m, r2, 2, ''c'', true);');
-0089             testCase.verifyEqual(numel(m3.spontaneous), numel(m3.rxns));
-0090             testCase.verifyTrue(m3.spontaneous(end));
-0091         end
-0092 
-0093         function addRxnsKeepsEquationsAligned(testCase)
-0094             % A model that already tracks equations must keep it aligned
-0095             % with rxns after adding a reaction, reusing the equation as
-0096             % given rather than leaving the field short by one.
-0097             m = testCase.model;
-0098             m.equations = constructEquations(m);
-0099             r.rxns = 'newRxn1';
-0100             r.equations = '2-Oxoglutarate => TEST';
-0101             evalc('m2 = addRxns(m, r, 2, ''c'', true);');
-0102             testCase.verifyEqual(numel(m2.equations), numel(m2.rxns));
-0103             testCase.verifySubstring(m2.equations{end}, 'TEST');
-0104         end
-0105 
-0106         function addRxnsStringEqnTypeIdAlias(testCase)
-0107             % 'id' is the string alias for eqnType=1 (match by model.mets).
-0108             r.rxns = {'idAliasRxn'};
-0109             r.equations = {[testCase.model.mets{1} ' => ' testCase.model.mets{2}]};
-0110             m1 = addRxns(testCase.model, r, 1);
-0111             m2 = addRxns(testCase.model, r, 'id');
-0112             testCase.verifyEqual(m1.S, m2.S);
-0113         end
-0114 
-0115         function addRxnsStringEqnTypeNameAlias(testCase)
-0116             % 'name' is the string alias for eqnType=2 (match by model.metNames).
-0117             r.rxns = {'nameAliasRxn'};
-0118             r.equations = {'2-Oxoglutarate => NAMETEST'};
-0119             evalc('m1 = addRxns(testCase.model, r, 2, ''c'', true);');
-0120             evalc('m2 = addRxns(testCase.model, r, ''name'', ''c'', true);');
-0121             testCase.verifyEqual(m1.S, m2.S);
-0122         end
-0123 
-0124         function addRxnsStringEqnTypeNameCompAlias(testCase)
-0125             % 'name[comp]' is the string alias for eqnType=3.
-0126             r.rxns = {'namecompAliasRxn'};
-0127             r.equations = {'2-Oxoglutarate[c] => COMPTEST[c]'};
-0128             evalc('m1 = addRxns(testCase.model, r, 3, [], true);');
-0129             evalc('m2 = addRxns(testCase.model, r, ''name[comp]'', [], true);');
-0130             testCase.verifyEqual(m1.S, m2.S);
-0131         end
-0132 
-0133         function addRxnsInvalidStringEqnTypeErrors(testCase)
-0134             % An unrecognised string for eqnType must throw.
-0135             r.rxns = {'errRxn'};
-0136             r.equations = {[testCase.model.mets{1} ' => ' testCase.model.mets{2}]};
-0137             testCase.verifyError(@() addRxns(testCase.model, r, 'invalid'), ?MException);
-0138         end
-0139 
-0140         function addRxnsGenesMetsCopiesFromSource(testCase)
-0141             sbmlFile = fullfile(testCase.ravenRoot,'tutorial','empty.xml');
-0142             testCase.assumeDependency(exist(sbmlFile,'file')==2, 'tutorial/empty.xml');
-0143             evalc('src = importModel(sbmlFile, [], true);');
-0144             evalc('m2 = addRxnsGenesMets(testCase.model, src, ''r1'', true);');
-0145             testCase.verifyTrue(ismember('r1', m2.rxns));
-0146         end
-0147 
-0148         function addRxnsGenesMetsKeepsPercentInAlreadyPresentNotice(testCase)
-0149             % A rxn id containing "%" that is already present in the
-0150             % target model must survive intact in the notice, not be
-0151             % truncated by fprintf misreading it as a format directive.
-0152             model = testCase.model;
-0153             model.rxns{1} = 'RXN_50%_present';
-0154             sourceModel = model;
-0155             r.rxns = 'newRxn1';
-0156             r.equations = [model.mets{1} ' => ' model.mets{2}];
-0157             evalc('sourceModel = addRxns(sourceModel, r, 1, ''c'', true);');
-0158             out = evalc(['addRxnsGenesMets(model, sourceModel, ' ...
-0159                 '{''RXN_50%_present'',''newRxn1''});']);
-0160             testCase.verifySubstring(out, 'RXN_50%_present');
-0161         end
-0162 
-0163         function addTransportAddsRxn(testCase)
-0164             evalc(['m2 = addTransport(testCase.model, ''c'', ''e'', ' ...
-0165                 '{''6-phospho-D-glucono-1,5-lactone''}, false, false, ''tr_'');']);
-0166             testCase.verifyGreaterThan(numel(m2.rxns), numel(testCase.model.rxns));
-0167         end
-0168 
-0169         function addTransportOnModelWithoutAnnotationField(testCase)
-0170             % The default LB/UB fallback must not assume model.annotation
-0171             % exists at all, only that it may lack defaultLB/defaultUB.
-0172             m = testCase.model;
-0173             if isfield(m,'annotation')
-0174                 m = rmfield(m,'annotation');
-0175             end
-0176             evalc(['m2 = addTransport(m, ''c'', ''e'', ' ...
-0177                 '{''6-phospho-D-glucono-1,5-lactone''}, false, false, ''tr_'');']);
-0178             testCase.verifyGreaterThan(numel(m2.rxns), numel(m.rxns));
-0179         end
-0180 
-0181         function addTransportAcceptsRowOrientedMetNames(testCase)
-0182             % Regression test for SysBioChalmers/RAVEN#722: a metNames cell
-0183             % array keeps whatever orientation the caller passed it in, and
-0184             % a plain {'a','b'} literal is row-oriented. A single-metabolite
-0185             % cell (as in addTransportAddsRxn above) cannot catch this ---
-0186             % a 1x1 cell is the same shape whichever way you call it "row"
-0187             % or "column" --- so this needs two or more metabolites, both
-0188             % already present in the target compartment (the default
-0189             % onlyToExisting=true path, which is where this crashed).
-0190             metNames = {'CO2', 'Formate'}; %#ok<NASGU> % row-oriented on purpose
-0191             evalc('m2 = addTransport(testCase.model, ''c'', {''e''}, ''metNames'', metNames);');
-0192             testCase.verifyEqual(numel(m2.rxns), numel(testCase.model.rxns) + 2);
-0193             testCase.verifyEqual(size(m2.rxnNames, 2), 1);
-0194         end
-0195 
-0196         function addRxnsCleanEquationDoesNotWarn(testCase)
-0197             % The "metabolite on both sides" warning must only fire for the
-0198             % equations it names, not on every call.
-0199             rxnsToAdd.rxns      = {'newRxn'};
-0200             rxnsToAdd.equations = {[testCase.model.mets{1} ' => ' testCase.model.mets{2}]};
-0201             lastwarn('');
-0202             evalc('addRxns(testCase.model, rxnsToAdd, 1);');
-0203             testCase.verifyEmpty(strfind(lastwarn, 'both as substrate and product')); %#ok<STRIFY>
-0204         end
-0205 
-0206         function changeGrRulesUpdatesRule(testCase)
-0207             m2 = changeGrRules(testCase.model, 'ACKr', 'b2296 and b1849', true);
-0208             idx = strcmp(m2.rxns, 'ACKr');
-0209             testCase.verifyEqual(m2.grRules{idx}, 'b2296 and b1849');
-0210         end
-0211 
-0212         function changeRxnsUpdatesEquation(testCase)
-0213             evalc('m2 = changeRxns(testCase.model, ''ACKr'', ''2-Oxoglutarate <=> TEST'', 2, ''c'', true);');
-0214             testCase.verifyEqual(numel(m2.rxns), numel(testCase.model.rxns));
-0215         end
-0216 
-0217         function closeModelReturnsStruct(testCase)
-0218             m2 = closeModel(testCase.model);
-0219             testCase.verifyClass(m2, 'struct');
-0220             testCase.verifyGreaterThanOrEqual(numel(m2.rxns), numel(testCase.model.rxns));
-0221         end
-0222 
-0223         function closeModelHandlesTwoColumnBAndMetNotes(testCase)
-0224             % b(numel(b)+1)=0 is a linear index, which for an N-by-2 b
-0225             % (net-production bounds) does not append a row; it errors.
-0226             % metNotes must also be padded like the other optional
-0227             % per-metabolite fields, or it goes out of sync with mets.
-0228             m = tManipulation.twoMetModel();  % R1: a => b
-0229             r.rxns = {'R2'}; r.equations = {'b =>'};  % sink, so closeModel adds a boundary met
-0230             evalc('m = addRxns(m, r, 1, [], false);');
-0231             m.b = [zeros(2,1) ones(2,1)];     % two-column b
-0232             m.metNotes = {'note a';'note b'};
-0233             m2 = closeModel(m);
-0234             testCase.verifyEqual(size(m2.b,2), 2);
-0235             testCase.verifyEqual(size(m2.b,1), numel(m2.mets));
-0236             testCase.verifyEqual(numel(m2.metNotes), numel(m2.mets));
-0237         end
-0238 
-0239         function closeModelDetectsScaledAndMultiMetSinks(testCase)
-0240             % closeModel's boundary-reaction rule is "metabolites on only
-0241             % one side" (matching getExchangeRxns), not "coefficients
-0242             % summing to 1 in absolute value": a scaled single-metabolite
-0243             % sink and a multi-metabolite one must both be detected and
-0244             % closed, while a genuine two-sided reaction is left alone.
-0245             m = tManipulation.twoMetModel();  % R1: a => b, a genuine reaction
-0246             r.rxns = {'R2';'R3'};
-0247             r.equations = {'2 a =>'; '0.5 a + 0.5 b =>'};
-0248             evalc('m = addRxns(m, r, 1, [], false);');
-0249             nMetsBefore = numel(m.mets);
-0250             m2 = closeModel(m);
-0251 
-0252             % Exactly R2 and R3 are exchange-like, so exactly two boundary
-0253             % metabolites are added, one per closed reaction.
-0254             testCase.verifyEqual(numel(m2.mets) - nMetsBefore, 2);
-0255             testCase.verifyEqual(numel(m2.metNames), numel(m2.mets));
-0256             testCase.verifyEqual(numel(m2.metComps), numel(m2.mets));
-0257 
-0258             idx = getIndexes(m2, {'R1';'R2';'R3'}, 'rxns');
-0259             boundaryComp = numel(m2.comps);
-0260             touchesBoundary = full(any(m2.S(m2.metComps==boundaryComp, idx) ~= 0, 1));
-0261             testCase.verifyEqual(touchesBoundary, [false true true]);
-0262         end
-0263 
-0264         function contractModelNoMoreRxns(testCase)
-0265             evalc('m2 = contractModel(testCase.model);');
-0266             testCase.verifyLessThanOrEqual(numel(m2.rxns), numel(testCase.model.rxns));
-0267         end
-0268 
-0269         function convertToIrrevAllIrreversible(testCase)
-0270             m2 = convertToIrrev(testCase.model);
-0271             testCase.verifyTrue(all(m2.rev == 0));
-0272             testCase.verifyGreaterThanOrEqual(numel(m2.rxns), numel(testCase.model.rxns));
-0273         end
-0274 
-0275         function convertToIrrevSplitsBoundsAndStoichiometry(testCase)
-0276             % A reversible reaction with bounds (-500,1000) keeps (0,1000) and
-0277             % its stoichiometry, while the _REV copy gets (0,500) and the
-0278             % negated stoichiometry.
-0279             m = tManipulation.twoMetModel();
-0280             m.rev = 1; m.lb = -500; m.ub = 1000;
-0281             m2 = convertToIrrev(m);
-0282 
-0283             fwd = strcmp(m2.rxns, 'R1');
-0284             rev = strcmp(m2.rxns, 'R1_REV');
-0285             testCase.verifyTrue(any(rev));
-0286             testCase.verifyEqual(full(m2.lb(fwd)), 0);
-0287             testCase.verifyEqual(full(m2.ub(fwd)), 1000);
-0288             testCase.verifyEqual(full(m2.lb(rev)), 0);
-0289             testCase.verifyEqual(full(m2.ub(rev)), 500);
-0290             testCase.verifyEqual(full(m2.S(:,fwd)), [-1;1]);
-0291             testCase.verifyEqual(full(m2.S(:,rev)), [1;-1]);
-0292         end
-0293 
-0294         function convertToIrrevReverseCopyInheritsGrRule(testCase)
-0295             % The _REV copy is catalysed by the same genes as the forward one.
-0296             m = tManipulation.twoMetModel();
-0297             m.rev = 1; m.lb = -500; m.ub = 1000;
-0298             m.genes = {'g1'}; m.grRules = {'g1'}; m.rxnGeneMat = sparse(1,1,1);
-0299             m2 = convertToIrrev(m);
-0300             testCase.verifyEqual(m2.grRules{strcmp(m2.rxns,'R1_REV')}, 'g1');
-0301         end
-0302 
-0303         function convertToIrrevReverseCopyInheritsSpontaneousAndPwys(testCase)
-0304             % The _REV copy must carry the same spontaneous/pwys annotation
-0305             % as the forward reaction, keeping both fields aligned with rxns.
-0306             m = tManipulation.twoMetModel();
-0307             m.rev = 1; m.lb = -500; m.ub = 1000;
-0308             m.spontaneous = true;
-0309             m.pwys = {'pathway1'};
-0310             m2 = convertToIrrev(m);
-0311             testCase.verifyEqual(numel(m2.spontaneous), numel(m2.rxns));
-0312             testCase.verifyEqual(numel(m2.pwys), numel(m2.rxns));
-0313             testCase.verifyTrue(m2.spontaneous(strcmp(m2.rxns,'R1_REV')));
-0314             testCase.verifyEqual(m2.pwys{strcmp(m2.rxns,'R1_REV')}, 'pathway1');
-0315         end
-0316 
-0317         function convertToIrrevRev2irrevPointsAtReverseCopy(testCase)
-0318             % rev2irrev{origIdx}'s second element must be the actual
-0319             % position of that reaction's reverse copy in irrevModel
-0320             % (numOrigRxns+i), not just its rank among reversible
-0321             % reactions.
-0322             m = tManipulation.twoMetModel();       % R1: a -> b
-0323             r.rxns = {'R2'}; r.equations = {'b <=> a'};
-0324             evalc('m = addRxns(m, r, 1, [], false);');
-0325             m.rev(2) = 1; m.lb(2) = -1000; m.ub(2) = 1000;
-0326             [irrevModel, ~, rev2irrev] = convertToIrrev(m);
-0327             pair = rev2irrev{2};
-0328             testCase.verifyEqual(pair(1), 2);
-0329             testCase.verifyEqual(irrevModel.rxns{pair(2)}, 'R2_REV');
-0330         end
-0331 
-0332         function findDuplicateRxnsIgnoreDirection(testCase)
-0333             % a -> b and b -> a are the same reaction run backwards, so they
-0334             % group by default and stay separate when direction matters.
-0335             m = tManipulation.twoMetModel();
-0336             m.rxns     = {'R1';'R2'};
-0337             m.rxnNames = {'R1';'R2'};
-0338             m.S   = sparse([-1 1; 1 -1]);
-0339             m.lb  = [0;0]; m.ub = [1000;1000]; m.rev = [0;0]; m.c = [0;0];
-0340             m.grRules = {'';''}; m.rxnGeneMat = sparse(2,0);
-0341 
-0342             pairs = findDuplicateRxns(m);
-0343             testCase.verifyEqual(pairs, [1 2]);
-0344 
-0345             pairs = findDuplicateRxns(m, 'ignoreDirection', false);
-0346             testCase.verifyEmpty(pairs);
-0347         end
-0348 
-0349         function changeGrRulesAppendsToExistingRule(testCase)
-0350             % replace=false must OR the new rule onto the existing one rather
-0351             % than overwrite it, and add the new gene to the model.
-0352             m2 = changeGrRules(testCase.model, 'ACKr', 'b9999', false);
-0353             rule = m2.grRules{strcmp(m2.rxns, 'ACKr')};
-0354             testCase.verifySubstring(rule, 'b9999');
-0355             testCase.verifySubstring(rule, ' or ');
-0356             % the gene the reaction already had must still be in the rule
-0357             oldRule = testCase.model.grRules{strcmp(testCase.model.rxns, 'ACKr')};
-0358             oldGene = regexp(oldRule, 'b\d+', 'match', 'once');
-0359             testCase.verifySubstring(rule, oldGene);
-0360             testCase.verifyTrue(ismember('b9999', m2.genes));
-0361         end
-0362 
-0363         function copyToCompsDeleteOriginalIsAMove(testCase)
-0364             % deleteOriginal turns the copy into a move: the reaction count is
-0365             % unchanged and the original compartment's copy is gone.
-0366             evalc('copied = copyToComps(testCase.model, {''p''}, ''rxns'', ''ACKr'');');
-0367             evalc(['moved = copyToComps(testCase.model, {''p''}, ''rxns'', ''ACKr'', ' ...
-0368                 '''deleteOriginal'', true);']);
-0369             testCase.verifyEqual(numel(copied.rxns), numel(testCase.model.rxns) + 1);
-0370             testCase.verifyEqual(numel(moved.rxns), numel(testCase.model.rxns));
-0371         end
-0372 
-0373         function mergeModelsMetParamDecidesUnification(testCase)
-0374             % The same metabolite under two ids unifies when matching on names
-0375             % and stays distinct when matching on ids.
-0376             a = tManipulation.namedMetModel('glc_c', 'A');
-0377             b = tManipulation.namedMetModel('glucose_c', 'B');
-0378             evalc('byName = mergeModels({a; b});');
-0379             evalc('byId   = mergeModels({a; b}, ''metParam'', ''mets'');');
-0380             testCase.verifyEqual(nnz(strcmp(byName.metNames, 'Glucose')), 1);
-0381             testCase.verifyEqual(nnz(strcmp(byId.metNames, 'Glucose')), 2);
-0382         end
-0383 
-0384         function mergeModelsKeepsSpontaneousAligned(testCase)
-0385             % A model carrying spontaneous merged with one that doesn't
-0386             % must keep the field aligned with rxns, defaulting the
-0387             % other model's reactions to false rather than leaving it
-0388             % short.
-0389             a = tManipulation.namedMetModel('glc_c', 'A');
-0390             a.spontaneous = true;
-0391             b = tManipulation.namedMetModel('glucose_c', 'B');
-0392             evalc('merged = mergeModels({a; b}, ''metParam'', ''mets'');');
-0393             testCase.verifyEqual(numel(merged.spontaneous), numel(merged.rxns));
-0394             testCase.verifyTrue(merged.spontaneous(strcmp(merged.rxns,'R_A')));
-0395             testCase.verifyFalse(merged.spontaneous(strcmp(merged.rxns,'R_B')));
-0396         end
-0397 
-0398         function copyToCompsDefaultCompOutsideAddsCompartment(testCase)
-0399             % Adding a new compartment without specifying compOutside must
-0400             % not error when the model already tracks compOutside.
-0401             m = testCase.model;
-0402             m.compOutside = repmat({''}, numel(m.comps), 1);
-0403             evalc('m2 = copyToComps(m, {''p''}, ''ACKr'');');
-0404             testCase.verifyEqual(numel(m2.compOutside), numel(m2.comps));
-0405         end
-0406 
-0407         function copyToCompsAddsCompartment(testCase)
-0408             evalc('m2 = copyToComps(testCase.model, {''p''}, ''ACKr'');');
-0409             testCase.verifyTrue(ismember('p', m2.comps));
-0410             testCase.verifyGreaterThan(numel(m2.rxns), numel(testCase.model.rxns));
-0411         end
-0412 
-0413         function deleteUnusedGenesNoMoreGenes(testCase)
-0414             evalc('m2 = deleteUnusedGenes(testCase.model);');
-0415             testCase.verifyLessThanOrEqual(numel(m2.genes), numel(testCase.model.genes));
-0416         end
-0417 
-0418         function expandModelNoFewerRxns(testCase)
-0419             evalc('m2 = expandModel(testCase.model);');
-0420             testCase.verifyGreaterThanOrEqual(numel(m2.rxns), numel(testCase.model.rxns));
-0421         end
-0422 
-0423         function findDuplicateRxnsReturnsResult(testCase)
-0424             pairs = findDuplicateRxns(testCase.model);
-0425             testCase.verifyTrue(isnumeric(pairs) || islogical(pairs) || iscell(pairs));
-0426         end
-0427 
-0428         function generateNewIdsUniqueAndPrefixed(testCase)
-0429             ids = generateNewIds(testCase.model, 'rxns', 'r_', 5);
-0430             testCase.verifyNumElements(ids, 5);
-0431             testCase.verifyEqual(numel(unique(ids)), 5);
-0432             testCase.verifyTrue(all(startsWith(ids, 'r_')));
-0433         end
-0434 
-0435         function generateNewIdsEscapesRegexPrefix(testCase)
-0436             % A prefix containing a regex metacharacter ('.') must be
-0437             % matched literally: 'pX999' does not use the 'p.' prefix and
-0438             % must not be picked up as if '.' were a wildcard.
-0439             m.rxns = {'p.001'; 'pX999'};
-0440             ids = generateNewIds(m, 'rxns', 'p.', 'quantity', 1);
-0441             testCase.verifyEqual(ids{1}, 'p.002');
-0442         end
-0443 
-0444         function mergeCompartmentsSingleComp(testCase)
-0445             evalc('m2 = mergeCompartments(testCase.model);');
-0446             testCase.verifyNumElements(m2.comps, 1);
-0447         end
-0448 
-0449         function mergeCompartmentsWarnsWhenUnconstrainedMissing(testCase)
-0450             % The warning's own text says it fires because there is no
-0451             % unconstrained field to tell single-metabolite reactions apart
-0452             % from real exchange reactions; the condition guarding it
-0453             % checked the opposite, firing only when the field WAS present.
-0454             m = testCase.model;
-0455             testCase.verifyWarning(@() mergeCompartments(m,'deleteRxnsWithOneMet',true), ...
-0456                 'RAVEN:warning');
-0457         end
-0458 
-0459         function mergeCompartmentsDropsStaleCompMiriams(testCase)
-0460             % Merging collapses every compartment into one, so any
-0461             % per-compartment MIRIAM annotation must not be left behind at the
-0462             % original length (which would desync compMiriams from comps).
-0463             m = testCase.model;
-0464             m.compMiriams = cell(numel(m.comps),1);
-0465             m.compMiriams{1}.name  = {'go'};
-0466             m.compMiriams{1}.value = {'GO:0005737'};
-0467             evalc('m2 = mergeCompartments(m);');
-0468             if isfield(m2,'compMiriams')
-0469                 testCase.verifyNumElements(m2.compMiriams, numel(m2.comps));
-0470             end
-0471         end
-0472 
-0473         function mergeModelsReturnsStruct(testCase)
-0474             modelB = removeReactions(testCase.model, testCase.model.rxns(1:5), true, true, true);
-0475             evalc('merged = mergeModels({testCase.model; modelB});');
-0476             testCase.verifyClass(merged, 'struct');
-0477             testCase.verifyNotEmpty(merged.rxns);
-0478         end
-0479 
-0480         function mergeModelsSelfCollisionGetsUniqueIds(testCase)
-0481             % A source model with two internally duplicate reaction ids
-0482             % ('R1' twice) that both collide with the growing merged model
-0483             % must each be renamed to a distinct id, not the same one.
-0484             a = tManipulation.twoMetModel();
-0485             b = tManipulation.duplicateRxnModel('M2');
-0486             evalc('merged = mergeModels({a; b});');
-0487             testCase.verifyEqual(numel(unique(merged.rxns)), numel(merged.rxns));
-0488             newIds = merged.rxns(2:3);
-0489             testCase.verifyTrue(all(startsWith(newIds, 'R1_M2')));
-0490         end
-0491 
-0492         function permuteModelReversesOrder(testCase)
-0493             n = numel(testCase.model.rxns);
-0494             m2 = permuteModel(testCase.model, n:-1:1, 'rxns');
-0495             testCase.verifyEqual(m2.rxns, flipud(testCase.model.rxns));
-0496         end
-0497 
-0498         function permuteModelRemapsRxnComps(testCase)
-0499             % After swapping compartments the rxnComps and geneComps indices
-0500             % in the OUTPUT must follow the permutation; the input is unchanged.
-0501             m = struct();
-0502             m.rxns = {'r1'; 'r2'};
-0503             m.mets = {'a_c'; 'b_e'};
-0504             m.S    = sparse([1 -1; -1 1]);
-0505             m.lb   = [0; 0]; m.ub = [1000; 1000]; m.rev = [0; 0];
-0506             m.c    = [1; 0]; m.b  = [0; 0];
-0507             m.comps     = {'c'; 'e'};
-0508             m.compNames = {'Cytoplasm'; 'Extracellular'};
-0509             m.metComps  = [1; 2];
-0510             m.genes     = {'g1'; 'g2'};
-0511             m.rxnGeneMat = sparse(eye(2));
-0512             m.grRules   = {'g1'; 'g2'};
-0513             m.rxnComps  = [1; 2];   % r1→comps{1}, r2→comps{2}
-0514             m.geneComps = [1; 2];
-0515             m2 = permuteModel(m, [2, 1], 'comps');
-0516             % Swap: old comps{1}→new position 2, old comps{2}→new position 1
-0517             testCase.verifyEqual(m2.rxnComps,  [2; 1]);
-0518             testCase.verifyEqual(m2.geneComps, [2; 1]);
-0519             % Input must be unmodified
-0520             testCase.verifyEqual(m.rxnComps,  [1; 2]);
-0521             testCase.verifyEqual(m.geneComps, [1; 2]);
-0522         end
-0523 
-0524         function removeBadRxnsReturnsStruct(testCase)
-0525             evalc('m2 = removeBadRxns(testCase.model);');
-0526             testCase.verifyClass(m2, 'struct');
-0527         end
-0528 
-0529         function removeBadRxnsSeedIsReproducible(testCase)
-0530             % Which reaction is removed among several equally-valid
-0531             % candidates is randomised; a given seed must make the choice
-0532             % (and thus the result) reproducible across runs.
-0533             evalc(['[~, r1] = removeBadRxns(testCase.model, ''rxnRules'', 3, ''seed'', 42);' ...
-0534                 '[~, r2] = removeBadRxns(testCase.model, ''rxnRules'', 3, ''seed'', 42);']);
-0535             testCase.verifyEqual(r1, r2);
-0536         end
-0537 
-0538         function removeGenesRemovesGene(testCase)
-0539             m2 = removeGenes(testCase.model, 'b1817', true, true, false);
-0540             testCase.verifyFalse(ismember('b1817', m2.genes));
-0541             testCase.verifyLessThan(numel(m2.genes), numel(testCase.model.genes));
-0542         end
-0543 
-0544         function removeMetsRemovesMet(testCase)
-0545             m2 = removeMets(testCase.model, 'Acetate', true, true, true, true);
-0546             testCase.verifyLessThan(numel(m2.mets), numel(testCase.model.mets));
-0547         end
-0548 
-0549         function removeReactionsRemovesRxn(testCase)
-0550             m2 = removeReactions(testCase.model, testCase.model.rxns(1), true, true, true);
-0551             testCase.verifyEqual(numel(m2.rxns), numel(testCase.model.rxns) - 1);
-0552             testCase.verifyFalse(ismember(testCase.model.rxns{1}, m2.rxns));
-0553         end
-0554 
-0555         function replaceMetsMergesMet(testCase)
-0556             evalc('m2 = replaceMets(testCase.model, ''Acetaldehyde'', ''Acetate'');');
-0557             testCase.verifyLessThan(numel(m2.mets), numel(testCase.model.mets));
-0558         end
-0559 
-0560         function replaceMetsVerboseKeepsPercentInRxnId(testCase)
-0561             % A reaction id containing "%" that is reported by 'verbose'
-0562             % must survive intact, not be truncated by fprintf misreading
-0563             % it as a format directive.
-0564             m = struct();
-0565             m.id='t'; m.rxns={'R_50%_test';'R2'}; m.rxnNames=m.rxns;
-0566             m.mets={'x';'y';'z'}; m.metNames=m.mets; m.metComps=[1;1;1];
-0567             m.comps={'c'}; m.compNames={'c'};
-0568             m.S=sparse([-1 0; 0 -1; 1 1]); % R_50%_test: x=>z   R2: y=>z
-0569             m.lb=[0;0]; m.ub=[1000;1000]; m.rev=[0;0]; m.c=[0;0]; m.b=zeros(3,1);
-0570             m.genes={}; m.grRules={'';''}; m.rxnGeneMat=sparse(2,0);
-0571             out = evalc('replaceMets(m, ''x'', ''y'', ''verbose'', true);');
-0572             testCase.verifySubstring(out, 'R_50%_test');
-0573         end
-0574 
-0575         function replaceMetsByIdAddsRatherThanOverwrites(testCase)
-0576             % A reaction where the replacement metabolite is already itself
-0577             % a participant must keep that contribution: x+y=> must become
-0578             % 2y=>, not just y=>, once x is replaced by y.
-0579             m = struct();
-0580             m.id='t'; m.rxns={'R1';'R2'}; m.rxnNames=m.rxns;
-0581             m.mets={'x';'y';'z'}; m.metNames=m.mets; m.metComps=[1;1;1];
-0582             m.comps={'c'}; m.compNames={'c'};
-0583             m.S=sparse([0 -1; 0 -1; -1 0]); % R1: z=>   R2: x+y=>
-0584             m.lb=[0;0]; m.ub=[1000;1000]; m.rev=[0;0]; m.c=[0;0]; m.b=zeros(3,1);
-0585             m.genes={}; m.grRules={'';''}; m.rxnGeneMat=sparse(2,0);
-0586             evalc('m2 = replaceMets(m, ''x'', ''y'', ''identifiers'', true);');
-0587             yRow = strcmp(m2.mets,'y');
-0588             r2 = strcmp(m2.rxns,'R2');
-0589             testCase.verifyEqual(full(m2.S(yRow,r2)), -2);
-0590         end
-0591 
-0592         function replaceMetsByNameKeepsBShapeAndRuns(testCase)
-0593             % A model with a two-column b (net-production bounds) must
-0594             % keep that shape after the metabolites-with-duplicate-name
-0595             % merge, and the final contractModel call must not error from
-0596             % a stale post-deletion metabolite index or a disabled
-0597             % distReverse.
-0598             m = struct();
-0599             m.id='t'; m.rxns={'R1';'R2'}; m.rxnNames=m.rxns;
-0600             m.mets={'ox1';'ox2';'w'}; m.metNames={'oxygen';'o2';'w'};
-0601             m.metComps=[1;1;1]; m.comps={'c'}; m.compNames={'c'};
-0602             m.S=sparse([-1 0; 0 -1; 1 1]); % R1: oxygen=>w   R2: o2=>w
-0603             m.lb=[0;0]; m.ub=[1000;1000]; m.rev=[0;0]; m.c=[0;0];
-0604             m.b=[zeros(3,1) ones(3,1)]; % two-column b
-0605             m.genes={}; m.grRules={'';''}; m.rxnGeneMat=sparse(2,0);
-0606             evalc('m2 = replaceMets(m, ''oxygen'', ''o2'');');
-0607             testCase.verifyEqual(size(m2.b,2), 2);
-0608             testCase.verifyEqual(size(m2.b,1), numel(m2.mets));
-0609             testCase.verifyEqual(numel(m2.mets), 2); % oxygen and o2 merged
-0610         end
-0611 
-0612         function setExchangeBoundsFindsAllMultiExchangeMets(testCase)
-0613             % Every metabolite exchanged by more than one reaction must be
-0614             % reported, not just whichever one happens to line up between
-0615             % two differently-sized index ranges compared directly against
-0616             % each other.
-0617             m = struct();
-0618             m.id='t';
-0619             m.rxns={'r1';'r2';'r3';'r4';'r5';'r6';'r7'}; m.rxnNames=m.rxns;
-0620             m.mets={'met1';'met2';'met3';'met4';'met5'};
-0621             m.metNames={'MetOne';'MetTwo';'MetThree';'MetFour';'MetFive'};
-0622             m.metComps=[1;1;1;1;1]; m.comps={'c'}; m.compNames={'c'};
-0623             % r1:=>met5  r2:=>met1  r3:=>met2  r4:met5=>  r5:=>met3  r6:=>met4  r7:met3=>
-0624             m.S = sparse(5,7);
-0625             m.S(5,1)=1; m.S(1,2)=1; m.S(2,3)=1; m.S(5,4)=-1; m.S(3,5)=1; m.S(4,6)=1; m.S(3,7)=-1;
-0626             m.lb=-1000*ones(7,1); m.ub=1000*ones(7,1); m.rev=ones(7,1); m.c=zeros(7,1); m.b=zeros(5,1);
-0627             m.genes={}; m.grRules=repmat({''},7,1); m.rxnGeneMat=sparse(7,0);
-0628             txt = evalc('setExchangeBounds(m);');
-0629             testCase.verifySubstring(txt, 'MetThree');
-0630             testCase.verifySubstring(txt, 'MetFive');
-0631         end
-0632 
-0633         function setExchangeBoundsRuns(testCase)
-0634             evalc('m2 = setExchangeBounds(testCase.model, {''ac_e'';''akg_e''}, -500, 500);');
-0635             testCase.verifyClass(m2, 'struct');
-0636         end
-0637 
-0638         function setParamUncOnModelWithoutAnnotationField(testCase)
-0639             % 'unc' falls back to default LB/UB (-1000/1000) when there is
-0640             % no annotation field at all, not just when annotation exists
-0641             % but lacks defaultLB/defaultUB.
-0642             m = testCase.model;
-0643             if isfield(m,'annotation')
-0644                 m = rmfield(m,'annotation');
-0645             end
-0646             m2 = setParam(m, 'unc', m.rxns(1), 0);
-0647             idx = strcmp(m2.rxns, m.rxns{1});
-0648             testCase.verifyEqual(m2.lb(idx), -1000);
-0649             testCase.verifyEqual(m2.ub(idx), 1000);
-0650         end
-0651 
-0652         function setParamObjective(testCase)
-0653             m2 = setParam(testCase.model, 'obj', testCase.model.rxns(1), 1);
-0654             idx = strcmp(m2.rxns, testCase.model.rxns{1});
-0655             testCase.verifyEqual(m2.c(idx), 1);
-0656         end
-0657 
-0658         function setParamUpperBound(testCase)
-0659             m2 = setParam(testCase.model, 'ub', testCase.model.rxns(1), 42);
-0660             idx = strcmp(m2.rxns, testCase.model.rxns{1});
-0661             testCase.verifyEqual(m2.ub(idx), 42);
-0662         end
-0663 
-0664         function simplifyModelReturnsStruct(testCase)
-0665             evalc('m2 = simplifyModel(testCase.model);');
-0666             testCase.verifyClass(m2, 'struct');
-0667             testCase.verifyLessThanOrEqual(numel(m2.rxns), numel(testCase.model.rxns));
-0668         end
-0669 
-0670         function simplifyModelKeepsMetsConstrainedByB(testCase)
-0671             % R2 is constrained to zero flux, so deleteZeroInterval removes
-0672             % it and leaves C in no reaction at all. A non-zero b is a
-0673             % boundary condition on C, and removing the metabolite takes
-0674             % that row of b with it, so whatever solves the reduced model
-0675             % is no longer asked to produce C.
-0676             m = struct();
-0677             m.id = 'test';
-0678             m.rxns = {'R1';'R2'}; m.rxnNames = m.rxns;
-0679             m.mets = {'A';'B';'C'}; m.metNames = m.mets; m.metComps = [1;1;1];
-0680             m.comps = {'c'}; m.compNames = m.comps;
-0681             m.S = sparse([-1 0; 1 -1; 0 1]);   % R1: A => B, R2: B => C
-0682             m.lb = [0;0]; m.ub = [1000;0]; m.rev = [0;0]; m.c = [0;0];
-0683             m.genes = {}; m.grRules = {'';''}; m.rxnGeneMat = sparse(2,0);
-0684             m.b = zeros(3,2);
-0685             m.b(3,:) = [1 1];                  % C must be produced, 1 unit
-0686 
-0687             evalc('[reduced,~,deletedMets] = simplifyModel(m,false,false,true);');
-0688             testCase.verifyTrue(ismember('C', reduced.mets));
-0689             testCase.verifyFalse(ismember('C', deletedMets));
-0690             testCase.verifyEqual(reduced.b(strcmp(reduced.mets,'C'),:), [1 1]);
-0691 
-0692             % Without that constraint C is dead weight and is removed.
-0693             m.b(3,:) = [0 0];
-0694             evalc('reduced = simplifyModel(m,false,false,true);');
-0695             testCase.verifyFalse(ismember('C', reduced.mets));
-0696         end
-0697 
-0698         function sortIdentifiersSortsRxns(testCase)
-0699             m2 = sortIdentifiers(testCase.model);
-0700             testCase.verifyEqual(sort(m2.rxns), m2.rxns);
-0701         end
-0702 
-0703         function sortModelPreservesCounts(testCase)
-0704             m2 = sortModel(testCase.model);
-0705             testCase.verifyEqual(numel(m2.rxns), numel(testCase.model.rxns));
-0706             testCase.verifyEqual(numel(m2.mets), numel(testCase.model.mets));
-0707         end
-0708 
-0709         function sortReactionOrderSeedIsReproducible(testCase)
-0710             % The local search proposes swaps randomly; a given seed must
-0711             % make its result reproducible across runs.
-0712             m = testCase.model;
-0713             m.subSystems = repmat({{'ALL'}}, numel(m.rxns), 1);
-0714             m1 = sortModel(m, 'sortReversible', false, 'sortReactionOrder', true, 'seed', 5);
-0715             m2 = sortModel(m, 'sortReversible', false, 'sortReactionOrder', true, 'seed', 5);
-0716             testCase.verifyEqual(m1.rxns, m2.rxns);
-0717         end
-0718 
-0719         function sortReactionOrderUsesSubsystemsOwnColumns(testCase)
-0720             % sortReactionOrder must score and reorder a subsystem's own
-0721             % reactions, not whichever columns happen to occupy the first
-0722             % nRxns positions of the whole model: with a chain A->B->C->D
-0723             % split across R2 (A=>B), R3 (B=>C) and R1 (C=>D) -- placed
-0724             % after two unrelated filler reactions so the subsystem is NOT
-0725             % at the start of model.rxns -- the only production-before-
-0726             % consumption order is R2, then R3, then R1.
-0727             m = struct();
-0728             m.id='t'; m.rxns={'FILLER1';'FILLER2';'R1';'R2';'R3'}; m.rxnNames=m.rxns;
-0729             m.mets={'fa';'fb';'fc';'fd';'A';'B';'C';'D'}; m.metNames=m.mets;
-0730             m.metComps=ones(8,1); m.comps={'c'}; m.compNames={'c'};
-0731             S=zeros(8,5);
-0732             S(1,1)=-1; S(2,1)=1;  % FILLER1: fa=>fb
-0733             S(3,2)=-1; S(4,2)=1;  % FILLER2: fc=>fd
-0734             S(7,3)=-1; S(8,3)=1;  % R1: C=>D
-0735             S(5,4)=-1; S(6,4)=1;  % R2: A=>B
-0736             S(6,5)=-1; S(7,5)=1;  % R3: B=>C
-0737             m.S=sparse(S);
-0738             m.lb=zeros(5,1); m.ub=ones(5,1)*1000; m.rev=zeros(5,1); m.c=zeros(5,1);
-0739             m.b=zeros(8,1);
-0740             m.genes={}; m.grRules=repmat({''},5,1); m.rxnGeneMat=sparse(5,0);
-0741             m.subSystems={{};{};{'CHAIN'};{'CHAIN'};{'CHAIN'}};
-0742 
-0743             rng(1);
-0744             m2=sortModel(m,'sortReversible',false,'sortReactionOrder',true);
-0745             posR1=find(strcmp(m2.rxns,'R1'));
-0746             posR2=find(strcmp(m2.rxns,'R2'));
-0747             posR3=find(strcmp(m2.rxns,'R3'));
-0748             testCase.verifyLessThan(posR2, posR3);
-0749             testCase.verifyLessThan(posR3, posR1);
-0750         end
-0751 
-0752         function standardizeGrRulesReturnsRules(testCase)
-0753             evalc('grRules = standardizeGrRules(testCase.model);');
-0754             testCase.verifyNumElements(grRules, numel(testCase.model.rxns));
-0755         end
-0756 
-0757         function findPotentialErrorsFlagsOnlyNonDnf(testCase)
-0758             m = struct();
-0759             m.rxns = {'R1';'R2';'R3';'R4'};
-0760             m.grRules = {'((G1 and G2) or G3)'      % DNF, just bracketed
-0761                          '(G1 or G2) or (G3 and G4)' % DNF
-0762                          'G1 or G2'                  % DNF
-0763                          '(G1 or G2) and (G3 or G4)'};% genuinely non-DNF
-0764             issues = findPotentialErrors(m);
-0765             testCase.verifyEqual(vertcat(issues.index), 4);
-0766         end
-0767 
-0768         function findPotentialErrorsReportsUnparseable(testCase)
-0769             m = struct();
-0770             m.rxns = {'R1'};
-0771             m.grRules = {'(G1 and G2'};
-0772             issues = findPotentialErrors(m);
-0773             testCase.verifyNumElements(issues, 1);
-0774             testCase.verifySubstring(issues(1).reason, 'Cannot be parsed');
-0775         end
-0776 
-0777         function standardizeGrRulesRepairsBracketedDnf(testCase)
-0778             % A rule that is DNF but redundantly bracketed must be repaired,
-0779             % not skipped: standardizeGrRules leaves flagged rules alone, so a
-0780             % false positive from findPotentialErrors silently prevents repair.
-0781             m = struct();
-0782             m.rxns = {'R1'};
-0783             m.grRules = {'((G1 and G2) or G3)'};
-0784             m.genes = {'G1';'G2';'G3'};
-0785             m.rxnGeneMat = sparse([1 1 1]);
-0786             [grRules,~,indexes2check] = standardizeGrRules(m, true);
-0787             testCase.verifyEmpty(indexes2check);
-0788             testCase.verifyEqual(grRules{1}, '(G1 and G2) or G3');
-0789         end
-0790 
-0791         function standardizeGrRulesKeepsPercentInWarning(testCase)
-0792             % A rxn id containing a literal "%" must survive intact in the
-0793             % "potentially problematic relationships" warning, not be
-0794             % truncated by sprintf/warning misreading it as a directive.
-0795             m.rxns = {'RXN_50%_TEST'};
-0796             m.grRules = {'(G1 or G2) and G3'};
-0797             m.genes = {'G1';'G2';'G3'};
-0798             m.rxnGeneMat = sparse([1 1 1]);
-0799             lastwarn('');
-0800             evalc('standardizeGrRules(m);');
-0801             msg = lastwarn();
-0802             testCase.verifySubstring(msg, 'RXN_50%_TEST');
-0803         end
-0804 
-0805         function removeGenesMatchesWholeGeneIds(testCase)
-0806             % Gene "10" is a prefix of "100". Removing it must not take "100"
-0807             % with it, which an unanchored substring search would.
-0808             m = testCase.gprTestModel('10 or 100', {'10';'100'}, [1 1]);
-0809             r = removeGenes(m, {'10'});
-0810             testCase.verifyEqual(r.grRules{1}, '100');
-0811             testCase.verifyEqual(r.ub(1), 1000);
-0812         end
-0813 
-0814         function removeGenesDropsWholeComplex(testCase)
-0815             % A complex missing a subunit cannot form; the other isozyme lives.
-0816             m = testCase.gprTestModel('(G1 and G2) or G3', {'G1';'G2';'G3'}, [1 1 1]);
-0817             r = removeGenes(m, {'G1'});
-0818             testCase.verifyEqual(r.grRules{1}, 'G3');
-0819         end
-0820 
-0821         function removeGenesBlocksWhenNoEnzymeLeft(testCase)
-0822             m = testCase.gprTestModel('G1 and G2', {'G1';'G2'}, [1 1]);
-0823             r = removeGenes(m, {'G1'});
-0824             testCase.verifyEmpty(r.grRules{1});
-0825             testCase.verifyEqual(r.lb(1), 0);
-0826             testCase.verifyEqual(r.ub(1), 0);
-0827         end
-0828 
-0829         function expandModelKeepsMandatorySubunit(testCase)
-0830             % "g1 and (g2 or g3)" is two isozymes, both needing g1. Stripping
-0831             % brackets and splitting on ' or ' loses g1 from the second.
-0832             m = testCase.gprTestModel('g1 and (g2 or g3)', {'g1';'g2';'g3'}, [1 1 1]);
-0833             e = expandModel(m);
-0834             testCase.verifyEqual(sort(e.grRules), {'g1 and g2';'g1 and g3'});
-0835         end
-0836 
-0837         function expandModelCopiesSpontaneousAndPwys(testCase)
-0838             % Each isozyme copy created by splitting an OR rule must inherit
-0839             % the source reaction's spontaneous/pwys annotation, keeping both
-0840             % fields aligned with rxns.
-0841             m = testCase.gprTestModel('g1 or g2', {'g1';'g2'}, [1 1]);
-0842             m.spontaneous = true;
-0843             m.pwys = {'pathway1'};
-0844             e = expandModel(m);
-0845             testCase.verifyEqual(numel(e.spontaneous), numel(e.rxns));
-0846             testCase.verifyEqual(numel(e.pwys), numel(e.rxns));
-0847             testCase.verifyTrue(all(e.spontaneous));
-0848             testCase.verifyTrue(all(strcmp(e.pwys, 'pathway1')));
-0849         end
-0850 
-0851         function expandModelCopiesRxnScores(testCase)
-0852             % Each isozyme copy created by splitting an OR rule must
-0853             % inherit the source reaction's rxnScores, keeping it aligned
-0854             % with rxns.
-0855             m = testCase.gprTestModel('g1 or g2', {'g1';'g2'}, [1 1]);
-0856             m.rxnScores = 2.5;
-0857             e = expandModel(m);
-0858             testCase.verifyEqual(numel(e.rxnScores), numel(e.rxns));
-0859             testCase.verifyTrue(all(e.rxnScores == 2.5));
-0860         end
-0861 
-0862         function expandModelDistributesBothSides(testCase)
-0863             % Two or:s, but four isozymes.
-0864             m = testCase.gprTestModel('(g1 or g2) and (g3 or g4)', ...
-0865                 {'g1';'g2';'g3';'g4'}, [1 1 1 1]);
-0866             [e, rxnToCheck] = expandModel(m);
-0867             testCase.verifyEqual(sort(e.grRules), ...
-0868                 {'g1 and g3';'g1 and g4';'g2 and g3';'g2 and g4'});
-0869             testCase.verifyEqual(rxnToCheck, {'R1'});
-0870         end
-0871 
-0872         function expandModelLeavesDnfAlone(testCase)
-0873             % An OR of complexes expands without needing distributivity, so it
-0874             % must not be reported as needing a check.
-0875             m = testCase.gprTestModel('(g1 and g2) or (g3 and g4)', ...
-0876                 {'g1';'g2';'g3';'g4'}, [1 1 1 1]);
-0877             [e, rxnToCheck] = expandModel(m);
-0878             testCase.verifyEqual(sort(e.grRules), {'g1 and g2';'g3 and g4'});
-0879             testCase.verifyEmpty(rxnToCheck);
-0880         end
-0881 
-0882     end
-0883 
-0884     methods (Access = private)
-0885         function m = gprTestModel(~, grRule, genes, rxnGeneRow)
-0886             % Smallest model that removeGenes/expandModel will operate on.
-0887             m = struct();
-0888             m.rxns = {'R1'}; m.rxnNames = {'R1'};
-0889             m.mets = {'A';'B'}; m.metNames = {'A';'B'}; m.metComps = [1;1];
-0890             m.comps = {'c'}; m.compNames = {'c'};
-0891             m.S = sparse([-1;1]); m.lb = 0; m.ub = 1000; m.rev = 0; m.c = 0;
-0892             m.b = [0;0];
-0893             m.genes = genes;
-0894             m.grRules = {grRule};
-0895             m.rxnGeneMat = sparse(rxnGeneRow);
-0896         end
-0897     end
-0898 
-0899     methods (Static, Access = private)
-0900 
-0901         function m = twoMetModel()
-0902             % Single reaction a -> b in one compartment.
-0903             m = struct();
-0904             m.id        = 'toy';
-0905             m.rxns      = {'R1'};
-0906             m.rxnNames  = {'R1'};
-0907             m.mets      = {'a';'b'};
-0908             m.metNames  = {'a';'b'};
-0909             m.metComps  = [1;1];
-0910             m.comps     = {'c'};
-0911             m.compNames = {'cytosol'};
-0912             m.S         = sparse([-1;1]);
-0913             m.lb = 0; m.ub = 1000; m.rev = 0; m.c = 0; m.b = zeros(2,1);
-0914             m.genes = {}; m.grRules = {''}; m.rxnGeneMat = sparse(1,0);
-0915             m.metFormulas = {'C';'C'};
-0916         end
-0917 
-0918         function m = namedMetModel(glucoseId, modelId)
-0919             % Glucose[c] under a caller-chosen id, consumed by one reaction.
-0920             m = struct();
-0921             m.id        = modelId;
-0922             m.rxns      = {['R_' modelId]};
-0923             m.rxnNames  = m.rxns;
-0924             m.mets      = {glucoseId; ['product_' modelId]};
-0925             m.metNames  = {'Glucose'; ['Product' modelId]};
-0926             m.metComps  = [1;1];
-0927             m.comps     = {'c'};
-0928             m.compNames = {'cytosol'};
-0929             m.S         = sparse([-1;1]);
-0930             m.lb = 0; m.ub = 1000; m.rev = 0; m.c = 0; m.b = zeros(2,1);
-0931             m.genes = {}; m.grRules = {''}; m.rxnGeneMat = sparse(1,0);
-0932             m.metFormulas = {'C6H12O6';'C'};
-0933         end
-0934 
-0935         function m = duplicateRxnModel(modelId)
-0936             % Two reactions, both named 'R1' internally. mergeModels'
-0937             % own docstring says duplicate reaction ids "might appear" in
-0938             % a model, so this is a real (if unusual) input shape to
-0939             % guard the merge against, not a contrived one.
-0940             m = struct();
-0941             m.id        = modelId;
-0942             m.rxns      = {'R1'; 'R1'};
-0943             m.rxnNames  = {'R1a'; 'R1b'};
-0944             m.mets      = {'x'; 'y'; 'z'};
-0945             m.metNames  = m.mets;
-0946             m.metComps  = [1;1;1];
-0947             m.comps     = {'c'};
-0948             m.compNames = {'cytosol'};
-0949             m.S         = sparse([-1 0; 1 -1; 0 1]);
-0950             m.lb = [0;0]; m.ub = [1000;1000]; m.rev = [0;0]; m.c = [0;0]; m.b = zeros(3,1);
-0951             m.genes = {}; m.grRules = {'';''}; m.rxnGeneMat = sparse(2,0);
-0952             m.metFormulas = {'C';'C';'C'};
-0953         end
-0954 
-0955     end
-0956 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tNamedArgs.html b/doc/testing/function_tests/tNamedArgs.html deleted file mode 100644 index 2c523225..00000000 --- a/doc/testing/function_tests/tNamedArgs.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - Description of tNamedArgs - - - - - - - - - -
Home > testing > function_tests > tNamedArgs.m
- - - -

tNamedArgs -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tNamedArgs < RavenTestCase
-0002 % tNamedArgs  Verifies that functions converted to parseRAVENargs accept both
-0003 %             the positional and the name-value calling conventions, and that
-0004 %             the two forms give identical results.
-0005 
-0006     methods (Test)
-0007 
-0008         function getIndexesPositionalAndNamedMatch(testCase)
-0009             pos = getIndexes(testCase.model, testCase.model.rxns(1:3), 'rxns', true);
-0010             nv  = getIndexes(testCase.model, testCase.model.rxns(1:3), 'rxns', ...
-0011                 'returnLogical', true);
-0012             testCase.verifyEqual(nv, pos);
-0013         end
-0014 
-0015         function addTransportPositionalAndNamedMatch(testCase)
-0016             evalc(['m1 = addTransport(testCase.model, ''c'', ''e'', ' ...
-0017                 '{''6-phospho-D-glucono-1,5-lactone''}, false, false, ''tr_'');']);
-0018             evalc(['m2 = addTransport(testCase.model, ''c'', ''e'', ' ...
-0019                 '''metNames'', {''6-phospho-D-glucono-1,5-lactone''}, ' ...
-0020                 '''isRev'', false, ''onlyToExisting'', false, ''prefix'', ''tr_'');']);
-0021             testCase.verifyEqual(numel(m2.rxns), numel(m1.rxns));
-0022         end
-0023 
-0024         function addTransportNamedSubsetOfOptions(testCase)
-0025             % A subset of options by name; the rest keep their defaults.
-0026             evalc(['m = addTransport(testCase.model, ''c'', ''e'', ' ...
-0027                 '''metNames'', {''6-phospho-D-glucono-1,5-lactone''}, ' ...
-0028                 '''onlyToExisting'', false);']);
-0029             testCase.verifyGreaterThan(numel(m.rxns), numel(testCase.model.rxns));
-0030         end
-0031 
-0032         function exportToExcelFormatPositionalAndNamed(testCase)
-0033             f1 = [tempname '.xlsx'];
-0034             f2 = [tempname '.xlsx'];
-0035             testCase.addTeardown(@() delete(f1));
-0036             testCase.addTeardown(@() delete(f2));
-0037             evalc('exportToExcelFormat(testCase.model, f1, true);');
-0038             evalc('exportToExcelFormat(testCase.model, ''fileName'', f2, ''sortIds'', true);');
-0039             testCase.verifyTrue(exist(f1,'file')==2);
-0040             testCase.verifyTrue(exist(f2,'file')==2);
-0041         end
-0042 
-0043         function tooManyPositionalErrors(testCase)
-0044             testCase.verifyError(@() getIndexes(testCase.model, ...
-0045                 testCase.model.rxns(1), 'rxns', true, 'extra'), ?MException);
-0046         end
-0047 
-0048     end
-0049 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tOmics.html b/doc/testing/function_tests/tOmics.html deleted file mode 100644 index 4e0f8c42..00000000 --- a/doc/testing/function_tests/tOmics.html +++ /dev/null @@ -1,103 +0,0 @@ - - - - Description of tOmics - - - - - - - - - -
Home > testing > function_tests > tOmics.m
- - - -

tOmics -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tOmics < RavenTestCase
-0002 % tOmics  Tests for the omics-integration functions in omics/.
-0003 
-0004     methods (Test)
-0005 
-0006         function scoreModelFromArrayData(testCase)
-0007             arrayData.genes = testCase.model.genes;
-0008             arrayData.tissues = {'tissue1'};
-0009             arrayData.levels = abs(randn(numel(testCase.model.genes), 1)) + 1;
-0010             arrayData.threshold = 1;
-0011             evalc('rxnScores = scoreModel(testCase.model, [], arrayData, ''tissue1'', []);');
-0012             testCase.verifyNumElements(rxnScores, numel(testCase.model.rxns));
-0013         end
-0014 
-0015         function parseHPAparsesSyntheticDump(testCase)
-0016             % parseHPA reads a tab-separated HPA dump with the columns
-0017             % Gene / Gene name / Tissue / Cell type / Level / Reliability.
-0018             % Synthesise a tiny one so the parser is exercised without the
-0019             % multi-gigabyte external download.
-0020             g = testCase.model.genes(1:3);
-0021             f = [tempname '.tsv'];
-0022             testCase.addTeardown(@() delete(f));
-0023             fid = fopen(f, 'w');
-0024             fprintf(fid, 'Gene\tGene name\tTissue\tCell type\tLevel\tReliability\n');
-0025             fprintf(fid, '%s\t%s\tliver\thepatocyte\tHigh\tApproved\n', g{1}, g{1});
-0026             fprintf(fid, '%s\t%s\tliver\thepatocyte\tMedium\tApproved\n', g{2}, g{2});
-0027             fprintf(fid, '%s\t%s\tbrain\tneuron\tLow\tSupported\n', g{3}, g{3});
-0028             fclose(fid);
-0029             evalc('hpa = parseHPA(f);');
-0030             testCase.verifyClass(hpa, 'struct');
-0031             testCase.verifyEqual(sort(hpa.genes), sort(g));
-0032             testCase.verifyNumElements(hpa.tissues, 2);   % liver+brain cell types
-0033         end
-0034 
-0035         function parseHPArnaParsesSyntheticDump(testCase)
-0036             % parseHPArna (version 19) reads Gene / Gene name / Tissue /
-0037             % TPM / pTPM / NX and builds a genes-by-tissues TPM matrix.
-0038             g = testCase.model.genes(1:3);
-0039             f = [tempname '.tsv'];
-0040             testCase.addTeardown(@() delete(f));
-0041             fid = fopen(f, 'w');
-0042             fprintf(fid, 'Gene\tGene name\tTissue\tTPM\tpTPM\tNX\n');
-0043             fprintf(fid, '%s\t%s\tliver\t12.5\t10.1\t8.0\n', g{1}, g{1});
-0044             fprintf(fid, '%s\t%s\tbrain\t3.2\t2.9\t1.5\n', g{2}, g{2});
-0045             fprintf(fid, '%s\t%s\tliver\t0.0\t0.0\t0.0\n', g{3}, g{3});
-0046             fclose(fid);
-0047             evalc('rna = parseHPArna(f);');
-0048             testCase.verifyClass(rna, 'struct');
-0049             testCase.verifyNumElements(rna.genes, 3);
-0050             testCase.verifyEqual(size(rna.levels), [3 2]);   % 3 genes x 2 tissues
-0051         end
-0052 
-0053     end
-0054 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tParseRAVENargs.html b/doc/testing/function_tests/tParseRAVENargs.html deleted file mode 100644 index 40710ef5..00000000 --- a/doc/testing/function_tests/tParseRAVENargs.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - Description of tParseRAVENargs - - - - - - - - - -
Home > testing > function_tests > tParseRAVENargs.m
- - - -

tParseRAVENargs -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tParseRAVENargs < matlab.unittest.TestCase
-0002 % tParseRAVENargs  Tests for the parseRAVENargs optional-argument resolver.
-0003 
-0004     methods (Test)
-0005 
-0006         function defaultsWhenEmpty(testCase)
-0007             p = parseRAVENargs({}, {'a', 1; 'b', 'x'});
-0008             testCase.verifyEqual(p.a, 1);
-0009             testCase.verifyEqual(p.b, 'x');
-0010         end
-0011 
-0012         function positionalAssignment(testCase)
-0013             p = parseRAVENargs({10, 'y'}, {'a', 1; 'b', 'x'});
-0014             testCase.verifyEqual(p.a, 10);
-0015             testCase.verifyEqual(p.b, 'y');
-0016         end
-0017 
-0018         function partialPositionalKeepsLaterDefaults(testCase)
-0019             p = parseRAVENargs({10}, {'a', 1; 'b', 'x'});
-0020             testCase.verifyEqual(p.a, 10);
-0021             testCase.verifyEqual(p.b, 'x');
-0022         end
-0023 
-0024         function nameValueAssignment(testCase)
-0025             p = parseRAVENargs({'b', 'y'}, {'a', 1; 'b', 'x'});
-0026             testCase.verifyEqual(p.a, 1);     % untouched -> default
-0027             testCase.verifyEqual(p.b, 'y');
-0028         end
-0029 
-0030         function nameValueIsCaseInsensitive(testCase)
-0031             p = parseRAVENargs({'B', 'y'}, {'a', 1; 'b', 'x'});
-0032             testCase.verifyEqual(p.b, 'y');
-0033         end
-0034 
-0035         function nameValueBothParams(testCase)
-0036             p = parseRAVENargs({'b', 'y', 'a', 5}, {'a', 1; 'b', 'x'});
-0037             testCase.verifyEqual(p.a, 5);
-0038             testCase.verifyEqual(p.b, 'y');
-0039         end
-0040 
-0041         function oddCountStaysPositional(testCase)
-0042             % A single string value that happens to match a name is positional.
-0043             p = parseRAVENargs({'a'}, {'a', 1; 'b', 'x'});
-0044             testCase.verifyEqual(p.a, 'a');
-0045         end
-0046 
-0047         function tooManyPositionalErrors(testCase)
-0048             testCase.verifyError(@() parseRAVENargs({1, 2, 3}, {'a', 1; 'b', 2}), ...
-0049                 'parseRAVENargs:tooManyArgs');
-0050         end
-0051 
-0052         function validatorRejectsInvalid(testCase)
-0053             spec = {'n', 1, @mustBeScalarOrEmpty};
-0054             testCase.verifyError(@() parseRAVENargs({'n', [1 2 3]}, spec), ?MException);
-0055         end
-0056 
-0057         function validatorAcceptsValid(testCase)
-0058             spec = {'n', 1, @mustBeScalarOrEmpty};
-0059             p = parseRAVENargs({'n', 7}, spec);
-0060             testCase.verifyEqual(p.n, 7);
-0061         end
-0062 
-0063     end
-0064 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tQueries.html b/doc/testing/function_tests/tQueries.html deleted file mode 100644 index 7cf93dd9..00000000 --- a/doc/testing/function_tests/tQueries.html +++ /dev/null @@ -1,491 +0,0 @@ - - - - Description of tQueries - - - - - - - - - -
Home > testing > function_tests > tQueries.m
- - - -

tQueries -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tQueries < RavenTestCase
-0002 % tQueries  Tests for the read-only query/accessor functions in queries/.
-0003 
-0004     methods (Test)
-0005 
-0006         function buildEquationReversible(testCase)
-0007             eqn = buildEquation({'a';'b'}, [-1;1], true);
-0008             testCase.verifyClass(eqn, 'char');
-0009             testCase.verifySubstring(eqn, 'a');
-0010             testCase.verifySubstring(eqn, 'b');
-0011             testCase.verifySubstring(eqn, '<=>');
-0012         end
-0013 
-0014         function buildEquationIrreversible(testCase)
-0015             eqn = buildEquation({'a';'b'}, [-1;1], false);
-0016             testCase.verifySubstring(eqn, '=>');
-0017             testCase.verifyEmpty(strfind(eqn, '<=>')); %#ok<STRIFY>
-0018         end
-0019 
-0020         function checkModelStructValidModel(testCase)
-0021             % A valid model must not throw when errors are requested.
-0022             % Advisory warnings (unused elements, bounds notes, etc.) are
-0023             % expected MATLAB warnings in the new system; the check is
-0024             % only that no error is raised.
-0025             checkModelStruct(testCase.model, true);
-0026         end
-0027 
-0028         function checkModelStructNoFalsePositiveOnWordStartingName(testCase)
-0029             % A metabolite name beginning with a non-numeric word must not be
-0030             % flagged as "begins with a number": str2double of that word is
-0031             % NaN, and any(NaN) is true in MATLAB.
-0032             m = testCase.model;
-0033             m.metNames{1} = 'alpha keto acid';
-0034             issues = checkModelStruct(m);
-0035             hit = arrayfun(@(x) contains(x.message,'begin with a number'), issues);
-0036             testCase.verifyFalse(any(hit));
-0037         end
-0038 
-0039         function checkModelStructFlagsDuplicateNonSboMiriam(testCase)
-0040             % Two metabolites with different names sharing the same
-0041             % non-SBO MIRIAM (e.g. the same KEGG id) must be flagged:
-0042             % regexp's [] "no match" result must not be mistaken for a
-0043             % match via bitwise negation (~[] is also [], so the check
-0044             % never fired at all before the fix).
-0045             m = testCase.model;
-0046             idx = find(~strcmp(m.metNames, m.metNames{1}), 1);
-0047             m.metMiriams = cell(numel(m.mets),1);
-0048             m.metMiriams{1}.name = {'kegg.compound'}; m.metMiriams{1}.value = {'C00031'};
-0049             m.metMiriams{idx}.name = {'kegg.compound'}; m.metMiriams{idx}.value = {'C00031'};
-0050             issues = checkModelStruct(m);
-0051             hit = arrayfun(@(x) contains(x.message,'more than one unique metabolite name'), issues);
-0052             testCase.verifyTrue(any(hit));
-0053         end
-0054 
-0055         function checkModelStructThrowErrorsFalseSkipsAfterMissingField(testCase)
-0056             % throwErrors=false must warn about a missing required field
-0057             % without then crashing trying to read that very field in a
-0058             % later check.
-0059             m = rmfield(testCase.model, 'id');
-0060             evalc('checkModelStruct(m, ''throwErrors'', false);');
-0061         end
-0062 
-0063         function constructEquationsAllRxns(testCase)
-0064             eqns = constructEquations(testCase.model);
-0065             testCase.verifyClass(eqns, 'cell');
-0066             testCase.verifyNumElements(eqns, numel(testCase.model.rxns));
-0067             testCase.verifyTrue(all(~cellfun(@isempty, eqns)));
-0068         end
-0069 
-0070         function constructEquationsSubset(testCase)
-0071             eqns = constructEquations(testCase.model, testCase.model.rxns(1:3));
-0072             testCase.verifyNumElements(eqns, 3);
-0073         end
-0074 
-0075         function constructSSimple(testCase)
-0076             [S, mets] = constructS({'a + b => c'});
-0077             testCase.verifyEqual(sort(mets(:)), {'a';'b';'c'});
-0078             testCase.verifySize(S, [3 1]);
-0079             % reactants negative, product positive (S is sparse)
-0080             testCase.verifyEqual(full(S), [-1;-1;1]);
-0081         end
-0082 
-0083         function constructSLeadingNumberIsPartOfName(testCase)
-0084             % A metabolite whose name starts with a number must not have that
-0085             % number read as a stoichiometric coefficient when the metabolite
-0086             % list says the whole entry is a metabolite.
-0087             mets = {'2 oxoglutarate';'succinate'};
-0088             [S, outMets] = constructS({'2 oxoglutarate => succinate'}, 'mets', mets);
-0089             testCase.verifyEqual(outMets, mets);
-0090             testCase.verifyEqual(full(S), [-1;1]);
-0091         end
-0092 
-0093         function constructSLeadingNumberStillReadsCoefficient(testCase)
-0094             % With no such metabolite, the leading number is a coefficient.
-0095             mets = {'oxoglutarate';'succinate'};
-0096             [S, outMets] = constructS({'2 oxoglutarate => succinate'}, 'mets', mets);
-0097             testCase.verifyEqual(outMets, mets);
-0098             testCase.verifyEqual(full(S), [-2;1]);
-0099         end
-0100 
-0101         function constructSSumsRepeatedMetabolite(testCase)
-0102             % A metabolite written more than once on the same side is one
-0103             % coefficient, not the last occurrence.
-0104             [S, mets] = constructS({'2 a + a => b'}, 'mets', {'a';'b'});
-0105             testCase.verifyEqual(mets, {'a';'b'});
-0106             testCase.verifyEqual(full(S), [-3;1]);
-0107         end
-0108 
-0109         function constructSCancelsMetaboliteOnBothSides(testCase)
-0110             % A metabolite on both sides cancels, which is exactly what
-0111             % badRxns reports: the reaction is empty in the S matrix.
-0112             [S, ~, badRxns] = constructS({'atp + adp <=> adp + atp'}, ...
-0113                 'mets', {'atp';'adp'});
-0114             testCase.verifyEqual(full(S), [0;0]);
-0115             testCase.verifyTrue(badRxns(1));
-0116         end
-0117 
-0118         function constructSMissingMetKeepsPercentInErrorNoRxns(testCase)
-0119             % A missing metabolite name containing "%" must survive intact
-0120             % in the error message, not be truncated by sprintf misreading
-0121             % it as a format directive.
-0122             testCase.verifyError(@() constructS({'a + met_with_%_sign => b'}, ...
-0123                 'mets', {'a';'b'}), 'RAVEN:badInput');
-0124             try
-0125                 constructS({'a + met_with_%_sign => b'}, 'mets', {'a';'b'});
-0126             catch e
-0127                 testCase.verifySubstring(e.message, 'met_with_%_sign');
-0128             end
-0129         end
-0130 
-0131         function constructSMissingMetKeepsPercentInErrorWithRxns(testCase)
-0132             % Same, but via the reaction-annotated branch (rxns supplied),
-0133             % which also splices in the reaction id.
-0134             try
-0135                 constructS({'a + met_with_%_sign => b'}, 'mets', {'a';'b'}, ...
-0136                     'rxns', {'rxn_%_id'});
-0137                 testCase.verifyFail('Expected an error to be thrown.');
-0138             catch e
-0139                 testCase.verifySubstring(e.message, 'met_with_%_sign');
-0140                 testCase.verifySubstring(e.message, 'rxn_%_id');
-0141             end
-0142         end
-0143 
-0144         function getAllRxnsFromGenesType(testCase)
-0145             % Use a reaction that has a gene association.
-0146             withGpr = testCase.model.rxns(find(~cellfun(@isempty, ...
-0147                 testCase.model.grRules), 1));
-0148             allRxns = getAllRxnsFromGenes(testCase.model, withGpr);
-0149             testCase.verifyClass(allRxns, 'cell');
-0150             testCase.verifyTrue(all(ismember(withGpr, allRxns)));
-0151         end
-0152 
-0153         function getElementalBalanceFields(testCase)
-0154             bs = getElementalBalance(testCase.model);
-0155             testCase.verifyTrue(isfield(bs, 'balanceStatus'));
-0156             testCase.verifyTrue(isfield(bs, 'leftComp'));
-0157             testCase.verifyTrue(isfield(bs, 'rightComp'));
-0158             testCase.verifySize(bs.balanceStatus, [numel(testCase.model.rxns) 1]);
-0159         end
-0160 
-0161         function getElementalBalanceEmptyRxnIsUnbalanced(testCase)
-0162             % A reaction with no metabolites (all-zero S column) must not
-0163             % be falsely reported as balanced.
-0164             m = testCase.model;
-0165             m.rxns{end+1}    = 'emptyRxn';
-0166             m.S(:, end+1)    = 0;
-0167             m.lb(end+1)      = 0;
-0168             m.ub(end+1)      = 1000;
-0169             m.rev(end+1)     = 0;
-0170             m.c(end+1)       = 0;
-0171             m.grRules{end+1} = '';
-0172             if isfield(m, 'rxnNames'),   m.rxnNames{end+1}  = 'emptyRxn'; end
-0173             if isfield(m, 'rxnGeneMat'), m.rxnGeneMat(end+1,:) = 0;       end
-0174             bs = getElementalBalance(m);
-0175             testCase.verifyLessThanOrEqual(bs.balanceStatus(end), -1);
-0176         end
-0177 
-0178         function getElementalBalanceReportsChargeBalance(testCase)
-0179             % A charge-imbalanced reaction is reported as such, while the
-0180             % elemental verdict is left alone.
-0181             m = testCase.model;
-0182             m.mets      = {'a';'b'};
-0183             m.metNames  = {'a';'b'};
-0184             m.metComps  = [1;1];
-0185             m.metFormulas = {'H';'H'};
-0186             m.metCharges  = [0;1];
-0187             m.comps     = {'c'};
-0188             m.compNames = {'cytosol'};
-0189             m.rxns      = {'R1'};
-0190             m.rxnNames  = {'R1'};
-0191             m.S         = sparse([-1;1]);
-0192             m.lb = 0; m.ub = 1000; m.rev = 0; m.c = 0; m.b = zeros(2,1);
-0193             m.grRules = {''}; m.genes = {}; m.rxnGeneMat = sparse(1,0);
-0194 
-0195             bs = getElementalBalance(m);
-0196             testCase.verifyEqual(bs.chargeStatus(1), 0);
-0197             testCase.verifyEqual(bs.chargeResidual(1), 1, 'AbsTol', 1e-9);
-0198             % a -> b is elementally balanced (H on both sides)
-0199             testCase.verifyEqual(bs.balanceStatus(1), 1);
-0200 
-0201             % Balancing the charge flips chargeStatus, not balanceStatus
-0202             m.metCharges = [1;1];
-0203             bs = getElementalBalance(m);
-0204             testCase.verifyEqual(bs.chargeStatus(1), 1);
-0205             testCase.verifyEqual(bs.chargeResidual(1), 0, 'AbsTol', 1e-9);
-0206         end
-0207 
-0208         function getElementalBalanceChargeUnknownIsNotZero(testCase)
-0209             % An unset charge on a participating metabolite makes the charge
-0210             % balance unknown, and must not be reported as balanced. An unset
-0211             % charge on a metabolite that does not participate must not leak
-0212             % into the reaction's residual.
-0213             m = testCase.model;
-0214             m.mets      = {'a';'b';'spectator'};
-0215             m.metNames  = {'a';'b';'spectator'};
-0216             m.metComps  = [1;1;1];
-0217             m.metFormulas = {'H';'H';'H'};
-0218             m.metCharges  = [1;1;NaN];
-0219             m.comps     = {'c'};
-0220             m.compNames = {'cytosol'};
-0221             m.rxns      = {'R1'};
-0222             m.rxnNames  = {'R1'};
-0223             m.S         = sparse([-1;1;0]);
-0224             m.lb = 0; m.ub = 1000; m.rev = 0; m.c = 0; m.b = zeros(3,1);
-0225             m.grRules = {''}; m.genes = {}; m.rxnGeneMat = sparse(1,0);
-0226 
-0227             % The NaN belongs to a metabolite outside the reaction
-0228             bs = getElementalBalance(m);
-0229             testCase.verifyEqual(bs.chargeStatus(1), 1);
-0230             testCase.verifyEqual(bs.chargeResidual(1), 0, 'AbsTol', 1e-9);
-0231 
-0232             % Now the NaN is on a participant: unknown, not balanced
-0233             m.metCharges = [1;NaN;0];
-0234             bs = getElementalBalance(m);
-0235             testCase.verifyEqual(bs.chargeStatus(1), -1);
-0236             testCase.verifyTrue(isnan(bs.chargeResidual(1)));
-0237         end
-0238 
-0239         function getExchangeRxnsConsistent(testCase)
-0240             [exch, idx] = getExchangeRxns(testCase.model);
-0241             testCase.verifyClass(exch, 'cell');
-0242             testCase.verifyNotEmpty(exch);
-0243             testCase.verifyEqual(testCase.model.rxns(idx), exch);
-0244         end
-0245 
-0246         function getExchangeRxnsWithBoundaryMets(testCase)
-0247             % A model that still carries boundary metabolites (an
-0248             % "unconstrained" field, as every model from importModel does)
-0249             % takes a different branch than one where they have been
-0250             % removed. Both have to return reaction indexes.
-0251             m = testCase.taskTestModel();  % closeModel'd, so it has boundary mets
-0252             testCase.assertTrue(isfield(m, 'unconstrained'));
-0253             testCase.assertTrue(any(m.unconstrained ~= 0));
-0254 
-0255             [exch, idx] = getExchangeRxns(m);
-0256             testCase.verifyEqual(sort(exch), {'R1'; 'R8'});
-0257             testCase.verifyEqual(m.rxns(idx), exch);
-0258 
-0259             % R1 supplies a[s] ("=> a[s]"), R8 removes e[s] ("e[s] =>")
-0260             testCase.verifyEqual(getExchangeRxns(m, 'in'), {'R1'});
-0261             testCase.verifyEqual(getExchangeRxns(m, 'out'), {'R8'});
-0262         end
-0263 
-0264         function getExchangeRxnsAgreeAcrossBoundaryRemoval(testCase)
-0265             % Removing the boundary metabolites must not change which
-0266             % reactions are exchange reactions.
-0267             m = testCase.taskTestModel();
-0268             withBoundary = getExchangeRxns(m);
-0269             withoutBoundary = getExchangeRxns(simplifyModel(m));
-0270             testCase.verifyEqual(sort(withoutBoundary), sort(withBoundary));
-0271         end
-0272 
-0273         function getExchangeRxnsThirdOutputIsMetIndex(testCase)
-0274             [~, idx, mets] = getExchangeRxns(testCase.model);
-0275             testCase.verifyNumElements(mets, numel(idx));
-0276             % Each returned index must point to a metabolite with a
-0277             % non-zero entry in the exchange reaction's S column.
-0278             for i = 1:numel(idx)
-0279                 testCase.verifyNotEqual(mets(i), 0);
-0280                 testCase.verifyNotEqual(testCase.model.S(mets(i), idx(i)), 0);
-0281             end
-0282         end
-0283 
-0284         function getGenesFromGrRulesMatchesModel(testCase)
-0285             [genes, rxnGeneMat] = getGenesFromGrRules(testCase.model.grRules);
-0286             testCase.verifyTrue(iscellstr(genes)); %#ok<ISCLSTR>
-0287             testCase.verifySize(rxnGeneMat, [numel(testCase.model.rxns) numel(genes)]);
-0288             testCase.verifyEqual(sort(genes(:)), sort(testCase.model.genes(:)));
-0289         end
-0290 
-0291         function getIndexesByName(testCase)
-0292             idx = getIndexes(testCase.model, testCase.model.rxns(1:3), 'rxns');
-0293             testCase.verifyEqual(idx, [1;2;3]);
-0294         end
-0295 
-0296         function getIndexesLogical(testCase)
-0297             lg = getIndexes(testCase.model, testCase.model.rxns(1), 'rxns', true);
-0298             testCase.verifyClass(lg, 'logical');
-0299             testCase.verifyEqual(find(lg), 1);
-0300         end
-0301 
-0302         function getIndexesLogicalMaskAllTrueReturnsIndices(testCase)
-0303             % A logical all-true mask of length n must return numeric [1..n],
-0304             % not be passed through as a logical array: this requires an
-0305             % islogical check, since all() cannot distinguish the two cases.
-0306             nR = numel(testCase.model.rxns);
-0307             idx = getIndexes(testCase.model, true(nR, 1), 'rxns');
-0308             testCase.verifyClass(idx, 'double');
-0309             testCase.verifyEqual(idx, (1:nR)');
-0310         end
-0311 
-0312         function getRxnsInCompType(testCase)
-0313             I = getRxnsInComp(testCase.model, 'c');
-0314             testCase.verifyNotEmpty(I);
-0315         end
-0316 
-0317         function getTransportRxnsType(testCase)
-0318             tr = getTransportRxns(testCase.model);
-0319             testCase.verifyClass(tr, 'logical');
-0320             testCase.verifyNumElements(tr, numel(testCase.model.rxns));
-0321         end
-0322 
-0323         function parseFormulasMW(testCase)
-0324             [elements, useMat, exitFlag, MW] = parseFormulas(testCase.model.metFormulas); %#ok<ASGLU>
-0325             testCase.verifyNumElements(MW, numel(testCase.model.mets));
-0326             testCase.verifyTrue(isfield(elements, 'abbrevs'));
-0327         end
-0328 
-0329         function parseFormulasRejectsPartialParse(testCase)
-0330             % A formula that stops at an unrecognised element is not parsed,
-0331             % even though the part before it was readable. Reporting it as
-0332             % parsed would hand back a silently truncated composition.
-0333             [elements, useMat, exitFlag] = parseFormulas({'C6H12O6';'C6Zz3'});
-0334             testCase.verifyEqual(exitFlag, [1;-1]);
-0335             testCase.verifyEqual(sum(useMat(2,:)), 0);
-0336             % The good formula is unaffected
-0337             cIdx = strcmp(elements.abbrevs, 'C');
-0338             testCase.verifyEqual(useMat(1, cIdx), 6);
-0339         end
-0340 
-0341         function parseFormulasSingleFormulaKeepsElements(testCase)
-0342             % With one formula useMat is a row vector; the unused-element
-0343             % pruning must still test each element separately.
-0344             [elements, useMat] = parseFormulas({'C6H12O6'});
-0345             testCase.verifySize(useMat, [1 numel(elements.abbrevs)]);
-0346             testCase.verifyEqual(sort(elements.abbrevs(:)), {'C';'H';'O'});
-0347         end
-0348 
-0349         function parseFormulasUnknownMassBlanksOnlyItsOwnFormula(testCase)
-0350             % R and X have no mass. A formula that uses one gets MW = NaN;
-0351             % formulas that do not must keep their weight.
-0352             [~, ~, ~, MW] = parseFormulas({'C6H12O6';'C2R';'H2O';'CX'});
-0353             testCase.verifyFalse(isnan(MW(1)));
-0354             testCase.verifyTrue(isnan(MW(2)));
-0355             testCase.verifyFalse(isnan(MW(3)));
-0356             testCase.verifyTrue(isnan(MW(4)));
-0357         end
-0358 
-0359         function parseRxnEquMetNames(testCase)
-0360             mets = parseRxnEqu({'a + b => c'});
-0361             testCase.verifyTrue(all(ismember({'a','b','c'}, mets)));
-0362         end
-0363 
-0364         function modelSummaryNoFluxRuns(testCase)
-0365             out = evalc('modelSummary(testCase.model)');
-0366             testCase.verifyClass(out, 'char');
-0367             testCase.verifySubstring(out, 'Reactions');
-0368         end
-0369 
-0370         function modelSummaryWithFluxRuns(testCase)
-0371             testCase.assumeSolver('solveLP');
-0372             sol = solveLP(testCase.model);
-0373             out = evalc('modelSummary(testCase.model, ''fluxes'', sol.x)');
-0374             testCase.verifyClass(out, 'char');
-0375             testCase.verifySubstring(out, 'Objective value');
-0376         end
-0377 
-0378         function printFluxesRecognizesBoundaryMetExchanges(testCase)
-0379             % A model closed via closeModel represents an exchange as
-0380             % realMet <=> realMet[b] -- one reactant and one product --
-0381             % which "no reactants or no products" alone does not recognise,
-0382             % printing nothing for any of them.
-0383             m = testCase.taskTestModel();
-0384             [~,exchIdx] = getExchangeRxns(m,'all');
-0385             testCase.assumeNotEmpty(exchIdx, 'Fixture has no exchange reactions after closeModel.');
-0386             flux = ones(numel(m.rxns),1);
-0387             out = evalc('printFluxes(m, flux, ''onlyExchange'', true);');
-0388             testCase.verifySubstring(out, m.rxns{exchIdx(1)});
-0389         end
-0390 
-0391         function getTransportRxnsExcludesBoundaryMetExchanges(testCase)
-0392             % closeModel copies the real metabolite's own name onto its
-0393             % boundary counterpart, so an exchange reaction has the same
-0394             % "same name, different compartment" shape as a genuine
-0395             % transport reaction.
-0396             m = testCase.taskTestModel();
-0397             [~,exchIdx] = getExchangeRxns(m,'all');
-0398             testCase.assumeNotEmpty(exchIdx, 'Fixture has no exchange reactions after closeModel.');
-0399             tr = getTransportRxns(m);
-0400             testCase.verifyFalse(any(tr(exchIdx)));
-0401         end
-0402 
-0403         function printFluxesRuns(testCase)
-0404             testCase.assumeSolver('solveLP');
-0405             sol = solveLP(testCase.model);
-0406             out = evalc('printFluxes(testCase.model, sol.x, true)');
-0407             testCase.verifyClass(out, 'char');
-0408         end
-0409 
-0410         function printFluxesKeepsPercentInRxnName(testCase)
-0411             % A rxnName containing a literal "%" must not be reinterpreted
-0412             % as a format directive, which would truncate everything after
-0413             % it instead of printing the flux line in full.
-0414             m = testCase.model;
-0415             m.rxnNames{1} = 'reaction with 50% yield';
-0416             flux = zeros(numel(m.rxns),1);
-0417             flux(1) = 1;
-0418             out = evalc('printFluxes(m, flux, false)');
-0419             testCase.verifySubstring(out, 'reaction with 50% yield');
-0420         end
-0421 
-0422         function printModelStatsRuns(testCase)
-0423             out = evalc('printModelStats(testCase.model)');
-0424             testCase.verifyClass(out, 'char');
-0425         end
-0426 
-0427         function printModelStatsKeepsPercentInModelName(testCase)
-0428             % model.name/model.id/compNames/mets/rxns are spliced into
-0429             % fprintf templates; a literal "%" in any of them must not be
-0430             % reinterpreted as a format directive.
-0431             m = testCase.model;
-0432             m.name = 'test model with 50% coverage';
-0433             m.S(1,:) = 0; % make mets{1} unused so it hits the errorText path
-0434             m.metNames{1} = 'unused met 30% pure';
-0435             out = evalc(['printModelStats(m, ''printModelIssues'', true, ' ...
-0436                 '''printDetails'', true);']);
-0437             testCase.verifySubstring(out, 'test model with 50% coverage');
-0438             testCase.verifySubstring(out, 'unused met 30% pure');
-0439         end
-0440 
-0441     end
-0442 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tReconstruction.html b/doc/testing/function_tests/tReconstruction.html deleted file mode 100644 index bcf564f2..00000000 --- a/doc/testing/function_tests/tReconstruction.html +++ /dev/null @@ -1,169 +0,0 @@ - - - - Description of tReconstruction - - - - - - - - - -
Home > testing > function_tests > tReconstruction.m
- - - -

tReconstruction -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tReconstruction < RavenTestCase
-0002 % tReconstruction  Tests for the de-novo reconstruction functions in reconstruction/.
-0003 %
-0004 %   Homology and KEGG reconstruction depend on external binaries (BLAST+,
-0005 %   DIAMOND) and on a local KEGG dump / aligners. Those are guarded and skip
-0006 %   when unavailable; the self-contained functions are tested directly.
-0007 
-0008     methods (Test)
-0009 
-0010         function guessCompositionRuns(testCase)
-0011             evalc('[m2, guessed] = guessComposition(testCase.model);');
-0012             testCase.verifyClass(m2, 'struct');
-0013         end
-0014 
-0015         function guessCompositionAccountsForStoichiometricCoefficient(testCase)
-0016             % A -> 2 B, with A's formula known (CH4). B's own coefficient is
-0017             % 2, so its atom counts must be half of A's (C0.5H4/2 = C0.5H2),
-0018             % not identical to A's.
-0019             model = tReconstruction.toyModel();
-0020             model.metFormulas = {'CH4'; ''};
-0021             evalc('[m2, guessedFor, couldNotGuess] = guessComposition(model);');
-0022             testCase.verifyEqual(guessedFor, {'B'});
-0023             testCase.verifyEmpty(couldNotGuess);
-0024             testCase.verifyEqual(m2.metFormulas{2}, 'C0.5H2');
-0025         end
-0026 
-0027         function makeFakeBlastStructureReturnsStruct(testCase)
-0028             % makeFakeBlastStructure requires at least 10 ortholog pairs.
-0029             ol = [testCase.model.genes(1:10), strcat('t_', testCase.model.genes(1:10))];
-0030             bs = makeFakeBlastStructure(ol, 'srcModel', 'tgtOrg');
-0031             testCase.verifyClass(bs, 'struct');
-0032         end
-0033 
-0034         function getModelFromHomologyBuildsDraft(testCase)
-0035             src = testCase.model; src.id = 'srcModel';
-0036             n = min(20, numel(src.genes));
-0037             ol = [src.genes(1:n), strcat('t_', src.genes(1:n))];
-0038             bs = makeFakeBlastStructure(ol, 'srcModel', 'tgtOrg');
-0039             evalc('draft = getModelFromHomology({src}, bs, ''tgtOrg'');');
-0040             testCase.verifyClass(draft, 'struct');
-0041         end
-0042 
-0043         function getBlastRunsWhenAvailable(testCase)
-0044             fa1 = fullfile(testCase.ravenRoot,'testing','function_tests','test_data','human_galactosidases.fa');
-0045             fa2 = fullfile(testCase.ravenRoot,'testing','function_tests','test_data','yeast_galactosidases.fa');
-0046             testCase.assumeDependency(exist(fa1,'file')==2, 'test FASTA files');
-0047             ok = true;
-0048             try, evalc('bs = getBlast(''human'', fa1, {''yeast''}, {fa2});'); catch, ok = false; end
-0049             testCase.assumeTrue(ok, 'BLAST+ binary not functional in this environment');
-0050             testCase.verifyClass(bs, 'struct');
-0051         end
-0052 
-0053         function getDiamondRunsWhenAvailable(testCase)
-0054             fa1 = fullfile(testCase.ravenRoot,'testing','function_tests','test_data','human_galactosidases.fa');
-0055             fa2 = fullfile(testCase.ravenRoot,'testing','function_tests','test_data','yeast_galactosidases.fa');
-0056             testCase.assumeDependency(exist(fa1,'file')==2, 'test FASTA files');
-0057             ok = true;
-0058             try, evalc('bs = getDiamond(''human'', fa1, {''yeast''}, {fa2});'); catch, ok = false; end
-0059             testCase.assumeTrue(ok, 'DIAMOND binary not functional in this environment');
-0060             testCase.verifyClass(bs, 'struct');
-0061         end
-0062 
-0063         function getKEGGModelForOrganismNeedsData(testCase)
-0064             testCase.assumeFail('Requires keggModel.mat and an HMM library from raven-data.');
-0065         end
-0066 
-0067         function getModelFromKEGGNeedsData(testCase)
-0068             matFile = fullfile(testCase.ravenRoot, 'reconstruction', 'kegg', 'keggModel.mat');
-0069             testCase.assumeFalse(exist(matFile, 'file') == 2, ...
-0070                 'keggModel.mat is present; skipping the build-from-artefacts path.');
-0071             testCase.assumeFail('Downloads and assembles the full KEGG artefact set from raven-data; not run automatically.');
-0072         end
-0073 
-0074         function buildGlobalGPRJoinsGenesThroughSharedKO(testCase)
-0075             % Offline unit test of the join at the core of buildGlobalKEGGModel:
-0076             % two reactions sharing a KO, a KO used by two organisms' genes, and
-0077             % a reaction with no KO at all (should end up with no genes).
-0078             rxns = {'R1'; 'R2'; 'R3'};
-0079             koReaction = table({'K1'; 'K1'; 'K2'}, {'R1'; 'R2'; 'R2'}, ...
-0080                 'VariableNames', {'ko', 'reaction'});
-0081             organismGeneKO = table({'a'; 'a'; 'b'}, {'g1'; 'g2'; 'g1'}, {'K1'; 'K2'; 'K1'}, ...
-0082                 'VariableNames', {'organism', 'gene', 'ko'});
-0083             [genes, rxnGeneMat] = buildGlobalGPR(rxns, koReaction, organismGeneKO);
-0084             testCase.verifyEqual(genes, {'a:g1'; 'a:g2'; 'b:g1'});
-0085             expected = [1 0 1; 1 1 1; 0 0 0];
-0086             testCase.verifyEqual(full(rxnGeneMat), expected);
-0087         end
-0088 
-0089         function getPhylDistNeedsData(testCase)
-0090             distFile = fullfile(testCase.ravenRoot, 'reconstruction', 'kegg', 'keggPhylDist.mat');
-0091             testCase.assumeFalse(exist(distFile, 'file') == 2, ...
-0092                 'keggPhylDist.mat is present; skipping error-path test.');
-0093             testCase.verifyError(@() getPhylDist(), 'getPhylDist:noData');
-0094         end
-0095 
-0096     end
-0097 
-0098     methods (Static)
-0099         function model = toyModel()
-0100             % Single reaction: A -> 2 B, one compartment.
-0101             model = struct();
-0102             model.id          = 'toy';
-0103             model.name        = 'toy';
-0104             model.mets        = {'m1'; 'm2'};
-0105             model.metNames    = {'A'; 'B'};
-0106             model.comps       = {'c'};
-0107             model.compNames   = {'cytosol'};
-0108             model.metComps    = [1; 1];
-0109             model.metFormulas = {''; ''};
-0110             model.rxns        = {'R1'};
-0111             model.rxnNames    = {'R1'};
-0112             model.S           = sparse([-1; 2]);
-0113             model.lb          = 0;
-0114             model.ub          = 1000;
-0115             model.rev         = 0;
-0116             model.c           = 0;
-0117             model.b           = zeros(2, 1);
-0118         end
-0119     end
-0120 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tSampling.html b/doc/testing/function_tests/tSampling.html deleted file mode 100644 index 2feb3b10..00000000 --- a/doc/testing/function_tests/tSampling.html +++ /dev/null @@ -1,235 +0,0 @@ - - - - Description of tSampling - - - - - - - - - -
Home > testing > function_tests > tSampling.m
- - - -

tSampling -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tSampling < RavenTestCase
-0002 % tSampling  Tests for the MCMC flux samplers (CHRR, ACHR, MVE) dispatched
-0003 % through randomSampling.
-0004 %
-0005 %   The maximum-volume ellipsoid solver is the numerical crux of CHRR, so it is
-0006 %   validated directly against analytic cases (box -> unit ball, scaled/sheared
-0007 %   box, triangle -> Steiner inellipse) before the end-to-end samplers are
-0008 %   exercised on the textbook model. All methods are LP-only, so the default
-0009 %   GLPK solver suffices (no MILP assumption needed).
-0010 
-0011     methods (Test)
-0012 
-0013         % ---- sampleMaxVolEllipse: analytic validation -------------------
-0014 
-0015         function sampleMaxVolEllipseUnitBox(testCase)
-0016             % Box {-1 <= x_i <= 1} -> unit ball: centre 0, E*E' = I.
-0017             A = [eye(3); -eye(3)];
-0018             b = ones(6, 1);
-0019             [x, E, converged] = sampleMaxVolEllipse(A, b, zeros(3, 1));
-0020             testCase.verifyTrue(converged);
-0021             testCase.verifyLessThan(norm(x), 1e-4);
-0022             testCase.verifyLessThan(norm(E*E' - eye(3)), 1e-3);
-0023         end
-0024 
-0025         function sampleMaxVolEllipseScaledBox(testCase)
-0026             % Box {-r_i <= x_i <= r_i} -> E*E' = diag(r_i^2).
-0027             r = [2; 0.5; 3];
-0028             A = [eye(3); -eye(3)];
-0029             b = [r; r];
-0030             [x, E, converged] = sampleMaxVolEllipse(A, b, zeros(3, 1));
-0031             testCase.verifyTrue(converged);
-0032             testCase.verifyLessThan(norm(x), 1e-4);
-0033             testCase.verifyLessThan(norm(E*E' - diag(r.^2)), 1e-3);
-0034         end
-0035 
-0036         function sampleMaxVolEllipseShearedBox(testCase)
-0037             % {-1 <= M*x <= 1} -> E*E' = inv(M)*inv(M)'.
-0038             M = [1 0.5; 0 1];
-0039             A = [M; -M];
-0040             b = ones(4, 1);
-0041             [x, E, converged] = sampleMaxVolEllipse(A, b, zeros(2, 1));
-0042             Minv = inv(M);
-0043             testCase.verifyTrue(converged);
-0044             testCase.verifyLessThan(norm(x), 1e-4);
-0045             testCase.verifyLessThan(norm(E*E' - Minv*Minv'), 1e-3);
-0046         end
-0047 
-0048         function sampleMaxVolEllipseTriangle(testCase)
-0049             % Standard simplex {x>=0, x1+x2<=1}: the max-area inscribed ellipse is
-0050             % the Steiner inellipse, centred at the centroid (1/3,1/3) with
-0051             % E2 = [[1/9,-1/18],[-1/18,1/9]] (det 1/108) -- NOT the incircle. This
-0052             % confirms the solver maximises volume, not just the largest ball.
-0053             A = [-1 0; 0 -1; 1 1];
-0054             b = [0; 0; 1];
-0055             [x, E, converged] = sampleMaxVolEllipse(A, b, [0.25; 0.25]);
-0056             E2exp = [1/9 -1/18; -1/18 1/9];
-0057             testCase.verifyTrue(converged);
-0058             testCase.verifyLessThan(norm(x - [1/3; 1/3]), 1e-3);
-0059             testCase.verifyLessThan(norm(E*E' - E2exp), 1e-3);
-0060             testCase.verifyLessThan(abs(det(E*E') - 1/108), 1e-4);
-0061         end
-0062 
-0063         function sampleMaxVolEllipseRejectsExterior(testCase)
-0064             A = [eye(2); -eye(2)];
-0065             b = ones(4, 1);
-0066             testCase.verifyError(@() sampleMaxVolEllipse(A, b, [5; 0]), ...
-0067                 'RAVEN:sampling');
-0068         end
-0069 
-0070         % ---- sampler implementations return diagnostics -----------------
-0071 
-0072         function sampleCHRRReturnsInfo(testCase)
-0073             evalc('[sols, sInfo] = sampleCHRR(testCase.model, 10, 5, 20, 1);');
-0074             nRxns = numel(testCase.model.rxns);
-0075             testCase.verifySize(sols, [nRxns, 10]);
-0076             testCase.verifyTrue(isfield(sInfo, 'nDimensions'));
-0077             testCase.verifyTrue(isfield(sInfo, 'mveConverged'));
-0078             % Samples must satisfy steady state and bounds.
-0079             resid = testCase.model.S * sols;
-0080             testCase.verifyLessThan(max(abs(resid(:))), 1e-6);
-0081             testCase.verifyGreaterThanOrEqual(min(sols, [], 2), testCase.model.lb - 1e-6);
-0082             testCase.verifyLessThanOrEqual(max(sols, [], 2), testCase.model.ub + 1e-6);
-0083         end
-0084 
-0085         function sampleACHRReturnsSteadyState(testCase)
-0086             evalc('sols = sampleACHR(testCase.model, 10, 5, [], 1);');
-0087             nRxns = numel(testCase.model.rxns);
-0088             testCase.verifySize(sols, [nRxns, 10]);
-0089             resid = testCase.model.S * sols;
-0090             testCase.verifyLessThan(max(abs(resid(:))), 1e-6);
-0091         end
-0092 
-0093         % ---- randomSampling dispatch ------------------------------------
-0094 
-0095         function randomSamplingDefaultsToACHR(testCase)
-0096             % The default method must be ACHR: with a fixed seed the default call
-0097             % must equal an explicit 'achr' call.
-0098             evalc('sDefault = randomSampling(testCase.model, 8, ''thinning'', 5, ''seed'', 3);');
-0099             evalc('sAchr = randomSampling(testCase.model, 8, ''method'', ''achr'', ''thinning'', 5, ''seed'', 3);');
-0100             testCase.verifyEqual(sDefault, sAchr);
-0101         end
-0102 
-0103         function randomSamplingCHRRDispatch(testCase)
-0104             evalc(['sols = randomSampling(testCase.model, 10, ''method'', ''chrr'', ' ...
-0105                 '''thinning'', 5, ''nBurnin'', 20, ''seed'', 1);']);
-0106             nRxns = numel(testCase.model.rxns);
-0107             testCase.verifySize(sols, [nRxns, 10]);
-0108             resid = testCase.model.S * sols;
-0109             testCase.verifyLessThan(max(abs(resid(:))), 1e-6);
-0110         end
-0111 
-0112         function randomSamplingCHRRExposesInfo(testCase)
-0113             % The CHRR convergence diagnostics must be reachable through the
-0114             % documented randomSampling entry point, not only sampleCHRR.
-0115             evalc(['[sols, gr, sInfo] = randomSampling(testCase.model, 10, ' ...
-0116                 '''method'', ''chrr'', ''thinning'', 5, ''nBurnin'', 20, ''seed'', 1);']);
-0117             testCase.verifyTrue(isfield(sInfo, 'mveConverged'));
-0118         end
-0119 
-0120         function randomSamplingCHRRDeterministic(testCase)
-0121             evalc(['s1 = randomSampling(testCase.model, 8, ''method'', ''chrr'', ' ...
-0122                 '''thinning'', 5, ''nBurnin'', 20, ''seed'', 42);']);
-0123             evalc(['s2 = randomSampling(testCase.model, 8, ''method'', ''chrr'', ' ...
-0124                 '''thinning'', 5, ''nBurnin'', 20, ''seed'', 42);']);
-0125             testCase.verifyEqual(s1, s2);
-0126         end
-0127 
-0128         function randomSamplingCHRRVaries(testCase)
-0129             % A correct CHRR run must mix: at least one reaction varies, and two
-0130             % seeds give distinct sample matrices. Guards against a degenerate
-0131             % sampler that collapses to a single point (which would still satisfy
-0132             % S*v=0 and the bounds, and remain deterministic).
-0133             evalc(['s1 = randomSampling(testCase.model, 50, ''method'', ''chrr'', ' ...
-0134                 '''thinning'', 5, ''nBurnin'', 20, ''seed'', 1);']);
-0135             evalc(['s2 = randomSampling(testCase.model, 50, ''method'', ''chrr'', ' ...
-0136                 '''thinning'', 5, ''nBurnin'', 20, ''seed'', 2);']);
-0137             testCase.verifyGreaterThan(max(std(s1, 0, 2)), 1e-3);
-0138             testCase.verifyNotEqual(s1, s2);
-0139         end
-0140 
-0141         function randomSamplingUnknownMethodErrors(testCase)
-0142             testCase.verifyError( ...
-0143                 @() randomSampling(testCase.model, 5, 'method', 'gibbs'), ...
-0144                 'RAVEN:badInput');
-0145         end
-0146 
-0147         function randomSamplingLoopDetectionUsesModelBounds(testCase)
-0148             % Loop detection must take its threshold from the model. R2/R3
-0149             % form an a -> b -> a loop that runs up to this model's 100 cap,
-0150             % while the linear path R1 -> R4 is held to 10. A threshold that
-0151             % assumes ±1000 bounds matches neither, leaving the loop
-0152             % reactions to be sampled as if they were loop-free.
-0153             m = tSampling.loopModel();
-0154             evalc(['[~, goodRxns] = randomSampling(m, 2, ''method'', ' ...
-0155                 '''randomObjective'', ''seed'', 1);']);
-0156             testCase.verifyEqual(sort(goodRxns(:))', [1 4]);
-0157         end
-0158 
-0159     end
-0160 
-0161     methods (Static, Access = private)
-0162 
-0163         function m = loopModel()
-0164             m = struct();
-0165             m.id       = 'loop';
-0166             m.rxns     = {'R1';'R2';'R3';'R4'};
-0167             m.rxnNames = m.rxns;
-0168             m.mets     = {'a';'b'};
-0169             m.metNames = {'a';'b'};
-0170             m.metComps = [1;1];
-0171             m.comps    = {'c'};
-0172             m.compNames= {'cytosol'};
-0173             %             R1  R2  R3  R4
-0174             m.S = sparse([ 1  -1   1   0;    % a
-0175                            0   1  -1  -1]);  % b
-0176             m.lb  = [0;0;0;0];
-0177             m.ub  = [10;100;100;100];
-0178             m.rev = [0;0;0;0];
-0179             m.c   = [0;0;0;1];
-0180             m.b   = zeros(2,1);
-0181             m.genes = {}; m.grRules = {'';'';'';''}; m.rxnGeneMat = sparse(4,0);
-0182             m.metFormulas = {'C';'C'};
-0183         end
-0184 
-0185     end
-0186 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tSolver.html b/doc/testing/function_tests/tSolver.html deleted file mode 100644 index 2809b627..00000000 --- a/doc/testing/function_tests/tSolver.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - Description of tSolver - - - - - - - - - -
Home > testing > function_tests > tSolver.m
- - - -

tSolver -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tSolver < RavenTestCase
-0002 % tSolver  Tests for the LP/QP solving and solver-configuration functions in solver/.
-0003 
-0004     methods (Test)
-0005 
-0006         function setRavenSolverAcceptsGlpk(testCase)
-0007             testCase.verifyWarningFree(@() setRavenSolver('glpk'));
-0008         end
-0009 
-0010         function setRavenSolverRejectsUnknown(testCase)
-0011             testCase.verifyError(@() setRavenSolver('notarealsolver'), ?MException);
-0012         end
-0013 
-0014         function solveLPFindsOptimum(testCase)
-0015             sol = solveLP(testCase.model);
-0016             testCase.verifyEqual(sol.stat, 1);
-0017             testCase.verifyNumElements(sol.x, numel(testCase.model.rxns));
-0018             testCase.verifyEqual(sol.f, 0.87392, 'AbsTol', 1e-3);
-0019         end
-0020 
-0021         function solveLPMinimiseFluxes(testCase)
-0022             sol = solveLP(testCase.model, 1);
-0023             testCase.verifyEqual(sol.stat, 1);
-0024         end
-0025 
-0026         function optimizeProbSolvesLP(testCase)
-0027             res = optimizeProb(testCase.modelLP());
-0028             testCase.verifyClass(res, 'struct');
-0029             testCase.verifyTrue(isfield(res, 'obj'));
-0030         end
-0031 
-0032         function checkSolutionOnFeasibleProblem(testCase)
-0033             res = optimizeProb(testCase.modelLP());
-0034             [isFeasible, isOptimal] = checkSolution(res);
-0035             testCase.verifyTrue(logical(isFeasible));
-0036             testCase.verifyTrue(logical(isOptimal));
-0037         end
-0038 
-0039         function splitProbForConditioningReturnsProb(testCase)
-0040             [p2, condInfo] = splitProbForConditioning(testCase.modelLP(), 1e6); %#ok<ASGLU>
-0041             testCase.verifyClass(p2, 'struct');
-0042         end
-0043 
-0044     end
-0045 
-0046     methods (Access = private)
-0047         function prob = modelLP(testCase)
-0048             % Build the COBRA-style LP problem for the test model exactly as
-0049             % solveLP does, so the low-level solver functions get a valid input.
-0050             m = testCase.model;
-0051             nMets = size(m.S, 1);
-0052             prob = [];
-0053             prob.c = [m.c * -1; zeros(nMets, 1)];
-0054             prob.b = zeros(nMets, 1);
-0055             prob.lb = [m.lb; m.b(:, 1)];
-0056             prob.ub = [m.ub; m.b(:, min(size(m.b, 2), 2))];
-0057             prob.csense = repmat('E', 1, numel(prob.b));
-0058             prob.osense = 1;
-0059             prob.vartype = repmat('C', 1, numel(prob.c));
-0060             prob.A = [m.S, -speye(nMets)];
-0061             prob.a = m.S;
-0062         end
-0063     end
-0064 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tSyntax.html b/doc/testing/function_tests/tSyntax.html deleted file mode 100644 index ccbb1fbf..00000000 --- a/doc/testing/function_tests/tSyntax.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - Description of tSyntax - - - - - - - - - -
Home > testing > function_tests > tSyntax.m
- - - -

tSyntax -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tSyntax < RavenTestCase
-0002 % tSyntax  Every RAVEN source file must parse.
-0003 %
-0004 %   A MATLAB file with unbalanced block keywords still sits in the repository
-0005 %   looking perfectly ordinary: the error only surfaces when something calls
-0006 %   it, so a file behind an unconditional skip (e.g. assumeFail) can carry a
-0007 %   broken parse indefinitely without any test catching it.
-0008 %
-0009 %   This test is the cheap backstop: it needs no data, no solver and no
-0010 %   network, and it runs over the entire source tree in seconds.
-0011 
-0012     methods (Test)
-0013         function allSourceFilesParse(testCase)
-0014             files = dir(fullfile(testCase.ravenRoot,'**','*.m'));
-0015             paths = fullfile({files.folder}, {files.name});
-0016 
-0017             % software/ is vendored third-party code (GLPKmex, libSBML). It is
-0018             % not ours to fix, so a syntax complaint there is not a RAVEN bug.
-0019             paths = paths(~contains(paths, [filesep 'software' filesep]));
-0020 
-0021             testCase.assertNotEmpty(paths, 'Found no .m files to check.');
-0022 
-0023             msgs = checkcode(paths{:}, '-id');
-0024             if ~iscell(msgs)
-0025                 msgs = {msgs};   % checkcode returns a bare struct for one file
-0026             end
-0027 
-0028             offenders = {};
-0029             for i = 1:numel(paths)
-0030                 if isempty(msgs{i})
-0031                     continue
-0032                 end
-0033                 % SYNER is checkcode's parse-error identifier. Only syntax is
-0034                 % checked here; style warnings are deliberately ignored so the
-0035                 % test stays a hard gate rather than a lint backlog.
-0036                 bad = strcmp({msgs{i}.id}, 'SYNER');
-0037                 for k = find(bad)
-0038                     offenders{end+1} = sprintf('%s (line %d): %s', ...
-0039                         strrep(paths{i}, testCase.ravenRoot, ''), ...
-0040                         msgs{i}(k).line, msgs{i}(k).message); %#ok<AGROW>
-0041                 end
-0042             end
-0043 
-0044             testCase.verifyEmpty(offenders, ...
-0045                 sprintf('Files with parse errors:\n%s', strjoin(offenders, newline)));
-0046         end
-0047     end
-0048 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tTasks.html b/doc/testing/function_tests/tTasks.html deleted file mode 100644 index f5e55eca..00000000 --- a/doc/testing/function_tests/tTasks.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - Description of tTasks - - - - - - - - - -
Home > testing > function_tests > tTasks.m
- - - -

tTasks -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tTasks < RavenTestCase
-0002 % tTasks  Tests for the metabolic-task functions in tasks/.
-0003 
-0004     methods (Test)
-0005 
-0006         function checkTasksSucceedsOnFullModel(testCase)
-0007             m = testCase.taskTestModel();
-0008             task = testCase.taskTestStruct();
-0009             [~, report] = evalc('checkTasks(m, [], true, false, false, task);');
-0010             testCase.verifyTrue(report.ok == 1);
-0011         end
-0012 
-0013         function checkTasksFailsWithoutKeyReaction(testCase)
-0014             m = removeReactions(testCase.taskTestModel(), {'R2'});
-0015             task = testCase.taskTestStruct();
-0016             [~, report] = evalc('checkTasks(m, [], true, false, false, task);');
-0017             testCase.verifyTrue(report.ok == 0);
-0018         end
-0019 
-0020         function parseTaskListReturnsStruct(testCase)
-0021             taskFile = fullfile(testCase.ravenRoot,'testing','function_tests', ...
-0022                 'test_data','test_tasks.txt');
-0023             testCase.assumeDependency(exist(taskFile,'file')==2, 'test_tasks.txt');
-0024             ts = parseTaskList(taskFile);
-0025             testCase.verifyClass(ts, 'struct');
-0026             testCase.verifyNotEmpty(ts);
-0027         end
-0028 
-0029         function parseTaskListMatchesExpected(testCase)
-0030             % The text and Excel task definitions must parse to identical
-0031             % structs with exactly the expected fields and values.
-0032             dataDir   = fullfile(testCase.ravenRoot,'testing','function_tests','test_data');
-0033             txtFile   = fullfile(dataDir,'test_tasks.txt');
-0034             xlsFile   = fullfile(dataDir,'test_tasks.xls');
-0035             testCase.assumeDependency(exist(txtFile,'file')==2, 'test_tasks.txt');
-0036             testCase.assumeDependency(exist(xlsFile,'file')==2, 'test_tasks.xls');
-0037 
-0038             taskStruct      = parseTaskList(txtFile);
-0039             taskStructExcel = parseTaskList(xlsFile);
-0040 
-0041             expTask1.id          = 'ER';
-0042             expTask1.description = 'Aerobic rephosphorylation of ATP from glucose';
-0043             expTask1.shouldFail  = true;
-0044             expTask1.printFluxes = true;
-0045             expTask1.comments    = 'Messed up reaction';
-0046             expTask1.inputs      = {'O2[s]';'glucose[s]'};
-0047             expTask1.LBin        = [23.6;23.6];
-0048             expTask1.UBin        = [23.8;23.8];
-0049             expTask1.outputs     = {'H2O[s]';'CO2[s]'};
-0050             expTask1.LBout       = [26.1;26.1];
-0051             expTask1.UBout       = [26.2;26.2];
-0052             expTask1.equations   = {'ATP[c] + H2O[c] => ADP[c] + Pi[c] + H+[c]'};
-0053             expTask1.LBequ       = 30.2;
-0054             expTask1.UBequ       = 30.6;
-0055             expTask1.changed     = {'ATP[a] + H2O[a] => ADP[a] + Pi[a] + H+[a]'};
-0056             expTask1.LBrxn       = 56.2;
-0057             % incomplete struct must not match
-0058             testCase.verifyNotEqual(taskStruct(1), expTask1);
-0059             expTask1.UBrxn = 60;
-0060             testCase.verifyEqual(taskStruct(1), expTask1);
-0061             testCase.verifyEqual(taskStructExcel(1), expTask1);
-0062 
-0063             testCase.verifyEqual(length(taskStruct), 2);
-0064             testCase.verifyEqual(length(taskStructExcel), 2);
-0065 
-0066             expTask2.id          = 'BS';
-0067             expTask2.description = 'ATP de novo synthesis';
-0068             expTask2.shouldFail  = false;
-0069             expTask2.printFluxes = false;
-0070             expTask2.comments    = '';
-0071             expTask2.inputs      = {'O2[s]';'glucose[s]';'NH3[s]';'Pi[s]'};
-0072             expTask2.LBin        = [0;0;0;0];
-0073             expTask2.UBin        = [1000;1000;1000;1000];
-0074             expTask2.outputs     = {'H2O[s]';'CO2[s]';'ATP[c]'};
-0075             expTask2.LBout       = [0;0;1];
-0076             expTask2.UBout       = [1.1;1.1;1.3];
-0077             expTask2.equations   = {'ATP[c] + H2O[c] <=> ADP[c] + Pi[c] + H+[c]'};
-0078             expTask2.LBequ       = -1000;
-0079             expTask2.UBequ       = 1000;
-0080             expTask2.changed     = {};
-0081             expTask2.LBrxn       = [];
-0082             expTask2.UBrxn       = [];
-0083             testCase.verifyEqual(taskStruct(2), expTask2);
-0084             testCase.verifyEqual(taskStructExcel(2), expTask2);
-0085 
-0086             % the two formats must produce identical results
-0087             testCase.verifyEqual(taskStruct, taskStructExcel);
-0088         end
-0089 
-0090     end
-0091 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/testing/function_tests/tUtils.html b/doc/testing/function_tests/tUtils.html deleted file mode 100644 index 3971ed84..00000000 --- a/doc/testing/function_tests/tUtils.html +++ /dev/null @@ -1,241 +0,0 @@ - - - - Description of tUtils - - - - - - - - - -
Home > testing > function_tests > tUtils.m
- - - -

tUtils -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef tUtils < RavenTestCase
-0002 % tUtils  Tests for the low-level helpers in utils/.
-0003 
-0004     methods (Test)
-0005 
-0006         function deprecationWarningWarnsOncePerSession(testCase)
-0007             % A name no deprecated wrapper uses, so this test does not
-0008             % depend on whether some other test warned first.
-0009             name = 'tUtilsDeprecationDummy';
-0010             testCase.verifyWarning( ...
-0011                 @() deprecationWarning(name,'somethingElse(model)'), ...
-0012                 'RAVEN:deprecated');
-0013             % Second call in the same session stays quiet.
-0014             testCase.verifyWarningFree( ...
-0015                 @() deprecationWarning(name,'somethingElse(model)'));
-0016         end
-0017 
-0018         function deprecationWarningNamesTheReplacement(testCase)
-0019             name = 'tUtilsDeprecationNamed';
-0020             lastwarn('');
-0021             deprecationWarning(name,'findLeakMetabolite(model,''produce'',...)');
-0022             testCase.verifySubstring(lastwarn, 'findLeakMetabolite');
-0023         end
-0024 
-0025         function legacyMethodNoticeWarnsOncePerSession(testCase)
-0026             % A name no legacy function uses, so this test does not depend on
-0027             % whether some other test noticed first.
-0028             name = 'tUtilsLegacyDummy';
-0029             testCase.verifyWarning( ...
-0030                 @() legacyMethodNotice(name,'somethingElse'), ...
-0031                 'RAVEN:legacyMethod');
-0032             % Second call in the same session stays quiet.
-0033             testCase.verifyWarningFree( ...
-0034                 @() legacyMethodNotice(name,'somethingElse'));
-0035         end
-0036 
-0037         function legacyMethodNoticePromisesNoRemoval(testCase)
-0038             % The point of a separate identifier from RAVEN:deprecated is that
-0039             % it commits to nothing being removed.
-0040             name = 'tUtilsLegacyNamed';
-0041             lastwarn('');
-0042             legacyMethodNotice(name,'ftINIT');
-0043             testCase.verifySubstring(lastwarn, 'ftINIT');
-0044             testCase.verifySubstring(lastwarn, 'remains supported');
-0045             testCase.verifyEmpty(strfind(lastwarn, 'will be removed'));
-0046         end
-0047 
-0048         function convertCharArrayFromChar(testCase)
-0049             testCase.verifyEqual(convertCharArray('abc'), {'abc'});
-0050         end
-0051 
-0052         function convertCharArrayFromCell(testCase)
-0053             testCase.verifyEqual(convertCharArray({'a','b'}), {'a','b'});
-0054         end
-0055 
-0056         function convertCharArrayFromString(testCase)
-0057             testCase.verifyEqual(convertCharArray("str"), {'str'});
-0058         end
-0059 
-0060         function ravenListFormatsItems(testCase)
-0061             msg = ravenList('Bad genes:', {'G1','G2'});
-0062             testCase.verifySubstring(msg, 'G1');
-0063             testCase.verifySubstring(msg, 'G2');
-0064         end
-0065 
-0066         function ravenListIndentsItemsWithATab(testCase)
-0067             % strcat(char(9), items) strips the tab entirely, since strcat
-0068             % removes trailing whitespace from a plain char argument and a
-0069             % lone tab is entirely trailing whitespace.
-0070             msg = ravenList('Bad genes:', {'G1'});
-0071             lines = strsplit(msg, newline);
-0072             testCase.verifyEqual(lines{2}(1), char(9));
-0073         end
-0074 
-0075         function ravenListTrimsToTenByDefault(testCase)
-0076             items = arrayfun(@(n) sprintf('G%d',n), 1:12, 'UniformOutput', false);
-0077             msg = ravenList('Too many:', items);
-0078             testCase.verifySubstring(msg, '...and 3 more');
-0079         end
-0080 
-0081         function ravenListNoTrimWhenFalse(testCase)
-0082             items = arrayfun(@(n) sprintf('G%d',n), 1:12, 'UniformOutput', false);
-0083             msg = ravenList('All:', items, false);
-0084             testCase.verifySubstring(msg, 'G12');
-0085             testCase.verifyFalse(contains(msg, '...and'));
-0086         end
-0087 
-0088         function printOrangeReturnsTextContainingInput(testCase)
-0089             evalc('s = printOrange(''hello'');');
-0090             testCase.verifySubstring(s, 'hello');
-0091         end
-0092 
-0093         function printOrangeKeepsPercentWhenPrinting(testCase)
-0094             % With nargout==0, printOrange prints via fprintf; a literal
-0095             % "%" in the input must not be reinterpreted as a format
-0096             % directive, which would truncate the rest of the text.
-0097             out = evalc('printOrange(''50% complete'');');
-0098             testCase.verifySubstring(out, '50% complete');
-0099         end
-0100 
-0101         function parallelWorkersRAVENFalseReturnsZero(testCase)
-0102             testCase.verifyEqual(parallelWorkersRAVEN(false), 0);
-0103         end
-0104 
-0105         function parallelWorkersRAVENTrueNoPCTReturnsZero(testCase)
-0106             testCase.assumeFalse(license('test','Distrib_Computing_Toolbox'), ...
-0107                 'Parallel Computing Toolbox present; skipping no-PCT path.');
-0108             testCase.verifyEqual(parallelWorkersRAVEN(true), 0);
-0109         end
-0110 
-0111         function parallelWorkersRAVENTruePCTReturnsInf(testCase)
-0112             testCase.assumeTrue(license('test','Distrib_Computing_Toolbox'), ...
-0113                 'Parallel Computing Toolbox not present; skipping PCT path.');
-0114             testCase.verifyEqual(parallelWorkersRAVEN(true), Inf);
-0115         end
-0116 
-0117         function runRAVENtestsExists(testCase)
-0118             % Do not execute it (would run the legacy suite); just confirm presence.
-0119             testCase.verifyEqual(exist('runRAVENtests','file'), 2);
-0120         end
-0121 
-0122         function parseGrRuleBuildsTree(testCase)
-0123             t = parseGrRule('(G1 and G2) or G3');
-0124             testCase.verifyEqual(t.type, 'or');
-0125             testCase.verifyEqual(numel(t.children), 2);
-0126             testCase.verifyEqual(t.children{1}.type, 'and');
-0127             testCase.verifyEqual(t.children{2}.id, 'G3');
-0128         end
-0129 
-0130         function parseGrRuleEmptyIsEmpty(testCase)
-0131             testCase.verifyEmpty(parseGrRule(''));
-0132             testCase.verifyEmpty(parseGrRule('   '));
-0133         end
-0134 
-0135         function parseGrRuleTakesWholeWordOperators(testCase)
-0136             % Gene IDs containing "and"/"or" are genes, not operators.
-0137             t = parseGrRule('RAND1 or ORF2');
-0138             testCase.verifyEqual(t.type, 'or');
-0139             testCase.verifyEqual(t.children{1}.id, 'RAND1');
-0140             testCase.verifyEqual(t.children{2}.id, 'ORF2');
-0141         end
-0142 
-0143         function parseGrRuleAcceptsSymbolOperators(testCase)
-0144             testCase.verifyEqual(parseGrRule('G1 & G2').type, 'and');
-0145             testCase.verifyEqual(parseGrRule('G1 | G2').type, 'or');
-0146             testCase.verifyEqual(parseGrRule('G1 AND G2').type, 'and');
-0147         end
-0148 
-0149         function parseGrRuleRejectsMalformed(testCase)
-0150             testCase.verifyError(@() parseGrRule('(G1 and G2'), 'RAVEN:badGrRule');
-0151             testCase.verifyError(@() parseGrRule('G1 and'), 'RAVEN:badGrRule');
-0152             testCase.verifyError(@() parseGrRule('G1 G2'), 'RAVEN:badGrRule');
-0153         end
-0154 
-0155         function isDnfGrRuleClassifies(testCase)
-0156             testCase.verifyTrue(isDnfGrRule('(G1 and G2) or G3'));
-0157             testCase.verifyTrue(isDnfGrRule('G1'));
-0158             testCase.verifyTrue(isDnfGrRule(''));
-0159             testCase.verifyFalse(isDnfGrRule('(G1 or G2) and G3'));
-0160             testCase.verifyFalse(isDnfGrRule('G1 and (G2 or G3)'));
-0161         end
-0162 
-0163         function isDnfGrRuleIgnoresRedundantBrackets(testCase)
-0164             % Bracketing alone never makes a rule non-DNF.
-0165             testCase.verifyTrue(isDnfGrRule('((G1 and G2) or G3)'));
-0166             testCase.verifyTrue(isDnfGrRule('(G1 or G2) or (G3 and G4)'));
-0167         end
-0168 
-0169         function grRuleToDNFDistributes(testCase)
-0170             testCase.verifyEqual(grRuleToDNF('g1 and (g2 or g3)'), ...
-0171                 {{'g1','g2'}, {'g1','g3'}});
-0172             % Two or:s, but four isozymes: the count of ' or ' is not the
-0173             % count of clauses.
-0174             testCase.verifyEqual(numel(grRuleToDNF('(g1 or g2) and (g3 or g4)')), 4);
-0175         end
-0176 
-0177         function grRuleToDNFSimpleCases(testCase)
-0178             testCase.verifyEqual(grRuleToDNF(''), {});
-0179             testCase.verifyEqual(grRuleToDNF('G1'), {{'G1'}});
-0180             testCase.verifyEqual(grRuleToDNF('(G1 and G2) or G3'), {{'G1','G2'},{'G3'}});
-0181         end
-0182 
-0183         function grRuleToStringRoundTrips(testCase)
-0184             rules = {'(G1 and G2) or G3', 'G1', 'G1 and G2', 'G1 or G2'};
-0185             for k = 1:numel(rules)
-0186                 testCase.verifyEqual(grRuleToString(parseGrRule(rules{k})), rules{k});
-0187             end
-0188             testCase.verifyEqual(grRuleToString(parseGrRule('')), '');
-0189         end
-0190 
-0191     end
-0192 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/tutorial/index.html b/doc/tutorial/index.html deleted file mode 100644 index 51323021..00000000 --- a/doc/tutorial/index.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - Index for Directory tutorial - - - - - - - - - - -
< Master indexIndex for tutorial >
- -

Index for tutorial

- -

Matlab files in this directory:

- -
 tutorial1tutorial1
 tutorial2tutorial2
 tutorial2_solutionstutorial2_solutions
 tutorial3tutorial3
 tutorial3_solutionstutorial3_solutions
 tutorial4tutorial4
 tutorial4_solutionstutorial4_solutions
 tutorial5tutorial5
 tutorial7tutorial7
- -

Other Matlab-specific files in this directory:

-
    -
  • empty.mat
- - -
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/tutorial/tutorial1.html b/doc/tutorial/tutorial1.html deleted file mode 100644 index 6ce769e5..00000000 --- a/doc/tutorial/tutorial1.html +++ /dev/null @@ -1,183 +0,0 @@ - - - - Description of tutorial1 - - - - - - - - - -
Home > tutorial > tutorial1.m
- - - -

tutorial1 -

- -

PURPOSE ^

-
tutorial1
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
 tutorial1
-   This is a short introduction that shows how to load a genome-scale
-   metabolic model (GEM), set reaction constraints, objective function and
-   perform an optimization through flux balance analysis (FBA). The
-   resulting fluxes are visualized and exported to a PDF file.
-   For a more detailed description of the individual functions, see
-   [raven_directory]/doc/index.html.
-   A GEM for the filamentous fungus Penicillium chrysogenum is used in
-   this tutorial. The model is provided as the SBML file
-   "iAL1006 v1.00.xml".
-   See Tutorial 1 on the RAVEN wiki for more details:
-   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 % tutorial1
-0002 %   This is a short introduction that shows how to load a genome-scale
-0003 %   metabolic model (GEM), set reaction constraints, objective function and
-0004 %   perform an optimization through flux balance analysis (FBA). The
-0005 %   resulting fluxes are visualized and exported to a PDF file.
-0006 %   For a more detailed description of the individual functions, see
-0007 %   [raven_directory]/doc/index.html.
-0008 %   A GEM for the filamentous fungus Penicillium chrysogenum is used in
-0009 %   this tutorial. The model is provided as the SBML file
-0010 %   "iAL1006 v1.00.xml".
-0011 %   See Tutorial 1 on the RAVEN wiki for more details:
-0012 %   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
-0013 
-0014 %Import the model from SBML. This function performs a number of checks
-0015 %regarding the model structure (such as for incorrectly written equations
-0016 %or illegal characters). In this structure there is only one warning; that
-0017 %the formula for the metabolite LPE could not be parsed. The "false" flag
-0018 %imports a model with exchange reactions in their "closed" form. This makes
-0019 %the model unsuited for modelling, but it is useful for some quality
-0020 %control steps.
-0021 model=importModel('iAL1006 v1.00.xml',false);
-0022 
-0023 %The following function prints some properties of the model. The two "true"
-0024 %flags say that it should also list potential problems such as dead-end
-0025 %reactions or unconnected metabolites.
-0026 printModelStats(model,true,true);
-0027 
-0028 %As can be seen the model contains 1632 reactions, 1395 metabolites and
-0029 %1006 genes
-0030 
-0031 %Most modelling approaches using GEMs are based on the mass balancing
-0032 %around the internal metabolites in the system. However, for the system to
-0033 %uptake or excrete metabolites, some metabolites have been defined as
-0034 %"unconstrained". To simulate anything, those metabolites have to be
-0035 %removed from the model. The function simplifyModel
-0036 %is a general-purpose function for making models smaller. This includes the
-0037 %options such as grouping linear reactions and deleting reactions which
-0038 %cannot carry flux. Here it is chosen to delete the exchange metabolites,
-0039 %all reactions that are constrained to zero (mainly uptake of non-standard
-0040 %carbon sources), and all reactions that cannot carry flux (mainly
-0041 %reactions that were dependent on any of those non-standard carbons
-0042 %sources).
-0043 model=simplifyModel(model,true,false,true,true);
-0044 
-0045 %As can be seen the model now contains only 1305 reactions, 1037
-0046 %metabolites and 1006 genes
-0047 
-0048 %As a first way of validating the model, calculate the theoretical yield of
-0049 %carbon dioxide from glucose. The supplied model already allows for uptake
-0050 %of phosphate, sulfate, NH3, O2 and the co-factor precursors thiamin and
-0051 %pimelate. The setParam function is used for setting constraints,
-0052 %reversibility and objective function coefficients.
-0053 %Set the uptake of glucose to be no more than 1 mmol/gDW/h and no uptake of
-0054 %ethanol.
-0055 model=setParam(model,'ub',{'glcIN' 'etohIN'},[1 0]);
-0056 
-0057 %Set the objective for the simulation to maximize CO2 production
-0058 model=setParam(model,'obj',{'co2OUT'},1);
-0059 
-0060 %The problem can now be solved using linear programming. The solveLP
-0061 %function takes a model and solves the linear programming problem defined
-0062 %by the constraints and the objective value coefficients.
-0063 sol=solveLP(model);
-0064 
-0065 %If everything worked fine one should see a structure that contains the
-0066 %fields .f which is the objective value, .stat which is 1 if the
-0067 %optimization terminated successfully and .x which contains the fluxes
-0068 %through each of the reactions in this particular solution of the problem.
-0069 %sol.x contains 1305 values which makes it rather difficult to interpret. A
-0070 %first approach is to look at only the reactions that transports
-0071 %metabolites in to or out from the system. The printFluxes function prints
-0072 %(parts of) the flux distribution to the console.
-0073 printFluxes(model, sol.x, true, 10^-7); %true means only print exchange fluxes
-0074 
-0075 %One can see that the system took up one unit of glucose and 6 units of O2
-0076 %while producing 6 units of CO2 and 6 units of H2O. To get more detailed
-0077 %insight how this happens, one can use the same function to plot all fluxes
-0078 %(above 10^-7).
-0079 printFluxes(model, sol.x, false, 10^-7);
-0080 
-0081 %The results show many reactions that have -1000 or 1000 flux. This is
-0082 %because there are loops in the solution. In order to clean up the solution
-0083 %one can minimize the sum of all the fluxes. This is done by setting the
-0084 %third flag to solveLP to true (take a look at solveLP, there are other
-0085 %options as well).
-0086 sol=solveLP(model,1);
-0087 printFluxes(model, sol.x, false, 10^-7);
-0088 
-0089 %Now there are much fewer reactions that are active. Some of them will
-0090 %probably appear to be a little "weird" but that is because the ATP and
-0091 %NAD(P)H produced has to go somewhere to be mass balanced.
-0092 
-0093 %If one wants to study the growth instead, change the objective to be the
-0094 %production of biomass instead of CO2
-0095 model=setParam(model,'obj',{'bmOUT'},1);
-0096 sol=solveLP(model,1);
-0097 printFluxes(model, sol.x, true, 10^-7);
-0098 
-0099 %The results show that the growth rate is 0.084/h and that the system now
-0100 %also requires sulfate, phosphate, NH3, thiamin and pimelate. Compare this
-0101 %to the growth on ethanol instead of glucose. Use three times the molar
-0102 %flux of ethanol since it contains 2 carbons rather than 6. 'eq' means
-0103 %'equal to' and sets the lower and upper bound to the same value.
-0104 modelETH=setParam(model,'eq',{'glcIN' 'etohIN'},[0 3]);
-0105 solETH=solveLP(modelETH,1);
-0106 printFluxes(modelETH, solETH.x, true, 10^-7);
-0107 
-0108 %Investigate how metabolism changes between the two carbon sources.
-0109 %compareFluxes takes two flux distributions and reports every reaction whose
-0110 %flux changed, largest change first, labelling those that were turned on,
-0111 %turned off or reversed direction. Here only changes above 0.5 mmol/gDW/h
-0112 %are considered.
-0113 res=compareFluxes(modelETH,sol.x,solETH.x,'cutoff',0.5);
-0114 
-0115 %The printed table is capped at 20 rows, but res.changed holds every changed
-0116 %reaction and res.turnedOn/turnedOff/flipped list those that switched state.
-0117 %By studying them one can start to get an idea about where the key changes
-0118 %occur. Visualization can help a lot in this regard. One can restrict the
-0119 %comparison to ATP metabolism by naming the metabolites of interest:
-0120 compareFluxes(modelETH,sol.x,solETH.x,'cutoff',0.4,'metaboliteList',{'ATP'});
-0121 
-0122 %See that on glucose ATP is generated in glycolysis but on ethanol it seems
-0123 %to have to do with acetate and so on. This allows the user to look further
-0124 %and further down until one understands the underlying flux redistributions
-0125 %that give rise to different phenotypes.
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/tutorial/tutorial2.html b/doc/tutorial/tutorial2.html deleted file mode 100644 index 2835eb9e..00000000 --- a/doc/tutorial/tutorial2.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - Description of tutorial2 - - - - - - - - - -
Home > tutorial > tutorial2.m
- - - -

tutorial2 -

- -

PURPOSE ^

-
tutorial2
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
 tutorial2
-   This exercise deals with the a small glycolysis model in RAVEN
-   compatible Excel format and shows the most basic aspects of the
-   stoichiometric modelling. It is shown how to build a simple model from
-   scratch, set parameters and perform simple simulations.
-   See Tutorial 2 on the RAVEN wiki for more details:
-   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 % tutorial2
-0002 %   This exercise deals with the a small glycolysis model in RAVEN
-0003 %   compatible Excel format and shows the most basic aspects of the
-0004 %   stoichiometric modelling. It is shown how to build a simple model from
-0005 %   scratch, set parameters and perform simple simulations.
-0006 %   See Tutorial 2 on the RAVEN wiki for more details:
-0007 %   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
-0008 
-0009 %Import the model into a RAVEN model structure
-0010 smallModel=importModel('empty.xml');
-0011 
-0012 %This solves the linear programming problem.
-0013 %NOTE: if sol.f is equal to zero then the problem is not solvable. Ensure
-0014 %that uptake and excretion of all necessary stuff are added to the model
-0015 %and run the solveLP again.
-0016 sol=solveLP(smallModel);
-0017 
-0018 %Print the resulting exchange fluxes
-0019 printFluxes(smallModel,sol.x,true);
-0020 
-0021 %Print all fluxes
-0022 printFluxes(smallModel,sol.x,false,10^-5,[],'%rxnID (%rxnName):\n\t%eqn\n\t%flux\n');
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/tutorial/tutorial2_solutions.html b/doc/tutorial/tutorial2_solutions.html deleted file mode 100644 index c5ded337..00000000 --- a/doc/tutorial/tutorial2_solutions.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - Description of tutorial2_solutions - - - - - - - - - -
Home > tutorial > tutorial2_solutions.m
- - - -

tutorial2_solutions -

- -

PURPOSE ^

-
tutorial2_solutions
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
 tutorial2_solutions
-   This script contains the solutions for Tutorial 2, see Tutorial 2 on
-   the RAVEN wiki for more details:
-   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 % tutorial2_solutions
-0002 %   This script contains the solutions for Tutorial 2, see Tutorial 2 on
-0003 %   the RAVEN wiki for more details:
-0004 %   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
-0005 
-0006 %Import the model into a RAVEN model structure
-0007 smallModel=readYAMLmodel('small.yml');
-0008 
-0009 %This solves the linear programming problem.
-0010 %NOTE: if sol.f is equal to zero then the problem is not solvable. Ensure
-0011 %that uptake and excretion of all necessary stuff are added to the model
-0012 %and run the solveLP again.
-0013 sol=solveLP(smallModel);
-0014 
-0015 %Print the resulting exchange fluxes
-0016 printFluxes(smallModel,sol.x,true);
-0017 
-0018 %Print all fluxes
-0019 printFluxes(smallModel,sol.x,false,10^-5,[],'%rxnID (%rxnName):\n\t%eqn\n\t%flux\n');
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/tutorial/tutorial3.html b/doc/tutorial/tutorial3.html deleted file mode 100644 index 1bc632d6..00000000 --- a/doc/tutorial/tutorial3.html +++ /dev/null @@ -1,112 +0,0 @@ - - - - Description of tutorial3 - - - - - - - - - -
Home > tutorial > tutorial3.m
- - - -

tutorial3 -

- -

PURPOSE ^

-
tutorial3
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
 tutorial3
-   This exercise shows how to run FBA simulations, gene deletion analysis,
-   and how one can use GEMs as a scaffold for interpreting microarray
-   data. A simplified model of yeast metabolism is used in this approach
-   as an example.
-   See Tutorial 3 on the RAVEN wiki for more details:
-   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
-
-   It is assumed that the user has already completed Tutorial 2
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 % tutorial3
-0002 %   This exercise shows how to run FBA simulations, gene deletion analysis,
-0003 %   and how one can use GEMs as a scaffold for interpreting microarray
-0004 %   data. A simplified model of yeast metabolism is used in this approach
-0005 %   as an example.
-0006 %   See Tutorial 3 on the RAVEN wiki for more details:
-0007 %   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
-0008 %
-0009 %   It is assumed that the user has already completed Tutorial 2
-0010 
-0011 %Import the model
-0012 model=readYAMLmodel('smallYeast.yml');
-0013 
-0014 %Set the upper bound of glucose uptake to 1 and O2 uptake to unlimited
-0015 model=setParam(model,'ub',{'glcIN' 'o2IN'},[1 1000]);
-0016 
-0017 %Set the objective to be ethanol production
-0018 model=setParam(model,'obj',{'ethOUT'},1);
-0019 
-0020 %Solve the model
-0021 sol=solveLP(model);
-0022 
-0023 %Print the resulting exchange fluxes
-0024 printFluxes(model,sol.x,true);
-0025 
-0026 %Run a single gene deletion
-0027 [genes, fluxes, originalGenes, details]=findGeneDeletions(model,'sgd');
-0028 
-0029 %Get the indexes of these reactions
-0030 I=getIndexes(model,{'biomassOUT'},'rxns');
-0031 J=getIndexes(model,{'glyOUT'},'rxns');
-0032 
-0033 okSolutions=find(fluxes(I,:)>10^-2); %Only look at solutions which are still growing
-0034 [maxGlycerol, J]=max(fluxes(J,okSolutions));
-0035 disp(maxGlycerol);
-0036 disp(originalGenes(genes(okSolutions(J),:)));
-0037 
-0038 %Compare the ZWF1 deletion strain to wild type, looking only at the
-0039 %reactions that involve the redox cofactors
-0040 model2=setParam(model,'eq',{'ZWF'},0);
-0041 sol2=solveLP(model2);
-0042 compareFluxes(model,sol.x,sol2.x,'cutoff',10^-2, ...
-0043     'metaboliteList',{'NADPH' 'NADH' 'NAD' 'NADP'});
-0044 
-0045 %Reload the model, since earlier steps modified its bounds and objective
-0046 model=readYAMLmodel('smallYeast.yml');
-0047 
-0048 %Read microarray results and calculate reporter metabolites (metabolites
-0049 %around which there are significant transcriptional changes)
-0050 [orfs, pvalues]=textread('expression.txt','%s%f');
-0051 repMets=reporterMetabolites(model,orfs,pvalues);
-0052 [I, J]=sort(repMets.metPValues);
-0053 
-0054 fprintf('TOP 10 REPORTER METABOLITES:\n');
-0055 for i=1:min(numel(J),10)
-0056     fprintf([repMets.mets{J(i)} '\t' num2str(I(i)) '\n']);
-0057 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/tutorial/tutorial3_solutions.html b/doc/tutorial/tutorial3_solutions.html deleted file mode 100644 index f7a58c1a..00000000 --- a/doc/tutorial/tutorial3_solutions.html +++ /dev/null @@ -1,137 +0,0 @@ - - - - Description of tutorial3_solutions - - - - - - - - - -
Home > tutorial > tutorial3_solutions.m
- - - -

tutorial3_solutions -

- -

PURPOSE ^

-
tutorial3_solutions
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
 tutorial3_solutions
-   This script contains the solutions for Tutorial 3, see Tutorial 3 on
-   the RAVEN wiki for more details:
-   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
-   All the parameters are set in this script, rather than modifying the
-   Excel model file.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 % tutorial3_solutions
-0002 %   This script contains the solutions for Tutorial 3, see Tutorial 3 on
-0003 %   the RAVEN wiki for more details:
-0004 %   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
-0005 %   All the parameters are set in this script, rather than modifying the
-0006 %   Excel model file.
-0007 
-0008 %Import the model
-0009 model=readYAMLmodel('smallYeast.yml');
-0010 
-0011 %Step 1
-0012 %Set the upper bound of glucose uptake to 1 and O2 uptake to zero
-0013 model=setParam(model,'ub',{'glcIN' 'o2IN'},[1 0]);
-0014 
-0015 %Set the objective to be ATP hydrolysis
-0016 model=setParam(model,'obj',{'ATPX'},1);
-0017 
-0018 %Solve the model
-0019 sol=solveLP(model);
-0020 
-0021 %Print the resulting fluxes. The ATP production rate should be 2.0. It was
-0022 %4.0 in Tutorial 2, but there sucrose was used instead of glucose.
-0023 printFluxes(model,sol.x,false);
-0024 
-0025 %Step 2
-0026 %Check the yield of different products and print the results
-0027 %Change to fully aerobic
-0028 model=setParam(model,'ub',{'glcIN' 'o2IN'},[1 1000]);
-0029 model=setParam(model,'obj',{'ethOUT'},1);
-0030 sol=solveLP(model);
-0031 fprintf(['Yield of ethanol is ' num2str(sol.f) ' mol/mol\n']);
-0032 model=setParam(model,'obj',{'acOUT'},1);
-0033 sol=solveLP(model);
-0034 fprintf(['Yield of acetate is ' num2str(sol.f) ' mol/mol\n']);
-0035 model=setParam(model,'obj',{'glyOUT'},1);
-0036 sol=solveLP(model);
-0037 fprintf(['Yield of glycerol is ' num2str(sol.f) ' mol/mol\n']);
-0038 model=setParam(model,'obj',{'biomassOUT'},1);
-0039 sol=solveLP(model);
-0040 fprintf(['Yield of biomass is ' num2str(sol.f) '/h\n']);
-0041 
-0042 %Step 3
-0043 %Solve for both aerobic and anaerobic growth
-0044 solA=solveLP(model);
-0045 model=setParam(model,'ub',{'o2IN'},0.5);
-0046 solB=solveLP(model);
-0047 
-0048 %Step 4
-0049 %Change to anaerobic growth and maximize for biomass
-0050 model=setParam(model,'eq',{'o2IN'},0);
-0051 model=setParam(model,'obj',{'biomassOUT'},1);
-0052 sol=solveLP(model);
-0053 printFluxes(model,sol.x,true);
-0054 %One can see that the model predicts a glycerol production of 0.23
-0055 %mmol/gDW/h
-0056 
-0057 %Run a single gene deletion
-0058 [genes, fluxes, originalGenes, details]=findGeneDeletions(model,'sgd');
-0059 
-0060 %Get the indexes of these reactions
-0061 I=getIndexes(model,{'biomassOUT'},'rxns');
-0062 J=getIndexes(model,{'glyOUT'},'rxns');
-0063 
-0064 okSolutions=find(fluxes(I,:)>10^-2); %Only look at solutions which are still growing
-0065 [maxGlycerol, J]=max(fluxes(J,okSolutions));
-0066 fprintf(['Glycerol production is ' num2str(maxGlycerol) ' after deletion of ' originalGenes{genes(okSolutions(J),:)} '\n']);
-0067 
-0068 %The best gene deletion corresponds to turning off the ZWF1 reaction
-0069 %(YNL241C)
-0070 model2=setParam(model,'eq',{'ZWF'},0);
-0071 sol2=solveLP(model2);
-0072 compareFluxes(model,sol.x,sol2.x,'cutoff',10^-2, ...
-0073     'metaboliteList',{'NADPH' 'NADH' 'NAD' 'NADP'});
-0074 
-0075 %Step 5
-0076 %Read microarray results and calculate reporter metabolites (metabolites
-0077 %around which there are significant transcriptional changes)
-0078 [orfs, pvalues]=textread('expression.txt','%s%f');
-0079 repMets=reporterMetabolites(model,orfs,pvalues);
-0080 [I, J]=sort(repMets.metPValues);
-0081 
-0082 fprintf('TOP 10 REPORTER METABOLITES:\n');
-0083 for i=1:min(numel(J),10)
-0084     fprintf([repMets.mets{J(i)} '\t' num2str(I(i)) '\n']);
-0085 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/tutorial/tutorial4.html b/doc/tutorial/tutorial4.html deleted file mode 100644 index 5197b2a3..00000000 --- a/doc/tutorial/tutorial4.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - Description of tutorial4 - - - - - - - - - -
Home > tutorial > tutorial4.m
- - - -

tutorial4 -

- -

PURPOSE ^

-
tutorial4
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
 tutorial4
-   This script contains the list of functions necessary for running
-   Tutorial 4, see Tutorial 4 on the RAVEN wiki for more details:
-   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
-   Several key stages may be missing, try to fill these gaps before
-   checking the solutions in tutorial4_solutions. It is assumed that the
-   user is somewhat familiar with linear programming.
-
-   It is assumed that the user has already completed Tutorials 2-3
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 % tutorial4
-0002 %   This script contains the list of functions necessary for running
-0003 %   Tutorial 4, see Tutorial 4 on the RAVEN wiki for more details:
-0004 %   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
-0005 %   Several key stages may be missing, try to fill these gaps before
-0006 %   checking the solutions in tutorial4_solutions. It is assumed that the
-0007 %   user is somewhat familiar with linear programming.
-0008 %
-0009 %   It is assumed that the user has already completed Tutorials 2-3
-0010 
-0011 %Import the model to gap-fill and the reference model to draw reactions from
-0012 model=readYAMLmodel('smallYeastBad.yml');
-0013 refModel=readYAMLmodel('smallYeast.yml');
-0014 model.b=[model.b inf(numel(model.b),1)];
-0015 sol=solveLP(model,1);
-0016 printFluxes(model,sol.x,false,10^-5,[],'%rxnID (%rxnName):\n\t%eqn\n\t%flux\n');
-0017 I=canExchange(model,'consume');
-0018 disp(model.mets(I));
-0019 gapReport(model,{refModel});
-0020 
-0021 [newConnected, cannotConnect, addedRxns, newModel]=fillGaps(model,{refModel},false);
-0022 disp(addedRxns);
-0023 disp(newConnected);
-0024 disp(cannotConnect);
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/tutorial/tutorial4_solutions.html b/doc/tutorial/tutorial4_solutions.html deleted file mode 100644 index 17391e87..00000000 --- a/doc/tutorial/tutorial4_solutions.html +++ /dev/null @@ -1,137 +0,0 @@ - - - - Description of tutorial4_solutions - - - - - - - - - -
Home > tutorial > tutorial4_solutions.m
- - - -

tutorial4_solutions -

- -

PURPOSE ^

-
tutorial4_solutions
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
 tutorial4_solutions
-   This script contains the solutions for Tutorial 4, see Tutorial 4 on
-   the RAVEN wiki for more details:
-   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
-
-   NOTE: Many of these changes are easier to do in the Excel sheet. They
-   are done here in code just to avoid having several model files.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 % tutorial4_solutions
-0002 %   This script contains the solutions for Tutorial 4, see Tutorial 4 on
-0003 %   the RAVEN wiki for more details:
-0004 %   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
-0005 %
-0006 %   NOTE: Many of these changes are easier to do in the Excel sheet. They
-0007 %   are done here in code just to avoid having several model files.
-0008 
-0009 %Import the model
-0010 model=readYAMLmodel('smallYeastBad.yml');
-0011 
-0012 %Close all uptake and maximize for production
-0013 model=setParam(model,'eq',{'glcIN', 'o2IN'},[0 0]);
-0014 model=setParam(model,'obj',{'acOUT' 'biomassOUT' 'co2OUT' 'ethOUT' 'glyOUT'},[1 1 1 1 1]);
-0015 sol=solveLP(model);
-0016 printFluxes(model,sol.x,true); %Nothing produced, good
-0017 
-0018 %Add some fake reactions
-0019 rxns.rxns={'FREE_ATP';'FREE_NADH';'FREE_NADPH'};
-0020 rxns.equations={'ATP <=> ADP + phosphate';'NAD(+) <=> NADH';'NADP(+) <=> NADPH'};
-0021 model=addRxns(model,rxns,2,'c');
-0022 sol=solveLP(model,1);
-0023 
-0024 %Lots of ethanol produced. Also plot the equations to make the error easier
-0025 %to find
-0026 printFluxes(model,sol.x,false,[],[],'%rxnID (%rxnName):%flux\n\t%eqn\n');
-0027 
-0028 %See that ADH1 should only produce one unit of ethanol. Change the reaction
-0029 %equation
-0030 model=changeRxns(model,'ADH1','acetaldehyde[c] + NADH[c] => ethanol[c] + NAD(+)[c]',3);
-0031 sol=solveLP(model,1);
-0032 printFluxes(model,sol.x,true); %Nothing produced, good
-0033 
-0034 %Add excretion of all metabolites
-0035 model.b=[model.b inf(numel(model.b),1)];
-0036 sol=solveLP(model,1);
-0037 printFluxes(model,sol.x,false,10^-5,[],'%rxnID (%rxnName):\n\t%eqn\n\t%flux\n');
-0038 
-0039 %By looking at the reactions which were unbalanced and that were in the
-0040 %flux list one can see that FBP should be changed to result in only one unit
-0041 %of F6P
-0042 model=changeRxns(model,'FBP','beta-D-fructofuranose 1,6-bisphosphate[c] => beta-D-fructofuranose 6-phosphate[c] + phosphate[c]',3);
-0043 sol=solveLP(model,1);
-0044 printFluxes(model,sol.x,false,10^-5,[],'%rxnID (%rxnName):\n\t%eqn\n\t%flux\n');
-0045 
-0046 %The same thing again and one should change PFK to only give one unit of
-0047 %F16P
-0048 model=changeRxns(model,'PFK','ATP[c] + beta-D-fructofuranose 6-phosphate[c] => ADP[c] + beta-D-fructofuranose 1,6-bisphosphate[c]',3);
-0049 sol=solveLP(model,1);
-0050 printFluxes(model,sol.x,false,10^-5,[],'%rxnID (%rxnName):\n\t%eqn\n\t%flux\n'); %Now it works
-0051 
-0052 %Set all uptakes and production to 0
-0053 model=setParam(model,'eq',getExchangeRxns(model),0);
-0054 
-0055 %Since it is checked which metabolites could be consumed without
-0056 %production, one can no longer have free production of all metabolites
-0057 model.b=model.b(:,1);
-0058 I=canExchange(model,'consume');
-0059 disp(model.mets(I)); %These 12 metabolites can be consumed without any production
-0060 
-0061 %Allow all uptake
-0062 model.b=[ones(numel(model.b),1)*-1000 model.b];
-0063 
-0064 %Pick CO2 and force uptake of it
-0065 model=setParam(model,'eq',{'co2OUT'},-1); %Negative output means input
-0066 sol=solveLP(model);
-0067 printFluxes(model,sol.x,false,10^-5,[],'%rxnID (%rxnName):\n\t%eqn\n\t%flux\n'); %Now it works
-0068 
-0069 %See that PDC converts pyruvate (3 carbons) to acetaldehyde (2 carbons)
-0070 %without any other products. If one googles, one may realize that CO2 is
-0071 %missing. This would be simpler to change in the Excel file (or using
-0072 %changeRxns), but one can change it here as an exercise. One therefore
-0073 %needs to find the index of the reactions and the index of cytosolic CO2 in
-0074 %order to change the reaction
-0075 Irxn=ismember(model.rxns,'PDC');
-0076 Imet=ismember(model.mets,'CO2_c');
-0077 model.S(Imet,Irxn)=1; %The coefficient is 1.0
-0078 
-0079 %Display the new equation just to be sure
-0080 constructEquations(model,Irxn)
-0081 
-0082 %The solution is now not feasible, meaning that it is no longer possible to
-0083 %force uptake of CO2 without any output
-0084 sol=solveLP(model);
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/tutorial/tutorial5.html b/doc/tutorial/tutorial5.html deleted file mode 100644 index 711fd5cd..00000000 --- a/doc/tutorial/tutorial5.html +++ /dev/null @@ -1,254 +0,0 @@ - - - - Description of tutorial5 - - - - - - - - - -
Home > tutorial > tutorial5.m
- - - -

tutorial5 -

- -

PURPOSE ^

-
tutorial5
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
 tutorial5
-   This exercise is about creating a model from KEGG, based on protein
-   sequences in a FASTA file, and doing some functionality checks on the
-   model. The example case is for the yeast Saccharomyces cerevisiae. This
-   tutorial is more of a showcase than the previous four, and its main
-   purpose is to serve as a scaffold to reconstruct a GEM for any
-   organism.
-   This refers to Tutorial 5 on the RAVEN wiki:
-   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
- 
- Start by downloading trained Hidden Markov Models for eukaryotes. This can
- be done automatically or manually from the RAVEN Wiki in its GitHub
- repository. In this tutorial, the archive "kegg118_eukaryotes" is picked for
- the automatic download. See the documentation in the RAVEN Wiki for more
- information regarding preparation of such archive.
- 
- This creates a model for S. cerevisiae. The parameters are set to exclude
- general or unclear reactions and reactions with undefined stoichiometry.
- Type "help getKEGGModelForOrganism" to see what the different parameters
- are for. This process takes up to 20-35 minutes in macOS, Unix systems and
- 40-55 minutes in Windows, depending on your hardware and the size of
- target organism proteome
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 % tutorial5
-0002 %   This exercise is about creating a model from KEGG, based on protein
-0003 %   sequences in a FASTA file, and doing some functionality checks on the
-0004 %   model. The example case is for the yeast Saccharomyces cerevisiae. This
-0005 %   tutorial is more of a showcase than the previous four, and its main
-0006 %   purpose is to serve as a scaffold to reconstruct a GEM for any
-0007 %   organism.
-0008 %   This refers to Tutorial 5 on the RAVEN wiki:
-0009 %   https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
-0010 %
-0011 % Start by downloading trained Hidden Markov Models for eukaryotes. This can
-0012 % be done automatically or manually from the RAVEN Wiki in its GitHub
-0013 % repository. In this tutorial, the archive "kegg118_eukaryotes" is picked for
-0014 % the automatic download. See the documentation in the RAVEN Wiki for more
-0015 % information regarding preparation of such archive.
-0016 %
-0017 % This creates a model for S. cerevisiae. The parameters are set to exclude
-0018 % general or unclear reactions and reactions with undefined stoichiometry.
-0019 % Type "help getKEGGModelForOrganism" to see what the different parameters
-0020 % are for. This process takes up to 20-35 minutes in macOS, Unix systems and
-0021 % 40-55 minutes in Windows, depending on your hardware and the size of
-0022 % target organism proteome
-0023 model=getKEGGModelForOrganism('sce','sce.fa','kegg118_eukaryotes','output',false,false,false,false,10^-30,0.8,0.3,-1);
-0024 
-0025 % The resulting model should contain around 1589 reactions, 1600
-0026 % metabolites and 836 genes. Small variations are possible since it is an
-0027 % heuristic algorithm and different KEGG versions will give slightly
-0028 % different results.
-0029 disp(model);
-0030 
-0031 % A first control is that the model should not be able to produce any
-0032 % metabolites without uptake of some metabolites. This commonly happens when
-0033 % metabolites have a different meaning in different reactions. The best way
-0034 % to find such reactions is to run findLeakMetabolite and analyze the resulting
-0035 % solution for bad reactions. An automated approach is to use removeBadRxns,
-0036 % which tries to do the same thing in an automated manner. Type
-0037 % "help removeBadRxns" for details.
-0038 [newModel, removedRxns]=removeBadRxns(model);
-0039 
-0040 % One can see an error about that H+ can be made even if no reactions were
-0041 % unbalanced. Protons are particularly problematic since it is rather
-0042 % arbitary at which pH the formulas are written for. For the purpose of this
-0043 % analysis, the protons can be ignored and fixed later.
-0044 [newModel, removedRxns]=removeBadRxns(model,1,{'H+'},true);
-0045 
-0046 % Only one reaction was removed because it enabled the model to produce
-0047 % something from nothing. Since it is only one reaction, it might be
-0048 % worthwhile to look into this in more detail.
-0049 disp(removedRxns);
-0050 
-0051 % According to the information in KEGG about this reaction it is a general
-0052 % polymer reaction. One might want to look at the flux distributions
-0053 % in more detail to try to find out if there is any better alternative to
-0054 % delete. Use findLeakMetabolite to do this.
-0055 [fluxes, metabolite]=findLeakMetabolite(model,'produce',{'H+'},true);
-0056 model.metNames(metabolite)
-0057 
-0058 % The model could produce H2O using the following reactions
-0059 printFluxes(model, fluxes, false, [], [],'%rxnID (%rxnName):\n\t%eqn: %flux\n')
-0060 
-0061 % That resulted in quite a lot of fluxes to look through. Maybe it is easier
-0062 % if the elementally balanced reactions are excluded.
-0063 balanceStructure=getElementalBalance(model);
-0064 
-0065 % The hydrogen balancing is a bit tricky, so this time it seems more
-0066 % relevant to only look at the ones unbalanced for oxygen (since water was
-0067 % produced)
-0068 goodOnes=balanceStructure.leftComp(:,6)==balanceStructure.rightComp(:,6);
-0069 printFluxes(removeReactions(model,goodOnes), fluxes(~goodOnes), false, [], [],'%rxnID (%rxnName):\n\t%eqn: %flux\n');
-0070 
-0071 % There is still a good number of reactions. Leave only the reactions which
-0072 % involve amylose or starch, from one of the problematic reactions that was
-0073 % identified earlier.
-0074 printFluxes(model, fluxes, false, [], [],'%rxnID (%rxnName):\n\t%eqn: %flux\n',{'Amylose';'Starch'});
-0075 
-0076 % There are two elementally unbalanced reactions, including, the reaction
-0077 % which was identified by removeBadRxns. When looking to these reactions
-0078 % closer, one can notice the contradiction between the reactions. The first
-0079 % one shows that amylose and starch are interconvertible, while the second
-0080 % reaction shows that amylose contains one less glucose unit than starch.
-0081 % This type of general reactions are problematic and should be fixed
-0082 % manually. It is hereby chosen to trust removeBadRxns and delete R02110.
-0083 model=removeReactions(model,'R02110');
-0084 
-0085 % The model can no longer make something from nothing. Can it consume
-0086 % something without any output?
-0087 [solution, metabolite]=findLeakMetabolite(model,'consume',{'H+'},true);
-0088 model.metNames(metabolite)
-0089 
-0090 % Nope, so that was good. Add some uptakes and see what it can produce.
-0091 [~, J]=ismember({'D-Glucose';'H2O';'Orthophosphate';'Oxygen';'NH3';'Sulfate'},model.metNames);
-0092 [model, addedRxns]=addExchangeRxns(model,'in',J);
-0093 
-0094 % Check which metabolites can be produced given these uptakes. The
-0095 % canExchange function allows for output of all metabolites. This will not
-0096 % happen in the real cell, but it is very useful for functionality testing
-0097 % of the model. Once it is functional, the excretion reactions based on
-0098 % evidence can be added as well.
-0099 I=canExchange(model,'produce');
-0100 
-0101 fprintf('%d%%\n', round(sum(I)/numel(model.mets)*100));
-0102 % It seems that around 31% of the metabolites could be synthesized. It is
-0103 % not directly clear whether this is a high or low number, many metabolites
-0104 % should not be possible to synthesize from those simple precursors.
-0105 
-0106 % Try to fill gaps using the full KEGG model to see if that gives a
-0107 % significantly higher number
-0108 keggModel=getModelFromKEGG(false,false,false,false);
-0109 
-0110 % The KEGG model is associated to more than 6,400,000 genes. They will not
-0111 % be used for the gapfilling, so they are removed to make this a little
-0112 % faster
-0113 keggModel=rmfield(keggModel,'genes');
-0114 keggModel=rmfield(keggModel,'rxnGeneMat');
-0115 
-0116 % It is already known from the previous commands that there are some
-0117 % unbalanced reactions in KEGG. Only use the balanced ones for the gap
-0118 % filling
-0119 balanceStructure=getElementalBalance(keggModel);
-0120 keggModel=removeReactions(keggModel,balanceStructure.balanceStatus~=1,true,true);
-0121 
-0122 % The function fillGaps with these settings will try to include reactions in
-0123 % order to have flux through all reactions in the model. There are other
-0124 % settings as well. The first flag says that production of all metabolites
-0125 % should be allowed.
-0126 params.relGap=0.6; %Lower number for a more exhaustive search
-0127 params.printReport=true;
-0128 [newConnected, cannotConnect, addedRxns, newModel, exitFlag]=fillGaps(model,keggModel,true,false,false,[],params);
-0129 
-0130 % The results show that fillGaps could connect around 29 reactions
-0131 % (newConnected) by including around 41 reactions from the KEGG model
-0132 % (addedRxns). Those should of course be checked manually to confirm that
-0133 % they exist in yeast, but in this tutorial it is assumed that all these
-0134 % reactions indeed occur in yeast.
-0135 
-0136 % Continue to improve the connectivity of the model by identifying
-0137 % metabolites that should be connected. A convenient way to get an overview
-0138 % of how connected the model is, and at the same time getting a lot of
-0139 % useful data, is to use gapReport. Note that it can take several to many
-0140 % hours to run, depending on the number of gaps in the model.
-0141 [noFluxRxns, noFluxRxnsRelaxed, subGraphs, notProducedMets, minToConnect,...
-0142     neededForProductionMat]=gapReport(newModel);
-0143 
-0144 % The results show that 532 from 1634 reactions cannot carry flux. Remember
-0145 % that an output is allowed for all metabolites, that is why it calculates
-0146 % 532 in both cases. 543 from 1617 metabolites cannot be synthesized from
-0147 % the precursors which are supplied. There are 6 subnetworks in the model,
-0148 % of which 1605 from 1617 metabolites belong to the first one.
-0149 
-0150 % It will also print something similar to:
-0151 
-0152 % To enable net production of all metabolites, a total of 322 metabolites
-0153 % must be connected
-0154 % Top 10 metabolites to connect:
-0155 %     1. Acyl-CoA[s] (connects 162 metabolites)
-0156 %     2. Thiamin diphosphate[s] (connects 17 metabolites)
-0157 %     3. G00003[s] (connects 14 metabolites)
-0158 %     4. Androstenediol[s] (connects 13 metabolites)
-0159 %     5. [Dihydrolipoyllysine-residue succinyltransferase] S-glutaryldihydrolipoyllysine[s] (connects 13 metabolites)
-0160 %     6. Selenomethionyl-tRNA(Met)[s] (connects 12 metabolites)
-0161 %     7. Retinyl ester[s] (connects 11 metabolites)
-0162 %     8. 1-Alkyl-2-acylglycerol[s] (connects 10 metabolites)
-0163 %     9. G00146[s] (connects 10 metabolites)
-0164 %     10. Progesterone[s] (connects 9 metabolites)
-0165 
-0166 % This is a very useful way of directing the gap-filling tasks to where they
-0167 % are of the greatest use. In this case it says that to have net
-0168 % production of all metabolites in the model from the simple inputs which
-0169 % have been defined (543 metabolites can currently not have net production)
-0170 % one needs to connect 322 unconnected metabolites in. However, by
-0171 % connecting only the top 10 in the list 271/543 (50%) of them will now be
-0172 % connected. When looking at the list one can see that the top ranking one
-0173 % is Acyl-CoA, which represents many different fatty acid chains. The second
-0174 % metabolite, thiamine disphosphate, is an enzyme co-factor. It turns out
-0175 % that yeast cannot grow on only the substrates that had been defined, but
-0176 % that it requires some other precursors for co-factor synthesis as well.
-0177 
-0178 % Add uptake reactions for the minimal media constituents needed for yeast
-0179 % to grow.
-0180 [~, J]=ismember({'4-Aminobenzoate';'Riboflavin';'Thiamine';'Biotin';'Folate';'Nicotinate';'Zymosterol';'Choline'},newModel.metNames);
-0181 [newModel, addedRxns]=addExchangeRxns(newModel,'in',J);
-0182 
-0183 % Rerun gapReport and use the output for targeting the gap-filling efforts.
-0184 % Note that only some info is printed; most of it is available in the output
-0185 % structures. Work like this in an iterative manner until the model is of
-0186 % sufficient quality.
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/tutorial/tutorial7.html b/doc/tutorial/tutorial7.html deleted file mode 100644 index a1085ae9..00000000 --- a/doc/tutorial/tutorial7.html +++ /dev/null @@ -1,209 +0,0 @@ - - - - Description of tutorial7 - - - - - - - - - -
Home > tutorial > tutorial7.m
- - - -

tutorial7 -

- -

PURPOSE ^

-
tutorial7
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
 tutorial7
-   This exercise shows how to use the biomass/ functions to calibrate a
-   model's biomass pseudoreaction against measured condition-specific
-   composition data: getBiomassFractions to see what the model currently
-   predicts, scaleBiomassFraction to rescale individual components to
-   measured values (with one component left to absorb whatever is left,
-   so the total stays at 1 g/gDW), and setGAM to apply a
-   growth-associated maintenance value. See Tutorial 7 on the RAVEN wiki
-   for more details: https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
-
-   A small hand-built model is used instead of a real GEM, so the
-   pseudoreaction layout is visible in full below. Real GEMs lay this out
-   the same way -- one pseudoreaction per macromolecular component,
-   feeding into a top-level biomass pseudoreaction -- and the file
-   biomassConditions_template.tsv in this folder is a template for the
-   condition data itself: one row per component, one column per named
-   condition, values in g/gDW. Real, published examples of the same
-   shape of data (per-metabolite abundances a modeller derives such
-   fractions from) are yeast-GEM's data/physiology/biomassComposition_*.tsv
-   files; the numbers used here are illustrative, not measured.
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 % tutorial7
-0002 %   This exercise shows how to use the biomass/ functions to calibrate a
-0003 %   model's biomass pseudoreaction against measured condition-specific
-0004 %   composition data: getBiomassFractions to see what the model currently
-0005 %   predicts, scaleBiomassFraction to rescale individual components to
-0006 %   measured values (with one component left to absorb whatever is left,
-0007 %   so the total stays at 1 g/gDW), and setGAM to apply a
-0008 %   growth-associated maintenance value. See Tutorial 7 on the RAVEN wiki
-0009 %   for more details: https://github.com/SysBioChalmers/RAVEN/wiki/Tutorials
-0010 %
-0011 %   A small hand-built model is used instead of a real GEM, so the
-0012 %   pseudoreaction layout is visible in full below. Real GEMs lay this out
-0013 %   the same way -- one pseudoreaction per macromolecular component,
-0014 %   feeding into a top-level biomass pseudoreaction -- and the file
-0015 %   biomassConditions_template.tsv in this folder is a template for the
-0016 %   condition data itself: one row per component, one column per named
-0017 %   condition, values in g/gDW. Real, published examples of the same
-0018 %   shape of data (per-metabolite abundances a modeller derives such
-0019 %   fractions from) are yeast-GEM's data/physiology/biomassComposition_*.tsv
-0020 %   files; the numbers used here are illustrative, not measured.
-0021 
-0022 model = buildBiomassTemplateModel();
-0023 biomassConfig = buildBiomassTemplateConfig();
-0024 
-0025 %What the template model's biomass pseudoreaction predicts before any
-0026 %calibration
-0027 fractions = getBiomassFractions(model, biomassConfig);
-0028 fprintf('Before calibration:\n');
-0029 disp(fractions);
-0030 
-0031 %Load the condition data. Each column after "component" is a named
-0032 %condition; "carbohydrate" is deliberately not a row in the file -- it is
-0033 %the component left to balance the total back to 1 g/gDW for whichever
-0034 %condition is applied.
-0035 condData = readtable('biomassConditions_template.tsv', 'FileType', 'text', ...
-0036     'Delimiter', '\t');
-0037 conditionNames = condData.Properties.VariableNames(2:end);
-0038 
-0039 for c = 1:numel(conditionNames)
-0040     conditionName = conditionNames{c};
-0041     condModel = model; %Start each condition from the same unscaled template
-0042 
-0043     fprintf('\n--- Condition: %s ---\n', conditionName);
-0044     for i = 1:height(condData)
-0045         component = condData.component{i};
-0046         target = condData.(conditionName)(i);
-0047         %Balance the total back to 1 g/gDW on the last explicit component,
-0048         %so carbohydrate absorbs whatever measuring the other four leaves.
-0049         if i == height(condData)
-0050             condModel = scaleBiomassFraction(condModel, biomassConfig, ...
-0051                 component, target, 'balanceOut', 'carbohydrate');
-0052         else
-0053             condModel = scaleBiomassFraction(condModel, biomassConfig, ...
-0054                 component, target);
-0055         end
-0056     end
-0057 
-0058     fractions = getBiomassFractions(condModel, biomassConfig);
-0059     fprintf('carbohydrate (balanced): %.4g g/gDW\n', fractions.carbohydrate);
-0060     fprintf('total: %.4g g/gDW\n', fractions.total);
-0061 end
-0062 
-0063 %setGAM applies a known growth-associated maintenance value directly,
-0064 %rather than deriving one from data -- use fitParameters for that instead,
-0065 %against measured exchange fluxes at a fixed growth rate (see its "See
-0066 %also" cross-reference to setGAM).
-0067 before = full(model.S(strcmp(model.mets, 'atp_c'), strcmp(model.rxns, biomassConfig.biomass_rxn)));
-0068 model = setGAM(model, 35, biomassConfig.biomass_rxn, ...
-0069     {'ATP', 'ADP', 'H2O', 'H+', 'phosphate'});
-0070 after = full(model.S(strcmp(model.mets, 'atp_c'), strcmp(model.rxns, biomassConfig.biomass_rxn)));
-0071 fprintf('\nATP coefficient in the biomass reaction: %.4g -> %.4g\n', before, after);
-0072 
-0073 
-0074 function model = buildBiomassTemplateModel()
-0075 %A minimal model with one pseudoreaction per biomass component, feeding
-0076 %into a top-level biomass pseudoreaction -- the layout getBiomassFractions,
-0077 %scaleBiomassFraction, scaleBiomassPseudoreaction and setGAM all expect.
-0078 model.id = 'biomassTemplateModel';
-0079 model.name = 'Template model for the biomass-composition tutorial';
-0080 
-0081 model.mets = {'aa_c'; 'glycogen_c'; 'ntp_c'; 'dntp_c'; 'fa_c'; ...
-0082     'protein_c'; 'carbohydrate_c'; 'rna_c'; 'dna_c'; 'lipid_c'; ...
-0083     'atp_c'; 'adp_c'; 'h2o_c'; 'h_c'; 'pi_c'};
-0084 model.metNames = {'amino acid pool'; 'glycogen'; 'NTP pool'; 'dNTP pool'; ...
-0085     'fatty acid pool'; 'protein'; 'carbohydrate'; 'RNA'; 'DNA'; ...
-0086     'lipid_backbone'; 'ATP'; 'ADP'; 'H2O'; 'H+'; 'phosphate'};
-0087 model.metFormulas = {'C5H9NO2'; 'C6H10O5'; 'C10H12N5O10P'; 'C10H12N5O9P'; ...
-0088     'C16H30O2'; ''; ''; ''; ''; ''; ...
-0089     'C10H12N5O13P3'; 'C10H12N5O10P2'; 'H2O'; 'H'; 'HO4P'};
-0090 model.metCharges = [0; 0; -2; -2; 0; 0; 0; 0; 0; 0; -4; -3; 0; 1; -2];
-0091 model.comps = {'c'};
-0092 model.compNames = {'cytosol'};
-0093 model.metComps = ones(numel(model.mets), 1);
-0094 
-0095 model.rxns = {'R_protein'; 'R_carb'; 'R_rna'; 'R_dna'; 'R_lipid'; 'R_biomass'};
-0096 model.rxnNames = {'protein pseudoreaction'; 'carbohydrate pseudoreaction'; ...
-0097     'RNA pseudoreaction'; 'DNA pseudoreaction'; ...
-0098     'lipid backbone pseudoreaction'; 'biomass pseudoreaction'};
-0099 
-0100 nMets = numel(model.mets);
-0101 nRxns = numel(model.rxns);
-0102 model.S = sparse(nMets, nRxns);
-0103 met = @(id) find(strcmp(model.mets, id));
-0104 model.S(met('aa_c'), 1) = -20;         model.S(met('protein_c'), 1) = 1;
-0105 model.S(met('glycogen_c'), 2) = -5;    model.S(met('carbohydrate_c'), 2) = 1;
-0106 model.S(met('ntp_c'), 3) = -10;        model.S(met('rna_c'), 3) = 1;
-0107 model.S(met('dntp_c'), 4) = -2;        model.S(met('dna_c'), 4) = 1;
-0108 model.S(met('fa_c'), 5) = -0.08;       model.S(met('lipid_c'), 5) = 1;
-0109 model.S(met('protein_c'), 6) = -1;     model.S(met('carbohydrate_c'), 6) = -1;
-0110 model.S(met('rna_c'), 6) = -1;         model.S(met('dna_c'), 6) = -1;
-0111 model.S(met('lipid_c'), 6) = -1;
-0112 model.S(met('atp_c'), 6) = -30;        model.S(met('h2o_c'), 6) = -30;
-0113 model.S(met('adp_c'), 6) = 30;         model.S(met('h_c'), 6) = 30;
-0114 model.S(met('pi_c'), 6) = 30;
-0115 
-0116 model.lb = zeros(nRxns, 1);
-0117 model.ub = repmat(1000, nRxns, 1);
-0118 model.rev = zeros(nRxns, 1);
-0119 model.c = [0; 0; 0; 0; 0; 1];
-0120 model.b = zeros(nMets, 1);
-0121 model.genes = {};
-0122 model.rxnGeneMat = sparse(nRxns, 0);
-0123 model.grRules = repmat({''}, nRxns, 1);
-0124 end
-0125 
-0126 
-0127 function biomassConfig = buildBiomassTemplateConfig()
-0128 biomassConfig.biomass_rxn = 'R_biomass';
-0129 biomassConfig.proton_met = 'h_c';
-0130 biomassConfig.components = { ...
-0131     struct('name', 'protein', 'pseudoreaction_name', 'protein pseudoreaction', ...
-0132         'mass_strategy', 'mw_minus_2h'), ...
-0133     struct('name', 'carbohydrate', 'pseudoreaction_name', 'carbohydrate pseudoreaction', ...
-0134         'mass_strategy', 'mw'), ...
-0135     struct('name', 'RNA', 'pseudoreaction_name', 'RNA pseudoreaction', ...
-0136         'mass_strategy', 'mw_minus_water'), ...
-0137     struct('name', 'DNA', 'pseudoreaction_name', 'DNA pseudoreaction', ...
-0138         'mass_strategy', 'mw_minus_water'), ...
-0139     struct('name', 'lipid_backbone', 'pseudoreaction_name', 'lipid backbone pseudoreaction', ...
-0140         'mass_strategy', 'grams')};
-0141 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/utils/legacyMethodNotice.html b/doc/utils/legacyMethodNotice.html deleted file mode 100644 index 2c9eb287..00000000 --- a/doc/utils/legacyMethodNotice.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - Description of legacyMethodNotice - - - - - - - - - -
Home > utils > legacyMethodNotice.m
- - - -

legacyMethodNotice -

- -

PURPOSE ^

-
legacyMethodNotice Point a caller at the recommended method, once.
- -

SYNOPSIS ^

-
function legacyMethodNotice(oldFunction, preferred)
- -

DESCRIPTION ^

-
 legacyMethodNotice  Point a caller at the recommended method, once.
-
- For functions that remain supported but are no longer the recommended way
- to do the job. Unlike deprecationWarning, this promises no removal: the
- function keeps working and keeps being maintained. It is a signpost, not a
- countdown.
-
- The notice is issued the first time the function is called in a MATLAB
- session and stays quiet afterwards, so a script that calls it in a loop
- does not bury the console. Call "clear functions" to reset. A caller that
- wants the legacy method deliberately can silence it with
- warning('off','RAVEN:legacyMethod').
-
- Parameters
- ----------
- oldFunction : char
-     name of the function being called.
- preferred : char
-     what to use instead for new work, written as it would be typed.
-
- Examples
- --------
-     legacyMethodNotice('getINITModel','ftINIT');
-
- See Also
- --------
- deprecationWarning
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function legacyMethodNotice(oldFunction, preferred)
-0002 % legacyMethodNotice  Point a caller at the recommended method, once.
-0003 %
-0004 % For functions that remain supported but are no longer the recommended way
-0005 % to do the job. Unlike deprecationWarning, this promises no removal: the
-0006 % function keeps working and keeps being maintained. It is a signpost, not a
-0007 % countdown.
-0008 %
-0009 % The notice is issued the first time the function is called in a MATLAB
-0010 % session and stays quiet afterwards, so a script that calls it in a loop
-0011 % does not bury the console. Call "clear functions" to reset. A caller that
-0012 % wants the legacy method deliberately can silence it with
-0013 % warning('off','RAVEN:legacyMethod').
-0014 %
-0015 % Parameters
-0016 % ----------
-0017 % oldFunction : char
-0018 %     name of the function being called.
-0019 % preferred : char
-0020 %     what to use instead for new work, written as it would be typed.
-0021 %
-0022 % Examples
-0023 % --------
-0024 %     legacyMethodNotice('getINITModel','ftINIT');
-0025 %
-0026 % See Also
-0027 % --------
-0028 % deprecationWarning
-0029 
-0030 persistent alreadyNoticed
-0031 
-0032 if isempty(alreadyNoticed)
-0033     alreadyNoticed=containers.Map('KeyType','char','ValueType','logical');
-0034 end
-0035 
-0036 oldFunction=char(oldFunction);
-0037 if isKey(alreadyNoticed,oldFunction)
-0038     return
-0039 end
-0040 alreadyNoticed(oldFunction)=true;
-0041 
-0042 warning('RAVEN:legacyMethod', ...
-0043     ['%s is the legacy implementation and remains supported, but %s is the ' ...
-0044      'recommended method for new work. Silence this with ' ...
-0045      'warning(''off'',''RAVEN:legacyMethod'').'], oldFunction, char(preferred));
-0046 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/utils/parseGrRule.html b/doc/utils/parseGrRule.html deleted file mode 100644 index e16cbdf1..00000000 --- a/doc/utils/parseGrRule.html +++ /dev/null @@ -1,269 +0,0 @@ - - - - Description of parseGrRule - - - - - - - - - -
Home > utils > parseGrRule.m
- - - -

parseGrRule -

- -

PURPOSE ^

-
parseGrRule Parse a grRule into a syntax tree.
- -

SYNOPSIS ^

-
function tree=parseGrRule(rule)
- -

DESCRIPTION ^

-
 parseGrRule  Parse a grRule into a syntax tree.
-
- Parses a gene-protein-reaction rule into a tree of AND/OR/gene nodes, so
- that callers can reason about its structure instead of searching the raw
- string. Gene IDs are matched as whole tokens, so IDs that contain "and" or
- "or" as a substring (RAND1), or that are prefixes of one another (10 and
- 100), are never confused with operators or with each other.
-
- Parameters
- ----------
- rule : char or string
-     a grRule, e.g. "(G1 and G2) or G3". Both the word forms ("and", "or",
-     any case) and the symbol forms ("&", "&&", "|", "||") are accepted.
-     An empty rule yields an empty tree.
-
- Returns
- -------
- tree : struct
-     a scalar struct with fields:
-
-     - type     : char — 'gene', 'and' or 'or'.
-     - id       : char — the gene ID; '' for 'and'/'or' nodes.
-     - children : cell — child nodes; {} for 'gene' nodes.
-
-     An empty rule returns a 0x0 struct array, for which isempty is true.
-
- Notes
- -----
- The grammar is the usual precedence, with AND binding tighter than OR:
-
-     expr   := term ( OR term )*
-     term   := factor ( AND factor )*
-     factor := gene | '(' expr ')'
-
- Malformed rules raise RAVEN:badGrRule rather than being silently
- misparsed. Callers that lint user input (findPotentialErrors) catch this
- and report it instead of failing.
-
- Examples
- --------
-     tree = parseGrRule('(G1 and G2) or G3');
-     tree.type            % 'or'
-     numel(tree.children) % 2
-
- See also: isDnfGrRule, grRuleToDNF, grRuleToString
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
  • grRuleToDNF grRuleToDNF Convert a grRule to disjunctive normal form.
  • isDnfGrRule isDnfGrRule Test whether a grRule is in disjunctive normal form.
- - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 function tree=parseGrRule(rule)
-0002 % parseGrRule  Parse a grRule into a syntax tree.
-0003 %
-0004 % Parses a gene-protein-reaction rule into a tree of AND/OR/gene nodes, so
-0005 % that callers can reason about its structure instead of searching the raw
-0006 % string. Gene IDs are matched as whole tokens, so IDs that contain "and" or
-0007 % "or" as a substring (RAND1), or that are prefixes of one another (10 and
-0008 % 100), are never confused with operators or with each other.
-0009 %
-0010 % Parameters
-0011 % ----------
-0012 % rule : char or string
-0013 %     a grRule, e.g. "(G1 and G2) or G3". Both the word forms ("and", "or",
-0014 %     any case) and the symbol forms ("&", "&&", "|", "||") are accepted.
-0015 %     An empty rule yields an empty tree.
-0016 %
-0017 % Returns
-0018 % -------
-0019 % tree : struct
-0020 %     a scalar struct with fields:
-0021 %
-0022 %     - type     : char — 'gene', 'and' or 'or'.
-0023 %     - id       : char — the gene ID; '' for 'and'/'or' nodes.
-0024 %     - children : cell — child nodes; {} for 'gene' nodes.
-0025 %
-0026 %     An empty rule returns a 0x0 struct array, for which isempty is true.
-0027 %
-0028 % Notes
-0029 % -----
-0030 % The grammar is the usual precedence, with AND binding tighter than OR:
-0031 %
-0032 %     expr   := term ( OR term )*
-0033 %     term   := factor ( AND factor )*
-0034 %     factor := gene | '(' expr ')'
-0035 %
-0036 % Malformed rules raise RAVEN:badGrRule rather than being silently
-0037 % misparsed. Callers that lint user input (findPotentialErrors) catch this
-0038 % and report it instead of failing.
-0039 %
-0040 % Examples
-0041 % --------
-0042 %     tree = parseGrRule('(G1 and G2) or G3');
-0043 %     tree.type            % 'or'
-0044 %     numel(tree.children) % 2
-0045 %
-0046 % See also: isDnfGrRule, grRuleToDNF, grRuleToString
-0047 
-0048 if nargin<1 || isempty(rule)
-0049     tree=emptyTree();
-0050     return
-0051 end
-0052 rule=char(rule);
-0053 if all(isspace(rule))
-0054     tree=emptyTree();
-0055     return
-0056 end
-0057 
-0058 toks=tokenizeGrRule(rule);
-0059 if isempty(toks)
-0060     tree=emptyTree();
-0061     return
-0062 end
-0063 
-0064 [tree,pos]=parseExpr(toks,1,rule);
-0065 if pos<=numel(toks)
-0066     error('RAVEN:badGrRule',['Unexpected "' tokenText(toks{pos}) '" in grRule: ' rule]);
-0067 end
-0068 end
-0069 
-0070 
-0071 function tree=emptyTree()
-0072 tree=struct('type',{},'id',{},'children',{});
-0073 end
-0074 
-0075 
-0076 function toks=tokenizeGrRule(rule)
-0077 % Split into '(' , ')' , 'AND' , 'OR' and gene tokens. Operators are matched
-0078 % as whole words, which is what keeps gene IDs containing "and"/"or" intact.
-0079 toks={};
-0080 i=1;
-0081 n=numel(rule);
-0082 while i<=n
-0083     c=rule(i);
-0084     if isspace(c)
-0085         i=i+1;
-0086         continue
-0087     end
-0088     if c=='(' || c==')'
-0089         toks{end+1}=c; %#ok<AGROW>
-0090         i=i+1;
-0091         continue
-0092     end
-0093     % Read a word: everything up to whitespace or a bracket.
-0094     j=i;
-0095     while j<=n && ~isspace(rule(j)) && rule(j)~='(' && rule(j)~=')'
-0096         j=j+1;
-0097     end
-0098     word=rule(i:j-1);
-0099     switch lower(word)
-0100         case {'and','&','&&'}
-0101             toks{end+1}='AND'; %#ok<AGROW>
-0102         case {'or','|','||'}
-0103             toks{end+1}='OR'; %#ok<AGROW>
-0104         otherwise
-0105             toks{end+1}=struct('gene',word); %#ok<AGROW>
-0106     end
-0107     i=j;
-0108 end
-0109 end
-0110 
-0111 
-0112 function [node,pos]=parseExpr(toks,pos,rule)
-0113 % expr := term ( OR term )*
-0114 [node,pos]=parseTerm(toks,pos,rule);
-0115 if pos>numel(toks) || ~isOp(toks{pos},'OR')
-0116     return
-0117 end
-0118 children={node};
-0119 while pos<=numel(toks) && isOp(toks{pos},'OR')
-0120     [child,pos]=parseTerm(toks,pos+1,rule);
-0121     children{end+1}=child; %#ok<AGROW>
-0122 end
-0123 node=struct('type','or','id','','children',{children});
-0124 end
-0125 
-0126 
-0127 function [node,pos]=parseTerm(toks,pos,rule)
-0128 % term := factor ( AND factor )*
-0129 [node,pos]=parseFactor(toks,pos,rule);
-0130 if pos>numel(toks) || ~isOp(toks{pos},'AND')
-0131     return
-0132 end
-0133 children={node};
-0134 while pos<=numel(toks) && isOp(toks{pos},'AND')
-0135     [child,pos]=parseFactor(toks,pos+1,rule);
-0136     children{end+1}=child; %#ok<AGROW>
-0137 end
-0138 node=struct('type','and','id','','children',{children});
-0139 end
-0140 
-0141 
-0142 function [node,pos]=parseFactor(toks,pos,rule)
-0143 % factor := gene | '(' expr ')'
-0144 if pos>numel(toks)
-0145     error('RAVEN:badGrRule',['grRule ends after an operator: ' rule]);
-0146 end
-0147 tok=toks{pos};
-0148 if isOp(tok,'(')
-0149     [node,pos]=parseExpr(toks,pos+1,rule);
-0150     if pos>numel(toks) || ~isOp(toks{pos},')')
-0151         error('RAVEN:badGrRule',['Unbalanced parentheses in grRule: ' rule]);
-0152     end
-0153     pos=pos+1;
-0154     return
-0155 end
-0156 if isstruct(tok)
-0157     node=struct('type','gene','id',tok.gene,'children',{{}});
-0158     pos=pos+1;
-0159     return
-0160 end
-0161 error('RAVEN:badGrRule',['Expected a gene but found "' tokenText(tok) '" in grRule: ' rule]);
-0162 end
-0163 
-0164 
-0165 function tf=isOp(tok,op)
-0166 tf=ischar(tok) && strcmp(tok,op);
-0167 end
-0168 
-0169 
-0170 function txt=tokenText(tok)
-0171 if isstruct(tok)
-0172     txt=tok.gene;
-0173 else
-0174     txt=tok;
-0175 end
-0176 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/utils/parseRAVENargs.html b/doc/utils/parseRAVENargs.html deleted file mode 100644 index a01e130c..00000000 --- a/doc/utils/parseRAVENargs.html +++ /dev/null @@ -1,253 +0,0 @@ - - - - Description of parseRAVENargs - - - - - - - - - -
Home > utils > parseRAVENargs.m
- - - -

parseRAVENargs -

- -

PURPOSE ^

-
parseRAVENargs Resolve optional arguments passed positionally or by name.
- -

SYNOPSIS ^

-
function args = parseRAVENargs(rawArgs, spec)
- -

DESCRIPTION ^

-
 parseRAVENargs  Resolve optional arguments passed positionally or by name.
-
- Helper that lets a RAVEN function accept its optional arguments either in
- the traditional positional order, as name-value pairs, or as a mix of
- leading positional values followed by name-value pairs. The required
- leading arguments stay explicit in the function signature; the optional
- tail is collected into varargin and passed here together with a
- specification of the optional parameters.
-
- Parameters
- ----------
- rawArgs : cell
-     the varargin of the function, i.e. whatever was supplied after the required
-     positional arguments.
- spec : cell
-     an N-by-2 or N-by-3 cell array describing the optional parameters, one
-     per row:
-
-     - column 1 : parameter name (char).
-     - column 2 : default value, used when the parameter is not supplied.
-     - column 3 : optional validator, a function handle that is called with
-       the resolved value (including the default, when the parameter was not
-       supplied) and should error on an invalid value, e.g. @mustBeNumeric.
-       Use [] for no validation.
-
- Returns
- -------
- args : struct
-     a struct with one field per parameter name, holding the resolved value.
-
- Notes
- -----
- Calling convention is resolved as follows:
-
-   1. If rawArgs is empty, all parameters take their defaults.
-   2. The first element in rawArgs that is a string matching a known
-      parameter name marks position k. Everything before k is treated
-      as positional (assigned left-to-right by the order in spec).
-      Everything from k onward must form valid name-value pairs: an even
-      count where every name-position element is a known parameter name.
-   3. If no such position exists (no element is a matching string), or the
-      trailing section from k does not form valid name-value pairs, the
-      entire rawArgs is treated as positional.
-
- This means three styles are accepted interchangeably:
-
-     f(model, v1, v2)                   % purely positional
-     f(model, "p1", v1, "p2", v2)       % purely named
-     f(model, v1, "p2", v2, "p3", v3)   % hybrid: v1 positional, rest named
-
- When a positional value could itself be a string that equals a parameter
- name, use the explicit name-value form for that argument to avoid
- ambiguity.
-
- Examples
- --------
-     % inside exportToExcelFormat(model, varargin):
-     p = parseRAVENargs(varargin, { ...
-         "fileName", []; ...
-         "sortIds",  false});
-     fileName = p.fileName;
-     sortIds  = p.sortIds;
-
-     % the caller may use any of these:
-     exportToExcelFormat(model, "model.xlsx", true)              % positional
-     exportToExcelFormat(model, "sortIds", true)                 % named
-     exportToExcelFormat(model, "model.xlsx", "sortIds", true)   % hybrid
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: -
    -
-This function is called by: -
    -
- - - - -

SOURCE CODE ^

-
0001 function args = parseRAVENargs(rawArgs, spec)
-0002 % parseRAVENargs  Resolve optional arguments passed positionally or by name.
-0003 %
-0004 % Helper that lets a RAVEN function accept its optional arguments either in
-0005 % the traditional positional order, as name-value pairs, or as a mix of
-0006 % leading positional values followed by name-value pairs. The required
-0007 % leading arguments stay explicit in the function signature; the optional
-0008 % tail is collected into varargin and passed here together with a
-0009 % specification of the optional parameters.
-0010 %
-0011 % Parameters
-0012 % ----------
-0013 % rawArgs : cell
-0014 %     the varargin of the function, i.e. whatever was supplied after the required
-0015 %     positional arguments.
-0016 % spec : cell
-0017 %     an N-by-2 or N-by-3 cell array describing the optional parameters, one
-0018 %     per row:
-0019 %
-0020 %     - column 1 : parameter name (char).
-0021 %     - column 2 : default value, used when the parameter is not supplied.
-0022 %     - column 3 : optional validator, a function handle that is called with
-0023 %       the resolved value (including the default, when the parameter was not
-0024 %       supplied) and should error on an invalid value, e.g. @mustBeNumeric.
-0025 %       Use [] for no validation.
-0026 %
-0027 % Returns
-0028 % -------
-0029 % args : struct
-0030 %     a struct with one field per parameter name, holding the resolved value.
-0031 %
-0032 % Notes
-0033 % -----
-0034 % Calling convention is resolved as follows:
-0035 %
-0036 %   1. If rawArgs is empty, all parameters take their defaults.
-0037 %   2. The first element in rawArgs that is a string matching a known
-0038 %      parameter name marks position k. Everything before k is treated
-0039 %      as positional (assigned left-to-right by the order in spec).
-0040 %      Everything from k onward must form valid name-value pairs: an even
-0041 %      count where every name-position element is a known parameter name.
-0042 %   3. If no such position exists (no element is a matching string), or the
-0043 %      trailing section from k does not form valid name-value pairs, the
-0044 %      entire rawArgs is treated as positional.
-0045 %
-0046 % This means three styles are accepted interchangeably:
-0047 %
-0048 %     f(model, v1, v2)                   % purely positional
-0049 %     f(model, "p1", v1, "p2", v2)       % purely named
-0050 %     f(model, v1, "p2", v2, "p3", v3)   % hybrid: v1 positional, rest named
-0051 %
-0052 % When a positional value could itself be a string that equals a parameter
-0053 % name, use the explicit name-value form for that argument to avoid
-0054 % ambiguity.
-0055 %
-0056 % Examples
-0057 % --------
-0058 %     % inside exportToExcelFormat(model, varargin):
-0059 %     p = parseRAVENargs(varargin, { ...
-0060 %         "fileName", []; ...
-0061 %         "sortIds",  false});
-0062 %     fileName = p.fileName;
-0063 %     sortIds  = p.sortIds;
-0064 %
-0065 %     % the caller may use any of these:
-0066 %     exportToExcelFormat(model, "model.xlsx", true)              % positional
-0067 %     exportToExcelFormat(model, "sortIds", true)                 % named
-0068 %     exportToExcelFormat(model, "model.xlsx", "sortIds", true)   % hybrid
-0069 
-0070 if nargin < 2 || isempty(spec)
-0071     error('parseRAVENargs:noSpec', 'A parameter specification is required.');
-0072 end
-0073 
-0074 names = spec(:, 1);
-0075 defaults = spec(:, 2);
-0076 hasValidators = size(spec, 2) >= 3;
-0077 
-0078 % Seed every parameter with its default.
-0079 args = struct();
-0080 for i = 1:numel(names)
-0081     args.(names{i}) = defaults{i};
-0082 end
-0083 
-0084 if ~isempty(rawArgs)
-0085     % Find which elements are strings matching a known parameter name.
-0086     isParamName = cellfun(@(x) (ischar(x) || isstring(x)) && isscalar(string(x)) && ...
-0087                           any(strcmpi(x, names)), rawArgs);
-0088 
-0089     % Position of the first parameter-name string — where name-value begins.
-0090     nvStart = find(isParamName, 1);
-0091 
-0092     nvOK = false;
-0093     if ~isempty(nvStart)
-0094         nv = rawArgs(nvStart:end);
-0095         % Valid name-value section: even count, all name-position elements
-0096         % are known parameter names.
-0097         nvOK = mod(numel(nv), 2) == 0 && ...
-0098                all(cellfun(@(x) (ischar(x) || isstring(x)) && isscalar(string(x)) && ...
-0099                    any(strcmpi(x, names)), nv(1:2:end)));
-0100     end
-0101 
-0102     if ~isempty(nvStart) && nvOK
-0103         % Assign leading positional arguments (may be zero when nvStart == 1).
-0104         nPos = nvStart - 1;
-0105         if nPos > numel(names)
-0106             error('parseRAVENargs:tooManyArgs', ...
-0107                 'Too many positional arguments: expected at most %d, got %d.', ...
-0108                 numel(names), nPos);
-0109         end
-0110         for k = 1:nPos
-0111             args.(names{k}) = rawArgs{k};
-0112         end
-0113         % Assign name-value pairs.
-0114         nv = rawArgs(nvStart:end);
-0115         for k = 1:2:numel(nv)
-0116             args.(names{strcmpi(nv{k}, names)}) = nv{k + 1};
-0117         end
-0118     else
-0119         % Purely positional.
-0120         if numel(rawArgs) > numel(names)
-0121             error('parseRAVENargs:tooManyArgs', ...
-0122                 'Too many positional arguments: expected at most %d, got %d.', ...
-0123                 numel(names), numel(rawArgs));
-0124         end
-0125         for k = 1:numel(rawArgs)
-0126             args.(names{k}) = rawArgs{k};
-0127         end
-0128     end
-0129 end
-0130 
-0131 % Apply optional validators.
-0132 if hasValidators
-0133     for i = 1:numel(names)
-0134         validator = spec{i, 3};
-0135         if ~isempty(validator)
-0136             validator(args.(names{i}));
-0137         end
-0138     end
-0139 end
-0140 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/utils/progressReport.html b/doc/utils/progressReport.html deleted file mode 100644 index ff1c8f4b..00000000 --- a/doc/utils/progressReport.html +++ /dev/null @@ -1,360 +0,0 @@ - - - - Description of progressReport - - - - - - - - - -
Home > utils > progressReport.m
- - - -

progressReport -

- -

PURPOSE ^

-
- -

SYNOPSIS ^

-
This is a script file.
- -

DESCRIPTION ^

-
- - -

CROSS-REFERENCE INFORMATION ^

-This function calls: - -This function is called by: - - - -

SUBFUNCTIONS ^

- - -

SOURCE CODE ^

-
0001 classdef progressReport < handle
-0002 % progressReport  Universal progress reporting for RAVEN functions.
-0003 %
-0004 % A single progress-reporting API used across RAVEN. The rendering backend
-0005 % is selected automatically so that the same call works on an interactive
-0006 % desktop, a plain command-line session and a headless server, and is
-0007 % silenced during unit tests:
-0008 %
-0009 %   - 'gui'  : MATLAB waitbar (interactive desktop).
-0010 %   - 'cli'  : animated command-line bar (interactive terminal).
-0011 %   - 'log'  : plain milestone lines, e.g. "Running BLAST: 30%". Contains no
-0012 %              control characters, so it stays readable in redirected logs
-0013 %              and on headless / -batch servers.
-0014 %   - 'none' : no output (during tests, or when disabled).
-0015 %
-0016 % The backend can be forced with setRavenProgress, e.g. setRavenProgress('log')
-0017 % on a compute cluster, or setRavenProgress('off') to silence all progress
-0018 % reporting.
-0019 %
-0020 % Usage
-0021 % -----
-0022 % For loops with one cheap-to-expensive iteration each, count one per pass:
-0023 %
-0024 %     PB = progressReport(N, 'Task name');   % before the loop
-0025 %     for i = 1:N
-0026 %         ...                                % loop body
-0027 %         PB.count;                          % or count(PB), also in parfor
-0028 %     end
-0029 %     PB.done;                               % optional; cleans up on early exit
-0030 %
-0031 % For very long loops over cheap iterations, set the absolute position at
-0032 % intervals to avoid per-iteration overhead:
-0033 %
-0034 %     PB = progressReport(N, 'Task name');
-0035 %     for i = 1:N
-0036 %         ...
-0037 %         if rem(i-1, 1000) == 0
-0038 %             PB.update(i);                  % or update(PB, i)
-0039 %         end
-0040 %     end
-0041 %     PB.done;
-0042 %
-0043 % Parameters
-0044 % ----------
-0045 % N : double
-0046 %     number of iterations the loop will perform (a positive integer).
-0047 % taskName : char, optional
-0048 %     label shown alongside the progress (default '').
-0049 %
-0050 % See also
-0051 % --------
-0052 % setRavenProgress
-0053 
-0054     properties (SetAccess = private)
-0055         mode char            % resolved backend: 'gui', 'cli', 'log' or 'none'
-0056         N    double
-0057         taskName char
-0058     end
-0059     properties (Access = private, Transient)
-0060         counter   = 0
-0061         lastPct   = -1       % last percentage rendered
-0062         logStep   = 10       % percent between 'log' lines
-0063         barWidth  = 30
-0064         prevLen   = 0        % characters on the current 'cli' line
-0065         guiHandle = []
-0066         Queue     = []       % DataQueue for parfor counting
-0067         Listener  = []
-0068         noPCT     = true
-0069         finished  = false
-0070     end
-0071 
-0072     methods
-0073         function obj = progressReport(N, taskName)
-0074             if nargin < 2 || isempty(taskName)
-0075                 taskName = '';
-0076             end
-0077             obj.N = double(N);
-0078             obj.taskName = truncateName(char(taskName), 40);
-0079             obj.mode = resolveProgressMode();
-0080 
-0081             % Nothing sensible to show for an empty or unknown-length loop.
-0082             if ~(isscalar(obj.N) && isfinite(obj.N) && obj.N >= 1)
-0083                 obj.mode = 'none';
-0084             end
-0085             if strcmp(obj.mode,'none')
-0086                 return
-0087             end
-0088 
-0089             obj.noPCT = isempty(ver('parallel'));
-0090             if ~obj.noPCT
-0091                 obj.Queue = parallel.pool.DataQueue;
-0092                 obj.Listener = afterEach(obj.Queue, @(v) localUpdate(obj, v));
-0093             end
-0094 
-0095             % Initial render.
-0096             switch obj.mode
-0097                 case 'gui'
-0098                     name = obj.taskName;
-0099                     if isempty(name); name = 'Progress'; end
-0100                     try
-0101                         obj.guiHandle = waitbar(0, '0%', 'Name', name);
-0102                     catch
-0103                         obj.mode = 'log';   % no display available
-0104                         emitLog(obj, 0);
-0105                     end
-0106                     obj.lastPct = 0;
-0107                 case 'cli'
-0108                     drawCli(obj, 0);
-0109                     obj.lastPct = 0;
-0110                 case 'log'
-0111                     emitLog(obj, 0);
-0112                     obj.lastPct = 0;
-0113             end
-0114         end
-0115 
-0116         function count(obj)
-0117             % Register one completed iteration.
-0118             if strcmp(obj.mode,'none')
-0119                 return
-0120             end
-0121             if obj.noPCT || isempty(getCurrentJob)
-0122                 localUpdate(obj, -1);       % serial execution
-0123             else
-0124                 send(obj.Queue, -1);        % parfor worker -> client
-0125             end
-0126         end
-0127 
-0128         function update(obj, n)
-0129             % Set the absolute number of completed iterations to n.
-0130             if strcmp(obj.mode,'none')
-0131                 return
-0132             end
-0133             if obj.noPCT || isempty(getCurrentJob)
-0134                 localUpdate(obj, n);        % serial execution
-0135             else
-0136                 send(obj.Queue, n);         % parfor worker -> client
-0137             end
-0138         end
-0139 
-0140         function done(obj)
-0141             % Finalise the bar (call when a loop may exit before reaching N).
-0142             finishUp(obj);
-0143         end
-0144     end
-0145 
-0146     methods (Hidden = true)
-0147         function delete(obj)
-0148             if ~isempty(obj.Queue)
-0149                 delete(obj.Queue);
-0150             end
-0151             finishUp(obj);
-0152         end
-0153     end
-0154 
-0155     methods (Access = private)
-0156         function localUpdate(obj, v)
-0157             if v < 0
-0158                 obj.counter = obj.counter + 1;
-0159             else
-0160                 obj.counter = max(obj.counter, v);
-0161             end
-0162             render(obj);
-0163         end
-0164 
-0165         function render(obj)
-0166             if obj.finished
-0167                 return
-0168             end
-0169             pct = min(100, floor(100 * obj.counter / obj.N));
-0170             switch obj.mode
-0171                 case 'gui'
-0172                     if pct ~= obj.lastPct
-0173                         obj.lastPct = pct;
-0174                         if ~isempty(obj.guiHandle) && isvalid(obj.guiHandle)
-0175                             waitbar(pct/100, obj.guiHandle, sprintf('%d%%', pct));
-0176                         end
-0177                     end
-0178                     if obj.counter >= obj.N; finishUp(obj); end
-0179                 case 'cli'
-0180                     if pct ~= obj.lastPct
-0181                         obj.lastPct = pct;
-0182                         drawCli(obj, pct);
-0183                     end
-0184                     if obj.counter >= obj.N; finishUp(obj); end
-0185                 case 'log'
-0186                     if pct >= obj.lastPct + obj.logStep ...
-0187                             || (obj.counter >= obj.N && obj.lastPct < 100)
-0188                         if obj.counter >= obj.N; pct = 100; end
-0189                         obj.lastPct = pct;
-0190                         emitLog(obj, pct);
-0191                     end
-0192             end
-0193         end
-0194 
-0195         function drawCli(obj, pct)
-0196             nfill = round(obj.barWidth * pct / 100);
-0197             bar = [repmat('#',1,nfill), repmat('-',1,obj.barWidth-nfill)];
-0198             if isempty(obj.taskName)
-0199                 s = sprintf('[%s] %3d%%', bar, pct);
-0200             else
-0201                 s = sprintf('%s [%s] %3d%%', obj.taskName, bar, pct);
-0202             end
-0203             fprintf('%s%s', repmat(sprintf('\b'),1,obj.prevLen), s);
-0204             obj.prevLen = numel(s);
-0205         end
-0206 
-0207         function emitLog(obj, pct)
-0208             if isempty(obj.taskName)
-0209                 fprintf('Progress: %d%%\n', pct);
-0210             else
-0211                 fprintf('%s: %d%%\n', obj.taskName, pct);
-0212             end
-0213         end
-0214 
-0215         function finishUp(obj)
-0216             if obj.finished
-0217                 return
-0218             end
-0219             switch obj.mode
-0220                 case 'gui'
-0221                     if ~isempty(obj.guiHandle) && isvalid(obj.guiHandle)
-0222                         delete(obj.guiHandle);
-0223                     end
-0224                 case 'cli'
-0225                     if obj.lastPct < 100
-0226                         drawCli(obj, 100);  % show completion
-0227                     end
-0228                     if obj.prevLen > 0
-0229                         fprintf('\n');      % move off the progress line
-0230                     end
-0231                 case 'log'
-0232                     if obj.lastPct < 100
-0233                         emitLog(obj, 100);
-0234                     end
-0235             end
-0236             obj.finished = true;
-0237         end
-0238     end
-0239 end
-0240 
-0241 % ---------------------------- Local functions ----------------------------
-0242 function mode = resolveProgressMode()
-0243 % Decide the backend from the RAVEN preference, the test context and the
-0244 % runtime environment.
-0245 mode = 'auto';
-0246 try
-0247     mode = char(getpref('RAVEN','progressBar','auto'));
-0248 catch
-0249 end
-0250 if strcmpi(mode,'off')
-0251     mode = 'none';
-0252 end
-0253 if ~strcmpi(mode,'auto')
-0254     if ~ismember(lower(mode),{'gui','cli','log','none'})
-0255         mode = 'cli';
-0256     end
-0257     mode = lower(mode);
-0258     return
-0259 end
-0260 
-0261 % 'auto': silence inside the test framework first.
-0262 if inTestContext()
-0263     mode = 'none';
-0264     return
-0265 end
-0266 
-0267 % Headless / deployed / -batch -> clean log lines.
-0268 isBatch = false;
-0269 try
-0270     isBatch = batchStartupOptionUsed;
-0271 catch
-0272 end
-0273 if isBatch || isdeployed
-0274     mode = 'log';
-0275     return
-0276 end
-0277 
-0278 % Desktop available -> GUI, otherwise an interactive terminal -> CLI.
-0279 hasDesktop = false;
-0280 try
-0281     hasDesktop = usejava('desktop');
-0282 catch
-0283 end
-0284 if hasDesktop
-0285     mode = 'gui';
-0286 else
-0287     mode = 'cli';
-0288 end
-0289 end
-0290 
-0291 function tf = inTestContext()
-0292 % Best-effort detection of the matlab.unittest framework on the call stack.
-0293 % RavenTestCase also forces the preference to 'none', so the maintained
-0294 % suite is silenced even if this heuristic misses a frame.
-0295 tf = false;
-0296 st = dbstack('-completenames');
-0297 for i = 1:numel(st)
-0298     if contains(st(i).name,'matlab.unittest','IgnoreCase',true) ...
-0299             || contains(st(i).name,'TestRunner','IgnoreCase',true) ...
-0300             || contains(st(i).name,'runtests','IgnoreCase',true)
-0301         tf = true;
-0302         return
-0303     end
-0304 end
-0305 end
-0306 
-0307 function name = truncateName(name, maxLen)
-0308 if numel(name) > maxLen
-0309     name = name(1:maxLen);
-0310 end
-0311 end
-
Generated by m2html © 2005
- - \ No newline at end of file diff --git a/doc/INIT/INITStepDesc.html b/docs/html/INIT/INITStepDesc.html similarity index 98% rename from doc/INIT/INITStepDesc.html rename to docs/html/INIT/INITStepDesc.html index 082addf4..eb3260f7 100644 --- a/doc/INIT/INITStepDesc.html +++ b/docs/html/INIT/INITStepDesc.html @@ -46,7 +46,7 @@

SUBFUNCTIONS ^SOURCE CODE ^

0001 classdef INITStepDesc
 0002 % Describes a step in the ftINIT algorithm. A cell array of objects of this
-0003 % class is used as input to ftINIT to specify how the algorithm should be run.
+0003 % class is the input to ftINIT that specifies how the algorithm is run.
 0004    properties
 0005       PosRevOff %If true, positive-scored reversible reactions are removed
 0006                 %from the MILP problem (used in step 1 of ftINIT)
@@ -67,7 +67,7 @@ 

SOURCE CODE ^% b8 - All rxns without GPRs 0022 0023 MetsToIgnore % Structure describing mets that can be removed from the model -0024 % before running ftINIT, such as water etc. +0024 % before running ftINIT, such as water and other ubiquitous metabolites. 0025 % .simpleMets 0026 % .mets Names of metabolites to remove 0027 % .compsToKeep Compartments for which metabolites should be kept. diff --git a/docs/html/INIT/ftINIT.html b/docs/html/INIT/ftINIT.html new file mode 100644 index 00000000..3a85612f --- /dev/null +++ b/docs/html/INIT/ftINIT.html @@ -0,0 +1,580 @@ + + + + Description of ftINIT + + + + + + + + + +
Home > INIT > ftINIT.m
+ + + +

ftINIT +

+ +

PURPOSE ^

+
+ +

SYNOPSIS ^

+
This is a script file.
+ +

DESCRIPTION ^

+
+ + +

CROSS-REFERENCE INFORMATION ^

+This function calls: + +This function is called by: + + + +

SUBFUNCTIONS ^

+ + +

SOURCE CODE ^

+
0001 function [model, metProduction, addedRxnsForTasks, deletedRxnsInINIT, fullMipRes] = ftINIT(prepData, tissue, celltype, hpaData, varargin)
+0002 % ftINIT  Generate a model using the ftINIT algorithm.
+0003 %
+0004 % Generates a model from proteomics, transcriptomics, metabolomics and
+0005 % metabolic tasks, in any combination except metabolomics alone. The function
+0006 % prepINITModel must be run first on the template model (such as the generic
+0007 % Human-GEM). It runs once per template and precalculates the parts that are
+0008 % independent of the omics data. Its output, prepData, is the input to this
+0009 % function.
+0010 %
+0011 % Parameters
+0012 % ----------
+0013 % prepData : struct
+0014 %     the prepData for the model, as returned by prepINITModel.
+0015 % tissue : char
+0016 %     tissue to score for. Must exist in hpaData.tissues or
+0017 %     transcrData.tissues.
+0018 % celltype : char
+0019 %     cell type to score for. Must exist in hpaData.celltypes or
+0020 %     transcrData.celltypes for this tissue. If empty, the maximum value
+0021 %     among all the cell types of the tissue is used.
+0022 % hpaData : struct
+0023 %     HPA data structure from parseHPA (required if transcrData is not
+0024 %     supplied, default []).
+0025 %
+0026 % Name-Value Arguments
+0027 % --------------------
+0028 % transcrData : struct
+0029 %     gene expression data structure (required if hpaData is not supplied,
+0030 %     default []). With fields:
+0031 %
+0032 %     - genes : cell array with the unique gene names.
+0033 %     - tissues : cell array with the tissue names. The list can contain
+0034 %       duplicates, as there can be multiple cell types per tissue.
+0035 %     - celltypes : cell array with the cell type names for each tissue.
+0036 %     - levels : GENESxTISSUES array with the expression level for each gene in
+0037 %       each tissue and cell type. NaN marks a gene that was not measured.
+0038 %     - threshold : a single value or a vector of gene expression thresholds,
+0039 %       above which genes are considered to be "expressed". If omitted, the
+0040 %       mean expression level of each gene across all tissues in transcrData
+0041 %       is used as the threshold.
+0042 % metabolomicsData : cell
+0043 %     cell array with metabolite names that the model should produce (default
+0044 %     []).
+0045 % INITSteps : cell
+0046 %     specifies the steps in the algorithm. For more info, see INITStepDesc and
+0047 %     getINITSteps (default getINITSteps([],'1+1'), the standard ftINIT).
+0048 % removeGenes : logical
+0049 %     if true, low-score genes are removed from grRules, unless they
+0050 %     are the only gene associated with a reaction, or a subunit of an enzyme
+0051 %     complex (see "removeLowScoreGenes" function for details). If false,
+0052 %     grRules are not modified; genes that were associated only with removed
+0053 %     reactions are not present in the final model (default true).
+0054 % useScoresForTasks : logical
+0055 %     true if the calculated reaction scores are used as weights when
+0056 %     fitting to tasks. Scores above -0.1 are set to -0.1 for this step
+0057 %     (default true).
+0058 % paramsFT : struct
+0059 %     solver parameters for the gap-filling step that fits the tasks, passed
+0060 %     to fitTasks as params (default []).
+0061 % verbose : logical
+0062 %     if true, the MILP progression is shown (default false).
+0063 % resolveTies : logical
+0064 %     if true, the degenerate MILP of each step is solved to a canonical
+0065 %     answer in addition to the solver Seed: the score objective is held at
+0066 %     its optimum, then the count of removable ("on") reactions is
+0067 %     minimised, then, among the sparsest solutions, their summed
+0068 %     reaction-id rank is minimised. This applies to every main-extraction
+0069 %     step and to the final gap-filling step (resolveTies of fitTasks); see
+0070 %     raven-gecko-parity#104 and resolve_ties in raven-toolbox. In each
+0071 %     main-extraction step except the last (ftINITInternalAlg), the fluxes
+0072 %     set the direction and magnitude at which a reaction is forced in the
+0073 %     next step. They are taken from the least-total-flux solution for that
+0074 %     on/off state and, among those, from the solution with the least
+0075 %     id-rank-weighted backward flux (a merged reaction is ranked by the
+0076 %     smallest original id in its group). The last step skips these flux
+0077 %     phases, as no later step reads its fluxes. A step whose primary solve
+0078 %     stops at the time limit keeps its primary solution without any of
+0079 %     these phases. The count and id-rank phases get half of the step's
+0080 %     TimeLimit each. A warning names each phase that does not prove optimal
+0081 %     or finds no solution. Measured with raven-toolbox's ftinit, whose phase
+0082 %     handling differs from ftINIT, on one cell line (Human-GEM v2.0.0-38,
+0083 %     seeds 1234 and 7, time limit 1800 s): two seeds with the default
+0084 %     escalation differ by 110 reactions and 17 predicted essential genes,
+0085 %     and with resolveTies alone by 41 reactions and 1 gene, at about 3.6
+0086 %     times the run time (about 14 min against 4 min) (default false).
+0087 % proveAbsGap : double
+0088 %     if set, each step is proven to this fixed absolute MILP gap in a single
+0089 %     solve, replacing the relative-gap escalation in stp.MILPParams. The
+0090 %     final near-zero-objective run of the escalation accepts an arbitrary
+0091 %     incumbent within the gap. A warning is raised when a step reaches the
+0092 %     time limit before the proof. The figures below were measured with
+0093 %     raven-toolbox's ftinit, whose phase handling differs from ftINIT, on one
+0094 %     cell line (Human-GEM v2.0.0-38, time limit 1800 s) with resolveTies on
+0095 %     and without gap-filling, in a sweep over four seeds (1234, 7, 1 and 2).
+0096 %     The step 1 objective of the escalation ended 4.0 to 12.0 units below the
+0097 %     proven optimum; with seeds 1234 and 7 alone it ended 7.0 and 12.0
+0098 %     below. A value of 5.0 stopped 2.0 below the optimum for one seed (seed
+0099 %     2), and values from 2.0 to 0.05 reached it for all four seeds and gave
+0100 %     the same models. Every primary solve of the steps (48 solves) ended
+0101 %     optimal; the flux phases phase2c-minflux (3 of 4 seeds) and
+0102 %     phase2d-direction (4 of 4 seeds) reached the time limit and were adopted
+0103 %     as unproven incumbents. With 1.0 and resolveTies the models of two seeds
+0104 %     differ by 1 reaction and 0 predicted essential genes, at about 26 times
+0105 %     the run time of the default escalation (about 100 min against 4 min);
+0106 %     the mean of a whole build was 5766 s at 0.05 and 5641 s at 1.0. The
+0107 %     study recommends 1.0 together with resolveTies when the variation of
+0108 %     resolveTies alone is too large and the run time is acceptable. Its
+0109 %     full-pipeline runs used a time limit of 600 s per step and its
+0110 %     determinism builds 1800 s (default [], which uses the escalation).
+0111 %
+0112 % Returns
+0113 % -------
+0114 % model : struct
+0115 %     the resulting model structure.
+0116 % metProduction : logical
+0117 %     array from the last MILP step that indicates which of the metabolites in
+0118 %     metabolomicsData could be produced (true) or not (false). Metabolites
+0119 %     with a producing reaction that is essential are not part of the MILP
+0120 %     and are not in the array. The array is determined before the
+0121 %     gap-filling for the defined tasks. To see which metabolites can be
+0122 %     produced in the final model, use canExchange.
+0123 % addedRxnsForTasks : cell
+0124 %     cell array of the reactions which were added in order to perform the
+0125 %     tasks.
+0126 % deletedRxnsInINIT : cell
+0127 %     cell array of the reactions of the template model that are not in the
+0128 %     final model, excluding exchange reactions (these are all kept). It
+0129 %     contains the reactions removed by the MILP steps that gap-filling did
+0130 %     not add back.
+0131 % fullMipRes : struct
+0132 %     the solver results from the last MILP step run.
+0133 %
+0134 % Notes
+0135 % -----
+0136 % The template model keeps its exchange metabolites,
+0137 % since checkTasks and fitTasks put specific constraints on them for each
+0138 % task. The INIT algorithm removes exchange metabolites if any are
+0139 % present. Use importModel(file,false) to import a model with exchange
+0140 % metabolites remaining.
+0141 %
+0142 % ftINIT passes a prodWeight of 5 (the reward for each produced metabolite of
+0143 % metabolomicsData) to ftINITInternalAlg, whose own default is 0.5.
+0144 %
+0145 
+0146 p=parseRAVENargs(varargin, {'transcrData',[]; 'metabolomicsData',[]; 'INITSteps',[]; 'removeGenes',[]; 'useScoresForTasks',[]; 'paramsFT',[]; 'verbose',false; 'resolveTies',false; 'proveAbsGap',[]});
+0147 transcrData=p.transcrData;
+0148 metabolomicsData=p.metabolomicsData;
+0149 INITSteps=p.INITSteps;
+0150 removeGenes=p.removeGenes;
+0151 useScoresForTasks=p.useScoresForTasks;
+0152 paramsFT=p.paramsFT;
+0153 verbose=p.verbose;
+0154 resolveTies=p.resolveTies;
+0155 proveAbsGap=p.proveAbsGap;
+0156 if isempty(INITSteps)
+0157     INITSteps = getINITSteps([],'1+1');
+0158 end
+0159 if isempty(removeGenes)
+0160     removeGenes = true;
+0161 end
+0162 if isempty(useScoresForTasks)
+0163     useScoresForTasks = true;
+0164 end
+0165 %Handle detected mets:
+0166 %Giving a bonus for secreting these metabolites does not work, since the
+0167 %metabolite secretion and uptake can be lost when we merge linearly dependent
+0168 %reactions. Instead, we need to figure out which reactions either produce or
+0169 %take up the mets, and give a bonus if any of them carry flux. To simplify
+0170 %things, we focus on reactions that produce the metabolite (since there must
+0171 %be one such reaction). We further reason that the direction does not matter -
+0172 %we can force one of these reactions in any direction - if it becomes a consumer, it will
+0173 %automatically force another producer on as well (otherwise we will have a net consumption).
+0174 
+0175 if (~isempty(metabolomicsData))
+0176     if length(unique(upper(metabolomicsData))) ~= length(metabolomicsData)
+0177         error('RAVEN:badInput', '%s', 'Metabolomics contains the same metabolite multiple times');
+0178     end
+0179     metData = false(numel(metabolomicsData), length(prepData.minModel.rxns)); %one row per metabolite that is a boolean vector
+0180     for i=1:numel(metabolomicsData)
+0181         %Get the matching mets
+0182         metSel = ismember(upper(prepData.refModel.metNames),upper(metabolomicsData{i}));
+0183         prodRxnsSel = any(prepData.refModel.S(metSel,:) > 0,1) | ... %direct producers
+0184                      (any(prepData.refModel.S(metSel,:) < 0,1) & prepData.refModel.rev.'); %reversible reactions that are consumers
+0185         %convert the production rxns from refModel to minModel
+0186         prepData.groupIds;
+0187         [~,ia,ib] = intersect(prepData.minModel.rxns,prepData.refModel.rxns);
+0188         grpIdsMerged = nan(length(prepData.minModel.rxns),1);
+0189         grpIdsMerged(ia) = prepData.groupIds(ib);
+0190         
+0191         groupIdsPos = unique(prepData.groupIds(prodRxnsSel));%gets all group ids which includes a production rxn
+0192         groupIdsPos = groupIdsPos(groupIdsPos ~= 0);%remove the 0 id, it means there is no group
+0193         %the other option is that there is a direct match between the rxn id in minModel and refModel:
+0194         posRxns = prepData.refModel.rxns(prodRxnsSel);
+0195         directMatch = ismember(prepData.minModel.rxns, posRxns).';
+0196         
+0197         metData(i,:) = ismember(grpIdsMerged, groupIdsPos).' | directMatch;
+0198     end
+0199     metData = sparse(metData);
+0200 else
+0201     metData = [];
+0202 end
+0203 
+0204 % Get rxn scores and adapt them to the minimized model
+0205 origRxnScores = scoreModel(prepData.refModel,hpaData,transcrData,tissue,celltype);
+0206 origRxnScores(origRxnScores > -0.1 & origRxnScores <= 0) = -0.1;%we don't want reaction scores that are exactly 0 (or close), this causes problems in the milp
+0207 origRxnScores(origRxnScores < 0.1 & origRxnScores > 0) = 0.1;
+0208 
+0209 rxnsTurnedOn = false(length(prepData.minModel.rxns),1);
+0210 fluxes = zeros(length(prepData.minModel.rxns),1);
+0211 
+0212 rxnsToIgnoreLastStep = [1;1;1;1;1;1;1;1];
+0213 
+0214 %We assume that all essential rxns are irrev - this is taken care of in
+0215 %prepINITModel. We then use an initial flux "from last run" of 0.1 for all
+0216 %reactions. This is used for knowing what flux should be forced through an
+0217 %essential rxn.
+0218 fluxes = ones(length(prepData.minModel.rxns), 1).*0.1;
+0219 
+0220 %resolveTies ranks a merged reaction by its group's smallest original id,
+0221 %which does not depend on which member names the merged reaction.
+0222 rankKeys = prepData.minModel.rxns;
+0223 [inRef, refInd] = ismember(rankKeys, prepData.refModel.rxns);
+0224 for i = find(inRef)'
+0225     g = prepData.groupIds(refInd(i));
+0226     if g ~= 0
+0227         members = sort(prepData.refModel.rxns(prepData.groupIds == g));
+0228         rankKeys{i} = members{1};
+0229     end
+0230 end
+0231 
+0232 for initStep = 1:length(INITSteps)
+0233     disp(['ftINIT: Running step ' num2str(initStep)])
+0234     stp = INITSteps{initStep};
+0235     
+0236     if any ((rxnsToIgnoreLastStep - stp.RxnsToIgnoreMask) < 0)
+0237         error('RAVEN:badInput', '%s', 'RxnsToIgnoreMask may not cover rxns not covered in previous steps, but the other way around is fine.');
+0238     end
+0239     rxnsToIgnoreLastStep = stp.RxnsToIgnoreMask;
+0240     %The fluxes of a step set the forced directions of the next one, so the
+0241     %last step skips the flux phases of resolveTies.
+0242     pinFluxes = initStep < numel(INITSteps);
+0243     
+0244     mm = prepData.minModel;
+0245     
+0246     if (~isempty(stp.MetsToIgnore))
+0247         if (~isempty(stp.MetsToIgnore.simpleMets))
+0248             %Here, we remove simple metabolites that will not really affect the milp but
+0249             %are very common in the S matrix. For example H2O, H+, etc.
+0250             %It is also possible to leave compartments untouched, for example the i compartment in the mitochondria (for H+).
+0251             metsToRem = ismember(mm.metNames,stp.MetsToIgnore.simpleMets.mets);
+0252             compsToKeep = find(ismember(mm.comps, stp.MetsToIgnore.simpleMets.compsToKeep));
+0253             metsToRem = metsToRem & ~ismember(mm.metComps, compsToKeep);
+0254             mm.S(metsToRem,:) = 0;
+0255         end    
+0256     end
+0257 
+0258     %Set up the reaction scores and essential rxns
+0259     rxnsToIgnore = getRxnsFromPattern(stp.RxnsToIgnoreMask, prepData);
+0260     rxnScores = groupRxnScores(prepData.minModel, origRxnScores, prepData.refModel.rxns, prepData.groupIds, rxnsToIgnore);
+0261 
+0262     essentialRxns = prepData.essentialRxns;
+0263     toRev = false(numel(mm.rxns),1);
+0264     %Handle the results from previous steps ("ignore", "exclude", "essential")
+0265     if strcmp(stp.HowToUsePrevResults, 'exclude')
+0266         rxnScores(rxnsTurnedOn) = 0; %This is not used anymore in any step setup.
+0267     elseif strcmp(stp.HowToUsePrevResults, 'essential')
+0268         %Make all reversible reactions turned on in previous steps reversible
+0269         %in the direction that they were previously carrying flux
+0270         
+0271         %first reverse the reactions that need to be reversed
+0272         rev = mm.rev == 1;
+0273         toRev = rxnsTurnedOn & rev & fluxes < 0;
+0274         mm = reverseRxns(mm, mm.rxns(toRev));
+0275         
+0276         %Then make them irreversible
+0277         mm.rev(rxnsTurnedOn) = 0;
+0278         mm.lb(rxnsTurnedOn) = 0;
+0279 
+0280         essentialRxns = unique([prepData.essentialRxns;mm.rxns(rxnsTurnedOn)]);
+0281     end
+0282 
+0283     
+0284     fullMipRes = [];
+0285     lastError = [];
+0286     if ~isempty(proveAbsGap)
+0287         %One solve proven to this fixed absolute gap, replacing the relative-gap
+0288         %escalation below. The final near-zero-objective run of the escalation
+0289         %accepts an arbitrary incumbent within the gap; on genome-scale Human-GEM
+0290         %the step 1 objective of the escalation ended 4.0 to 12.0 units below the
+0291         %proven optimum (raven-toolbox's prove_abs_gap, raven-gecko-parity#104).
+0292         if ~isempty(stp.MILPParams) && isfield(stp.MILPParams{1}, 'TimeLimit')
+0293             params = struct('TimeLimit', stp.MILPParams{1}.TimeLimit);
+0294         else
+0295             params = struct('TimeLimit', 5000);
+0296         end
+0297         params.MIPGap = 0;
+0298         params.MIPGapAbs = proveAbsGap;
+0299         try
+0300             [deletedRxnsInINIT1, metProduction,fullMipRes,rxnsTurnedOn1,fluxes1] = ftINITInternalAlg(mm,rxnScores,metData,essentialRxns,5,stp.AllowMetSecr,stp.PosRevOff,params, [], fluxes, verbose, 0.1, resolveTies, rankKeys, pinFluxes);
+0301             fluxes1(toRev) = -fluxes1(toRev);
+0302             success = true;
+0303             if fullMipRes.stat ~= 1
+0304                 %stat==2 (checkSolution.m): feasible but not proven within the time
+0305                 %limit -- an unproven incumbent, same caveat as the escalation path
+0306                 %below, but disclosed since proveAbsGap explicitly asks for a proof.
+0307                 EM=['ftINIT step ' num2str(initStep) ' hit the time limit before proving proveAbsGap; its kept reactions are an unproven incumbent. Raise TimeLimit, or loosen proveAbsGap.'];
+0308                 warning('RAVEN:warning', '%s', EM);
+0309             end
+0310         catch e
+0311             success = false;
+0312             lastError = e;
+0313         end
+0314     else
+0315         mipGap = 1;
+0316         first = true;
+0317         success = false;
+0318         for rn = 1:length(stp.MILPParams)
+0319             params = stp.MILPParams{rn};
+0320             if ~isfield(params, 'MIPGap')
+0321                 params.MIPGap = 0.0004;
+0322             end
+0323 
+0324             if ~isfield(params, 'TimeLimit')
+0325                 params.TimeLimit = 5000;
+0326             end
+0327 
+0328             if ~first
+0329                 %There is sometimes a problem with that the objective function becomes close to zero,
+0330                 %which leads to that a small percentage of that (which is the MIPGap sent in) is very small
+0331                 %and the MILP hence takes a lot of time to finish. We also therefore use an absolute MIP gap,
+0332                 %converted to a percentage using the last value of the objective function.
+0333                 params.MIPGap = min(max(params.MIPGap, stp.AbsMIPGaps{rn}/abs(lastObjVal)),1);
+0334                 params.seed = 1234;%use another seed, may work better
+0335 
+0336                 if mipGap <= params.MIPGap
+0337                     success = true;
+0338                     break; %we're done - this will not happen the first time
+0339                 else
+0340                     disp(['MipGap too high, trying with a different run. MipGap = ' num2str(mipGap) ' New MipGap Limit = ' num2str(params.MIPGap)])
+0341                 end
+0342             end
+0343 
+0344             first = false;
+0345 
+0346             %now run the MILP
+0347             try
+0348                 %The prodweight for metabolomics is set to 5. This value has not
+0349                 %been evaluated, but is assumed in the test cases - if changed, update the test case
+0350                 startVals = [];
+0351                 if ~isempty(fullMipRes)
+0352                     startVals = fullMipRes.full;
+0353                 end
+0354                 [deletedRxnsInINIT1, metProduction,fullMipRes,rxnsTurnedOn1,fluxes1] = ftINITInternalAlg(mm,rxnScores,metData,essentialRxns,5,stp.AllowMetSecr,stp.PosRevOff,params, startVals, fluxes, verbose, 0.1, resolveTies, rankKeys, pinFluxes);
+0355                 %This is a bit tricky - since we reversed some reactions, those fluxes also need to be reversed
+0356                 fluxes1(toRev) = -fluxes1(toRev);
+0357 
+0358                 mipGap = fullMipRes.mipgap;
+0359                 lastObjVal = fullMipRes.obj;
+0360             catch e
+0361                 mipGap = Inf;
+0362                 lastObjVal = Inf; %we need to set something here, Inf leads to that this doesn't come into play
+0363                 lastError = e; %kept so a throw is not reported as a time-limit miss
+0364             end
+0365 
+0366             success = mipGap <= params.MIPGap;
+0367         end
+0368     end
+0369 
+0370     if ~success
+0371         if ~isempty(lastError)
+0372             %The MILP threw rather than returning a poor gap; reporting this
+0373             %as a time-limit miss sends users off to raise params.TimeLimit
+0374             %for what is often a solver or input problem.
+0375             error('RAVEN:badInput', '%s', ['Failed to find good enough solution within the time frame. The last MILP attempt failed with: ' lastError.message]);
+0376         else
+0377             error('RAVEN:badInput', '%s', ['Failed to find good enough solution within the time frame. MIPGap: ' num2str(mipGap)]);
+0378         end
+0379     end
+0380     
+0381     %save the reactions turned on and their fluxes for the next step
+0382     rxnsTurnedOn = rxnsTurnedOn | rxnsTurnedOn1.';
+0383     %The fluxes are a bit tricky - what if they change direction between the steps?
+0384     %The fluxes are used to determine the direction in which reactions are forced on
+0385     %(to simplify the problem it is good if they are unidirectional).
+0386     %We use the following strategy:
+0387     %1. Use the fluxes from the most recent step.
+0388     %2. If any flux is very low there (i.e. basically zero), use the flux from the previous steps
+0389     %This could in theory cause problems, but seems to work well practically
+0390     fluxesOld = fluxes;
+0391     fluxes = fluxes1;
+0392 end
+0393 
+0394 
+0395 %get the essential rxns
+0396 essential = ismember(prepData.minModel.rxns,prepData.essentialRxns);
+0397 %So, we only add reactions where the linearly merged scores are zero for all linearly dependent reactions
+0398 % (this cannot happen by chance, taken care of in the function groupRxnScores)
+0399 rxnsToIgn = rxnScores == 0; 
+0400 deletedRxnsInINITSel = ~(rxnsTurnedOn | rxnsToIgn | essential);
+0401 deletedRxnsInINIT = prepData.minModel.rxns(deletedRxnsInINITSel);
+0402 
+0403 %Here we need to figure out which original reactions (before the linear merge)
+0404 %that were removed. These are all reactions with the same group ids as the removed reactions
+0405 groupIdsRemoved = prepData.groupIds(ismember(prepData.refModel.rxns, deletedRxnsInINIT)); %can improve this slightly, use sel above
+0406 groupIdsRemoved = groupIdsRemoved(groupIdsRemoved ~= 0);%zero means that the reaction was not grouped, all with zeros are not a group!
+0407 rxnsToRem = union(prepData.refModel.rxns(ismember(prepData.groupIds,groupIdsRemoved)), deletedRxnsInINIT);%make a union here to include the ungrouped (unmerged) as well
+0408 
+0409 initModel = removeReactions(prepData.refModel,rxnsToRem,false,true);
+0410 
+0411 % remove metabolites separately to avoid removing those needed for tasks
+0412 unusedMets = initModel.mets(all(initModel.S == 0,2));
+0413 initModel = removeMets(initModel, setdiff(unusedMets, prepData.essentialMetsForTasks));
+0414 
+0415 %if printReport == true
+0416 %    printScores(initModel,"INIT model statistics",hpaData,transcrData,tissue,celltype);
+0417 %    printScores(removeReactions(cModel,setdiff(cModel.rxns,rxnsToRem),true,true),"Reactions deleted by INIT",hpaData,transcrData,tissue,celltype);
+0418 %end
+0419 
+0420 %The full model has exchange reactions in it. fitTasks with gapFillMode
+0421 %'preMerged' calls ftINITFillGaps, which automatically removes exchange
+0422 %metabolites (because it
+0423 %assumes that the reactions are constrained when appropriate). In this case the
+0424 %uptakes/outputs are retrieved from the task sheet instead. To prevent
+0425 %exchange reactions being used to fill gaps, they are deleted from the
+0426 %reference model here.
+0427 initModel.id = 'INITModel';
+0428 
+0429 %If gaps in the model should be filled using a task list
+0430 if ~isempty(prepData.taskStruct)
+0431     %Remove exchange reactions and reactions already included in the INIT
+0432     %model. Exchange rxns that are essential are kept.
+0433 
+0434     %At this stage the model is fully connected and most of the genes with
+0435     %good scores should have been included. The final gap-filling should
+0436     %take the scores of the genes into account, so that "rather bad"
+0437     %reactions are preferred to "very bad" reactions. However, reactions
+0438     %with positive scores will be included even if they are not connected
+0439     %in the current formulation. Therefore, such reactions will have to be
+0440     %assigned a small negative score instead.
+0441     exchRxns = getExchangeRxns(prepData.refModel);
+0442     refModelNoExc = removeReactions(prepData.refModelWithBM,exchRxns,false,true);
+0443     exchRxns = getExchangeRxns(initModel);
+0444     initModelNoExc = removeReactions(closeModel(initModel),exchRxns,false,true);
+0445     
+0446     if useScoresForTasks == true
+0447         %map the rxn scores to the model without exchange rxns
+0448         [~,ia,ib] = intersect(refModelNoExc.rxns,prepData.refModel.rxns);
+0449         rxnScores2nd = NaN(length(refModelNoExc.rxns),1);
+0450         rxnScores2nd(ia) = origRxnScores(ib);
+0451         %all(rxnScores2nd == refRxnScores);%should be the same, ok!
+0452         [outModel,addedRxnMat] = fitTasks(initModelNoExc,refModelNoExc,[], ...
+0453             'printOutput',true,'rxnScores',min(rxnScores2nd,-0.1), ...
+0454             'taskStructure',prepData.taskStruct,'gapFillMode','preMerged', ...
+0455             'params',paramsFT,'verbose',verbose,'resolveTies',resolveTies);
+0456     else
+0457         [outModel,addedRxnMat] = fitTasks(initModelNoExc,refModelNoExc,[], ...
+0458             'printOutput',true,'rxnScores',[], ...
+0459             'taskStructure',prepData.taskStruct,'gapFillMode','preMerged', ...
+0460             'params',paramsFT,'verbose',verbose,'resolveTies',resolveTies);
+0461     end
+0462     %if printReport == true
+0463     %    printScores(outModel,"Functional model statistics",hpaData,transcrData,tissue,celltype);
+0464     %    printScores(removeReactions(outModel,intersect(outModel.rxns,initModel.rxns),true,true),"Reactions added to perform the tasks",hpaData,transcrData,tissue,celltype);
+0465     %end
+0466     
+0467     addedRxnsForTasks = refModelNoExc.rxns(any(addedRxnMat,2));
+0468 else
+0469     outModel = initModel;
+0470     addedRxnMat = [];
+0471     addedRxnsForTasks = {};
+0472 end
+0473 
+0474 % The model can now perform all the tasks defined in the task list.
+0475 model = outModel;
+0476 
+0477 
+0478 % At this stage the model will contain some exchange reactions but probably
+0479 % not all (and maybe zero). This can be inconvenient, so all exchange
+0480 % reactions from the reference model are added, except for those which
+0481 % involve metabolites that are not in the model.
+0482 
+0483 %Start from the original model, and just remove the reactions that are no longer there (and keep exchange rxns). The model we got out
+0484 %from the problem is not complete, it does not have GRPs etc.
+0485 %The logic below is a bit complicated. We identify the reactions that should be removed from the full model as
+0486 %reactions that have been removed in the init model except the ones that were added back. In addition, we make
+0487 %sure that no exchange rxns are removed - they can be removed in the init model if they were linearly merged with other
+0488 %reactions that were decided to be removed from the model. We want to keep all exchange rxns to make sure the tasks can
+0489 %be performed also without manipulating the b vector in the model (which is what is done in the gap-filling).
+0490 exchRxns = getExchangeRxns(prepData.refModel);
+0491 deletedRxnsInINIT = setdiff(prepData.refModel.rxns,union(union(initModel.rxns, addedRxnsForTasks), exchRxns));
+0492 outModel = removeReactions(prepData.refModel, deletedRxnsInINIT, true, true);
+0493 
+0494 % If requested, attempt to remove negative-score genes from the model,
+0495 % depending on their role (isozyme or complex subunit) in each grRule.
+0496 % See the "removeLowScoreGenes" function more more details, and to adjust
+0497 % any default parameters therein.
+0498 if ( removeGenes )
+0499     [~, geneScores] = scoreModel(outModel,hpaData,transcrData,tissue,celltype);
+0500     outModel = removeLowScoreGenes(outModel,geneScores);
+0501 end
+0502 
+0503 
+0504 model = outModel;
+0505 
+0506 end
+0507 
+0508 %This is for printing a summary of a model
+0509 function [rxnS, geneS] = printScores(model,name,hpaData,transcrData,tissue,celltype)
+0510     [a, b] = scoreModel(model,hpaData,transcrData,tissue,celltype);
+0511     rxnS = mean(a);
+0512     geneS = mean(b,'omitnan');
+0513     fprintf([name ':\n']);
+0514     fprintf(['\t' num2str(numel(model.rxns)) ' reactions, ' num2str(numel(model.genes)) ' genes\n']);
+0515     fprintf(['\tMean reaction score: ' num2str(rxnS) '\n']);
+0516     fprintf(['\tMean gene score: ' num2str(geneS) '\n']);
+0517     fprintf(['\tReactions with positive scores: ' num2str(100*sum(a>0)/numel(a)) '%%\n\n']);
+0518 end
+0519 
+0520 function rxnsToIgnore = getRxnsFromPattern(rxnsToIgnorePattern, prepData)
+0521     rxnsToIgnore = false(length(prepData.toIgnoreExch),1);
+0522     if rxnsToIgnorePattern(1) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreExch; end;
+0523     if rxnsToIgnorePattern(2) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreImportRxns; end;
+0524     if rxnsToIgnorePattern(3) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreSimpleTransp; end;
+0525     if rxnsToIgnorePattern(4) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreAdvTransp; end;
+0526     if rxnsToIgnorePattern(5) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreSpont; end;
+0527     if rxnsToIgnorePattern(6) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreS; end;
+0528     if rxnsToIgnorePattern(7) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreCustomRxns; end;
+0529     if rxnsToIgnorePattern(8) rxnsToIgnore = rxnsToIgnore | prepData.toIgnoreAllWithoutGPRs; end;
+0530 end
+0531
+
Generated by m2html © 2005
+ + \ No newline at end of file diff --git a/docs/html/INIT/ftINITInternalAlg.html b/docs/html/INIT/ftINITInternalAlg.html new file mode 100644 index 00000000..3089d03b --- /dev/null +++ b/docs/html/INIT/ftINITInternalAlg.html @@ -0,0 +1,851 @@ + + + + Description of ftINITInternalAlg + + + + + + + + + +
Home > INIT > ftINITInternalAlg.m
+ + + +

ftINITInternalAlg +

+ +

PURPOSE ^

+
ftINITInternalAlg
+ +

SYNOPSIS ^

+
function [deletedRxns,metProduction,res,turnedOnRxns,fluxes]=ftINITInternalAlg(model,rxnScores,metData,essentialRxns,prodWeight,allowExcretion,remPosRev,params,startVals,fluxes,verbose,forceOn,resolveTies,rankKeys,pinFluxes)
+ +

DESCRIPTION ^

+
 ftINITInternalAlg
+    Run the MILP for one step of ftINIT.
+
+   model           a reference model structure
+   rxnScores       a vector of scores for the reactions in the model.
+                   Positive scores are reactions to keep and negative
+                   scores are reactions to exclude. Reactions with score 0
+                   are not part of the problem
+   metData         boolean matrix with mets as rows and rxns as columns
+                   saying which reaction produces each detected met (optional, default [])
+   essentialRxns   cell array of reactions that are essential and that
+                   have to be in the resulting model. ftINIT passes the
+                   reactions that the tasks require (see fitTasks)
+   prodWeight      a reward for each detected metabolite that has at least
+                   one producing reaction turned on; net production is not
+                   checked. The sum of these rewards and the scores for the
+                   reactions is optimised. The reward gives a reason for
+                   including reactions with a negative score for
+                   connectivity reasons, which gives a more functional
+                   network (default 0.5)
+   allowExcretion  true if excretion of all metabolites is allowed.
+                   This results in fewer reactions being considered
+                   dead-ends, but not every reaction in the resulting model
+                   can carry flux. If this is "false" then the
+                   equality constraints are taken from model.b. If the
+                   input model lacks exchange reactions, set it to true;
+                   with false a large part of the model is excluded for
+                   connectivity reasons
+   remPosRev       If true, the positive reversible reactions are removed from the problem.
+                   This is used in step 1 of ftINIT
+   params          parameters for the MILP, for example MIPGap and TimeLimit
+   startVals       Start values for the MILP. ftINIT passes the result of
+                   the previous run when it reruns the MILP with a higher
+                   MIPGap
+   fluxes          Fluxes from the last run. The flux of an essential
+                   irreversible reaction is forced to at least
+                   min(0.99*|flux|, 0.1), clamped to the reaction's capacity
+                   in the forced direction (its upper bound, as ftINIT
+                   passes reactions in the forward direction)
+   verbose         If true, the MILP progression is shown.
+   forceOn         minimum flux magnitude enforced through a reaction once
+                   it is turned on in the MILP (optional, default 0.1)
+   resolveTies     if true, the MILP's degenerate optimum is solved to a
+                   canonical answer in addition to the solver Seed: the
+                   score objective is held at its optimum, then the count
+                   of "on" removable (negative-score) reactions is
+                   minimised, then, among the sparsest solutions, their
+                   summed reaction-id rank is minimised. Positive-score
+                   reactions have a continuous on/off variable (pinned near
+                   1 by the score objective) and only the negative-score
+                   ones have a binary on/off variable, so only these are
+                   tied. The fluxes are then taken from the
+                   least-total-flux solution for that on/off state and,
+                   among those, from the one with the least id-rank-weighted
+                   backward flux, so the directions and forced magnitudes
+                   the next ftINIT step reads from them are fixed too.
+                   This follows resolve_ties in raven-toolbox
+                   (raven-gecko-parity#104). The phases are skipped when
+                   the primary solve stops at params.TimeLimit, and the
+                   primary solution is kept. Phases 2a and 2b get half of
+                   params.TimeLimit each (rounded down, at least 1 s), the
+                   flux phases the full limit. A warning names each phase
+                   that does not prove optimal or finds no solution; when a
+                   flux phase finds none, the fluxes of the phase before it
+                   are kept (optional, default false)
+   rankKeys        cell array with, for each reaction in model.rxns, the key
+                   that the id-rank phases of resolveTies sort it by
+                   (optional, default model.rxns). ftINIT passes the smallest
+                   original reaction id of each merge group, so the order
+                   does not depend on which member names the merged reaction
+   pinFluxes       if false, resolveTies skips phases 2c and 2d and the
+                   fluxes and metProduction are those of phase 2b
+                   (optional, default true).
+                   ftINIT passes false in its last step, whose fluxes no
+                   later step reads
+
+   deletedRxns     reactions which were deleted by the algorithm (only
+                   rxns included in the problem)
+   metProduction   logical array that indicates which of the metabolites in
+                   metData could be produced (true) or not (false).
+                   Metabolites with a producing reaction that is essential
+                   are not part of the MILP and are not in the array
+   res             The result from the MILP
+   turnedOnRxns    The reactions determined to be present (only 
+                   rxns included in the problem) 
+   fluxes          The fluxes from the MILP
+
+   This function is the implementation of the algorithm. See
+   ftINIT for a higher-level function for model reconstruction. 
+
+ Usage: [deletedRxns,metProduction,res,turnedOnRxns,fluxes]=ftINITInternalAlg(model,...
+           rxnScores,metData,essentialRxns,prodWeight,allowExcretion,...
+           remPosRev,params,startVals,fluxes,verbose,forceOn,resolveTies,...
+           rankKeys,pinFluxes)
+ + +

CROSS-REFERENCE INFORMATION ^

+This function calls: +
    +
+This function is called by: + + + + + +

SOURCE CODE ^

+
0001 function [deletedRxns,metProduction,res,turnedOnRxns,fluxes]=ftINITInternalAlg(model,rxnScores,metData,essentialRxns,prodWeight,allowExcretion,remPosRev,params,startVals,fluxes,verbose,forceOn,resolveTies,rankKeys,pinFluxes)
+0002 % ftINITInternalAlg
+0003 %    Run the MILP for one step of ftINIT.
+0004 %
+0005 %   model           a reference model structure
+0006 %   rxnScores       a vector of scores for the reactions in the model.
+0007 %                   Positive scores are reactions to keep and negative
+0008 %                   scores are reactions to exclude. Reactions with score 0
+0009 %                   are not part of the problem
+0010 %   metData         boolean matrix with mets as rows and rxns as columns
+0011 %                   saying which reaction produces each detected met (optional, default [])
+0012 %   essentialRxns   cell array of reactions that are essential and that
+0013 %                   have to be in the resulting model. ftINIT passes the
+0014 %                   reactions that the tasks require (see fitTasks)
+0015 %   prodWeight      a reward for each detected metabolite that has at least
+0016 %                   one producing reaction turned on; net production is not
+0017 %                   checked. The sum of these rewards and the scores for the
+0018 %                   reactions is optimised. The reward gives a reason for
+0019 %                   including reactions with a negative score for
+0020 %                   connectivity reasons, which gives a more functional
+0021 %                   network (default 0.5)
+0022 %   allowExcretion  true if excretion of all metabolites is allowed.
+0023 %                   This results in fewer reactions being considered
+0024 %                   dead-ends, but not every reaction in the resulting model
+0025 %                   can carry flux. If this is "false" then the
+0026 %                   equality constraints are taken from model.b. If the
+0027 %                   input model lacks exchange reactions, set it to true;
+0028 %                   with false a large part of the model is excluded for
+0029 %                   connectivity reasons
+0030 %   remPosRev       If true, the positive reversible reactions are removed from the problem.
+0031 %                   This is used in step 1 of ftINIT
+0032 %   params          parameters for the MILP, for example MIPGap and TimeLimit
+0033 %   startVals       Start values for the MILP. ftINIT passes the result of
+0034 %                   the previous run when it reruns the MILP with a higher
+0035 %                   MIPGap
+0036 %   fluxes          Fluxes from the last run. The flux of an essential
+0037 %                   irreversible reaction is forced to at least
+0038 %                   min(0.99*|flux|, 0.1), clamped to the reaction's capacity
+0039 %                   in the forced direction (its upper bound, as ftINIT
+0040 %                   passes reactions in the forward direction)
+0041 %   verbose         If true, the MILP progression is shown.
+0042 %   forceOn         minimum flux magnitude enforced through a reaction once
+0043 %                   it is turned on in the MILP (optional, default 0.1)
+0044 %   resolveTies     if true, the MILP's degenerate optimum is solved to a
+0045 %                   canonical answer in addition to the solver Seed: the
+0046 %                   score objective is held at its optimum, then the count
+0047 %                   of "on" removable (negative-score) reactions is
+0048 %                   minimised, then, among the sparsest solutions, their
+0049 %                   summed reaction-id rank is minimised. Positive-score
+0050 %                   reactions have a continuous on/off variable (pinned near
+0051 %                   1 by the score objective) and only the negative-score
+0052 %                   ones have a binary on/off variable, so only these are
+0053 %                   tied. The fluxes are then taken from the
+0054 %                   least-total-flux solution for that on/off state and,
+0055 %                   among those, from the one with the least id-rank-weighted
+0056 %                   backward flux, so the directions and forced magnitudes
+0057 %                   the next ftINIT step reads from them are fixed too.
+0058 %                   This follows resolve_ties in raven-toolbox
+0059 %                   (raven-gecko-parity#104). The phases are skipped when
+0060 %                   the primary solve stops at params.TimeLimit, and the
+0061 %                   primary solution is kept. Phases 2a and 2b get half of
+0062 %                   params.TimeLimit each (rounded down, at least 1 s), the
+0063 %                   flux phases the full limit. A warning names each phase
+0064 %                   that does not prove optimal or finds no solution; when a
+0065 %                   flux phase finds none, the fluxes of the phase before it
+0066 %                   are kept (optional, default false)
+0067 %   rankKeys        cell array with, for each reaction in model.rxns, the key
+0068 %                   that the id-rank phases of resolveTies sort it by
+0069 %                   (optional, default model.rxns). ftINIT passes the smallest
+0070 %                   original reaction id of each merge group, so the order
+0071 %                   does not depend on which member names the merged reaction
+0072 %   pinFluxes       if false, resolveTies skips phases 2c and 2d and the
+0073 %                   fluxes and metProduction are those of phase 2b
+0074 %                   (optional, default true).
+0075 %                   ftINIT passes false in its last step, whose fluxes no
+0076 %                   later step reads
+0077 %
+0078 %   deletedRxns     reactions which were deleted by the algorithm (only
+0079 %                   rxns included in the problem)
+0080 %   metProduction   logical array that indicates which of the metabolites in
+0081 %                   metData could be produced (true) or not (false).
+0082 %                   Metabolites with a producing reaction that is essential
+0083 %                   are not part of the MILP and are not in the array
+0084 %   res             The result from the MILP
+0085 %   turnedOnRxns    The reactions determined to be present (only
+0086 %                   rxns included in the problem)
+0087 %   fluxes          The fluxes from the MILP
+0088 %
+0089 %   This function is the implementation of the algorithm. See
+0090 %   ftINIT for a higher-level function for model reconstruction.
+0091 %
+0092 % Usage: [deletedRxns,metProduction,res,turnedOnRxns,fluxes]=ftINITInternalAlg(model,...
+0093 %           rxnScores,metData,essentialRxns,prodWeight,allowExcretion,...
+0094 %           remPosRev,params,startVals,fluxes,verbose,forceOn,resolveTies,...
+0095 %           rankKeys,pinFluxes)
+0096 
+0097 if nargin < 12 || isempty(forceOn)
+0098     forceOn = 0.1;
+0099 end
+0100 if nargin < 14 || isempty(rankKeys)
+0101     rankKeys = model.rxns;
+0102 end
+0103 if nargin < 13 || isempty(resolveTies)
+0104     resolveTies = false;
+0105 end
+0106 if nargin < 15 || isempty(pinFluxes)
+0107     pinFluxes = true;
+0108 end
+0109 if isempty(essentialRxns)
+0110     essentialRxns={};
+0111 end
+0112 essentialRxns=essentialRxns(:);
+0113 if isempty(prodWeight)
+0114     prodWeight=0.5;
+0115 end
+0116 
+0117 
+0118 %The model should be in the reversible format and all relevant exchange
+0119 %reactions should be open
+0120 if isfield(model,'unconstrained')
+0121     EM='Exchange metabolites are still present in the model. Use simplifyModel if this is not intended';
+0122     warning('RAVEN:warning', '%s', EM);
+0123 end
+0124 
+0125 
+0126 essential = ismember(model.rxns,essentialRxns);
+0127 
+0128 %Some nice to have numbers
+0129 nMets=numel(model.mets);
+0130 nRxns=numel(model.rxns);
+0131 nRxnsWithOnOff = nRxns;
+0132 
+0133 %Reactions with score 0 will just be left in the model, and will not be part of the problem (but can carry flux).
+0134 %It is possible to set score = 0 for e.g. spontaneous reactions, exchange rxns, etc., which may not be that interesting
+0135 %to remove.
+0136 
+0137 %if makeIrrev is on, we can just as well skip all positive reversible rxns - they will be turned on without carrying any flux
+0138 %since they can form a loop within themself (fwd-rev)
+0139 if remPosRev
+0140     rxnScores(rxnScores > 0 & model.rev~=0) = 0;
+0141 end
+0142 
+0143 %Handle metabolomics
+0144 %A problem with the metabolomics is that some of the producer reactions
+0145 %for a metabolite could be excluded from the problem. We solve this by
+0146 %adding them with score 0. They can still be seen as positive, since they
+0147 %either do not matter (there is another producer) or they can contribute to an
+0148 %increased score.
+0149 revRxns=model.rev~=0; 
+0150 essRevRxns = find(revRxns & essential);
+0151 essIrrevRxns = find(~revRxns & essential);
+0152 
+0153 if ~isempty(metData)
+0154     %remove any metData rows that is connected to an essential rxn -
+0155     %these will be on regardless and will cause problems below.
+0156     containsEssential = any(metData(:,essential),2);
+0157     metData = metData(~containsEssential,:);
+0158 end
+0159 
+0160 if ~isempty(metData)
+0161     metRxns = any(metData,1).';
+0162     posRxns = rxnScores > 0 | ((rxnScores == 0) & metRxns);
+0163 else
+0164     posRxns = rxnScores > 0;
+0165 end
+0166 negRxns = rxnScores < 0;
+0167 
+0168 posRevRxns = find(posRxns & revRxns & ~essential);
+0169 negRevRxns = find(negRxns & revRxns & ~essential);
+0170 posIrrevRxns = find(posRxns & ~revRxns & ~essential);
+0171 negIrrevRxns = find(negRxns & ~revRxns & ~essential);
+0172 nPosRev = numel(posRevRxns);
+0173 nNegRev = numel(negRevRxns);
+0174 nPosIrrev = numel(posIrrevRxns);
+0175 nNegIrrev = numel(negIrrevRxns);
+0176 nEssRev = numel(essRevRxns);
+0177 nEssIrrev = numel(essIrrevRxns); %not used, but left for symmetry
+0178 nMetabolMets = size(metData,1);
+0179 
+0180 if ~isempty(metData)
+0181     metNegRev = negRxns & revRxns & ~essential & metRxns;
+0182     metNegIrrev = negRxns & ~revRxns & ~essential & metRxns;
+0183     nMetNegRev = sum(metNegRev);
+0184     nMetNegIrrev = sum(metNegIrrev);
+0185 end
+0186 
+0187 
+0188 milpModel = model;
+0189 
+0190 %These six categories need to be handled separately:
+0191 %
+0192 %PosIrrev (positive reaction score, irreversible):
+0193 %flux >= 0.1*Yi, flux - 0.1*Yi - VPI == 0, 0 <= Yi(onoff) <= 1, 0 <= VPI <= Inf
+0194 %The nice thing with rxns with positive score is that they do not require a boolean. The optimizer will
+0195 %strive for maximizing the Yi here, so it can be a continuous variable, where we just force a flux if
+0196 %the variable is on.
+0197 %PosRev:
+0198 % 1: Split up the flux into positive and negative: flux - vnrp + vnrn == 0, 0 <= vprp,vprn <= Inf.
+0199 % 2. Force one of the fluxes on: vprp + vprn >= 0.1*Yi, vprp + vprn - 0.1*Yi - vprv1 == 0. 0 <= Yi(onoff) <= 1, vprv1 >= 0
+0200 % 3. We need a bool to make sure that one of vprp and vprn are always zero:
+0201 %    vprb E {0,1}:  vprp <= 100*vprb (if bool is 0, vprp is zero): vprp - 100*vprb + vprv2 == 0, vprv2 >= 0
+0202 %    vprn <= (1-vprb)*100 (if bool is one, vprn is zero): vprn + 100*vprb + vprv3 == 0, -100 <= vprv3 <= inf
+0203 %Neg Irrev:
+0204 %For negative scores, we need to use an integer, I see no way around that.
+0205 % flux < 100*Yi, Yi E {0,1}. Flux - 100*Yi + vni == 0
+0206 %Neg Rev:
+0207 %Two cases:
+0208 %A: Without irrev model
+0209 % 1: Split up the flux into positive and negative: flux - vnrp + vnrn == 0, 0 <= vprp,vprn <= Inf.
+0210 % 2: Force the Yi (on/off) var on if the flux is on: vnrp + vnrn <= 0.1 * Yi, Yi E {0,1}: vnrp + vnrn - 0.1 * Yi + vnrv1 == 0, vnrv1 >= 0.
+0211 %B: Irrev model (Not used for now)
+0212 % 1. We now have two reactions, but still just boolean variable. We know that
+0213 %    the flux can only be positive, so we just say that flux1 + flux2 <= 0.1 * Yi, Yi E {0,1}: flux1 + flux2 - 0.1 * Yi + vnrv1 == 0, vnrv1 >= 0.
+0214 %    There is no need to prevent loops - there is no benefit for the objective to generate one, and it does not matter.
+0215 %Ess Irrev (essential rxn, i.e. forced to carry flux, irreversible):
+0216 %flux >= 0.1 - solved with lb = 0.1.
+0217 %Ess Rev (these are not really used):
+0218 % 1: Split up the flux into positive and negative: flux - verp + vern == 0, 0 <= verp,vern <= Inf.
+0219 % 2. Force one of the fluxes on: verp + vern >= 0.1, verp + vern - verv1 == 0. verv1 >= 0.1
+0220 % 3. We need a bool to make sure that one of verp and vern are always zero:
+0221 %    verb E {0,1}:  verp <= 100*verb (if bool is 0, verp is zero): verp - 100*verb + verv2 == 0, verv2 >= 0
+0222 %    vern <= (1-verb)*100 (if bool is one, vern is zero): vern + 100*verb + verv3 == 0, -100 <= verv3 <= inf
+0223 
+0224 % Now metabolomics
+0225 % The basic idea is that the bonus is gotten if any of the producer reactions are on. We can
+0226 % therefore use the "on" variables directly for the reactions that are included. We therefore add one variable per met, mon, which can be
+0227 % continuous and between 0 and 1. We then say that mon <= v1 + v2 + ... + vn, i.e. that
+0228 % mon + mv1 - v1 - v2 ... - vn == 0, 0 <= mon <= 1, 0 <= mv1 <= inf
+0229 % mon gets -prodWeight in the c vector (i.e. objective function)
+0230 % A tricky part is that some of the reactions producing a metabolite are left outside the problem
+0231 % This is solved by moving them into the problem, but with the score 0. They can still be treated as positive
+0232 % reactions. In the end, we are not interested if they are on though, but they may
+0233 % allow for production of a metabolite, giving no benefit of turning on another producer reaction.
+0234 % Another tricky thing is that some metabolite producers have negative score. There is
+0235 % no automatic mechanism for forcing flux on if the variable is on - this is simply
+0236 % not needed for negative variables - unless they are a metabolite producer. We therefore need
+0237 % to add an extra constraint there, similar to the case of positive. It gets a bit complicated.
+0238 % For reversible, we need a bool to make sure that one of vnrp and vnrn stays zero:
+0239 %    vnrbm E {0,1}:  vnrp <= 100*vnrbm (if bool is 0, vnrp is zero): vnrp - 100*vnrbm + vnrvm1 == 0, vnrvm1 >= 0
+0240 %    vnrn <= (1-vnrbm)*100 (if bool is one, vnrn is zero): vnrn + 100*vnrbm + vnrvm2 == 0, -100 <= vnrvm2 <= inf
+0241 % We then also say that vnrp + vnrn >= 0.1*Yi, vnrp + vnrn - 0.1*Yi - vnrvm3 == 0, vnrvm3 >= 0
+0242 % For irreversible, it is fairly straight-forward. We just say that flux >= 0.1*Yi, i.e. flux - 0.1*Yi - vnim == 0, 0 <= vnim <= Inf.
+0243 
+0244 
+0245 %The total matrix then looks like this. Note that we have ordered the categories, two reactions each,
+0246 %in the S matrix to make it easier to follow the figure. In practice, they come in random order, which
+0247 %is why the SEye variable is used further down.
+0248 %all categories below have two columns - the two column starts where label starts
+0249 %since the label cannot fit in two chars, they are on multiple lines
+0250 %
+0251 %The met variables and constraints are not in this figure for practical reasons.
+0252 %
+0253 %                           vprb  vprv3 vnrn  vern  verv2 mv1
+0254 % pi  ni  ei  Ypi Yni vpi vprn  vprv2 vnrp  verp  verb  mon
+0255 %   pr  nr  er  Ypr Ynr vprp  vprv1 vni   vnrv1 verv1 verv3
+0256 % SSSSSSSSSSSS0000000000000000000000000000000000000000000000
+0257 % SSSSSSSSSSSS0000000000000000000000000000000000000000000000 S block
+0258 % SSSSSSSSSSSS0000000000000000000000000000000000000000000000
+0259 % 1           N       -                                      Pos irrev block
+0260 %  1           N       -
+0261 %   1                   - 1                                  Pos rev block 1
+0262 %    1                   - 1
+0263 %               N       1 1   -                              Pos rev block 2
+0264 %                N       1 1   -
+0265 %                       1   M   1                            Pos rev block 3
+0266 %                        1   M   1
+0267 %                         1 C     1                          Pos rev block 4
+0268 %                          1 C     1
+0269 %     1           M                 1                        Neg irrev block
+0270 %      1           M                 1
+0271 %       1                             - 1                    Neg rev block 1
+0272 %        1                             - 1
+0273 %                   M                 1 1 1                  Neg rev block 2
+0274 %                    M                 1 1 1
+0275 %           1                               - 1              Ess rev block 1
+0276 %            1                               - 1
+0277 %                                           1 1 -            Ess rev block 2
+0278 %                                            1 1 -
+0279 %                                           1     M 1        Ess rev block 3
+0280 %                                            1     M 1
+0281 %                                             1   C   1      Ess rev block 4
+0282 %                                              1   C   1
+0283 %             --------                                  1 1  Met block - Here, we assume that all variables support each met.
+0284 %             --------                                   1 1             In practice, fewer of the "on" variables with -1 should be included
+0285 %
+0286 %
+0287 %M = -100
+0288 %N = -0.1
+0289 %D = 0.1
+0290 %C = 100
+0291 %- = -1
+0292 %
+0293 %It looks a bit different for the irrev model, but still quite similar
+0294 
+0295 %build the A matrix
+0296 %S row
+0297 %When forcing on essential rxns, use the flux value of the previous run (set to 0.1 the first time)
+0298 %Do not set it above 0.1, may starve something else out. Leave a margin of 1% from the last run.
+0299 forceOnLim = forceOn;
+0300 forceOnLimEss=min(abs(fluxes)*0.99,0.1);
+0301 forceOnLimEss(essIrrevRxns) = min(forceOnLimEss(essIrrevRxns), milpModel.ub(essIrrevRxns));
+0302 
+0303 % Per-reaction big-M: use each reaction own flux capacity (capped at 1000).
+0304 % This replaces the fixed big-M=100, which underestimates capacity for standard
+0305 % RAVEN models where ub=1000. Must be at least forceOnLim so the constraints are feasible.
+0306 bigMCap = 1000;
+0307 ubSafe = min(milpModel.ub, bigMCap); ubSafe(~isfinite(ubSafe)) = bigMCap;
+0308 lbSafe = min(-milpModel.lb, bigMCap); lbSafe(~isfinite(lbSafe)) = bigMCap; lbSafe = max(lbSafe, 0);
+0309 bigMPosRev   = max(max(ubSafe(posRevRxns),  lbSafe(posRevRxns)),  forceOnLim);
+0310 bigMNegIrrev = max(ubSafe(negIrrevRxns), forceOnLim);
+0311 bigMNegRev   = max(max(ubSafe(negRevRxns),  lbSafe(negRevRxns)),  forceOnLim);
+0312 bigMEssRev   = max(max(ubSafe(essRevRxns),  lbSafe(essRevRxns)),  forceOnLim);
+0313 
+0314 varsPerNegRev = 3;
+0315 
+0316 
+0317 %Figure out the number of variables needed for metabolomics
+0318 if ~isempty(metData)
+0319     nMetVars = 2*size(metData,1) + 4*nMetNegRev + nMetNegIrrev; %mon,mv1; vnrbm,vnrvm1,vnrvm2,vnrvm3; vnim
+0320 else
+0321     nMetVars = 0;
+0322 end
+0323 
+0324 
+0325 sRow = [milpModel.S sparse(nMets, nPosIrrev*2+nPosRev*7+nNegIrrev*2+nNegRev*(1+varsPerNegRev)+nEssRev*6 + nMetVars)];
+0326 sEye = speye(nRxns);
+0327 nYBlock = nPosIrrev+nPosRev+nNegIrrev+nNegRev;
+0328 piRows = [sEye(posIrrevRxns,:) speye(nPosIrrev)*-forceOnLim sparse(nPosIrrev,nPosRev+nNegIrrev+nNegRev) speye(nPosIrrev)*-1 sparse(nPosIrrev,nPosRev*6+nNegIrrev+nNegRev*varsPerNegRev+nEssRev*6+nMetVars)];
+0329 
+0330 prRows1 = [sEye(posRevRxns,:) sparse(nPosRev,nYBlock + nPosIrrev) speye(nPosRev)*-1 speye(nPosRev) sparse(nPosRev,nPosRev*4+nNegIrrev+nNegRev*varsPerNegRev+nEssRev*6+nMetVars)];
+0331 prRows2 = [sparse(nPosRev,nRxns + nPosIrrev) speye(nPosRev)*-forceOnLim sparse(nPosRev,nNegIrrev + nNegRev + nPosIrrev) speye(nPosRev) speye(nPosRev) sparse(nPosRev,nPosRev) speye(nPosRev)*-1 sparse(nPosRev,nPosRev*2+nNegIrrev+nNegRev*varsPerNegRev+nEssRev*6+nMetVars)];
+0332 prRows3 = [sparse(nPosRev,nRxns + nYBlock + nPosIrrev) speye(nPosRev) sparse(nPosRev,nPosRev) spdiags(-bigMPosRev,0,nPosRev,nPosRev) sparse(nPosRev,nPosRev) speye(nPosRev) sparse(nPosRev,nPosRev+nNegIrrev+nNegRev*varsPerNegRev+nEssRev*6+nMetVars)];
+0333 prRows4 = [sparse(nPosRev,nRxns + nYBlock + nPosIrrev + nPosRev) speye(nPosRev) spdiags(bigMPosRev,0,nPosRev,nPosRev) sparse(nPosRev,nPosRev*2) speye(nPosRev) sparse(nPosRev, nNegIrrev+nNegRev*varsPerNegRev+nEssRev*6+nMetVars)];
+0334 
+0335 niRows = [sEye(negIrrevRxns,:) sparse(nNegIrrev,nPosIrrev + nPosRev) spdiags(-bigMNegIrrev,0,nNegIrrev,nNegIrrev) sparse(nNegIrrev,nNegRev + nPosIrrev + nPosRev*6) speye(nNegIrrev) sparse(nNegIrrev,nNegRev*varsPerNegRev + nEssRev*6+nMetVars)];
+0336 
+0337 nrRows1 = [sEye(negRevRxns,:) sparse(nNegRev,nYBlock + nPosIrrev + nPosRev*6 + nNegIrrev) speye(nNegRev)*-1 speye(nNegRev) sparse(nNegRev,nNegRev + nEssRev*6+nMetVars)];
+0338 nrRows2 = [sparse(nNegRev,nRxns + nPosIrrev + nPosRev + nNegIrrev) spdiags(-bigMNegRev,0,nNegRev,nNegRev) sparse(nNegRev,nPosIrrev + nPosRev*6 + nNegIrrev) speye(nNegRev) speye(nNegRev) speye(nNegRev) sparse(nNegRev,nEssRev*6+nMetVars)];
+0339 nrRows = [nrRows1;nrRows2];
+0340 
+0341 erRows1 = [sEye(essRevRxns,:) sparse(nEssRev,nYBlock + nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev*varsPerNegRev) speye(nEssRev)*-1 speye(nEssRev) sparse(nEssRev, nEssRev*4+nMetVars)];
+0342 erRows2 = [sparse(nEssRev,nRxns + nYBlock + nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev*varsPerNegRev) speye(nEssRev) speye(nEssRev) speye(nEssRev)*-1 sparse(nEssRev, nEssRev*3+nMetVars)];
+0343 erRows3 = [sparse(nEssRev,nRxns + nYBlock + nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev*varsPerNegRev) speye(nEssRev) sparse(nEssRev, nEssRev*2) spdiags(-bigMEssRev,0,nEssRev,nEssRev) speye(nEssRev) sparse(nEssRev, nEssRev+nMetVars)];
+0344 erRows4 = [sparse(nEssRev,nRxns + nYBlock + nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev*varsPerNegRev + nEssRev) speye(nEssRev) sparse(nEssRev, nEssRev) spdiags(bigMEssRev,0,nEssRev,nEssRev) sparse(nEssRev, nEssRev) speye(nEssRev) sparse(nEssRev, nMetVars)];
+0345 
+0346 %now the mets
+0347 if ~isempty(metData)
+0348     %Order of rxn "on" vars: Ypi Ypr Yni Ynr. This is a bit messy, since they are not in the
+0349     %same order as the reactions in the S matrix or metData. We therefore resort the vars in
+0350     %metData to match the order of the vars.
+0351     srtMetData = sparse([metData(:,posIrrevRxns) metData(:,posRevRxns) metData(:,negIrrevRxns) metData(:,negRevRxns)]);
+0352     
+0353     %First the setup for giving bonus if the met is included.
+0354     %The mon vars come first followed by the mv1 vars
+0355     metRows1 = [sparse(nMetabolMets,nRxns) -srtMetData sparse(nMetabolMets,  nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev*varsPerNegRev + nEssRev*6) speye(nMetabolMets) speye(nMetabolMets) sparse(nMetabolMets, 4*nMetNegRev + nMetNegIrrev)];
+0356     %Then negative rev:
+0357     % Variable order: eye(vnrbm),eye(vnrvm1),eye(vnrvm2),eye(vnrvm3)
+0358     % vnrp,vnrn are the two first variables among the neg rev vars.
+0359     % For reversible, we need a bool to make sure that one of vnrp and vnrn stays zero:
+0360     %    vnrbm E {0,1}:  vnrp <= 100*vnrbm (if bool is 0, vnrp is zero): vnrp - 100*vnrbm + vnrvm1 == 0, vnrvm1 >= 0 (metRows2)
+0361     %    vnrn <= (1-vnrbm)*100 (if bool is one, vnrn is zero): vnrn + 100*vnrbm + vnrvm2 == 0, -100 <= vnrvm2 <= inf (metRows3)
+0362     % We then also say that vnrp + vnrn >= 0.1*Yi, -0.1*Yi + vnrp + vnrn - vnrvm3 == 0, vnrvm3 >= 0 (metRows4)
+0363     nrEye = speye(nNegRev);
+0364     %Force a column: when no met reaction is negative-reversible, indexing a
+0365     %scalar bigMNegRev with an all-false mask returns a 0-by-0 empty, which
+0366     %the spdiags calls below reject (they need a 0-by-1 column).
+0367     bigMMetNegRev = bigMNegRev(metNegRev(negRevRxns));
+0368     bigMMetNegRev = bigMMetNegRev(:);
+0369     %vnrp - M*vnrbm + vnrvm1 == 0
+0370     metRows2 = [sparse(nMetNegRev,nRxns + nYBlock + nPosIrrev + nPosRev*6 + nNegIrrev) ... %zeros up to vnrp
+0371                 nrEye(metNegRev(negRevRxns),:) ... %vnrp
+0372                 sparse(nMetNegRev, nNegRev*(varsPerNegRev-1) + nEssRev*6 + nMetabolMets*2) ... %zeros up to vnrbm
+0373                 spdiags(-bigMMetNegRev,0,nMetNegRev,nMetNegRev) ... %vnrbm
+0374                 speye(nMetNegRev) ... % vnrvm1
+0375                 sparse(nMetNegRev, nMetNegRev*2 + nMetNegIrrev)];%fill up the rest with zeros
+0376     %vnrn + M*vnrbm + vnrvm2 == 0
+0377     metRows3 = [sparse(nMetNegRev,nRxns + nYBlock + nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev) ... %zeros up to vnrn
+0378                 nrEye(metNegRev(negRevRxns),:) ... %vnrp
+0379                 sparse(nMetNegRev, nNegRev*(varsPerNegRev-2) + nEssRev*6 + nMetabolMets*2) ... %zeros up to vnrbm
+0380                 spdiags(bigMMetNegRev,0,nMetNegRev,nMetNegRev) ... %vnrbm
+0381                 sparse(nMetNegRev, nMetNegRev) ... %zeros up to vnrvm2
+0382                 speye(nMetNegRev) ... % vnrvm2
+0383                 sparse(nMetNegRev, nMetNegRev + nMetNegIrrev)];%fill up the rest with zeros
+0384     %-0.1*Yi + vnrp + vnrn - vnrvm3 == 0
+0385     metRows4 = [sparse(nMetNegRev,nRxns + nPosIrrev + nPosRev + nNegIrrev) ... %zeros up to Yi for neg rev
+0386                 nrEye(metNegRev(negRevRxns),:)*-0.1 ... %Yi for met neg rev
+0387                 sparse(nMetNegRev, nPosIrrev + nPosRev*6 + nNegIrrev) ... %zeros up to vnrp
+0388                 nrEye(metNegRev(negRevRxns),:) ... %vnrp
+0389                 nrEye(metNegRev(negRevRxns),:) ... %vnrn
+0390                 sparse(nMetNegRev, nNegRev*(varsPerNegRev-2) + nEssRev*6 + nMetabolMets*2 + nMetNegRev*3) ...%zeros up to vnrvm3
+0391                 speye(nMetNegRev)*-1 ... % vnrvm3
+0392                 sparse(nMetNegRev, nMetNegIrrev)];%fill up the rest with zeros
+0393     
+0394     %Then negative irrev, i.e. flux - 0.1*Yi - vnim == 0
+0395     niEye = speye(nNegIrrev);
+0396     metRows5 = [sEye(metNegIrrev,:) ... %flux
+0397                 sparse(nMetNegIrrev, nPosIrrev + nPosRev) ... % zeros up to Yi for neg irrev
+0398                 niEye(metNegIrrev(negIrrevRxns),:)*-0.1 ... %Yi for met neg irrev
+0399                 sparse(nMetNegIrrev, nNegRev + nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev*varsPerNegRev + nEssRev*6 + nMetabolMets*2 + 4*nMetNegRev) ... %zeros up to vnim
+0400                 -speye(nMetNegIrrev) ]; %vnim
+0401     metRows = [metRows1;metRows2;metRows3;metRows4;metRows5];
+0402     metVarC = [ones(nMetabolMets,1)*-prodWeight;zeros(nMetVars - nMetabolMets,1)];
+0403     %vnrbm is boolean, so between 0 and 1
+0404     %vnrvm1 >= 0
+0405     %-100 <= vnrvm2 <= inf
+0406     % vnrvm3 >= 0
+0407     %0 <= vnim <= Inf
+0408     metLb = [zeros(nMetabolMets*2 + 2*nMetNegRev,1);-bigMMetNegRev;zeros(nMetNegRev + nMetNegIrrev,1)];
+0409     metUb = [ones(nMetabolMets,1);inf(nMetabolMets,1);ones(nMetNegRev,1);inf(3*nMetNegRev + nMetNegIrrev,1)];
+0410     metVartype = [repmat('C', 1, nMetabolMets*2), ...
+0411                   repmat('B', 1, nMetNegRev), ...
+0412                   repmat('C', 1, 3*nMetNegRev + nMetNegIrrev)];
+0413 
+0414 else
+0415     metRows = [];
+0416     metVarC = [];
+0417     metLb = [];
+0418     metUb = [];
+0419     metVartype = [];
+0420 end
+0421 
+0422 prob.a = [sRow;piRows;prRows1;prRows2;prRows3;prRows4;niRows;nrRows;erRows1;erRows2;erRows3;erRows4;metRows];
+0423 prob.A = prob.a;
+0424 prob.b = zeros(size(prob.A,1),1);
+0425 prob.c = [zeros(nRxns,1);rxnScores(posIrrevRxns)*-1;rxnScores(posRevRxns)*-1;rxnScores(negIrrevRxns)*-1;rxnScores(negRevRxns)*-1;zeros(nPosIrrev + nPosRev*6 + nNegIrrev + nNegRev*varsPerNegRev + nEssRev*6,1);metVarC];
+0426 prob.lb = [milpModel.lb;zeros(nYBlock + nPosIrrev + 5*nPosRev,1);-bigMPosRev;zeros(nNegIrrev+nNegRev*varsPerNegRev+nEssRev*2,1);ones(nEssRev,1)*forceOnLim;zeros(nEssRev*2,1);-bigMEssRev;metLb];
+0427 prob.lb(essIrrevRxns) = forceOnLimEss(essIrrevRxns);%force flux for the ess irrev rxns - this is the only way these are handled
+0428 prob.ub = [milpModel.ub;ones(nYBlock,1);inf(nPosIrrev+nPosRev*2,1);ones(nPosRev,1);inf(3*nPosRev+nNegIrrev+varsPerNegRev*nNegRev+3*nEssRev,1);ones(nEssRev,1);inf(2*nEssRev,1);metUb];
+0429 
+0430 prob.vartype = [repmat('C', 1, nRxns + nPosIrrev + nPosRev), ...
+0431                 repmat('B', 1, nNegIrrev + nNegRev), ...
+0432                 repmat('C', 1, nPosIrrev + 2*nPosRev), ... 
+0433                 repmat('B', 1, nPosRev), ...
+0434                 repmat('C', 1, 3*nPosRev + nNegIrrev + varsPerNegRev*nNegRev + 3*nEssRev), ...
+0435                 repmat('B', 1, nEssRev), ...
+0436                 repmat('C', 1, 2*nEssRev), ...
+0437                 metVartype];
+0438             
+0439 onoffVarInd = (1:(nPosIrrev + nPosRev + nNegIrrev + nNegRev)) + nRxns;
+0440 onoffPosIrrev = onoffVarInd(1:nPosIrrev);
+0441 onoffPosRev = onoffVarInd((1:nPosRev)+nPosIrrev);
+0442 onoffNegIrrev = onoffVarInd((1:nNegIrrev)+nPosIrrev+nPosRev);
+0443 onoffNegRev = onoffVarInd((1:nNegRev)+nPosIrrev+nPosRev+nNegIrrev);
+0444 
+0445 metVarInd = (1:nMetabolMets) + (length(prob.vartype) - nMetVars);
+0446 
+0447 if allowExcretion
+0448     %S*v >= 0: a metabolite may be produced in excess of what is consumed,
+0449     %and the surplus is implicitly excreted. 'L' would say S*v <= 0, which
+0450     %lets a metabolite be consumed without ever being produced -- free
+0451     %uptake, the opposite of excretion, and enough for the MILP to switch a
+0452     %reaction on by conjuring its substrates out of nothing.
+0453     prob.csense = [repmat('G', 1, length(milpModel.mets)), ...
+0454                   repmat('E', 1, length(prob.b) - length(milpModel.mets))];
+0455 else
+0456     %One character per row: optimizeProb maps csense elementwise for glpk and
+0457     %cobra, so a scalar '=' only happens to work on gurobi.
+0458     prob.csense = repmat('E', 1, length(prob.b));
+0459 end
+0460 
+0461 params.intTol = 10^-7; %This value is very important. If set too low
+0462                        %there is a risk that gurobi fails due to numerical
+0463                        %issues - this happened for Gurobi v. 10.0 with TestModelL.
+0464                        %On the other hand, it should not be too large
+0465                        %either. With this value, fluxes of 10-7 can slip
+0466                        %through, which should be fine. Another option if
+0467                        %this becomes a problem is to set NumericFocus=2,
+0468                        %which makes the solver slower but fixes the issue
+0469                        %with TestModelL.
+0470 
+0471 prob.osense = 1; %minimize
+0472 
+0473 if ~isempty(startVals)
+0474      prob.start = startVals; %This doesn't work...
+0475 end
+0476 
+0477 res=optimizeProb(prob,params,verbose);
+0478 
+0479 
+0480 if ~checkSolution(res)
+0481     if res.hitTimeLimit
+0482         EM='Time limit reached without finding a solution. Try increasing the TimeLimit parameter.';
+0483     else
+0484         EM='The problem is infeasible';
+0485     end
+0486     error('RAVEN:badInput', '%s', EM);
+0487 end
+0488 
+0489 if resolveTies && (nNegIrrev + nNegRev) > 0 && ~res.hitTimeLimit
+0490     %Skipped when the primary solve stopped at the time limit: the floor
+0491     %constraint would pin an unproven incumbent, so the phases cannot select
+0492     %among true optima.
+0493     %This MILP is highly degenerate: many reaction subsets reach the same
+0494     %score optimum, and the solver returns an arbitrary one -- reproducible
+0495     %for a fixed solver build + Seed, but fragile to a solver version bump
+0496     %(raven-gecko-parity#104). This runs a lexicographic phase 2, holding
+0497     %the score objective at its optimum with a floor constraint: first
+0498     %minimise the count of "on" removable reactions (the sparsest optimum),
+0499     %then, among the sparsest, minimise their summed reaction-id rank
+0500     %(prefer lower ids). Only the negative-score "on" variables (onoffNegIrrev,
+0501     %onoffNegRev) are a genuine tie to resolve: positive-score ones are
+0502     %continuous, pinned near 1 by the score objective rather than a free
+0503     %binary choice. Ports raven-toolbox's _resolve_ties (raven-toolbox#114).
+0504     negCols = [onoffNegIrrev, onoffNegRev];
+0505     negRxnIdx = [negIrrevRxns; negRevRxns];
+0506     primaryObj = full(prob.c' * res.full);
+0507     tol = max(abs(primaryObj)*1e-7, 1e-7);
+0508 
+0509     floorRow = prob.c';
+0510     tieProb = prob;
+0511     tieProb.A = [prob.A; floorRow];
+0512     tieProb.a = tieProb.A;
+0513     tieProb.b = [prob.b; primaryObj+tol];
+0514     tieProb.csense = [prob.csense 'L'];
+0515 
+0516     %An integer objective needs only a cheap absolute-gap proof, not a tiny
+0517     %relative one -- mirrors raven-toolbox's own MIPGap=0/MIPGapAbs=0.4 for
+0518     %the same two phases.
+0519     tieParams = params;
+0520     tieParams.MIPGap = 0;
+0521     tieParams.MIPGapAbs = 0.4;
+0522     %Phases 2a and 2b get half of the time limit each, as in raven-toolbox.
+0523     if isfield(tieParams,'TimeLimit')
+0524         tieParams.TimeLimit = max(1,floor(tieParams.TimeLimit*0.5));
+0525     end
+0526 
+0527     unproven = {};
+0528 
+0529     %Phase 2a: fewest "on" removable reactions among the score-optimal solutions.
+0530     countObj = zeros(size(prob.c));
+0531     countObj(negCols) = 1;
+0532     tieProb.c = countObj;
+0533     res2a = optimizeProb(tieProb,tieParams,verbose);
+0534     [isFeasible2a, isOptimal2a] = checkSolution(res2a);
+0535     if ~isOptimal2a
+0536         unproven{end+1} = 'phase2a-parsimony';
+0537     end
+0538     if isFeasible2a
+0539         kmin = full(countObj'*res2a.full);
+0540 
+0541         %Phase 2b: among the sparsest, prefer lower reaction ids -- a
+0542         %deterministic tie-break independent of solver/seed.
+0543         countRow = sparse(1,size(tieProb.A,2));
+0544         countRow(negCols) = 1;
+0545         tieProb2b = tieProb;
+0546         tieProb2b.A = [tieProb.A; countRow];
+0547         tieProb2b.a = tieProb2b.A;
+0548         tieProb2b.b = [tieProb.b; kmin+0.5];
+0549         tieProb2b.csense = [tieProb.csense 'L'];
+0550 
+0551         [~, sortOrder] = sort(rankKeys(negRxnIdx));
+0552         ranks = zeros(numel(negRxnIdx),1);
+0553         ranks(sortOrder) = (1:numel(negRxnIdx))';
+0554         idObj = zeros(size(prob.c));
+0555         idObj(negCols) = ranks;
+0556         tieProb2b.c = idObj;
+0557         res2b = optimizeProb(tieProb2b,tieParams,verbose);
+0558         [isFeasible2b, isOptimal2b] = checkSolution(res2b);
+0559         if ~isOptimal2b
+0560             unproven{end+1} = 'phase2b-idrank';
+0561         end
+0562         if isFeasible2b
+0563             %Adopt the tie-resolved solution for the on/off read-out below.
+0564             res = res2b;
+0565 
+0566             %Phases 2c and 2d change the fluxes, not the on/off state; the next
+0567             %ftINIT step reads the fluxes, and pinFluxes is false when no step
+0568             %follows.
+0569             if pinFluxes
+0570                 %Phase 2c: the least total absolute flux for this on/off state.
+0571                 %The next step forces each turned-on reaction in the direction
+0572                 %of its flux, at min(0.99*|flux|,0.1), so the flux read-out
+0573                 %comes from a minimal-flux solution instead of an arbitrary
+0574                 %vertex. A binary on/off variable is fixed at its value; a
+0575                 %continuous one that is on keeps at least its value, one that
+0576                 %is off at most its value. Directions of reversible scored
+0577                 %reactions stay free, so this is still a MILP.
+0578                 %It stops at a relative or absolute gap of 1e-6, as raven-toolbox's
+0579                 %phase 2c.
+0580                 nCols = numel(prob.c);
+0581                 minProb = tieProb2b;
+0582                 if isfield(minProb,'start')
+0583                     minProb = rmfield(minProb,'start');
+0584                 end
+0585                 x = res2b.full;
+0586                 negVal = round(x(negCols));
+0587                 minProb.lb(negCols) = negVal;
+0588                 minProb.ub(negCols) = negVal;
+0589                 posCols = [onoffPosIrrev, onoffPosRev];
+0590                 posVal = min(max(x(posCols),0),1);
+0591                 posOn = posVal >= 0.5;
+0592                 minProb.lb(posCols(posOn)) = posVal(posOn);
+0593                 minProb.ub(posCols(~posOn)) = posVal(~posOn);
+0594                 %|v| is v for a forward-only flux, -v for a backward-only one,
+0595                 %and an added t >= v, t >= -v otherwise.
+0596                 fwdOnly = minProb.lb(1:nRxns) >= 0;
+0597                 bwdOnly = ~fwdOnly & minProb.ub(1:nRxns) <= 0;
+0598                 tRxns = find(~fwdOnly & ~bwdOnly);
+0599                 nT = numel(tRxns);
+0600                 selV = sparse(1:nT, tRxns, 1, nT, nCols);
+0601                 minProb.A = [minProb.A sparse(size(minProb.A,1),nT); -selV speye(nT); selV speye(nT)];
+0602                 minProb.a = minProb.A;
+0603                 minProb.b = [minProb.b; zeros(2*nT,1)];
+0604                 minProb.csense = [minProb.csense repmat('G',1,2*nT)];
+0605                 minProb.lb = [minProb.lb; zeros(nT,1)];
+0606                 minProb.ub = [minProb.ub; inf(nT,1)];
+0607                 minProb.vartype = [minProb.vartype repmat('C',1,nT)];
+0608                 minProb.c = [zeros(nCols,1); ones(nT,1)];
+0609                 minProb.c(fwdOnly) = 1;
+0610                 minProb.c(bwdOnly) = -1;
+0611                 minParams = params;
+0612                 minParams.MIPGap = 1e-6;
+0613                 minParams.MIPGapAbs = 1e-6;
+0614                 res2c = optimizeProb(minProb,minParams,verbose);
+0615                 [isFeasible2c, isOptimal2c] = checkSolution(res2c);
+0616                 if ~isOptimal2c
+0617                     unproven{end+1} = 'phase2c-minflux';
+0618                 end
+0619                 if isFeasible2c
+0620                     %Only the fluxes change; the status stays that of phase 2b.
+0621                     res.full = res2c.full(1:nCols);
+0622 
+0623                     %Phase 2d: the least total flux can leave a direction open
+0624                     %when two directions carry the same total flux, for example
+0625                     %a pair of reversible reactions closing a loop either way.
+0626                     %Keep the on/off state of phase 2c, hold the total absolute
+0627                     %flux at most at its phase-2c value plus
+0628                     %max(1e-7*total, 1e-6), and minimise the backward flux
+0629                     %max(0,-v) of the reactions whose flux can take either sign,
+0630                     %weighted by id rank from the end: sorted by rankKeys, the
+0631                     %first of n reactions gets weight n and the last 1, so a
+0632                     %reaction with a lower id prefers its forward direction.
+0633                     if nT > 0
+0634                         total = minProb.c' * res2c.full;
+0635                         dirProb = minProb;
+0636                         nCols2c = numel(minProb.c);
+0637                         [~, ord] = sort(rankKeys(tRxns));
+0638                         w = zeros(nT,1);
+0639                         w(ord) = (nT:-1:1)';
+0640                         dirProb.A = [minProb.A sparse(size(minProb.A,1),nT); ...
+0641                                      minProb.c' sparse(1,nT); ...
+0642                                      selV sparse(nT,nT) speye(nT)];
+0643                         dirProb.a = dirProb.A;
+0644                         dirProb.b = [minProb.b; total + max(abs(total)*1e-7, 1e-6); zeros(nT,1)];
+0645                         dirProb.csense = [minProb.csense 'L' repmat('G',1,nT)];
+0646                         dirProb.lb = [minProb.lb; zeros(nT,1)];
+0647                         dirProb.ub = [minProb.ub; inf(nT,1)];
+0648                         dirProb.vartype = [minProb.vartype repmat('C',1,nT)];
+0649                         dirProb.c = [zeros(nCols2c,1); w];
+0650                         %The phase-2c solution is the MIP start: the tight cap on
+0651                         %the total flux leaves the solver's heuristics little room
+0652                         %to find a first solution.
+0653                         dirProb.start = [res2c.full; max(0, -res2c.full(tRxns))];
+0654                         res2d = optimizeProb(dirProb,minParams,verbose);
+0655                         [isFeasible2d, isOptimal2d] = checkSolution(res2d);
+0656                         if ~isOptimal2d
+0657                             unproven{end+1} = 'phase2d-direction';
+0658                         end
+0659                         if isFeasible2d
+0660                             res.full = res2d.full(1:nCols);
+0661                         end
+0662                     end
+0663                 end
+0664             end
+0665         end
+0666     end
+0667     if ~isempty(unproven)
+0668         EM = ['ftINITInternalAlg tie resolution did not converge (' strjoin(unproven,', ') '): the selection among equally score-optimal solutions is itself an unproven incumbent, so resolveTies has reduced but not removed the run-to-run spread. Raise params.TimeLimit for a proven tie-break.'];
+0669         warning('RAVEN:warning', '%s', EM);
+0670     end
+0671 end
+0672 
+0673 %get the on/off vals
+0674 onoff = ones(nRxnsWithOnOff,1);%standard is on, i.e. all reactions not included in the problem + ess is on
+0675 onoff(posIrrevRxns) = res.full(onoffPosIrrev);
+0676 onoff(posRevRxns) = res.full(onoffPosRev);
+0677 onoff(negIrrevRxns) = res.full(onoffNegIrrev);
+0678 onoff(negRevRxns) = res.full(onoffNegRev);
+0679 
+0680 onoff2 = zeros(nRxnsWithOnOff,1);%standard is off, i.e. all reactions not included in the problem + ess is off
+0681 onoff2(posIrrevRxns) = res.full(onoffPosIrrev);
+0682 onoff2(posRevRxns) = res.full(onoffPosRev);
+0683 onoff2(negIrrevRxns) = res.full(onoffNegIrrev);
+0684 onoff2(negRevRxns) = res.full(onoffNegRev);
+0685 
+0686 
+0687 %investigate a bit - this code could be used for fault finding. We have
+0688 %this code commented out, but it is definitely worth testing this if for example
+0689 %the solver is changed.
+0690 %problematic = onoff < 0.99 & onoff > 0.01 & (rxnScores ~= 0).';
+0691 %if sum(problematic) > 0
+0692 %    disp("There are reactions that are in between on and off in the MILP. This may be due to the MILP parameter settings, the tolerances may be too large.")
+0693 %    disp(["No problematic reactions: ", num2str(sum(problematic))])
+0694 %end
+0695 
+0696 %so, it is only positive that are problematic. Likely because the 0.1 is too large. Test to change to 0.01
+0697 %unique(onoff(onoff < 0.99 & onoff > 0.01))
+0698 
+0699 %Get all reactions used in the irreversible model
+0700 %The reaction score check is for metabolomics - we add those reactions as
+0701 %positive even though the score is 0. And, we do not want them included in the
+0702 %results.
+0703 deletedRxns=(onoff < 0.5).' & (rxnScores ~= 0).'; 
+0704 turnedOnRxns=(onoff2 >= 0.5).' & (rxnScores ~= 0).';
+0705 
+0706 fluxes = res.full(1:nRxns);
+0707 
+0708 %extract the met data
+0709 metProduction = logical(round(res.full(metVarInd)));
+0710 
+0711 end
+
Generated by m2html © 2005
+ + \ No newline at end of file diff --git a/doc/INIT/getINITSteps.html b/docs/html/INIT/getINITSteps.html similarity index 51% rename from doc/INIT/getINITSteps.html rename to docs/html/INIT/getINITSteps.html index 750e07a3..265c66f4 100644 --- a/doc/INIT/getINITSteps.html +++ b/docs/html/INIT/getINITSteps.html @@ -29,38 +29,36 @@

SYNOPSIS ^DESCRIPTION ^

 getINITSteps  Define the steps used as input to ftINIT.
 
- Defines the steps in the way ftINIT is run. Note that all combinations of
- steps will not work. Avoid "exclude" if you want to define new
- ways to run the algorithm.
+ Defines the steps in the way ftINIT is run. Not all combinations of steps
+ work. A new series that sets the HowToUsePrevResults option to "exclude",
+ which no predefined series uses, may not work.
 
  Name-Value Arguments
  --------------------
  metsToIgnore : struct
      structure describing mets that can be removed from the model before
-     running ftINIT, such as water etc. With fields:
+     running ftINIT, such as water and other ubiquitous metabolites. With fields:
 
      - simpleMets.mets : names of metabolites to remove.
      - simpleMets.compsToKeep : compartments for which metabolites are kept.
 
-     (default []).
+     (default []). Ignored for series "full".
  series : char
      describes the way to run ftINIT (default "1+1"):
 
-     - "1+1" : standard behavior. Step 1 and 2 described in the paper are
+     - "1+1" : standard behaviour. Step 1 and 2 described in the paper are
        merged into 1.
      - "2+1" : the 3-step procedure described in the paper. Faster and
-       slightly less accurate than "1+1 steps".
-     - "1+0" : same as "1+1 steps", but skips step 3 described in the paper.
-       This will result in a model including a lot of reactions without GPRs.
-       It is particularly useful for structural comparison, since the
-       reactions removed in step 3 may be a bit random and does not really add
-       any extra information. Faster than "1+1 steps".
-     - "2+0" : same as "2+1 steps", but skips step 3 described in the paper.
-       Faster and slightly less accurate than "1+0 steps", but will yield
-       similar results.
-     - "full" : 1-step run - similar to the old tINIT version but without
-       simplifications. Accurate, but very slow. This is mainly used for
-       testing purposes.
+       slightly less accurate than "1+1".
+     - "1+0" : same as "1+1", but skips step 3 described in the paper.
+       The model includes many reactions without GPRs. The series suits
+       structural comparison, because the reactions removed in step 3 can
+       be arbitrary and add no information. Faster than "1+1".
+     - "2+0" : same as "2+1", but skips step 3 described in the paper.
+       Faster and slightly less accurate than "1+0", with similar results.
+     - "full" : 1-step run, similar to tINIT, without the model
+       simplifications that ftINIT applies in the other series. Accurate, but
+       slow. This series is used for testing.
 
  Returns
  -------
@@ -83,128 +81,126 @@ 

SOURCE CODE ^
0001 function steps = getINITSteps(varargin)
 0002 % getINITSteps  Define the steps used as input to ftINIT.
 0003 %
-0004 % Defines the steps in the way ftINIT is run. Note that all combinations of
-0005 % steps will not work. Avoid "exclude" if you want to define new
-0006 % ways to run the algorithm.
+0004 % Defines the steps in the way ftINIT is run. Not all combinations of steps
+0005 % work. A new series that sets the HowToUsePrevResults option to "exclude",
+0006 % which no predefined series uses, may not work.
 0007 %
 0008 % Name-Value Arguments
 0009 % --------------------
 0010 % metsToIgnore : struct
 0011 %     structure describing mets that can be removed from the model before
-0012 %     running ftINIT, such as water etc. With fields:
+0012 %     running ftINIT, such as water and other ubiquitous metabolites. With fields:
 0013 %
 0014 %     - simpleMets.mets : names of metabolites to remove.
 0015 %     - simpleMets.compsToKeep : compartments for which metabolites are kept.
 0016 %
-0017 %     (default []).
+0017 %     (default []). Ignored for series "full".
 0018 % series : char
 0019 %     describes the way to run ftINIT (default "1+1"):
 0020 %
-0021 %     - "1+1" : standard behavior. Step 1 and 2 described in the paper are
+0021 %     - "1+1" : standard behaviour. Step 1 and 2 described in the paper are
 0022 %       merged into 1.
 0023 %     - "2+1" : the 3-step procedure described in the paper. Faster and
-0024 %       slightly less accurate than "1+1 steps".
-0025 %     - "1+0" : same as "1+1 steps", but skips step 3 described in the paper.
-0026 %       This will result in a model including a lot of reactions without GPRs.
-0027 %       It is particularly useful for structural comparison, since the
-0028 %       reactions removed in step 3 may be a bit random and does not really add
-0029 %       any extra information. Faster than "1+1 steps".
-0030 %     - "2+0" : same as "2+1 steps", but skips step 3 described in the paper.
-0031 %       Faster and slightly less accurate than "1+0 steps", but will yield
-0032 %       similar results.
-0033 %     - "full" : 1-step run - similar to the old tINIT version but without
-0034 %       simplifications. Accurate, but very slow. This is mainly used for
-0035 %       testing purposes.
-0036 %
-0037 % Returns
-0038 % -------
-0039 % steps : cell
-0040 %     cell array of steps, used as input to ftINIT.
-0041 p=parseRAVENargs(varargin, {'metsToIgnore',[]; 'series','1+1'});
-0042 metsToIgnore=p.metsToIgnore;
-0043 series=p.series;
-0044 
-0045 if strcmp(series,'1+1') %step 1 and 2 are joined
-0046     params1 = struct();
-0047     params1.MIPGap = 0.0004;
-0048     params1.TimeLimit = 120;
-0049     params2 = struct();
-0050     params2.MIPGap = 0.0030;
-0051     params2.TimeLimit = 5000;
-0052     params = {params1;params2};
-0053     params3 = struct();
-0054     params3.MIPGap = 0.0004;
-0055     params3.TimeLimit = 5;
-0056     paramsStep3 = {params3;params1;params2};
-0057     %The paramsStep3 involves a quick first run. The objective value is often
-0058     %small in the third step (~800), and 0.0004 of that is a very small number
-0059     %With this first step, the rough value of the objective function will be
-0060     %estimated, which will generate an absolute MIPGap limit that is much larger
-0061     %for the second iteration.
-0062     
-0063     steps = { ...
-0064         INITStepDesc(false, false, 'ignore', [1,1,1,1,1,1,1,0], metsToIgnore, params, {10;20}); ...
-0065         INITStepDesc(false, false, 'essential', [1,0,0,0,1,0,0,0], metsToIgnore, paramsStep3, {10;10;20}); ...
-0066         };
-0067 elseif strcmp(series,'2+1') %the 3 step process described in the ftINIT paper
-0068     params1 = struct();
-0069     params1.MIPGap = 0.0004;
-0070     params1.TimeLimit = 120;
-0071     params2 = struct();
-0072     params2.MIPGap = 0.0030;
-0073     params2.TimeLimit = 5000;
-0074     params = {params1;params2};
-0075     params3 = struct();
-0076     params3.MIPGap = 0.0004;
-0077     params3.TimeLimit = 5;
-0078     paramsStep3 = {params3;params1;params2};
-0079     %The paramsStep3 involves a quick first run. The objective value is often
-0080     %small in the third step (~800), and 0.0004 of that is a very small number
-0081     %With this first step, the rough value of the objective function will be
-0082     %estimated, which will generate an absolute MIPGap limit that is much larger
-0083     %for the second iteration.
-0084     
-0085     steps = { ...
-0086         INITStepDesc(true, true, 'ignore', [1,1,1,1,1,1,1,0], metsToIgnore, params, {10;20}); ...
-0087         INITStepDesc(false, false, 'essential', [1,1,1,1,1,1,1,0], metsToIgnore, params, {10;20}); ...
-0088         INITStepDesc(false, false, 'essential', [1,0,0,0,1,0,0,0], metsToIgnore, paramsStep3, {10;10;20}); ...
-0089         };
-0090 elseif strcmp(series,'1+0') %Joins step 1 and 2, skips step 3
-0091     params1 = struct();
-0092     params1.MIPGap = 0.0004;
-0093     params1.TimeLimit = 120;
-0094     params2 = struct();
-0095     params2.MIPGap = 0.0030;
-0096     params2.TimeLimit = 5000;
-0097     params = {params1;params2};
-0098     steps = { ...
-0099         INITStepDesc(false, false, 'ignore', [1,1,1,1,1,1,1,0], metsToIgnore, params, {10;20}); ...
-0100         };
-0101 elseif strcmp(series,'2+0') %Skips step 3
-0102     params1 = struct();
-0103     params1.MIPGap = 0.0004;
-0104     params1.TimeLimit = 120;
-0105     params2 = struct();
-0106     params2.MIPGap = 0.0030;
-0107     params2.TimeLimit = 5000;
-0108     params = {params1;params2};
-0109     steps = { ...
-0110         INITStepDesc(true, true, 'ignore', [1,1,1,1,1,1,1,0], metsToIgnore, params, {10;20}); ...
-0111         INITStepDesc(false, false, 'essential', [1,1,1,1,1,1,1,0], metsToIgnore, params, {10;20}); ...
-0112         };
-0113 elseif strcmp(series,'full') %Just one run, slow on large models, but this is the 'perfect' setup
-0114     params1 = struct();
-0115     params1.MIPGap = 0.0004;
-0116     params1.TimeLimit = 10000;
-0117     params = {params1};
-0118     steps = { ...
-0119         INITStepDesc(false, false, 'ignore', [0,0,0,0,0,0,0,0], [], params) ...
-0120         };
-0121 else
-0122     error('RAVEN:badInput', '%s', ['Invalid series in getINITSteps: ' series])
-0123 end
-0124 
-0125 end

+0024 % slightly less accurate than "1+1". +0025 % - "1+0" : same as "1+1", but skips step 3 described in the paper. +0026 % The model includes many reactions without GPRs. The series suits +0027 % structural comparison, because the reactions removed in step 3 can +0028 % be arbitrary and add no information. Faster than "1+1". +0029 % - "2+0" : same as "2+1", but skips step 3 described in the paper. +0030 % Faster and slightly less accurate than "1+0", with similar results. +0031 % - "full" : 1-step run, similar to tINIT, without the model +0032 % simplifications that ftINIT applies in the other series. Accurate, but +0033 % slow. This series is used for testing. +0034 % +0035 % Returns +0036 % ------- +0037 % steps : cell +0038 % cell array of steps, used as input to ftINIT. +0039 p=parseRAVENargs(varargin, {'metsToIgnore',[]; 'series','1+1'}); +0040 metsToIgnore=p.metsToIgnore; +0041 series=p.series; +0042 +0043 if strcmp(series,'1+1') %step 1 and 2 are joined +0044 params1 = struct(); +0045 params1.MIPGap = 0.0004; +0046 params1.TimeLimit = 120; +0047 params2 = struct(); +0048 params2.MIPGap = 0.0030; +0049 params2.TimeLimit = 5000; +0050 params = {params1;params2}; +0051 params3 = struct(); +0052 params3.MIPGap = 0.0004; +0053 params3.TimeLimit = 5; +0054 paramsStep3 = {params3;params1;params2}; +0055 %The paramsStep3 involves a quick first run. The objective value is often +0056 %small in the third step (~800), and 0.0004 of that is a very small number +0057 %With this first step, the rough value of the objective function will be +0058 %estimated, which will generate an absolute MIPGap limit that is much larger +0059 %for the second iteration. +0060 +0061 steps = { ... +0062 INITStepDesc(false, false, 'ignore', [1,1,1,1,1,1,1,0], metsToIgnore, params, {10;20}); ... +0063 INITStepDesc(false, false, 'essential', [1,0,0,0,1,0,0,0], metsToIgnore, paramsStep3, {10;10;20}); ... +0064 }; +0065 elseif strcmp(series,'2+1') %the 3 step process described in the ftINIT paper +0066 params1 = struct(); +0067 params1.MIPGap = 0.0004; +0068 params1.TimeLimit = 120; +0069 params2 = struct(); +0070 params2.MIPGap = 0.0030; +0071 params2.TimeLimit = 5000; +0072 params = {params1;params2}; +0073 params3 = struct(); +0074 params3.MIPGap = 0.0004; +0075 params3.TimeLimit = 5; +0076 paramsStep3 = {params3;params1;params2}; +0077 %The paramsStep3 involves a quick first run. The objective value is often +0078 %small in the third step (~800), and 0.0004 of that is a very small number +0079 %With this first step, the rough value of the objective function will be +0080 %estimated, which will generate an absolute MIPGap limit that is much larger +0081 %for the second iteration. +0082 +0083 steps = { ... +0084 INITStepDesc(true, true, 'ignore', [1,1,1,1,1,1,1,0], metsToIgnore, params, {10;20}); ... +0085 INITStepDesc(false, false, 'essential', [1,1,1,1,1,1,1,0], metsToIgnore, params, {10;20}); ... +0086 INITStepDesc(false, false, 'essential', [1,0,0,0,1,0,0,0], metsToIgnore, paramsStep3, {10;10;20}); ... +0087 }; +0088 elseif strcmp(series,'1+0') %Joins step 1 and 2, skips step 3 +0089 params1 = struct(); +0090 params1.MIPGap = 0.0004; +0091 params1.TimeLimit = 120; +0092 params2 = struct(); +0093 params2.MIPGap = 0.0030; +0094 params2.TimeLimit = 5000; +0095 params = {params1;params2}; +0096 steps = { ... +0097 INITStepDesc(false, false, 'ignore', [1,1,1,1,1,1,1,0], metsToIgnore, params, {10;20}); ... +0098 }; +0099 elseif strcmp(series,'2+0') %Skips step 3 +0100 params1 = struct(); +0101 params1.MIPGap = 0.0004; +0102 params1.TimeLimit = 120; +0103 params2 = struct(); +0104 params2.MIPGap = 0.0030; +0105 params2.TimeLimit = 5000; +0106 params = {params1;params2}; +0107 steps = { ... +0108 INITStepDesc(true, true, 'ignore', [1,1,1,1,1,1,1,0], metsToIgnore, params, {10;20}); ... +0109 INITStepDesc(false, false, 'essential', [1,1,1,1,1,1,1,0], metsToIgnore, params, {10;20}); ... +0110 }; +0111 elseif strcmp(series,'full') %Just one run, slow on large models, but this is the 'perfect' setup +0112 params1 = struct(); +0113 params1.MIPGap = 0.0004; +0114 params1.TimeLimit = 10000; +0115 params = {params1}; +0116 steps = { ... +0117 INITStepDesc(false, false, 'ignore', [0,0,0,0,0,0,0,0], [], params) ... +0118 }; +0119 else +0120 error('RAVEN:badInput', '%s', ['Invalid series in getINITSteps: ' series]) +0121 end +0122 +0123 end

Generated by m2html © 2005
\ No newline at end of file diff --git a/doc/INIT/groupRxnScores.html b/docs/html/INIT/groupRxnScores.html similarity index 67% rename from doc/INIT/groupRxnScores.html rename to docs/html/INIT/groupRxnScores.html index c18fc74a..6d52cf5e 100644 --- a/doc/INIT/groupRxnScores.html +++ b/docs/html/INIT/groupRxnScores.html @@ -28,9 +28,9 @@

SYNOPSIS ^DESCRIPTION ^

 groupRxnScores
- This function sums up the reaction scores for all reactions that were merged 
- into one by the linear merge.
- 
+ Sums the reaction scores of all reactions that were merged into one by the
+ linear merge.
+
  model          The model with linearly merged rxns.
  origRxnScores  The rxnScores from the model before the linear merge.
  origRxnIds     The rxn ids of the model before the linear merge.
@@ -39,10 +39,11 @@ 

DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

@@ -59,8 +60,8 @@

CROSS-REFERENCE INFORMATION ^
 <h2><a name=SOURCE CODE ^

0001 function newRxnScores = groupRxnScores(model, origRxnScores, origRxnIds, groupIds, origRxnsToZero)
 0002 % groupRxnScores
-0003 % This function sums up the reaction scores for all reactions that were merged
-0004 % into one by the linear merge.
+0003 % Sums the reaction scores of all reactions that were merged into one by the
+0004 % linear merge.
 0005 %
 0006 % model          The model with linearly merged rxns.
 0007 % origRxnScores  The rxnScores from the model before the linear merge.
@@ -70,39 +71,40 @@ 

SOURCE CODE ^% the reaction was not merged with any other reaction. A 0012 % non-zero integer means the reaction was merged with all 0013 % other rxns sharing the same integer. -0014 % origRxnsToZero A logical vector saying which of the original rxns that should not +0014 % origRxnsToZero A logical vector saying which of the original rxns should not 0015 % be part of the problem; the score for these reactions is set to 0. -0016 % To keep 0 uniquely meaning "excluded", any genuine rxnScore that is -0017 % (or sums to) exactly 0 is nudged to 0.01 instead. -0018 +0016 % Since 0 means "excluded", an original score of 0 and a group +0017 % score that sums to 0 while at least one score of the group +0018 % is not 0 are set to 0.01. 0019 -0020 newRxnScores = zeros(length(model.rxns),1); -0021 [~,ia,ib] = intersect(model.rxns,origRxnIds); -0022 grpIdsMerged = nan(length(model.rxns),1); -0023 grpIdsMerged(ia) = groupIds(ib); -0024 %check if any of the original scores are 0, in that case change them to 0.01 (unlikely) -0025 origRxnScores(origRxnScores == 0) = 0.01; -0026 %Then set the rxn scores for rxns to zero to 0 -0027 origRxnScores(origRxnsToZero) = 0; -0028 -0029 %fill in original scores -0030 newRxnScores(ia) = origRxnScores(ib); -0031 -0032 for i = 1:length(model.rxns) -0033 %for reactions that are not merged with anything, just keep score as it is -0034 if grpIdsMerged(i) ~= 0 -0035 %find all original rxns in the group -0036 sel = groupIds == grpIdsMerged(i); -0037 newRxnScores(i) = sum(origRxnScores(sel)); -0038 if (newRxnScores(i) == 0 && any(origRxnScores(sel) ~= 0)) -0039 %special unfortunate case, where the reactions happen to sum to 0 while some of them are nonzero -0040 %set to 0.01 in this case (proabably pretty unusual) -0041 newRxnScores(i) = 0.01; -0042 end -0043 end -0044 end -0045 -0046 end

+0020 +0021 newRxnScores = zeros(length(model.rxns),1); +0022 [~,ia,ib] = intersect(model.rxns,origRxnIds); +0023 grpIdsMerged = nan(length(model.rxns),1); +0024 grpIdsMerged(ia) = groupIds(ib); +0025 %check if any of the original scores are 0, in that case change them to 0.01 (unlikely) +0026 origRxnScores(origRxnScores == 0) = 0.01; +0027 %Then set the rxn scores for rxns to zero to 0 +0028 origRxnScores(origRxnsToZero) = 0; +0029 +0030 %fill in original scores +0031 newRxnScores(ia) = origRxnScores(ib); +0032 +0033 for i = 1:length(model.rxns) +0034 %for reactions that are not merged with anything, just keep score as it is +0035 if grpIdsMerged(i) ~= 0 +0036 %find all original rxns in the group +0037 sel = groupIds == grpIdsMerged(i); +0038 newRxnScores(i) = sum(origRxnScores(sel)); +0039 if (newRxnScores(i) == 0 && any(origRxnScores(sel) ~= 0)) +0040 %special unfortunate case, where the reactions happen to sum to 0 while some of them are nonzero +0041 %set to 0.01 in this case (proabably pretty unusual) +0042 newRxnScores(i) = 0.01; +0043 end +0044 end +0045 end +0046 +0047 end

Generated by m2html © 2005
\ No newline at end of file diff --git a/doc/INIT/index.html b/docs/html/INIT/index.html similarity index 69% rename from doc/INIT/index.html rename to docs/html/INIT/index.html index abb05745..4baaf28d 100644 --- a/doc/INIT/index.html +++ b/docs/html/INIT/index.html @@ -19,7 +19,7 @@

Index for INIT

Matlab files in this directory:

-
 INITStepDesc
 ftINIT
 ftINITInternalAlgftINITInternalAlg
 getINITStepsgetINITSteps Define the steps used as input to ftINIT.
 groupRxnScoresgroupRxnScores
 mergeLinearmergeLinear
 prepINITModelprepINITModel Precompute omics-independent data for ftINIT.
 rescaleModelForINITrescaleModelForINIT Rescale a model to avoid large stoichiometric differences.
+ INITStepDesc  ftINIT  ftINITInternalAlgftINITInternalAlg  getINITStepsgetINITSteps Define the steps used as input to ftINIT.  groupRxnScoresgroupRxnScores  mergeLinearmergeLinear  parseHPAparseHPA Parse a database dump of the Human Protein Atlas (HPA).  parseHPArnaparseHPArna Parse a dump of Human Protein Atlas (HPA) RNA-Seq data.  prepINITModelprepINITModel Precompute omics-independent data for ftINIT.  rescaleModelForINITrescaleModelForINIT Rescale a model to avoid large stoichiometric differences.  scoreModelscoreModel Score reactions and genes in a model with complex gene rules.

Subsequent directories:

diff --git a/docs/html/INIT/mergeLinear.html b/docs/html/INIT/mergeLinear.html new file mode 100644 index 00000000..9647dd3b --- /dev/null +++ b/docs/html/INIT/mergeLinear.html @@ -0,0 +1,294 @@ + + + + Description of mergeLinear + + + + + + + + + +
Home > INIT > mergeLinear.m
+ + + +

mergeLinear +

+ +

PURPOSE ^

+
mergeLinear
+ +

SYNOPSIS ^

+
function [reducedModel,origRxnIds,groupIds,reversedRxns]=mergeLinear(model, noMergeRxns)
+ +

DESCRIPTION ^

+
 mergeLinear
+   Simplifies a model by merging rxns with linear dependencies. It differs
+   from the grouping in simplifyModel in two ways:
+   1) It returns which reactions were merged.
+   2) It also merges reversible reactions.
+   The reduced model has no genes and empty grRules.
+
+   model                 a model structure
+   noMergeRxns           Cell array with reaction IDs that are not allowed to be
+                         merged
+
+   reducedModel          an updated model structure
+   origRxnIds            Cell array: original rxn ids, used together with the groupIds variable
+   groupIds              Vector of ids: merged rxns have the same id, same length as origRxnIds
+                         0 means a reaction was not merged. The groups are numbered 1, 2, ...
+                         in the order of their first reaction in origRxnIds.
+   reversedRxns          Vector of booleans, saying for each rxn if it is reversed in the combined rxns.
+
+ Usage: [reducedModel,origRxnIds,groupIds,reversedRxns]=mergeLinear(model,noMergeRxns)
+ + +

CROSS-REFERENCE INFORMATION ^

+This function calls: +
    +
+This function is called by: +
    +
  • prepINITModel prepINITModel Precompute omics-independent data for ftINIT.
+ + +

SUBFUNCTIONS ^

+ + +

SOURCE CODE ^

+
0001 function [reducedModel,origRxnIds,groupIds,reversedRxns]=mergeLinear(model, noMergeRxns)
+0002 % mergeLinear
+0003 %   Simplifies a model by merging rxns with linear dependencies. It differs
+0004 %   from the grouping in simplifyModel in two ways:
+0005 %   1) It returns which reactions were merged.
+0006 %   2) It also merges reversible reactions.
+0007 %   The reduced model has no genes and empty grRules.
+0008 %
+0009 %   model                 a model structure
+0010 %   noMergeRxns           Cell array with reaction IDs that are not allowed to be
+0011 %                         merged
+0012 %
+0013 %   reducedModel          an updated model structure
+0014 %   origRxnIds            Cell array: original rxn ids, used together with the groupIds variable
+0015 %   groupIds              Vector of ids: merged rxns have the same id, same length as origRxnIds
+0016 %                         0 means a reaction was not merged. The groups are numbered 1, 2, ...
+0017 %                         in the order of their first reaction in origRxnIds.
+0018 %   reversedRxns          Vector of booleans, saying for each rxn if it is reversed in the combined rxns.
+0019 %
+0020 % Usage: [reducedModel,origRxnIds,groupIds,reversedRxns]=mergeLinear(model,noMergeRxns)
+0021 
+0022 
+0023 reducedModel=model;
+0024 
+0025 
+0026 reducedModel.genes={};
+0027 reducedModel.rxnGeneMat=sparse(numel(reducedModel.rxns),0);
+0028 reducedModel.grRules(:)={''};
+0029 
+0030 if isfield(reducedModel,'geneShortNames')
+0031     reducedModel.geneShortNames={};
+0032 end
+0033 if isfield(reducedModel,'proteins')
+0034     reducedModel.proteins={};
+0035 end
+0036 if isfield(reducedModel,'geneMiriams')
+0037     reducedModel.geneMiriams={};
+0038 end
+0039 if isfield(reducedModel,'geneComps')
+0040     reducedModel.geneComps=[];
+0041 end
+0042 
+0043 nextGroupId = 1;
+0044 origRxnIds = reducedModel.rxns;
+0045 groupIds = zeros(numel(reducedModel.rxns),1);
+0046 reversedRxns = false(numel(reducedModel.rxns),1);
+0047 %rxn id -> original index lookup, built once rather than re-scanning all
+0048 %of origRxnIds (via strcmp) for every merge candidate examined below.
+0049 origRxnIdMap = containers.Map(origRxnIds,num2cell(1:numel(origRxnIds)));
+0050 
+0051 %Loop through and iteratively group linear reactions
+0052 while 1
+0053     %Get the banned reaction indexes. Note that the indexes will change
+0054     %in each iteration, but the names will not as they won't be merged
+0055     %with any other reaction
+0056     bannedIndexes=getIndexes(reducedModel,noMergeRxns,'rxns');
+0057 
+0058     %Select all metabolites that are only present as reactants/products
+0059     %in one reaction
+0060     twoNonZero = find(sum(reducedModel.S ~= 0, 2) == 2);
+0061 
+0062     mergedSome=false;
+0063 
+0064     %Loop through each of them and see if the reactions should be
+0065     %merged
+0066     for i=1:numel(twoNonZero)
+0067         involvedRxns=find(reducedModel.S(twoNonZero(i),:));
+0068         %Check that we can have one positive and one negative
+0069         pos = sum(reducedModel.S(twoNonZero(i),involvedRxns).' > 0 | reducedModel.rev(involvedRxns));
+0070         neg = sum(reducedModel.S(twoNonZero(i),involvedRxns).' < 0 | reducedModel.rev(involvedRxns));
+0071         
+0072                 
+0073         %Check so that one or both of the reactions haven't been merged
+0074         %already
+0075         if numel(involvedRxns)==2 && isempty(intersect(bannedIndexes,involvedRxns)) && pos >= 1 && neg >= 1
+0076             %first, take care of a special case: If the first reaction is producing the metabolite and if it is reversible,
+0077             %and the second is also producing it and is not reversible, change the order - the code below will not work otherwise
+0078             if reducedModel.rev(involvedRxns(1)) && (~reducedModel.rev(involvedRxns(2))) && ...
+0079                (reducedModel.S(twoNonZero(i),involvedRxns(1)) > 0) && (reducedModel.S(twoNonZero(i),involvedRxns(2)) > 0)
+0080                 involvedRxns = flip(involvedRxns);
+0081             end
+0082             
+0083             %first make sure the first reaction is producing the metabolite
+0084             if reducedModel.S(twoNonZero(i),involvedRxns(1)) < 0
+0085                 %it is not producing the metabolite - fix that
+0086                 %first choice: use the second reaction as producer if it is producing
+0087                 if reducedModel.S(twoNonZero(i),involvedRxns(2)) > 0
+0088                     involvedRxns = flip(involvedRxns);%make the second the first
+0089                 else
+0090                     %now we know that the second reaction is not producing, so we can safely try to make the first a producer
+0091                     if reducedModel.rev(involvedRxns(1)) == 1
+0092                         [reducedModel,reversedRxns] = flipRxn(reducedModel, involvedRxns(1), groupIds, reversedRxns);
+0093                     else %ok, finally try to flip the second reaction
+0094                         if reducedModel.rev(involvedRxns(2)) == 1
+0095                             [reducedModel,reversedRxns] = flipRxn(reducedModel, involvedRxns(2), groupIds, reversedRxns);
+0096                             involvedRxns = flip(involvedRxns);%make the second the first
+0097                         else
+0098                             error('We should never end up here!');
+0099                         end
+0100                     end
+0101                 end
+0102             end
+0103             %Now, make sure the second rxn is a consumer
+0104             if reducedModel.S(twoNonZero(i),involvedRxns(2)) > 0
+0105                 if reducedModel.rev(involvedRxns(2)) == 1
+0106                     [reducedModel,reversedRxns] = flipRxn(reducedModel, involvedRxns(2), groupIds, reversedRxns);
+0107                 else
+0108                     error('We should never end up here!');
+0109                 end
+0110             end
+0111 
+0112             %Calculate how many times the second reaction has to be
+0113             %multiplied before being merged with the first
+0114             stoichRatio=abs(reducedModel.S(twoNonZero(i),involvedRxns(1))/reducedModel.S(twoNonZero(i),involvedRxns(2)));
+0115 
+0116             %Add the second to the first
+0117             reducedModel.S(:,involvedRxns(1))=reducedModel.S(:,involvedRxns(1))+reducedModel.S(:,involvedRxns(2))*stoichRatio;
+0118 
+0119             %Clear the second reaction
+0120             reducedModel.S(:,involvedRxns(2))=0;
+0121 
+0122             %This is to prevent numerical issues. It should be 0
+0123             %already
+0124             reducedModel.S(twoNonZero(i),involvedRxns(1))=0;
+0125 
+0126             %At this point the second reaction is certain to be deleted
+0127             %in a later step and can therefore be ignored
+0128 
+0129             %Recalculate the bounds for the new reaction. This can be
+0130             %problematic since the scale of the bounds may change
+0131             %dramatically. Let the most constraining reaction determine
+0132             %the new bound
+0133             lb1=reducedModel.lb(involvedRxns(1));
+0134             lb2=reducedModel.lb(involvedRxns(2));
+0135             ub1=reducedModel.ub(involvedRxns(1));
+0136             ub2=reducedModel.ub(involvedRxns(2));
+0137 
+0138             if lb2~=-inf
+0139                 reducedModel.lb(involvedRxns(1))=max(lb1,lb2/stoichRatio);
+0140             end
+0141             if ub2~=inf
+0142                 reducedModel.ub(involvedRxns(1))=min(ub1,ub2/stoichRatio);
+0143             end
+0144             
+0145             %take care of the .rev flag - it could be that the combined rxn changes from rev to irrev
+0146             reducedModel.rev(involvedRxns(1)) = reducedModel.rev(involvedRxns(1))*reducedModel.rev(involvedRxns(2));%this is a way to do an "and" operation with 0 and 1 numbers
+0147 
+0148             %Then recalculate the objective coefficient. The resulting
+0149             %coefficient is the weighted sum of the previous
+0150             reducedModel.c(involvedRxns(1))=reducedModel.c(involvedRxns(1))+reducedModel.c(involvedRxns(2))*stoichRatio;
+0151 
+0152             %store which reactions that have been merged
+0153             rxnInd1 = origRxnIdMap(reducedModel.rxns{involvedRxns(1)});
+0154             rxnInd2 = origRxnIdMap(reducedModel.rxns{involvedRxns(2)});
+0155             grpId = max(groupIds(rxnInd1),groupIds(rxnInd2));
+0156             if grpId == 0
+0157                grpId = nextGroupId;
+0158                nextGroupId = nextGroupId + 1;
+0159             end
+0160 
+0161             if groupIds(rxnInd1) ~= grpId
+0162                if groupIds(rxnInd1) == 0
+0163                    %not merged before, just set the group id
+0164                    groupIds(rxnInd1) = grpId;
+0165                else
+0166                    %merged before - all rxns with the same group id should be changed
+0167                    groupIds(groupIds == groupIds(rxnInd1)) = grpId;
+0168                end
+0169             end
+0170             if groupIds(rxnInd2) ~= grpId
+0171                if groupIds(rxnInd2) == 0
+0172                    %not merged before, just set the group id
+0173                    groupIds(rxnInd2) = grpId;
+0174                else
+0175                    %merged before - all rxns with the same group id should be changed
+0176                    groupIds(groupIds == groupIds(rxnInd2)) = grpId;
+0177                end
+0178             end
+0179 
+0180             %Iterate again
+0181             mergedSome=true;
+0182         end
+0183     end
+0184 
+0185     %All possible reactions merged
+0186     if mergedSome==false
+0187         break;
+0188     end
+0189 
+0190     %Now delete all reactions that involve no metabolites
+0191     I=find(sum(reducedModel.S~=0,1)==0);
+0192 
+0193     %Remove reactions
+0194     reducedModel=removeReactions(reducedModel,I);
+0195 
+0196     %Remove metabolites
+0197     notInUse=sum(reducedModel.S~=0,2)==0;
+0198     reducedModel=removeMets(reducedModel,notInUse);
+0199 end
+0200 
+0201 %Number the groups in the order of their first reaction in origRxnIds, so
+0202 %the numbers do not depend on the order in which the reactions were merged
+0203 merged=groupIds~=0;
+0204 [~,~,groupIds(merged)]=unique(groupIds(merged),'stable');
+0205 
+0206 function [model1,reversedRxns1] = flipRxn(model1, rxnInd, groupIds1, reversedRxns1)
+0207     model1.S(:,rxnInd) = model1.S(:,rxnInd)*-1;
+0208     %swap the bounds
+0209     ub = model1.ub(rxnInd);
+0210     model1.ub(rxnInd) = -model1.lb(rxnInd);
+0211     model1.lb(rxnInd) = -ub;
+0212     %flip the objective
+0213     model1.c(rxnInd) = -model1.c(rxnInd);
+0214     
+0215     %now take care of the reversedRxns - if this is a group, reverse all of the
+0216     %reactions in the group in the reversedRxns index - they will all be reversed at the
+0217     %same time since they are the same rxn. rxnInd indexes model1, which loses
+0218     %reactions after each pass, while groupIds1 and reversedRxns1 follow
+0219     %origRxnIds, so the reaction is looked up by its id.
+0220     origInd = origRxnIdMap(model1.rxns{rxnInd});
+0221     rxnIndices = origInd;
+0222     if groupIds1(origInd) > 0
+0223         rxnIndices = find(groupIds1 == groupIds1(origInd));
+0224     end
+0225     reversedRxns1(rxnIndices) = ~reversedRxns1(rxnIndices);
+0226 end
+0227 end
+
Generated by m2html © 2005
+ + \ No newline at end of file diff --git a/doc/omics/parseHPA.html b/docs/html/INIT/parseHPA.html similarity index 54% rename from doc/omics/parseHPA.html rename to docs/html/INIT/parseHPA.html index 9dfefcad..213c7486 100644 --- a/doc/omics/parseHPA.html +++ b/docs/html/INIT/parseHPA.html @@ -12,10 +12,10 @@ -
Home > omics > parseHPA.m
+
Home > INIT > parseHPA.m
+Index for INIT >-->

parseHPA

@@ -38,8 +38,7 @@

DESCRIPTION ^DESCRIPTION ^SOURCE CODE ^% Name-Value Arguments 0011 % -------------------- 0012 % version : double -0013 % accepted for backward compatibility but ignored; the format is now -0014 % inferred from the column headers (default 19). -0015 % -0016 % Returns -0017 % ------- -0018 % hpaData : struct -0019 % parsed HPA data with fields: -0020 % -0021 % - genes : cell array with the unique gene IDs (Ensembl in v>=18) -0022 % - geneNames : cell array with the gene symbols (present in v>=18 only) -0023 % - tissues : cell array with the tissue names (may not be unique; one -0024 % entry per tissue-cell-type combination) -0025 % - celltypes : cell array with the cell type names for each tissue -0026 % - levels : cell array with the unique expression level labels -0027 % - types : cell array with the unique evidence types (v<18 only) -0028 % - reliabilities : cell array with the unique reliability levels -0029 % - gene2Level : sparse gene × tissue-cell-type matrix; value i,j is -0030 % the index in hpaData.levels for gene i in tissue-cell-type j -0031 % - gene2Type : sparse gene × tissue-cell-type matrix (v<18 only) -0032 % - gene2Reliability : sparse gene × tissue-cell-type matrix; value i,j -0033 % is the index in hpaData.reliabilities for gene i in tissue-cell-type j -0034 % -0035 % Examples -0036 % -------- -0037 % hpaData = parseHPA(fileName); -0038 -0039 p=parseRAVENargs(varargin, {'version',19}); %#ok<NASGU> retained for backward compat -0040 -0041 fileName=char(fileName); -0042 if ~isfile(fileName) -0043 error('HPA file %s cannot be found', string(fileName)); -0044 end -0045 -0046 %Read header line and auto-detect delimiter -0047 fid=fopen(fileName,'r'); -0048 headerLine=fgetl(fid); -0049 fclose(fid); -0050 -0051 if any(headerLine==9) % char(9) = horizontal tab -0052 delim=sprintf('\t'); -0053 else -0054 delim=','; -0055 end -0056 -0057 %Parse header and all data columns -0058 fid=fopen(fileName,'r'); -0059 headers=strsplit(fgetl(fid), delim); -0060 fmtStr=strip(repmat('%q ',1,numel(headers))); -0061 hpa=textscan(fid, fmtStr, 'Delimiter',delim); -0062 fclose(fid); -0063 -0064 %Helper: column index by name -0065 ci=@(name) find(strcmp(headers,name),1); -0066 -0067 %Validate required columns common to all versions -0068 for h={'Gene','Tissue','Cell type','Level','Reliability'} -0069 if isempty(ci(h{1})) -0070 error(['Could not find the column "' h{1} '". ' ... -0071 'Make sure the input file matches the format at https://www.proteinatlas.org/about/download']); -0072 end -0073 end -0074 -0075 hasGeneName=~isempty(ci('Gene name')); -0076 hasExprType=~isempty(ci('Expression type')); -0077 -0078 [hpaData.genes, P, I]=unique(hpa{ci('Gene')}); -0079 if hasGeneName -0080 hpaData.geneNames=hpa{ci('Gene name')}(P); -0081 end -0082 -0083 tissueCellStr=strcat(hpa{ci('Tissue')},'€',hpa{ci('Cell type')}); -0084 [~, J, K]=unique(tissueCellStr); -0085 hpaData.tissues=hpa{ci('Tissue')}(J); -0086 hpaData.celltypes=hpa{ci('Cell type')}(J); -0087 -0088 [hpaData.levels, ~, L]=unique(hpa{ci('Level')}); -0089 [hpaData.reliabilities, ~, N]=unique(hpa{ci('Reliability')}); -0090 -0091 nGenes=numel(hpaData.genes); -0092 nTissues=max(K); -0093 -0094 hpaData.gene2Level=sparse(I,K,L,nGenes,nTissues); -0095 hpaData.gene2Reliability=sparse(I,K,N,nGenes,nTissues); -0096 -0097 if hasExprType -0098 [hpaData.types, ~, M]=unique(hpa{ci('Expression type')}); -0099 hpaData.gene2Type=sparse(I,K,M,nGenes,nTissues); -0100 end -0101 end +0013 % ignored; the format is inferred from the column headers (default 19). +0014 % +0015 % Returns +0016 % ------- +0017 % hpaData : struct +0018 % parsed HPA data with fields: +0019 % +0020 % - genes : cell array with the unique gene IDs (Ensembl in v>=18) +0021 % - geneNames : cell array with the gene symbols (present in v>=18 only) +0022 % - tissues : cell array with the tissue names (can repeat; one +0023 % entry per tissue-cell-type combination) +0024 % - celltypes : cell array with the cell type names for each tissue +0025 % - levels : cell array with the unique expression level labels +0026 % - types : cell array with the unique evidence types (v<18 only) +0027 % - reliabilities : cell array with the unique reliability levels +0028 % - gene2Level : sparse gene × tissue-cell-type matrix; value i,j is +0029 % the index in hpaData.levels for gene i in tissue-cell-type j +0030 % - gene2Type : sparse gene × tissue-cell-type matrix (v<18 only) +0031 % - gene2Reliability : sparse gene × tissue-cell-type matrix; value i,j +0032 % is the index in hpaData.reliabilities for gene i in tissue-cell-type j +0033 % +0034 % Examples +0035 % -------- +0036 % hpaData = parseHPA(fileName); +0037 +0038 p=parseRAVENargs(varargin, {'version',19}); %#ok<NASGU> retained for backward compat +0039 +0040 fileName=char(fileName); +0041 if ~isfile(fileName) +0042 error('HPA file %s cannot be found', string(fileName)); +0043 end +0044 +0045 %Read header line and auto-detect delimiter +0046 fid=fopen(fileName,'r'); +0047 headerLine=fgetl(fid); +0048 fclose(fid); +0049 +0050 if any(headerLine==9) % char(9) = horizontal tab +0051 delim=sprintf('\t'); +0052 else +0053 delim=','; +0054 end +0055 +0056 %Parse header and all data columns +0057 fid=fopen(fileName,'r'); +0058 headers=strsplit(fgetl(fid), delim); +0059 fmtStr=strip(repmat('%q ',1,numel(headers))); +0060 hpa=textscan(fid, fmtStr, 'Delimiter',delim); +0061 fclose(fid); +0062 +0063 %Helper: column index by name +0064 ci=@(name) find(strcmp(headers,name),1); +0065 +0066 %Validate required columns common to all versions +0067 for h={'Gene','Tissue','Cell type','Level','Reliability'} +0068 if isempty(ci(h{1})) +0069 error(['Could not find the column "' h{1} '". ' ... +0070 'Make sure the input file matches the format at https://www.proteinatlas.org/about/download']); +0071 end +0072 end +0073 +0074 hasGeneName=~isempty(ci('Gene name')); +0075 hasExprType=~isempty(ci('Expression type')); +0076 +0077 [hpaData.genes, P, I]=unique(hpa{ci('Gene')}); +0078 if hasGeneName +0079 hpaData.geneNames=hpa{ci('Gene name')}(P); +0080 end +0081 +0082 tissueCellStr=strcat(hpa{ci('Tissue')},'€',hpa{ci('Cell type')}); +0083 [~, J, K]=unique(tissueCellStr); +0084 hpaData.tissues=hpa{ci('Tissue')}(J); +0085 hpaData.celltypes=hpa{ci('Cell type')}(J); +0086 +0087 [hpaData.levels, ~, L]=unique(hpa{ci('Level')}); +0088 [hpaData.reliabilities, ~, N]=unique(hpa{ci('Reliability')}); +0089 +0090 nGenes=numel(hpaData.genes); +0091 nTissues=max(K); +0092 +0093 hpaData.gene2Level=sparse(I,K,L,nGenes,nTissues); +0094 hpaData.gene2Reliability=sparse(I,K,N,nGenes,nTissues); +0095 +0096 if hasExprType +0097 [hpaData.types, ~, M]=unique(hpa{ci('Expression type')}); +0098 hpaData.gene2Type=sparse(I,K,M,nGenes,nTissues); +0099 end +0100 end
Generated by m2html © 2005
\ No newline at end of file diff --git a/doc/omics/parseHPArna.html b/docs/html/INIT/parseHPArna.html similarity index 57% rename from doc/omics/parseHPArna.html rename to docs/html/INIT/parseHPArna.html index 40d646c2..3008f6f8 100644 --- a/doc/omics/parseHPArna.html +++ b/docs/html/INIT/parseHPArna.html @@ -12,10 +12,10 @@ -
Home > omics > parseHPArna.m
+
Home > INIT > parseHPArna.m
+Index for INIT >-->

parseHPArna

@@ -38,8 +38,7 @@

DESCRIPTION ^SOURCE CODE ^% Name-Value Arguments 0011 % -------------------- 0012 % version : double -0013 % accepted for backward compatibility but ignored; the format is now -0014 % inferred from the column headers (default 19). -0015 % -0016 % Returns -0017 % ------- -0018 % arrayData : struct -0019 % parsed HPA RNA data with fields: -0020 % -0021 % - genes : cell array with the unique Ensembl gene IDs -0022 % - geneNames : cell array with the gene symbols -0023 % - tissues : cell array with the unique tissue (or sample) names -0024 % - levels : matrix of expression values (rows = genes, cols = tissues) -0025 % -0026 % Examples -0027 % -------- -0028 % arrayData = parseHPArna(fileName); -0029 -0030 p=parseRAVENargs(varargin, {'version',19}); %#ok<NASGU> retained for backward compat -0031 -0032 fileName=char(fileName); -0033 if ~isfile(fileName) -0034 error('HPA file %s cannot be found', string(fileName)); -0035 end -0036 -0037 %Parse header and all data columns (RNA files are always TSV) -0038 fid=fopen(fileName,'r'); -0039 headers=strsplit(fgetl(fid), sprintf('\t')); -0040 fmtStr=strip(repmat('%q ',1,numel(headers))); -0041 hpa=textscan(fid, fmtStr, 'Delimiter','\t'); -0042 fclose(fid); -0043 -0044 %Helper: column index by name (returns empty if not found) -0045 ci=@(name) find(strcmp(headers,name),1); -0046 -0047 %Validate required columns -0048 for h={'Gene','Gene name'} -0049 if isempty(ci(h{1})) -0050 error(['Could not find the column "' h{1} '". ' ... -0051 'Make sure the input file matches the format at https://www.proteinatlas.org/about/download']); -0052 end -0053 end -0054 -0055 %Detect tissue column: 'Tissue' (v19) or 'Sample' (v18) -0056 tissueCol='Tissue'; -0057 if isempty(ci('Tissue')) && ~isempty(ci('Sample')) -0058 tissueCol='Sample'; -0059 elseif isempty(ci('Tissue')) -0060 error(['Could not find a tissue column ("Tissue" or "Sample"). ' ... -0061 'Make sure the input file matches the format at https://www.proteinatlas.org/about/download']); -0062 end -0063 -0064 %Detect expression value column: 'TPM' (v19) or 'Value' (v18) -0065 valueCol='TPM'; -0066 if isempty(ci('TPM')) && ~isempty(ci('Value')) -0067 valueCol='Value'; -0068 elseif isempty(ci('TPM')) -0069 error(['Could not find an expression value column ("TPM" or "Value"). ' ... -0070 'Make sure the input file matches the format at https://www.proteinatlas.org/about/download']); -0071 end -0072 -0073 [arrayData.genes, P, I]=unique(hpa{ci('Gene')}); -0074 arrayData.geneNames=hpa{ci('Gene name')}(P); -0075 [arrayData.tissues, ~, J]=unique(hpa{ci(tissueCol)}); -0076 -0077 arrayData.levels=NaN(max(I),max(J)); -0078 linearInd=sub2ind(size(arrayData.levels),I,J); -0079 arrayData.levels(linearInd)=str2double(hpa{ci(valueCol)}); -0080 end +0013 % ignored; the format is inferred from the column headers (default 19). +0014 % +0015 % Returns +0016 % ------- +0017 % arrayData : struct +0018 % parsed HPA RNA data with fields: +0019 % +0020 % - genes : cell array with the unique Ensembl gene IDs +0021 % - geneNames : cell array with the gene symbols +0022 % - tissues : cell array with the unique tissue (or sample) names +0023 % - levels : matrix of expression values (rows = genes, cols = tissues) +0024 % +0025 % Examples +0026 % -------- +0027 % arrayData = parseHPArna(fileName); +0028 +0029 p=parseRAVENargs(varargin, {'version',19}); %#ok<NASGU> retained for backward compat +0030 +0031 fileName=char(fileName); +0032 if ~isfile(fileName) +0033 error('HPA file %s cannot be found', string(fileName)); +0034 end +0035 +0036 %Parse header and all data columns (RNA files are always TSV) +0037 fid=fopen(fileName,'r'); +0038 headers=strsplit(fgetl(fid), sprintf('\t')); +0039 fmtStr=strip(repmat('%q ',1,numel(headers))); +0040 hpa=textscan(fid, fmtStr, 'Delimiter','\t'); +0041 fclose(fid); +0042 +0043 %Helper: column index by name (returns empty if not found) +0044 ci=@(name) find(strcmp(headers,name),1); +0045 +0046 %Validate required columns +0047 for h={'Gene','Gene name'} +0048 if isempty(ci(h{1})) +0049 error(['Could not find the column "' h{1} '". ' ... +0050 'Make sure the input file matches the format at https://www.proteinatlas.org/about/download']); +0051 end +0052 end +0053 +0054 %Detect tissue column: 'Tissue' (v19) or 'Sample' (v18) +0055 tissueCol='Tissue'; +0056 if isempty(ci('Tissue')) && ~isempty(ci('Sample')) +0057 tissueCol='Sample'; +0058 elseif isempty(ci('Tissue')) +0059 error(['Could not find a tissue column ("Tissue" or "Sample"). ' ... +0060 'Make sure the input file matches the format at https://www.proteinatlas.org/about/download']); +0061 end +0062 +0063 %Detect expression value column: 'TPM' (v19) or 'Value' (v18) +0064 valueCol='TPM'; +0065 if isempty(ci('TPM')) && ~isempty(ci('Value')) +0066 valueCol='Value'; +0067 elseif isempty(ci('TPM')) +0068 error(['Could not find an expression value column ("TPM" or "Value"). ' ... +0069 'Make sure the input file matches the format at https://www.proteinatlas.org/about/download']); +0070 end +0071 +0072 [arrayData.genes, P, I]=unique(hpa{ci('Gene')}); +0073 arrayData.geneNames=hpa{ci('Gene name')}(P); +0074 [arrayData.tissues, ~, J]=unique(hpa{ci(tissueCol)}); +0075 +0076 arrayData.levels=NaN(max(I),max(J)); +0077 linearInd=sub2ind(size(arrayData.levels),I,J); +0078 arrayData.levels(linearInd)=str2double(hpa{ci(valueCol)}); +0079 end
Generated by m2html © 2005
\ No newline at end of file diff --git a/docs/html/INIT/prepINITModel.html b/docs/html/INIT/prepINITModel.html new file mode 100644 index 00000000..7aa72a20 --- /dev/null +++ b/docs/html/INIT/prepINITModel.html @@ -0,0 +1,457 @@ + + + + Description of prepINITModel + + + + + + + + + +
Home > INIT > prepINITModel.m
+ + + +

prepINITModel +

+ +

PURPOSE ^

+
prepINITModel Precompute omics-independent data for ftINIT.
+ +

SYNOPSIS ^

+
function prepData = prepINITModel(origRefModel, taskStruct, varargin)
+ +

DESCRIPTION ^

+
 prepINITModel  Precompute omics-independent data for ftINIT.
+
+ Runs the calculation steps that do not depend on the omics data, once per
+ template model.
+
+ Parameters
+ ----------
+ origRefModel : struct
+     the template model, such as Human-GEM or Mouse-GEM.
+ taskStruct : struct
+     the essential tasks, for example loaded with
+     taskStruct = parseTaskList('../data/metabolicTasks_Essential.txt').
+     The task ids must be character vectors: a non-text id or a missing id
+     field raises RAVEN:badInput. Ids may repeat (for example as category
+     codes) and may be empty; each task is one row group of the task list,
+     and a task that cannot be performed is removed by its position.
+
+ Name-Value Arguments
+ --------------------
+ spontRxnNames : cell
+     the spontaneous rxns (default {}).
+ convertGenes : logical
+     if true the genes are converted to gene names (from ENSEMBL). This
+     uses translateGrRules, which is distributed with Human-GEM and not with
+     RAVEN, so Human-GEM must be on the MATLAB path (default false).
+ customRxnsToIgnore : cell
+     reactions that are left outside the MILP when bit b7 of the
+     RxnsToIgnoreMask of INITStepDesc is 1 (default {}).
+ extComp : char
+     name of the external compartment, such as 's' or 'e'. This is used for
+     identifying exchange and import reactions (default 'e').
+ skipScaling : logical
+     if true the scaling step is not run on the minimal model. The scaling
+     removes large differences between the stoichiometric coefficients
+     within a reaction (see rescaleModelForINIT), since such differences
+     create numerical issues in the ftINIT algorithm due to limitations in
+     solver resolution. The scaling changes the stoichiometry of the
+     reactions with large differences, and some reactions can become unable
+     to carry flux, which can make the solution infeasible. If the solution
+     is infeasible, turning the scaling off may help; the bounds then stay as in
+     the template and the MILP caps the per-reaction big-M at 1000 (bigMCap
+     in ftINITInternalAlg). In raven-toolbox's ftinit on Human-GEM (one cell
+     line, big_m 1000) the pipeline without scaling was feasible but took
+     157 s against 83 s with scaling. The bounds of the scaled model are set to 1000 where the upper bound is above 0 and
+     to -1000 where the lower bound is below 0; zero bounds stay 0. Only the
+     minModel is scaled; the scaling is not present in the final model
+     (default false).
+ runParallel : logical
+     true to run the task-essentiality check (checkTasks) across parallel
+     workers, since each task is independent of the others (see
+     parallelWorkersRAVEN) (default false, serial).
+
+ Returns
+ -------
+ prepData : struct
+     the resulting prepData structure which is used as input to ftINIT.
+ + +

CROSS-REFERENCE INFORMATION ^

+This function calls: + +This function is called by: +
    +
+ + + + +

SOURCE CODE ^

+
0001 function prepData = prepINITModel(origRefModel, taskStruct, varargin)
+0002 % prepINITModel  Precompute omics-independent data for ftINIT.
+0003 %
+0004 % Runs the calculation steps that do not depend on the omics data, once per
+0005 % template model.
+0006 %
+0007 % Parameters
+0008 % ----------
+0009 % origRefModel : struct
+0010 %     the template model, such as Human-GEM or Mouse-GEM.
+0011 % taskStruct : struct
+0012 %     the essential tasks, for example loaded with
+0013 %     taskStruct = parseTaskList('../data/metabolicTasks_Essential.txt').
+0014 %     The task ids must be character vectors: a non-text id or a missing id
+0015 %     field raises RAVEN:badInput. Ids may repeat (for example as category
+0016 %     codes) and may be empty; each task is one row group of the task list,
+0017 %     and a task that cannot be performed is removed by its position.
+0018 %
+0019 % Name-Value Arguments
+0020 % --------------------
+0021 % spontRxnNames : cell
+0022 %     the spontaneous rxns (default {}).
+0023 % convertGenes : logical
+0024 %     if true the genes are converted to gene names (from ENSEMBL). This
+0025 %     uses translateGrRules, which is distributed with Human-GEM and not with
+0026 %     RAVEN, so Human-GEM must be on the MATLAB path (default false).
+0027 % customRxnsToIgnore : cell
+0028 %     reactions that are left outside the MILP when bit b7 of the
+0029 %     RxnsToIgnoreMask of INITStepDesc is 1 (default {}).
+0030 % extComp : char
+0031 %     name of the external compartment, such as 's' or 'e'. This is used for
+0032 %     identifying exchange and import reactions (default 'e').
+0033 % skipScaling : logical
+0034 %     if true the scaling step is not run on the minimal model. The scaling
+0035 %     removes large differences between the stoichiometric coefficients
+0036 %     within a reaction (see rescaleModelForINIT), since such differences
+0037 %     create numerical issues in the ftINIT algorithm due to limitations in
+0038 %     solver resolution. The scaling changes the stoichiometry of the
+0039 %     reactions with large differences, and some reactions can become unable
+0040 %     to carry flux, which can make the solution infeasible. If the solution
+0041 %     is infeasible, turning the scaling off may help; the bounds then stay as in
+0042 %     the template and the MILP caps the per-reaction big-M at 1000 (bigMCap
+0043 %     in ftINITInternalAlg). In raven-toolbox's ftinit on Human-GEM (one cell
+0044 %     line, big_m 1000) the pipeline without scaling was feasible but took
+0045 %     157 s against 83 s with scaling. The bounds of the scaled model are set to 1000 where the upper bound is above 0 and
+0046 %     to -1000 where the lower bound is below 0; zero bounds stay 0. Only the
+0047 %     minModel is scaled; the scaling is not present in the final model
+0048 %     (default false).
+0049 % runParallel : logical
+0050 %     true to run the task-essentiality check (checkTasks) across parallel
+0051 %     workers, since each task is independent of the others (see
+0052 %     parallelWorkersRAVEN) (default false, serial).
+0053 %
+0054 % Returns
+0055 % -------
+0056 % prepData : struct
+0057 %     the resulting prepData structure which is used as input to ftINIT.
+0058 
+0059 
+0060 p=parseRAVENargs(varargin, {'spontRxnNames',{}; 'convertGenes',false; 'customRxnsToIgnore',{}; 'extComp','e'; 'skipScaling',false; 'runParallel',false});
+0061 spontRxnNames=p.spontRxnNames;
+0062 convertGenes=p.convertGenes;
+0063 customRxnsToIgnore=p.customRxnsToIgnore;
+0064 extComp=p.extComp;
+0065 skipScaling=p.skipScaling;
+0066 runParallel=p.runParallel;
+0067 
+0068 if ~isempty(taskStruct)
+0069     if ~isfield(taskStruct,'id')
+0070         error('RAVEN:badInput','The task structure must have an id field');
+0071     end
+0072     taskIds = {taskStruct.id};
+0073     if ~all(cellfun(@(x) ischar(x) && (isrow(x) || isempty(x)),taskIds))
+0074         error('RAVEN:badInput','The task ids must be character vectors');
+0075     end
+0076 end
+0077 
+0078 disp('Step 1: Gene rules')
+0079 [origRefModel.grRules, origRefModel.rxnGeneMat] = standardizeGrRules(origRefModel, true);
+0080 
+0081 if convertGenes %For mouse we might want to translate in the opposite direction - this has to be done before calling this function in that case.
+0082     if ~exist('translateGrRules','file')
+0083         EM=['convertGenes requires translateGrRules, which is distributed with ' ...
+0084             'Human-GEM and not with RAVEN. Add Human-GEM to the MATLAB path, or ' ...
+0085             'convert the gene identifiers before calling prepINITModel.'];
+0086         error('RAVEN:badInput', '%s', EM);
+0087     end
+0088     [origRefModel.grRules, origRefModel.genes, origRefModel.rxnGeneMat] = translateGrRules(origRefModel.grRules, 'Name');
+0089 end
+0090 
+0091 
+0092 % Get list of all dead-end/inacessible/constrained reactions in the model.
+0093 % We don't merge linear dependent reactions here, that will happen later.
+0094 % We also don't remove reversibility here, we don't want that in the final
+0095 % models produced. Therefore, this is done as a separate step.
+0096 % This takes quite some time to run
+0097 disp('Step 2: First simplification')
+0098 [~,deletedDeadEndRxns] = simplifyModel(origRefModel,true,false,true,true,true);
+0099 
+0100 % get reduced model
+0101 cModel = removeReactions(origRefModel,deletedDeadEndRxns,false,true);
+0102 
+0103 
+0104 %Then, run the tasks to find out which reactions are essential. These rxns
+0105 %will be forced to carry flux in the optimization later
+0106 
+0107 % determine reactions essential for tasks - takes ~10 min
+0108 % need to add boundary mets first - but only to a temporary model, we don't
+0109 % want those for further processing
+0110 disp('Step 3: Check tasks (~10 min)')
+0111 if ~isempty(taskStruct)
+0112     bModel = closeModel(cModel);
+0113     [taskReport, essentialRxnMat, ~, essentialFluxes] = checkTasks(bModel,[],true,false,true,taskStruct,'runParallel',runParallel);
+0114 
+0115     %extract the essential rxns:
+0116     sel = sum(essentialRxnMat,2) > 0;
+0117     selInd = find(sel);
+0118     essentialRxns = bModel.rxns(selInd);%382 rxns for human-GEM
+0119 
+0120     %Find metabolites present in taskStruct. We want to avoid removing
+0121     %these metabolites from the final model (even though they may no longer
+0122     %participate in any reacitons) so that the final model is still able to
+0123     %complete all of the tasks without any errors.
+0124     taskMets = union(vertcat(taskStruct.inputs),vertcat(taskStruct.outputs));
+0125     taskMets = union(taskMets, parseRxnEqu(vertcat(taskStruct.equations)));
+0126     modelMets = strcat(cModel.metNames,'[',cModel.comps(cModel.metComps),']');
+0127     [inModel,metInd] = ismember(taskMets,modelMets);
+0128     essentialMetsForTasks = cModel.mets(metInd(inModel));%118 mets
+0129 
+0130     %Remove tasks that cannot be performed
+0131     taskStruct(taskReport.ok == false) = [];
+0132 
+0133 else
+0134     essentialRxns = {};
+0135     essentialMetsForTasks = {};
+0136     taskReport = {};
+0137 end
+0138 
+0139 
+0140 % remove metabolites separately to avoid removing those needed for tasks
+0141 unusedMets = cModel.mets(all(cModel.S == 0,2));%1248 in Human-GEM
+0142 cModel = removeMets(cModel, setdiff(unusedMets,essentialMetsForTasks));
+0143 
+0144 if ~isempty(taskStruct)
+0145 
+0146     %Here, we decide on a direction in which the essential reactions should be forced.
+0147     essentialRevDir = false(length(essentialRxns),1);
+0148     pp = zeros(length(essentialRxns),1);
+0149     nn = zeros(length(essentialRxns),1);
+0150     for i = 1:length(selInd)
+0151         pos = sum(essentialFluxes(selInd(i),essentialRxnMat(selInd(i),:)) > 0);
+0152         neg = sum(essentialFluxes(selInd(i),essentialRxnMat(selInd(i),:)) < 0);
+0153         essentialRevDir(i) = pos < neg;
+0154         pp(i) = pos;
+0155         nn(i) = neg;
+0156     end
+0157 
+0158     %sum(pp>0 & nn>0) %0, so it is not a big problem that different tasks share essential
+0159     %reactions but in different directions, at least no such cases exist now, so we ignore this for now.
+0160 
+0161     %Now create a minimal model for the MILP - we want to make this as small as we can, but we
+0162     %don't want to use this later when we create the final models - just for the first MILP.
+0163     %We don't want boundary metabolites in here either.
+0164 
+0165     %We do a trick with the essential rxns here:
+0166     %What we want is to get rid of the reversibility of the reactions, because reversible reactions require
+0167     %an integer each in the MILP, and we can also get the case that it may be cheaper to force flux in the wrong direction, which
+0168     %will probably not give the gap-filling of reactions that we want.
+0169     %So, we simply change the reversible reactions to irreversible, and flip their direction if the direction
+0170     %to force flux in is negative. We don't need to do anything else, runINIT will then handle all essential rxns
+0171     %as irreversible (and will not add any integers etc. for them).
+0172 
+0173     minModel1 = cModel;
+0174     %for the essential rxns where we should force flux in the positive direction, just make them irreversible
+0175     %for the negative direction, we do the same, but also flip the direction of the reaction
+0176 
+0177     %first flip the reactions with negative direction
+0178     %constructEquations(minModel1,minModel1.rxns(selInd(essentialRevDir))) %looks good
+0179     minModel1 = reverseRxns(minModel1, minModel1.rxns(selInd(essentialRevDir)));
+0180     %constructEquations(minModel2,minModel2.rxns(selInd(essentialRevDir))) %looks good
+0181 
+0182     %Then make them irreversible
+0183     minModel1.rev(selInd) = 0;
+0184     minModel1.lb(selInd) = 0;
+0185 else
+0186     minModel1 = cModel;
+0187 end
+0188 %Now, remove reversibility on any reactions that can only be run in one direction - that simplifies the problem
+0189 %This takes a long time to run (at least an hour or so).
+0190 %This is an important step, we go from 5533 to 3734 reversible rxns, which reduces the size of the MILP,
+0191 %since irreversible rxns are easier to handle.
+0192 disp('Step 4: Second simplification (~1 hour)')
+0193 minModel2 = simplifyModel(minModel1,false,false,false,false,false,false,true);
+0194 
+0195 disp('Step 5: Final work')
+0196 
+0197 %Now, merge all linear dependent rxns - this reduces the size of the model a lot.
+0198 %Size goes from 11888 rxns to 7922 rxns for human-GEM, so this is an important step
+0199 [minModel3,origRxnIds,groupIds]=mergeLinear(minModel2, {});
+0200 
+0201 %we now need to figure out what happened to the essential rxns in the merge step above.
+0202 %Note that it would be ideal to run checkTasks on the result from mergeLinear, but that doesn't work.
+0203 %The exchange rxns have in many cases been merged with other reactions, making it difficult to run
+0204 %the tasks. So, instead we run checkTasks before, and figure out which merged reactions the
+0205 %essential reactions belong to now
+0206 
+0207 if ~isempty(taskStruct)
+0208 
+0209     %find all potential rxns
+0210     rxnIndOrig = ismember(origRxnIds, essentialRxns);
+0211     ids = unique(groupIds(rxnIndOrig));
+0212     ids = ids(ids > 0);%0 means it has not been merged
+0213     rxnCandidates = unique([essentialRxns;origRxnIds(ismember(groupIds, ids))]);%388, essentialRxns is 382
+0214     newEssentialRxns = minModel3.rxns(ismember(minModel3.rxns,rxnCandidates));%196 in Human-GEM
+0215     %setdiff(newEssentialRxns, essentialRxns)%only 2, that is very few
+0216     %setdiff(essentialRxns, newEssentialRxns)%188, that is very many
+0217     %It is probably the case that many essential reactions are linearly dependent on one another, which makes sense.
+0218 else
+0219     newEssentialRxns = {};
+0220 end
+0221 %What is left now is to remove some rxns from the problem, such as exchange rxns and spontaneous rxns.
+0222 %We set the rxn scores of these to exactly 0, and make sure no other scores are zero.
+0223 %runINIT will then handle this, we do >0 and <0 to get positive and negative scores, the rest
+0224 %are not included in the problem.
+0225 %But, this has to happen in the scoring - we identify the reactions in the origrxns here, and then fix this
+0226 %in the scoring for each sample (the function groupRxnScores handles this and is run for each sample).
+0227 
+0228 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+0229 %identify the reactions to ignore in the minModel2
+0230 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+0231 
+0232 %Identify reactions that should be ignored by tINIT, i.e. tINIT will not remove these regardless of score
+0233 
+0234 
+0235 [~,exchRxnInd] = getExchangeRxns(minModel2);
+0236 toIgnoreExch = false(numel(minModel2.rxns),1);
+0237 toIgnoreExch(exchRxnInd) = true;
+0238 toIgnoreImportRxns = false(numel(minModel2.rxns),1);
+0239 toIgnoreSimpleTransp = false(numel(minModel2.rxns),1);
+0240 toIgnoreAdvTransp = false(numel(minModel2.rxns),1);
+0241 toIgnoreS = false(numel(minModel2.rxns),1);
+0242 
+0243 
+0244 
+0245 %sum(toIgnore) %1500 for Human-GEM
+0246 %find simple transport reactions with no GPRs:
+0247 %We really only skip the reactions with two metabolites
+0248 %where they are the same but different compartments - only skip the transport into the cell for now
+0249 numMets = sum(minModel2.S ~= 0, 1);
+0250 scomp = find(strcmp(minModel2.comps,extComp));
+0251 for i = 1:length(minModel2.rxns)
+0252     %check that it has no GPR and that the rxn has two metabolites
+0253     if strcmp(minModel2.grRules(i),'') && (numMets(i) == 2)
+0254       metsComps = minModel2.metComps(minModel2.S(:,i) ~= 0);
+0255       metNames = minModel2.metNames(minModel2.S(:,i) ~= 0);
+0256       if metsComps(1) ~= metsComps(2) && (metsComps(1) == scomp || metsComps(2) == scomp) %different compartments, one is the extComp
+0257           if strcmp(metNames{1}, metNames{2}) %the same metabolite, it is a transport reaction
+0258               toIgnoreImportRxns(i) = true;
+0259           end
+0260       elseif metsComps(1) ~= metsComps(2) %different compartments, no check for extComp
+0261           if strcmp(metNames{1}, metNames{2}) %the same metabolite, it is a transport reaction
+0262               toIgnoreSimpleTransp(i) = true;
+0263           end
+0264       end
+0265     else %check for advanced
+0266         metsInd = find(minModel2.S(:,i) ~= 0);
+0267         %check that we have an even number of mets that is larger than 2 and that we don't have a GPR
+0268         if rem(length(metsInd),2) == 0 && length(metsInd) > 2 && isempty(minModel2.grRules{i})
+0269             %Now check that we have pairs of metabolites that are transported
+0270             SVals = full(minModel2.S(metsInd,i));
+0271             metNames = minModel2.metNames(metsInd);
+0272             comps = minModel2.metComps(metsInd);
+0273             success = true;
+0274             while ~isempty(metNames)
+0275                 if length(metNames) < 2 %this should not happen
+0276                    error('We should never arrive here!');
+0277                 end
+0278                 metMatch = find(strcmp(metNames(2:length(metNames)),metNames{1})) + 1;
+0279                 if length(metMatch) ~= 1 %we want a match, and only one (if there is some other complex transport rxn, we skip that)
+0280                    success = false;
+0281                    break;
+0282                 end
+0283                 if (SVals(1) + SVals(metMatch)) ~= 0 %the stoichiometry is not right
+0284                    success = false;
+0285                    break;
+0286                 end
+0287                 if comps(1) == comps(metMatch) %they must be in different compartments
+0288                    success = false;
+0289                    break;
+0290                 end
+0291                 %now remove the pair and continue with the next
+0292                 SVals([1;metMatch]) = [];
+0293                 metNames([1;metMatch]) = [];
+0294                 comps([1;metMatch]) = [];
+0295             end
+0296             toIgnoreAdvTransp(i) = success;
+0297         end          
+0298     end
+0299 end
+0300 
+0301 %Spontaneous reactions:
+0302 toIgnoreSpont = ismember(minModel2.rxns, spontRxnNames);%only 10 rxns in human-GEM
+0303 
+0304 %rxns without GPRs in the s compartment (outside the cell)
+0305 sCompInd = find(strcmp(minModel2.comps, extComp));
+0306 for i = 1:length(minModel2.rxns)
+0307     metsInd = find(minModel2.S(:,i) ~= 0);
+0308     %check that it has more than 1 metabolite (not exch rxn) and no GPR
+0309     if length(metsInd) > 1 && isempty(minModel2.grRules{i})
+0310         if  all(minModel2.metComps(metsInd) == sCompInd)
+0311             toIgnoreS(i) = true;
+0312         end
+0313     end
+0314 end
+0315 
+0316 toIgnoreAllWithoutGPRs = cellfun(@isempty,minModel2.grRules);
+0317 
+0318 
+0319 %sum(toIgnore)%in total 2375 rxns in human-GEM
+0320 %sum(toIgnoreAllTransp)%in total 3337 rxns in human-GEM, so 1,000 more
+0321 
+0322 %Now, try to scale the model to become more favorable for the solver.
+0323 %We try to make all fluxes as similar as possible.
+0324 %      There is room for improvement here, this function is pretty simple.
+0325 %      It only rescales reactions, while it would be possible to rescale
+0326 %      metabolites as well (ROS => kROS, albumin => millialbumin, etc., but
+0327 %      scaled freely with a mathematical method). It could potentially open up
+0328 %      for using less strict margins in the MILP, and make it run faster.
+0329 if (skipScaling)
+0330     scaledMinModel = minModel3;
+0331 else
+0332     scaledMinModel = rescaleModelForINIT(minModel3);
+0333     scaledMinModel.ub(scaledMinModel.ub > 0) = 1000;
+0334     scaledMinModel.lb(scaledMinModel.lb < 0) = -1000;
+0335 end
+0336 % create data structure with pre-processing results
+0337 prepData.taskReport = taskReport;
+0338 prepData.essentialRxns = newEssentialRxns; %essential rxns in the minModel
+0339 prepData.taskStruct = taskStruct;
+0340 prepData.refModel = cModel;
+0341 prepData.minModel = scaledMinModel;
+0342 prepData.refModelWithBM = closeModel(cModel); %do this here so we don't have to do this for each sample
+0343 prepData.groupIds = groupIds;
+0344 prepData.essentialMetsForTasks = essentialMetsForTasks;
+0345 
+0346 prepData.toIgnoreExch = toIgnoreExch;
+0347 prepData.toIgnoreImportRxns = toIgnoreImportRxns;
+0348 prepData.toIgnoreSimpleTransp = toIgnoreSimpleTransp;
+0349 prepData.toIgnoreAdvTransp = toIgnoreAdvTransp;
+0350 prepData.toIgnoreSpont = toIgnoreSpont;
+0351 prepData.toIgnoreS = toIgnoreS;
+0352 prepData.toIgnoreCustomRxns = ismember(minModel2.rxns, customRxnsToIgnore);
+0353 prepData.toIgnoreAllWithoutGPRs = toIgnoreAllWithoutGPRs;
+0354 
+0355 end
+
Generated by m2html © 2005
+ + \ No newline at end of file diff --git a/docs/html/INIT/rescaleModelForINIT.html b/docs/html/INIT/rescaleModelForINIT.html new file mode 100644 index 00000000..d39cf456 --- /dev/null +++ b/docs/html/INIT/rescaleModelForINIT.html @@ -0,0 +1,152 @@ + + + + Description of rescaleModelForINIT + + + + + + + + + +
Home > INIT > rescaleModelForINIT.m
+ + + +

rescaleModelForINIT +

+ +

PURPOSE ^

+
rescaleModelForINIT Rescale a model to avoid large stoichiometric differences.
+ +

SYNOPSIS ^

+
function model=rescaleModelForINIT(model, varargin)
+ +

DESCRIPTION ^

+
 rescaleModelForINIT  Rescale a model to avoid large stoichiometric differences.
+
+ Rescales the stoichiometric matrix for the MILP problem in ftINIT to reduce
+ differences in flux magnitudes between reactions. Such differences cause,
+ among other effects, difficulties with the tolerances for integer variables.
+ In each reaction, each coefficient with an absolute value above
+ maxStoichDiff times the smallest absolute coefficient of the reaction is
+ set to that limit (keeping its sign). The reaction is then multiplied by a
+ factor that sets the mean absolute coefficient to 1.
+
+ Parameters
+ ----------
+ model : struct
+     a model structure.
+
+ Name-Value Arguments
+ --------------------
+ maxStoichDiff : double
+     largest allowed ratio between the largest and the smallest absolute
+     stoichiometric coefficient in a reaction (default 25).
+
+ Returns
+ -------
+ model : struct
+     the rescaled model structure.
+ + +

CROSS-REFERENCE INFORMATION ^

+This function calls: +
    +
+This function is called by: +
    +
  • prepINITModel prepINITModel Precompute omics-independent data for ftINIT.
+ + + + +

SOURCE CODE ^

+
0001 function model=rescaleModelForINIT(model, varargin)
+0002 % rescaleModelForINIT  Rescale a model to avoid large stoichiometric differences.
+0003 %
+0004 % Rescales the stoichiometric matrix for the MILP problem in ftINIT to reduce
+0005 % differences in flux magnitudes between reactions. Such differences cause,
+0006 % among other effects, difficulties with the tolerances for integer variables.
+0007 % In each reaction, each coefficient with an absolute value above
+0008 % maxStoichDiff times the smallest absolute coefficient of the reaction is
+0009 % set to that limit (keeping its sign). The reaction is then multiplied by a
+0010 % factor that sets the mean absolute coefficient to 1.
+0011 %
+0012 % Parameters
+0013 % ----------
+0014 % model : struct
+0015 %     a model structure.
+0016 %
+0017 % Name-Value Arguments
+0018 % --------------------
+0019 % maxStoichDiff : double
+0020 %     largest allowed ratio between the largest and the smallest absolute
+0021 %     stoichiometric coefficient in a reaction (default 25).
+0022 %
+0023 % Returns
+0024 % -------
+0025 % model : struct
+0026 %     the rescaled model structure.
+0027 
+0028 p=parseRAVENargs(varargin, {'maxStoichDiff',25});
+0029 maxStoichDiff=p.maxStoichDiff;
+0030 
+0031 %Define maxMinRatio to be the ratio between the largest and smallest
+0032 %stoichiometric coefficients in each reaction.
+0033 %Scale all rxns with maxMinRatio > maxStoichDiff - just set all coeffs that are
+0034 %larger than maxStoichDiff*minVal to that value, and center the mean coeff to 1 for all rxns.
+0035 SAbs = abs(model.S);
+0036 for i = 1:numel(model.rxns)
+0037     tmp = SAbs(:,i);
+0038     tmp = tmp(tmp ~= 0);
+0039     mn = min(tmp);
+0040     %modify matrix
+0041     sign = ones(numel(model.mets),1);
+0042     sign(model.S(:,i) < 0) = -1;
+0043     sel = SAbs(:,i) > maxStoichDiff*mn;
+0044     model.S(sel,i) = sign(sel) .* maxStoichDiff*mn;
+0045 end
+0046 %center around 1
+0047 boolMat = model.S ~= 0;
+0048 absMat = abs(model.S);
+0049 rxnScales = sum(boolMat,1)./sum(absMat,1);
+0050 
+0051 model.S = model.S .* rxnScales;
+0052 
+0053 %{
+0054 if (nargin < 2)
+0055     maxStoichVal = 250;
+0056 end
+0057 
+0058 
+0059 %find all reactions containing high stoichiometry coefficients
+0060 maxCoeffs = max(model.S, [], 1);
+0061 rxnsToCheck = maxCoeffs > maxStoichVal;
+0062 
+0063 %for debugging:
+0064 %constructEquations(model, model.rxns(rxnsToCheck))
+0065 
+0066 rxnsToCheckInd = find(rxnsToCheck);
+0067 
+0068 %We just scale all reactions so they don't have a coefficient > maxStoichVal for now
+0069 
+0070 %do this with a loop, doesn't really matter
+0071 for rxnInd = rxnsToCheckInd
+0072     %find the lowest index which is not a pointless metabolite, such as H+, H2O etc.
+0073     rxnS = model.S(:,rxnInd);
+0074     %metInd = find(rxnS ~= 0);
+0075     
+0076     largestCoeff = max(abs(rxnS));
+0077     scaleFactor = maxStoichVal/largestCoeff;
+0078     model.S(:,rxnInd) = model.S(:,rxnInd) .* scaleFactor;
+0079 end
+0080 %}
+0081 end
+
Generated by m2html © 2005
+ + \ No newline at end of file diff --git a/doc/omics/scoreModel.html b/docs/html/INIT/scoreModel.html similarity index 82% rename from doc/omics/scoreModel.html rename to docs/html/INIT/scoreModel.html index 3279f4c6..42792c3c 100644 --- a/doc/omics/scoreModel.html +++ b/docs/html/INIT/scoreModel.html @@ -12,10 +12,10 @@ -
Home > omics > scoreModel.m
+
Home > INIT > scoreModel.m
+Index for INIT >-->

scoreModel

@@ -30,11 +30,8 @@

DESCRIPTION ^
 scoreModel  Score reactions and genes in a model with complex gene rules.
 
  Scores the reactions and genes in a model containing complex gene rules
- based on expression data from HPA and/or gene arrays.
-
- It is highly recommended that the model grRules are "cleaned" and verified by
- the "cleanModelGeneRules" function prior to scoring the model with
- scoreModel.
+ based on expression data from HPA, gene arrays or both. The model grRules
+ should be standardised with standardizeGrRules before scoring.
 
  Parameters
  ----------
@@ -48,26 +45,27 @@ 

DESCRIPTION ^DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 </ul>
 This function is called by:
 <ul style= - +
  • ftINIT
  • SUBFUNCTIONS ^

    @@ -129,79 +129,79 @@

    SOURCE CODE ^% scoreModel Score reactions and genes in a model with complex gene rules. 0003 % 0004 % Scores the reactions and genes in a model containing complex gene rules -0005 % based on expression data from HPA and/or gene arrays. -0006 % -0007 % It is highly recommended that the model grRules are "cleaned" and verified by -0008 % the "cleanModelGeneRules" function prior to scoring the model with -0009 % scoreModel. -0010 % -0011 % Parameters -0012 % ---------- -0013 % model : struct -0014 % a model structure. -0015 % hpaData : struct -0016 % HPA data structure from parseHPA (optional if arrayData is supplied, -0017 % default []). -0018 % arrayData : struct -0019 % gene expression data structure (optional if hpaData is supplied, default -0020 % []). With fields: -0021 % -0022 % - genes : cell array with the unique gene names. -0023 % - tissues : cell array with the tissue names. The list may not be unique, -0024 % as there can be multiple cell types per tissue. -0025 % - celltypes : cell array with the cell type names for each tissue. -0026 % - levels : GENESxTISSUES array with the expression level for each gene in -0027 % each tissue/celltype. NaN should be used when no measurement was -0028 % performed. -0029 % - threshold : a single value or a vector of gene expression thresholds, -0030 % above which genes are considered to be "expressed". (optional, by -0031 % default, the mean expression levels of each gene across all tissues in -0032 % arrayData will be used as the threshold values). -0033 % tissue : char -0034 % tissue to score for. Should exist in either hpaData.tissues or -0035 % arrayData.tissues. -0036 % -0037 % Name-Value Arguments -0038 % -------------------- -0039 % celltype : char -0040 % cell type to score for. Should exist in either hpaData.celltypes or -0041 % arrayData.celltypes for this tissue (default is to use the max values -0042 % among all the cell types for the tissue). -0043 % noGeneScore : double -0044 % score for reactions without genes (default -2). -0045 % isozymeScoring : char -0046 % determines how scores are calculated for reactions with multiple genes -0047 % joined by "OR" expression(s) ("min", "max", "median", "average") -0048 % (default "max"). -0049 % complexScoring : char -0050 % determines how scores are calculated for reactions with multiple genes -0051 % joined by "AND" expression(s) ("min", "max", "median", "average") -0052 % (default "min"). -0053 % multipleCellScoring : char -0054 % determines how scores are calculated when several cell types are used -0055 % ("max" or "average") (default "max"). -0056 % hpaLevelScores : struct -0057 % structure with numerical scores for the expression level categories from -0058 % HPA. The structure should have a "names" and a "scores" field (default, -0059 % see code for default scores). -0060 % dataPrecedence : char -0061 % how hpaData takes precedence over arrayData when both are supplied -0062 % (default "gene"): -0063 % -0064 % - "gene" : per gene. A gene with HPA data is scored from it, a gene -0065 % without is scored from arrayData, and a rule can mix the two. -0066 % - "reaction" : per rule. As soon as any one gene in a rule has HPA data, -0067 % the whole rule is scored from HPA alone and its array-only genes count -0068 % as unmeasured. -0069 % -0070 % Returns -0071 % ------- -0072 % rxnScores : double -0073 % scores for each of the reactions in model. -0074 % geneScores : double -0075 % scores for each of the genes in model. Genes which are not in the -0076 % dataset(s) have NaN as scores, which removeLowScoreGenes reads as "no -0077 % evidence" and leaves in place, where a negative score would be pruned. +0005 % based on expression data from HPA, gene arrays or both. The model grRules +0006 % should be standardised with standardizeGrRules before scoring. +0007 % +0008 % Parameters +0009 % ---------- +0010 % model : struct +0011 % a model structure. +0012 % hpaData : struct +0013 % HPA data structure from parseHPA (optional if arrayData is supplied, +0014 % default []). +0015 % arrayData : struct +0016 % gene expression data structure (optional if hpaData is supplied, default +0017 % []). With fields: +0018 % +0019 % - genes : cell array with the unique gene names. +0020 % - tissues : cell array with the tissue names. The list can contain +0021 % duplicates, as there can be multiple cell types per tissue. +0022 % - celltypes : cell array with the cell type names for each tissue. +0023 % - levels : GENESxTISSUES array with the expression level for each gene in +0024 % each tissue and cell type. NaN marks a gene that was not measured. +0025 % - threshold : a single value or a vector of gene expression thresholds, +0026 % above which genes are considered to be "expressed". If omitted, the +0027 % mean expression level of each gene across all tissues in arrayData +0028 % is used as the threshold. +0029 % tissue : char +0030 % tissue to score for. Must exist in either hpaData.tissues or +0031 % arrayData.tissues (matched without regard to case); otherwise an error +0032 % is raised. +0033 % +0034 % Name-Value Arguments +0035 % -------------------- +0036 % celltype : char +0037 % cell type to score for. Must exist in either hpaData.celltypes or +0038 % arrayData.celltypes (matched without regard to case); otherwise an error +0039 % is raised (default [], which uses all cell types of the tissue, +0040 % combined as set by multipleCellScoring). +0041 % noGeneScore : double +0042 % score for reactions without genes (default -2). +0043 % isozymeScoring : char +0044 % determines how scores are calculated for reactions with multiple genes +0045 % joined by "OR" expression(s) ("min", "max", "median", "average") +0046 % (default "max"). +0047 % complexScoring : char +0048 % determines how scores are calculated for reactions with multiple genes +0049 % joined by "AND" expression(s) ("min", "max", "median", "average") +0050 % (default "min"). +0051 % multipleCellScoring : char +0052 % determines how scores are calculated when several cell types are used +0053 % ("max" or "average") (default "max"). +0054 % hpaLevelScores : struct +0055 % structure with numerical scores for the expression level categories from +0056 % HPA. The structure has a "names" and a "scores" field. The default names +0057 % are High, Medium, Low, None, Strong, Moderate, Weak, Negative and Not +0058 % detected, with the scores 20, 15, 10, -8, 20, 15, 10, -8 and -8. +0059 % dataPrecedence : char +0060 % how hpaData takes precedence over arrayData when both are supplied +0061 % (default "gene"): +0062 % +0063 % - "gene" : per gene. A gene with HPA data is scored from it, a gene +0064 % without is scored from arrayData, and a rule can mix the two. +0065 % - "reaction" : per rule. As soon as any one gene in a rule has HPA data, +0066 % the whole rule is scored from HPA alone and its array-only genes count +0067 % as unmeasured. +0068 % +0069 % Returns +0070 % ------- +0071 % rxnScores : double +0072 % scores for each of the reactions in model. +0073 % geneScores : double +0074 % scores for each of the genes in model. Genes which are not in the +0075 % dataset(s) have NaN as scores. removeLowScoreGenes reads NaN as "no +0076 % evidence" and leaves the gene in place, and prunes genes with a +0077 % negative score. 0078 % hpaScores : double 0079 % scores for each of the genes in model if only taking hpaData into 0080 % account. Genes which are not in the dataset(s) have -Inf as scores. diff --git a/docs/html/INIT/tINIT/getINITModel.html b/docs/html/INIT/tINIT/getINITModel.html new file mode 100644 index 00000000..0da8a679 --- /dev/null +++ b/docs/html/INIT/tINIT/getINITModel.html @@ -0,0 +1,547 @@ + + + + Description of getINITModel + + + + + + + + + +
    Home > INIT > tINIT > getINITModel.m
    + + + +

    getINITModel +

    + +

    PURPOSE ^

    +
    + +

    SYNOPSIS ^

    +
    This is a script file.
    + +

    DESCRIPTION ^

    +
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: + + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [model, metProduction, essentialRxnsForTasks, addedRxnsForTasks, deletedDeadEndRxns, deletedRxnsInINIT, taskReport]=getINITModel(refModel, tissue, varargin)
    +0002 % getINITModel  Generate a model using the original tINIT algorithm.
    +0003 %
    +0004 % Generates a model using the tINIT algorithm, from proteomics,
    +0005 % transcriptomics, metabolomics and metabolic tasks in any combination (PLoS
    +0006 % Comput Biol. 2012;8(5):e1002518, Mol Syst Biol. 2014;10:721).
    +0007 %
    +0008 % ftINIT replaces this function for context-specific model extraction. This
    +0009 % function is kept to reproduce models built with the original tINIT. It
    +0010 % issues a RAVEN:legacyMethod notice once per session, which
    +0011 % warning('off','RAVEN:legacyMethod') silences.
    +0012 %
    +0013 % Not all settings are available through this function; runINIT and fitTasks
    +0014 % can be called individually to set the others.
    +0015 %
    +0016 % Parameters
    +0017 % ----------
    +0018 % refModel : struct
    +0019 %     a model structure in the closed form (no exchange reactions open),
    +0020 %     imported with importModel(filename,false). A model that is not loaded
    +0021 %     with importModel can lack the "unconstrained" field, and the function
    +0022 %     then raises RAVEN:badInput. The field can be added with
    +0023 %     model.unconstrained=false(numel(model.mets),1).
    +0024 % tissue : char
    +0025 %     tissue to score for. Must exist in either hpaData.tissues or
    +0026 %     arrayData.tissues (scoreModel raises an error otherwise).
    +0027 %
    +0028 % Name-Value Arguments
    +0029 % --------------------
    +0030 % celltype : char
    +0031 %     cell type to score for. Must exist in either hpaData.celltypes or
    +0032 %     arrayData.celltypes (scoreModel raises an error otherwise). The default
    +0033 %     is to use the maximum values among all the cell types for the tissue.
    +0034 % hpaData : struct
    +0035 %     HPA data structure from parseHPA (optional if arrayData is supplied,
    +0036 %     default []).
    +0037 % arrayData : struct
    +0038 %     gene expression data structure (optional if hpaData is supplied, default
    +0039 %     []). With fields:
    +0040 %
    +0041 %     - genes : cell array with the unique gene names.
    +0042 %     - tissues : cell array with the tissue names. The list can contain
    +0043 %       duplicates, as there can be multiple cell types per tissue.
    +0044 %     - celltypes : cell array with the cell type names for each tissue.
    +0045 %     - levels : GENESxTISSUES array with the expression level for each gene in
    +0046 %       each tissue and cell type. NaN marks a gene that was not measured.
    +0047 %     - threshold : a single value or a vector of gene expression thresholds,
    +0048 %       above which genes are considered to be "expressed". If omitted, the
    +0049 %       mean expression level of each gene across all tissues in arrayData
    +0050 %       is used as the threshold.
    +0051 %     - singleCells : binary value selecting whether to use the single-cell
    +0052 %       algorithm to identify expressed genes. If used, the cell
    +0053 %       subpopulations are specified in celltypes (default []).
    +0054 %     - plotResults : true if single cell probability distributions are
    +0055 %       plotted (default false).
    +0056 % metabolomicsData : cell
    +0057 %     cell array with metabolite names that the model should produce (default
    +0058 %     []).
    +0059 % taskFile : char
    +0060 %     a task list in Excel format. See parseTaskList for details (default []).
    +0061 % useScoresForTasks : logical
    +0062 %     true if the calculated reaction scores are used as weights in the
    +0063 %     fitting to tasks. Scores above -0.1 are set to -0.1 (default true).
    +0064 % printReport : logical
    +0065 %     true if a report is printed to the screen (default true).
    +0066 % taskStructure : struct
    +0067 %     task structure as returned by parseTaskList. It defines the tasks when
    +0068 %     Excel sheets are not used and overrides taskFile (default []).
    +0069 % params : struct
    +0070 %     parameter structure for use by the INIT solver (default []).
    +0071 % paramsFT : struct
    +0072 %     parameter structure for the fitTasks step (default []).
    +0073 % seed : double
    +0074 %     seed for the random number generator used to break ties among genes
    +0075 %     with equal uninformative (-Inf) scores, which makes the choice of the
    +0076 %     kept gene reproducible across runs (default [], which leaves the rng
    +0077 %     state unchanged).
    +0078 %
    +0079 % Returns
    +0080 % -------
    +0081 % model : struct
    +0082 %     the resulting model structure.
    +0083 % metProduction : double
    +0084 %     array that indicates which of the metabolites in metabolomicsData could
    +0085 %     be produced. The array is determined before the gap-filling for the
    +0086 %     defined tasks. To see which metabolites can be produced in the final
    +0087 %     model, use canExchange:
    +0088 %
    +0089 %     - -2 : metabolite name not found in model.
    +0090 %     - -1 : metabolite found, but it could not be produced.
    +0091 %     - 1 : metabolite could be produced.
    +0092 % essentialRxnsForTasks : cell
    +0093 %     cell array of the reactions which were essential to perform the tasks.
    +0094 % addedRxnsForTasks : cell
    +0095 %     cell array of the reactions which were added in order to perform the
    +0096 %     tasks.
    +0097 % deletedDeadEndRxns : cell
    +0098 %     cell array of reactions deleted because they could not carry flux (INIT
    +0099 %     requires a functional input model).
    +0100 % deletedRxnsInINIT : cell
    +0101 %     cell array of the reactions which were deleted by the INIT algorithm.
    +0102 % taskReport : struct
    +0103 %     structure with the results for each task. With fields:
    +0104 %
    +0105 %     - id : cell array with the id of the task.
    +0106 %     - description : cell array with the description of the task.
    +0107 %     - ok : boolean array with true if the task was successful.
    +0108 %     - essential : cell array with cell arrays of essential reactions for the
    +0109 %       task.
    +0110 %     - gapfill : cell array of cell arrays of reactions included in the
    +0111 %       gap-filling for the task.
    +0112 %
    +0113 % Notes
    +0114 % -----
    +0115 % The model keeps its exchange metabolites, since checkTasks
    +0116 % and fitTasks put specific constraints on them for each task. The INIT
    +0117 % algorithm removes exchange metabolites if any are present. Use
    +0118 % importModel(file,false) to import a model with exchange metabolites
    +0119 % remaining.
    +0120 %
    +0121 % See also
    +0122 % --------
    +0123 % ftINIT, prepINITModel, runINIT, scoreModel
    +0124 
    +0125 legacyMethodNotice('getINITModel','ftINIT');
    +0126 
    +0127 p=parseRAVENargs(varargin, {'celltype',[]; 'hpaData',[]; 'arrayData',[]; 'metabolomicsData',[]; 'taskFile',[]; 'useScoresForTasks',[]; 'printReport',[]; 'taskStructure',[]; 'params',[]; 'paramsFT',[]; 'seed',[]});
    +0128 if ~isempty(p.seed)
    +0129     rng(p.seed);
    +0130 end
    +0131 celltype=p.celltype;
    +0132 hpaData=p.hpaData;
    +0133 arrayData=p.arrayData;
    +0134 metabolomicsData=p.metabolomicsData;
    +0135 taskFile=p.taskFile;
    +0136 useScoresForTasks=p.useScoresForTasks;
    +0137 printReport=p.printReport;
    +0138 taskStructure=p.taskStructure;
    +0139 params=p.params;
    +0140 paramsFT=p.paramsFT;
    +0141 if ~isempty(celltype)
    +0142     celltype=char(celltype);
    +0143 end
    +0144 if ~isempty(taskFile)
    +0145     taskFile=char(taskFile);
    +0146 end
    +0147 if isempty(useScoresForTasks)
    +0148     useScoresForTasks=true;
    +0149 end
    +0150 if isempty(printReport)
    +0151     printReport=true;
    +0152 end
    +0153 
    +0154 %Check that the model is in the closed form
    +0155 if ~isfield(refModel,'unconstrained')
    +0156     EM='Exchange metabolites should normally not be removed from the model when using getINITModel. Use importModel(file,false) to import a model with exchange metabolites remaining (see the documentation for details)';
    +0157     error('RAVEN:badInput', '%s', EM);
    +0158 end
    +0159 
    +0160 %Create the task structure if not supplied
    +0161 if any(taskFile) && isempty(taskStructure)
    +0162     taskStructure=parseTaskList(taskFile);
    +0163 end
    +0164 
    +0165 
    +0166 % sc-tINIT to identify confidence levels of gene expression
    +0167 if ~isempty(arrayData) && isfield(arrayData,'singleCells')
    +0168     if arrayData.singleCells == 1
    +0169         % Check to ensure cell type is defined
    +0170         if ~isfield(arrayData,'celltypes')
    +0171             warning('RAVEN:warning', '%s', 'arrayData must contain cell type information if sc-tINIT is to be used');
    +0172         end
    +0173         if ~ismember(upper(celltype),upper(arrayData.celltypes))
    +0174             error('RAVEN:badInput', '%s', 'The cell type name does not match');
    +0175         end
    +0176         
    +0177         % Analyze only cell type of interest
    +0178         J= strcmpi(arrayData.celltypes,celltype);
    +0179         
    +0180         % Analyze only genes included in the reference model
    +0181         I=ismember(arrayData.genes,refModel.genes);
    +0182         
    +0183         % Convert expression data to population fractions
    +0184         binary_levels = arrayData.levels(I,J)~=0; % Classify each gene as detected (1) or not (0) in each cell
    +0185         cell_count_levels = sum(binary_levels,2); % Number of cells expressing each transcript
    +0186         cell_frac_levels = cell_count_levels/size(binary_levels,2); % Number of cells expressing each transcript
    +0187 
    +0188         % Bin cell_frac_counts manually
    +0189         x = 0:.01:1;
    +0190         for(i = 1:length(x))
    +0191             cell_frac_count(i) = sum(round(cell_frac_levels,2)==x(i));
    +0192         end
    +0193         
    +0194         % Fit four beta distributions
    +0195         cell_frac_count(cell_frac_count==0) = NaN; % Remove zeros from optimization
    +0196         cell_frac_count(1) = NaN; % Remove non-expressed genes from optimization
    +0197         x_lim = 1; % Somewhat arbitrary parameter to fit left tail of distr.
    +0198         myfun = @(par) sum((cell_frac_count(1:find(x>=x_lim,1)) - ...
    +0199             abs(par(1))*betapdf(x(1:find(x>=x_lim,1)),abs(par(2)),abs(par(3))) - ...
    +0200             abs(par(4))*betapdf(x(1:find(x>=x_lim,1)),abs(par(5)),abs(par(6))) - ...
    +0201             abs(par(7))*betapdf(x(1:find(x>=x_lim,1)),abs(par(8)),abs(par(9))) - ...
    +0202             abs(par(10))*betapdf(x(1:find(x>=x_lim,1)),abs(par(11)),abs(par(12)))).^2, 'omitnan');
    +0203         
    +0204         par0 = [4,2,100,7,2,30,7,5,20,5,15,20];
    +0205         opts = optimset('Display','off');
    +0206         [par,f_val] = fminsearch(myfun,par0,opts);
    +0207         par = abs(par);
    +0208         
    +0209         % Plot results
    +0210         if (isfield(arrayData,'plotResults'))
    +0211             if arrayData.plotResults == true
    +0212                 figure(); hold on; plot(x,cell_frac_count,'ko','MarkerSize',5);
    +0213                 plot(x,abs(par(1))*betapdf(x,abs(par(2)),abs(par(3))),'b-','LineWidth',1)
    +0214                 plot(x,abs(par(4))*betapdf(x,abs(par(5)),abs(par(6))),'b-','LineWidth',1)
    +0215                 plot(x,abs(par(7))*betapdf(x,abs(par(8)),abs(par(9))),'b-','LineWidth',1)
    +0216                 plot(x,abs(par(10))*betapdf(x,abs(par(11)),abs(par(12))),'b-','LineWidth',1)
    +0217                 plot(x,abs(par(1))*betapdf(x,abs(par(2)),abs(par(3))) + ...
    +0218                     abs(par(4))*betapdf(x,abs(par(5)),abs(par(6))) + ...
    +0219                     abs(par(7))*betapdf(x,abs(par(8)),abs(par(9))) + ...
    +0220                     abs(par(10))*betapdf(x,abs(par(11)),abs(par(12))),'-','Color',[.5 .5 .5],'LineWidth',2)
    +0221                 xlabel('Expression Probability');ylabel('# of genes');set(gca,'FontSize',14,'LineWidth',1.25);
    +0222                 title('Expression prediction','FontSize',18,'FontWeight','bold')
    +0223             end
    +0224         end
    +0225         
    +0226         % Score genes based on population expression (p = .05)
    +0227         exprs_cutoff_1 = find(cdf('beta',x,par(2),par(3)) >.95,1)-1; % Find index of no confidence genes
    +0228         exprs_cutoff_2 = find(cdf('beta',x,par(5),par(6)) >.95,1)-1; % Find index of low confidence genes
    +0229         exprs_cutoff_3 = find(cdf('beta',x,par(8),par(9)) >.95,1)-1; % Find index of low confidence genes
    +0230         exprs_cutoffs = sort([exprs_cutoff_1,exprs_cutoff_2,exprs_cutoff_3]);
    +0231         gene_scores = cell_frac_levels*0;
    +0232         gene_scores(cell_frac_levels <= x(exprs_cutoffs(1))) = 4; % Not detected
    +0233         gene_scores(logical((cell_frac_levels >= x(exprs_cutoffs(1))).*(cell_frac_levels < x(exprs_cutoffs(2))))) = 3; % Low detection
    +0234         gene_scores(logical((cell_frac_levels >= x(exprs_cutoffs(2))).*(cell_frac_levels < x(exprs_cutoffs(3))))) = 2; % Medium detection
    +0235         gene_scores(cell_frac_levels > x(exprs_cutoffs(3))) = 1; % High detection
    +0236 
    +0237         % Replace hpaData with singleCellData
    +0238         if printReport==true
    +0239             %warning()'s '%s' passes the message through literally, so its
    +0240             %"\n" must be resolved to a real newline beforehand, not left
    +0241             %to print as a literal backslash-n.
    +0242             warning('RAVEN:warning', '%s', sprintf('Single cell data is not currently compatible with HPA data. \n         Replacing hpaData with single cell-based scoring.'));
    +0243         end
    +0244         hpaData.genes = arrayData.genes;
    +0245         hpaData.tissues = arrayData.tissues;
    +0246         hpaData.celltypes = arrayData.celltypes;
    +0247         hpaData.levels = [{'High'},{'Medium'},{'Low'},{'None'}];
    +0248         hpaData.gene2Level = zeros(length(arrayData.genes),length(arrayData.celltypes));
    +0249         for i = 1:length(find(J))
    +0250             find_var = find(J,i);
    +0251             hpaData.gene2Level(I,find_var(end)) = gene_scores;
    +0252         end
    +0253         
    +0254         % Remove arrayData from the analysis
    +0255         clear arrayData
    +0256         arrayData=[];
    +0257     end
    +0258 end
    +0259 
    +0260 
    +0261 if printReport==true
    +0262     if any(celltype)
    +0263         fprintf(['***Generating model for: ' tissue ' - ' celltype '\n']);
    +0264     else
    +0265         fprintf(['***Generating model for: ' tissue '\n']);
    +0266     end
    +0267     fprintf('-Using the legacy tINIT algorithm; ftINIT is the recommended method\n');
    +0268     if ~isempty(hpaData)
    +0269         fprintf('-Using HPA data\n');
    +0270     end
    +0271     if ~isempty(arrayData)
    +0272         fprintf('-Using array data\n');
    +0273     end
    +0274     if ~isempty(metabolomicsData)
    +0275         fprintf('-Using metabolomics data\n');
    +0276     end
    +0277     if ~isempty(taskFile) || ~isempty(taskStructure)
    +0278         fprintf('-Using metabolic tasks\n');
    +0279     end
    +0280     fprintf('\n');
    +0281     
    +0282     printScores(refModel,'Reference model statistics',hpaData,arrayData,tissue,celltype);
    +0283 end
    +0284 
    +0285 %Remove dead-end reactions to speed up the optimization and to
    +0286 %differentiate between reactions removed by INIT and those that are
    +0287 %dead-end
    +0288 [~, deletedDeadEndRxns]=simplifyModel(refModel,true,false,true,true,true);
    +0289 cModel=removeReactions(refModel,deletedDeadEndRxns,false,true);
    +0290 
    +0291 %Store the connected model like this to keep track of stuff
    +0292 if printReport==true
    +0293     printScores(cModel,'Pruned model statistics',hpaData,arrayData,tissue,celltype);
    +0294 end
    +0295 
    +0296 %If tasks have been defined, then go through them and get essential
    +0297 %reactions
    +0298 if ~isempty(taskStructure)
    +0299     [taskReport, essentialRxnMat]=checkTasks(cModel,[],printReport,true,true,taskStructure);
    +0300     
    +0301     essentialRxnsForTasks=cModel.rxns(any(essentialRxnMat,2));
    +0302     
    +0303     %Remove tasks that cannot be performed
    +0304     taskStructure(taskReport.ok==false)=[];
    +0305     if printReport==true
    +0306         printScores(removeReactions(cModel,setdiff(cModel.rxns,essentialRxnsForTasks),true,true),'Reactions essential for tasks',hpaData,arrayData,tissue,celltype);
    +0307     end
    +0308 else
    +0309     essentialRxnsForTasks={};
    +0310 end
    +0311 
    +0312 %Score the connected model. isozymeScoring/complexScoring use one operator
    +0313 %for both and/or (a rule reduces to a single statistic over its genes) and
    +0314 %dataPrecedence is per-reaction, matching the original tINIT algorithm.
    +0315 [rxnScores, geneScores]=scoreModel(cModel,hpaData,arrayData,tissue,celltype, ...
    +0316     'isozymeScoring','max','complexScoring','max','dataPrecedence','reaction');
    +0317 %scoreModel marks an unmeasured gene NaN (removeLowScoreGenes reads that as
    +0318 %"no evidence" and leaves it be); this algorithm instead needs -Inf, so an
    +0319 %unmeasured gene never ranks equal-or-above a measured one further down.
    +0320 geneScores(isnan(geneScores))=-Inf;
    +0321 
    +0322 %Run the INIT algorithm. The exchange reactions that are used in the final
    +0323 %reactions will be open, which does not fit with the last step. Therefore
    +0324 %delete reactions from the original model instead of taking the output. The
    +0325 %default implementation does not constrain reversible reactions to only
    +0326 %carry flux in one direction. Runs without the constraints on reversibility
    +0327 %and with all output allowed. This is to reduce the complexity of the
    +0328 %problem.
    +0329 [~, deletedRxnsInINIT, metProduction]=runINIT(simplifyModel(cModel),rxnScores,metabolomicsData,essentialRxnsForTasks,0,true,false,params);
    +0330 initModel=removeReactions(cModel,deletedRxnsInINIT,true,true);
    +0331 if printReport==true
    +0332     printScores(initModel,'INIT model statistics',hpaData,arrayData,tissue,celltype);
    +0333     printScores(removeReactions(cModel,setdiff(cModel.rxns,deletedRxnsInINIT),true,true),'Reactions deleted by INIT',hpaData,arrayData,tissue,celltype);
    +0334 end
    +0335 
    +0336 %The full model has exchange reactions in it. fitTasks calls on fillGaps,
    +0337 %which automatically removes exchange metabolites (because it assumes that
    +0338 %the reactions are constrained when appropriate). In this case the
    +0339 %uptakes/outputs are retrieved from the task sheet instead. To prevent
    +0340 %exchange reactions being used to fill gaps, they are delete from the
    +0341 %reference model here.
    +0342 initModel.id='INITModel';
    +0343 
    +0344 %If gaps in the model should be filled using a task list
    +0345 if ~isempty(taskStructure)
    +0346     %Remove exchange reactions and reactions already included in the INIT
    +0347     %model
    +0348     refModelNoExc=removeReactions(refModel,union(initModel.rxns,getExchangeRxns(refModel)),true,true);
    +0349     
    +0350     %At this stage the model is fully connected and most of the genes with
    +0351     %good scores should have been included. The final gap-filling should
    +0352     %take the scores of the genes into account, so that "rather bad"
    +0353     %reactions are preferred to "very bad" reactions. However, reactions
    +0354     %with positive scores will be included even if they are not connected
    +0355     %in the current formulation. Therefore, such reactions will have to be
    +0356     %assigned a small negative score instead.
    +0357     if useScoresForTasks==true
    +0358         refRxnScores=scoreModel(refModelNoExc,hpaData,arrayData,tissue,celltype, ...
    +0359             'isozymeScoring','max','complexScoring','max','dataPrecedence','reaction');
    +0360         [outModel, addedRxnMat]=fitTasks(initModel,refModelNoExc,[],true,min(refRxnScores,-0.1),taskStructure,paramsFT);
    +0361     else
    +0362         [outModel, addedRxnMat]=fitTasks(initModel,refModelNoExc,[],true,[],taskStructure,paramsFT);
    +0363     end
    +0364     if printReport==true
    +0365         printScores(outModel,'Functional model statistics',hpaData,arrayData,tissue,celltype);
    +0366         printScores(removeReactions(outModel,intersect(outModel.rxns,initModel.rxns),true,true),'Reactions added to perform the tasks',hpaData,arrayData,tissue,celltype);
    +0367     end
    +0368     
    +0369     addedRxnsForTasks=refModelNoExc.rxns(any(addedRxnMat,2));
    +0370 else
    +0371     outModel=initModel;
    +0372     addedRxnMat=[];
    +0373     addedRxnsForTasks={};
    +0374 end
    +0375 
    +0376 %The model can now perform all the tasks defined in the task list. The
    +0377 %algorithm cannot deal with gene-complexes at the moment. It is therefore
    +0378 %ok to remove bad genes from a reaction (as long as at least one gene is
    +0379 %kept)
    +0380 model=outModel;
    +0381 
    +0382 [~, I]=ismember(model.genes,cModel.genes); %All should be found
    +0383 %This is a little weird way to make sure that only one bad gene is included
    +0384 %if there are no good ones (since all -Inf==max(-Inf))
    +0385 geneScores(isinf(geneScores))=-1000+rand(sum(isinf(geneScores)),1);
    +0386 
    +0387 model.grRules(:)={''};
    +0388 for i=1:numel(model.rxns)
    +0389     ids=find(model.rxnGeneMat(i,:));
    +0390     if numel(ids)>1
    +0391         scores=geneScores(I(ids));
    +0392         %Only keep the positive ones if possible
    +0393         model.rxnGeneMat(i,ids(~(scores>0 | scores==max(scores))))=0;
    +0394     end
    +0395     %Rewrite the grRules to be only OR
    +0396     if isfield(model,'grRules')
    +0397         J=find(model.rxnGeneMat(i,:));
    +0398         for j=1:numel(J)
    +0399             model.grRules{i}=[model.grRules{i} '(' model.genes{J(j)} ')'];
    +0400             if j<numel(J)
    +0401                 model.grRules{i}=[model.grRules{i} ' or '];
    +0402             end
    +0403         end
    +0404     end
    +0405 end
    +0406 
    +0407 %Find all genes that are not used and delete them
    +0408 I=sum(model.rxnGeneMat)==0;
    +0409 model.genes(I)=[];
    +0410 model.rxnGeneMat(:,I)=[];
    +0411 if isfield(model,'geneShortNames')
    +0412     model.geneShortNames(I)=[];
    +0413 end
    +0414 if isfield(model,'proteins')
    +0415     model.proteins(I)=[];
    +0416 end
    +0417 if isfield(model,'geneMiriams')
    +0418     model.geneMiriams(I)=[];
    +0419 end
    +0420 if isfield(model,'geneFrom')
    +0421     model.geneFrom(I)=[];
    +0422 end
    +0423 if isfield(model,'geneComps')
    +0424     model.geneComps(I)=[];
    +0425 end
    +0426 
    +0427 %At this stage the model will contain some exchange reactions but probably
    +0428 %not all (and maybe zero). This can be inconvenient, so all exchange
    +0429 %reactions from the reference model are added, except for those which
    +0430 %involve metabolites that are not in the model.
    +0431 
    +0432 %First delete and included exchange reactions in order to prevent the order
    +0433 %from changing
    +0434 model=removeReactions(model,getExchangeRxns(model));
    +0435 
    +0436 %Create a model with only the exchange reactions in refModel
    +0437 excModel=removeReactions(refModel,setdiff(refModel.rxns,getExchangeRxns(refModel)),true,true);
    +0438 
    +0439 %Find the metabolites there which are not exchange metabolites and which do
    +0440 %not exist in the output model
    +0441 I=~ismember(excModel.mets,model.mets) & excModel.unconstrained==0;
    +0442 
    +0443 %Then find those reactions and delete them
    +0444 [~, J]=find(excModel.S(I,:));
    +0445 excModel=removeReactions(excModel,J,true,true);
    +0446 
    +0447 %Merge with the output model
    +0448 model=mergeModels({model;excModel},'metNames');
    +0449 model.id='INITModel';
    +0450 model.name=['Automatically generated model for ' tissue];
    +0451 if any(celltype)
    +0452     model.name=[model.name ' - ' celltype];
    +0453 end
    +0454 
    +0455 if printReport==true
    +0456     printScores(model,'Final model statistics',hpaData,arrayData,tissue,celltype);
    +0457 end
    +0458 
    +0459 %Add information about essential reactions and reactions included for
    +0460 %gap-filling and return a taskReport
    +0461 if ~isempty(taskStructure)
    +0462     I=find(taskReport.ok); %Ignore failed tasks
    +0463     for i=1:numel(I)
    +0464         taskReport.essential{I(i),1}=cModel.rxns(essentialRxnMat(:,I(i)));
    +0465         taskReport.gapfill{I(i),1}=refModelNoExc.rxns(addedRxnMat(:,i));
    +0466     end
    +0467 else
    +0468     taskReport=[];
    +0469 end
    +0470 
    +0471 %Fix grRules and reconstruct rxnGeneMat
    +0472 [grRules,rxnGeneMat] = standardizeGrRules(model,true);
    +0473 model.grRules = grRules;
    +0474 model.rxnGeneMat = rxnGeneMat;
    +0475 end
    +0476 
    +0477 %This is for printing a summary of a model
    +0478 function [rxnS, geneS]=printScores(model,name,hpaData,arrayData,tissue,celltype)
    +0479 [a, b]=scoreModel(model,hpaData,arrayData,tissue,celltype, ...
    +0480     'isozymeScoring','max','complexScoring','max','dataPrecedence','reaction');
    +0481 b(isnan(b))=-Inf;
    +0482 rxnS=mean(a);
    +0483 geneS=mean(b(~isinf(b)));
    +0484 fprintf([name ':\n']);
    +0485 fprintf(['\t' num2str(numel(model.rxns)) ' reactions, ' num2str(numel(model.genes)) ' genes\n']);
    +0486 fprintf(['\tMean reaction score: ' num2str(rxnS) '\n']);
    +0487 fprintf(['\tMean gene score: ' num2str(geneS) '\n']);
    +0488 fprintf(['\tReactions with positive scores: ' num2str(100*sum(a>0)/numel(a)) '%%\n\n']);
    +0489 end
    +0490 
    +0491 %Local beta probability density function, equivalent to the Statistics and
    +0492 %Machine Learning Toolbox betapdf but using only base MATLAB (beta). This
    +0493 %avoids a dependency on that toolbox. The density is zero outside [0,1].
    +0494 function y=betapdf(x,a,b)
    +0495 y=zeros(size(x));
    +0496 k=x>=0 & x<=1;
    +0497 y(k)=(x(k).^(a-1)).*((1-x(k)).^(b-1))/beta(a,b);
    +0498 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/INIT/tINIT/index.html b/docs/html/INIT/tINIT/index.html similarity index 100% rename from doc/INIT/tINIT/index.html rename to docs/html/INIT/tINIT/index.html diff --git a/docs/html/INIT/tINIT/runINIT.html b/docs/html/INIT/tINIT/runINIT.html new file mode 100644 index 00000000..f2890576 --- /dev/null +++ b/docs/html/INIT/tINIT/runINIT.html @@ -0,0 +1,492 @@ + + + + Description of runINIT + + + + + + + + + +
    Home > INIT > tINIT > runINIT.m
    + + + +

    runINIT +

    + +

    PURPOSE ^

    +
    runINIT Solve the MILP of the original INIT algorithm.
    + +

    SYNOPSIS ^

    +
    function [outModel, deletedRxns, metProduction, fValue]=runINIT(model,varargin)
    + +

    DESCRIPTION ^

    +
     runINIT  Solve the MILP of the original INIT algorithm.
    +
    + Generates a model using the INIT algorithm, from proteomics,
    + transcriptomics, metabolomics and metabolic tasks in any combination. This
    + function is the implementation of the algorithm; see getINITModel for the
    + higher-level reconstruction function, and PLoS Comput Biol. 2012;8(5):e1002518
    + for the formulation.
    +
    + ftINIT replaces this function for context-specific model extraction and
    + solves a different MILP in ftINITInternalAlg. This function is kept to
    + reproduce models built with the original INIT. It issues a
    + RAVEN:legacyMethod notice once per session, which
    + warning('off','RAVEN:legacyMethod') silences.
    +
    + Parameters
    + ----------
    + model : struct
    +     a reference model structure.
    +
    + Name-Value Arguments
    + --------------------
    + rxnScores : double
    +     a vector of scores for the reactions in the model. Positive scores are
    +     reactions to keep and negative scores are reactions to exclude (default
    +     all 0.0).
    + presentMets : cell
    +     cell array with unique metabolite names that the model should produce
    +     (default []).
    + essentialRxns : cell
    +     cell array of reactions that are essential and that have to be in the
    +     resulting model. fitTasks passes the reactions that the tasks require
    +     (default []).
    + prodWeight : double
    +     a score for each metabolite that has net production. The sum of these
    +     weights and the scores for the reactions is optimised. Net production
    +     of metabolites gives a reason for including reactions with a negative
    +     score for connectivity reasons, which gives a more functional network
    +     (default 0.5).
    + allowExcretion : logical
    +     true if excretion of all metabolites is allowed. This results in
    +     fewer reactions being considered dead-ends, but not every reaction in the
    +     resulting model can carry flux. If this is "false" then
    +     the equality constraints are taken from model.b. If the input model
    +     lacks exchange reactions, set it to true; with false a large part of
    +     the model is excluded for connectivity reasons
    +     (default false).
    + noRevLoops : logical
    +     true if reversible reactions are constrained to carry flux in only one
    +     direction. This prevents reversible reactions from being assigned as
    +     connected wrongly (the forward and backward reactions can form a loop
    +     and therefore appear connected). It adds two binary variables per
    +     reversible reaction to the problem, which makes the MILP larger and more
    +     computationally intensive (default false).
    + params : struct
    +     parameter structure for use by optimizeProb.
    + eps : double
    +     the lower flux bound of a reaction that is turned on, which also
    +     applies to the essential reactions (capped by their upper bound)
    +     (default 1).
    +
    + Returns
    + -------
    + outModel : struct
    +     the resulting model structure.
    + deletedRxns : cell
    +     reactions which were deleted by the algorithm.
    + metProduction : double
    +     array that indicates which of the metabolites in presentMets that could
    +     be produced:
    +
    +     - -2 : metabolite name not found in model.
    +     - -1 : metabolite found, but it could not be produced.
    +     - 1 : metabolite could be produced.
    + fValue : double
    +     objective value (sum of (the negative of) reaction scores for the
    +     included reactions and prodWeight*number of produced metabolites).
    +
    + See also
    + --------
    + getINITModel, ftINITInternalAlg, ftINIT
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: + + + + + +

    SOURCE CODE ^

    +
    0001 function [outModel, deletedRxns, metProduction, fValue]=runINIT(model,varargin)
    +0002 % runINIT  Solve the MILP of the original INIT algorithm.
    +0003 %
    +0004 % Generates a model using the INIT algorithm, from proteomics,
    +0005 % transcriptomics, metabolomics and metabolic tasks in any combination. This
    +0006 % function is the implementation of the algorithm; see getINITModel for the
    +0007 % higher-level reconstruction function, and PLoS Comput Biol. 2012;8(5):e1002518
    +0008 % for the formulation.
    +0009 %
    +0010 % ftINIT replaces this function for context-specific model extraction and
    +0011 % solves a different MILP in ftINITInternalAlg. This function is kept to
    +0012 % reproduce models built with the original INIT. It issues a
    +0013 % RAVEN:legacyMethod notice once per session, which
    +0014 % warning('off','RAVEN:legacyMethod') silences.
    +0015 %
    +0016 % Parameters
    +0017 % ----------
    +0018 % model : struct
    +0019 %     a reference model structure.
    +0020 %
    +0021 % Name-Value Arguments
    +0022 % --------------------
    +0023 % rxnScores : double
    +0024 %     a vector of scores for the reactions in the model. Positive scores are
    +0025 %     reactions to keep and negative scores are reactions to exclude (default
    +0026 %     all 0.0).
    +0027 % presentMets : cell
    +0028 %     cell array with unique metabolite names that the model should produce
    +0029 %     (default []).
    +0030 % essentialRxns : cell
    +0031 %     cell array of reactions that are essential and that have to be in the
    +0032 %     resulting model. fitTasks passes the reactions that the tasks require
    +0033 %     (default []).
    +0034 % prodWeight : double
    +0035 %     a score for each metabolite that has net production. The sum of these
    +0036 %     weights and the scores for the reactions is optimised. Net production
    +0037 %     of metabolites gives a reason for including reactions with a negative
    +0038 %     score for connectivity reasons, which gives a more functional network
    +0039 %     (default 0.5).
    +0040 % allowExcretion : logical
    +0041 %     true if excretion of all metabolites is allowed. This results in
    +0042 %     fewer reactions being considered dead-ends, but not every reaction in the
    +0043 %     resulting model can carry flux. If this is "false" then
    +0044 %     the equality constraints are taken from model.b. If the input model
    +0045 %     lacks exchange reactions, set it to true; with false a large part of
    +0046 %     the model is excluded for connectivity reasons
    +0047 %     (default false).
    +0048 % noRevLoops : logical
    +0049 %     true if reversible reactions are constrained to carry flux in only one
    +0050 %     direction. This prevents reversible reactions from being assigned as
    +0051 %     connected wrongly (the forward and backward reactions can form a loop
    +0052 %     and therefore appear connected). It adds two binary variables per
    +0053 %     reversible reaction to the problem, which makes the MILP larger and more
    +0054 %     computationally intensive (default false).
    +0055 % params : struct
    +0056 %     parameter structure for use by optimizeProb.
    +0057 % eps : double
    +0058 %     the lower flux bound of a reaction that is turned on, which also
    +0059 %     applies to the essential reactions (capped by their upper bound)
    +0060 %     (default 1).
    +0061 %
    +0062 % Returns
    +0063 % -------
    +0064 % outModel : struct
    +0065 %     the resulting model structure.
    +0066 % deletedRxns : cell
    +0067 %     reactions which were deleted by the algorithm.
    +0068 % metProduction : double
    +0069 %     array that indicates which of the metabolites in presentMets that could
    +0070 %     be produced:
    +0071 %
    +0072 %     - -2 : metabolite name not found in model.
    +0073 %     - -1 : metabolite found, but it could not be produced.
    +0074 %     - 1 : metabolite could be produced.
    +0075 % fValue : double
    +0076 %     objective value (sum of (the negative of) reaction scores for the
    +0077 %     included reactions and prodWeight*number of produced metabolites).
    +0078 %
    +0079 % See also
    +0080 % --------
    +0081 % getINITModel, ftINITInternalAlg, ftINIT
    +0082 
    +0083 legacyMethodNotice('runINIT','ftINIT');
    +0084 
    +0085 p=parseRAVENargs(varargin, {'rxnScores',[]; 'presentMets',[]; 'essentialRxns',[]; 'prodWeight',[]; 'allowExcretion',false; 'noRevLoops',false; 'params',[]; 'eps',1});
    +0086 rxnScores=p.rxnScores;
    +0087 presentMets=p.presentMets;
    +0088 essentialRxns=p.essentialRxns;
    +0089 prodWeight=p.prodWeight;
    +0090 allowExcretion=p.allowExcretion;
    +0091 noRevLoops=p.noRevLoops;
    +0092 params=p.params;
    +0093 initEps=p.eps;
    +0094 if isempty(rxnScores)
    +0095     rxnScores=zeros(numel(model.rxns),1);
    +0096 end
    +0097 if isempty(presentMets)
    +0098     presentMets={};
    +0099 else
    +0100     presentMets=convertCharArray(presentMets);
    +0101 end
    +0102 if isempty(essentialRxns)
    +0103     essentialRxns={};
    +0104 else
    +0105     essentialRxns=convertCharArray(essentialRxns);
    +0106 end
    +0107 if isempty(prodWeight)
    +0108     prodWeight=0.5;
    +0109 end
    +0110 
    +0111 if numel(presentMets)~=numel(unique(presentMets))
    +0112     EM='Duplicate metabolite names in presentMets';
    +0113     error('RAVEN:badInput', '%s', EM);
    +0114 end
    +0115 
    +0116 %Default is that the metabolites cannot be produced
    +0117 if ~isempty(presentMets)
    +0118     metProduction=ones(numel(presentMets),1)*-2;
    +0119     presentMets=upper(presentMets);
    +0120     pmIndexes=find(ismember(presentMets,upper(model.metNames)));
    +0121     metProduction(pmIndexes)=-1; %Then set that they are at least found
    +0122 else
    +0123     metProduction=[];
    +0124     pmIndexes=[];
    +0125 end
    +0126 
    +0127 %The model should be in the reversible format and all relevant exchange
    +0128 %reactions should be open
    +0129 if isfield(model,'unconstrained')
    +0130     EM='Exchange metabolites are still present in the model. Use simplifyModel if this is not intended';
    +0131     warning('RAVEN:warning', '%s', EM);
    +0132 end
    +0133 
    +0134 %The irreversible reactions that are essential must have a flux and are
    +0135 %therefore not optimized for using MILP, which reduces the problem size.
    +0136 %However, reversible reactions must have a flux in one direction, so they
    +0137 %have to stay in the problem. The essentiality constraint on reversible
    +0138 %reactions is implemented in the same manner as for reversible reactions
    +0139 %when noRevLoops==true, but with the additional constraint that C ub=-1.
    +0140 %This forces one of the directions to be active.
    +0141 revRxns=find(model.rev~=0);
    +0142 essentialReversible=find(ismember(model.rxns(revRxns),essentialRxns));
    +0143 essentialRxns=intersect(essentialRxns,model.rxns(model.rev==0));
    +0144 
    +0145 %Convert the model to irreversible
    +0146 irrevModel=convertToIrrev(model);
    +0147 rxnScores=[rxnScores;rxnScores(model.rev==1)];
    +0148 %These are used if noRevLoops is true
    +0149 if noRevLoops==true
    +0150     forwardIndexes=find(model.rev~=0);
    +0151     backwardIndexes=(numel(model.rxns)+1:numel(irrevModel.rxns))';
    +0152 else
    +0153     %Then they should only be used for essential reversible reactions
    +0154     forwardIndexes=revRxns(essentialReversible);
    +0155     backwardIndexes=essentialReversible+numel(model.rxns);
    +0156 end
    +0157 
    +0158 %Get the indexes of the essential reactions and remove them from the
    +0159 %scoring vector
    +0160 essentialIndex=find(ismember(irrevModel.rxns,essentialRxns));
    +0161 rxnScores(essentialIndex)=[];
    +0162 
    +0163 %Go through each of the presentMets (if they exist) and modify the S matrix
    +0164 %so that each reaction which produces any of them also produces a
    +0165 %corresponding fake metabolite and the opposite in the reverse direction.
    +0166 
    +0167 %This is to deal with the fact that there is no compartment info regarding
    +0168 %the presentMets. This modifies the irrevModel structure, but that is fine
    +0169 %since it is the model structure that is returned.
    +0170 if any(pmIndexes)
    +0171     irrevModel.metNames=upper(irrevModel.metNames);
    +0172     % num2str on the whole column right-aligns every row to a common width,
    +0173     % embedding padding spaces once the indices span more than one digit
    +0174     % count; format each one independently.
    +0175     metsToAdd.mets=arrayfun(@(x) sprintf('FAKEFORPM%d',x), pmIndexes, 'UniformOutput', false);
    +0176     metsToAdd.metNames=metsToAdd.mets;
    +0177     metsToAdd.compartments=irrevModel.comps{1};
    +0178     
    +0179     %There is no constraints on the metabolites yet, since maybe not all of
    +0180     %them could be produced
    +0181     irrevModel=addMets(irrevModel,metsToAdd);
    +0182 end
    +0183 
    +0184 %Modify the matrix
    +0185 for i=1:numel(pmIndexes)
    +0186     %Get the matching mets
    +0187     I=ismember(irrevModel.metNames,presentMets(pmIndexes(i)));
    +0188     
    +0189     %Find the reactions where any of them are used.
    +0190     [~, K, L]=find(irrevModel.S(I,:));
    +0191     
    +0192     %This ugly loop is to avoid problems if a metabolite occurs several
    +0193     %times in one reaction
    +0194     KK=unique(K);
    +0195     LL=zeros(numel(KK),1);
    +0196     for j=1:numel(KK)
    +0197         LL(j)=sum(L(K==KK(j)));
    +0198     end
    +0199     irrevModel.S(numel(irrevModel.mets)-numel(pmIndexes)+i,KK)=LL;
    +0200 end
    +0201 
    +0202 %Some nice to have numbers
    +0203 nMets=numel(irrevModel.mets);
    +0204 nRxns=numel(irrevModel.rxns);
    +0205 nEssential=numel(essentialIndex);
    +0206 nNonEssential=nRxns-nEssential;
    +0207 nonEssentialIndex=setdiff(1:nRxns,essentialIndex);
    +0208 % Per-reaction big-M: use each reaction upper bound, capped at 1000.
    +0209 % Must be at least initEps so the inclusion constraint is feasible.
    +0210 nonEssUBs = min(irrevModel.ub(nonEssentialIndex), 1000);
    +0211 nonEssUBs = max(nonEssUBs(:), initEps);
    +0212 S=irrevModel.S;
    +0213 
    +0214 %Add so that each non-essential reaction produces one unit of a fake
    +0215 %metabolite
    +0216 temp=sparse(1:nRxns,1:nRxns,1);
    +0217 temp(essentialIndex,:)=[];
    +0218 S=[S;temp];
    +0219 
    +0220 %Add another set of reactions (will be binary) which also produce these
    +0221 %fake metabolites, but with a stoichiometry of 1000
    +0222 temp=sparse(1:nNonEssential,1:nNonEssential,nonEssUBs);
    +0223 temp=[sparse(nMets,nNonEssential);temp];
    +0224 S=[S temp];
    +0225 
    +0226 %Add reactions for net-production of (real) metabolites
    +0227 if prodWeight~=0
    +0228     temp=[speye(nMets-numel(pmIndexes))*-1;sparse(nNonEssential+numel(pmIndexes),nMets-numel(pmIndexes))];
    +0229     S=[S temp];
    +0230     %To keep the number of reactions added like this
    +0231     nNetProd=nMets-numel(pmIndexes);
    +0232 else
    +0233     nNetProd=0;
    +0234 end
    +0235 
    +0236 %Add constraints so that reversible reactions can only be used in one
    +0237 %direction. This is done by adding the fake metabolites A, B, C for each
    +0238 %reversible reaction in the following manner
    +0239 % forward: A + .. => ... backwards: B + ... => ... int1: C => 1000 A int2:
    +0240 % C => 1000 B A ub=999.9 B ub=999.9 C lb=-1 int1 and int2 are binary
    +0241 if any(forwardIndexes)
    +0242     nRevBounds=numel(forwardIndexes);
    +0243     
    +0244     %Add the A metabolites for the forward reactions and the B metabolites
    +0245     %for the reverse reactions
    +0246     I=speye(numel(irrevModel.rxns))*-1;
    +0247     temp=[I(forwardIndexes,:);I(backwardIndexes,:)];
    +0248     
    +0249     %Padding
    +0250     temp=[temp sparse(size(temp,1),size(S,2)-numel(irrevModel.rxns))];
    +0251     
    +0252     %Add the int1 & int2 reactions that produce A and B
    +0253     temp=[temp speye(nRevBounds*2)*1000];
    +0254     
    +0255     %And add that they also consume C
    +0256     temp=[temp;[sparse(nRevBounds,size(S,2)) speye(nRevBounds)*-1 speye(nRevBounds)*-1]];
    +0257     
    +0258     %Add the new reactions and metabolites
    +0259     S=[S sparse(size(S,1),nRevBounds*2)];
    +0260     S=[S;temp];
    +0261 else
    +0262     nRevBounds=0;
    +0263 end
    +0264 
    +0265 %Add so that the essential reactions must have a small flux and that the
    +0266 %binary ones (and net-production reactions) may have zero flux. The integer
    +0267 %reactions for reversible reactions have [0 1]
    +0268 prob.blx=[irrevModel.lb;zeros(nNonEssential+nNetProd+nRevBounds*2,1)];
    +0269 prob.blx(essentialIndex)=min(max(initEps,prob.blx(essentialIndex)),irrevModel.ub(essentialIndex));
    +0270 
    +0271 %Add so that the binary ones and net-production reactions can have at the
    +0272 %most flux 1.0
    +0273 prob.bux=[irrevModel.ub;ones(nNonEssential+nNetProd+nRevBounds*2,1)];
    +0274 
    +0275 %Add that the fake metabolites must be produced in a small amount and that
    +0276 %the A and B metabolites for reversible reactions can be [0 999.9] and C
    +0277 %metabolites [-1 0]
    +0278 prob.blc=[irrevModel.b(:,1);ones(nNonEssential,1)*initEps;zeros(nRevBounds*2,1);ones(nRevBounds,1)*-1];
    +0279 
    +0280 %Add that normal metabolites can be freely excreted if
    +0281 %allowExcretion==true, and that the fake ones can be excreted 1000 units at
    +0282 %most. C metabolites for essential reversible reactions should have an
    +0283 %upper bound of -1. If noRevLoops is false, then add this constraint for
    +0284 %all the reactions instead.
    +0285 if noRevLoops==true
    +0286     revUB=zeros(nRevBounds,1);
    +0287     revUB(essentialReversible)=-1;
    +0288 else
    +0289     revUB=ones(nRevBounds,1)*-1;
    +0290 end
    +0291 if allowExcretion==true
    +0292     metUB=inf(nMets,1);
    +0293 else
    +0294     metUB=irrevModel.b(:,min(size(irrevModel.b,2),2));
    +0295 end
    +0296 prob.buc=[metUB;nonEssUBs;ones(nRevBounds*2,1)*999.9;revUB];
    +0297 
    +0298 %Add objective coefficients for the binary reactions. The negative is used
    +0299 %since we are minimizing. The negative is taken for the prodWeight as well,
    +0300 %to be consistent with the syntax that positive scores are good
    +0301 prob.c=[zeros(nRxns,1);rxnScores;ones(nNetProd,1)*prodWeight*-1;zeros(nRevBounds*2,1)];
    +0302 prob.a=S;
    +0303 
    +0304 % adapt problem structure for cobra-style solver
    +0305 prob.c=[prob.c;zeros(size(prob.a,1),1)];
    +0306 prob.A=[prob.a -speye(size(prob.a,1))];
    +0307 prob.b=zeros(size(prob.a,1), 1);
    +0308 prob.ub=[prob.bux; prob.buc];
    +0309 prob.osense=1;
    +0310 prob.csense=char(zeros(1,size(prob.a,1)));
    +0311 prob.csense(:)='E';
    +0312 
    +0313 %Add that the binary reactions may only take integer values. This has to be
    +0314 %declared before the presentMets loop below: optimizeProb reads vartype on
    +0315 %every call, and the producibility test is only meaningful against the same
    +0316 %integer problem that is solved at the end.
    +0317 prob.vartype = repmat('C', 1, size(prob.A, 2));
    +0318 allInt=[(nRxns+1):(nRxns+nNonEssential) size(S,2)-nRevBounds*2+1:size(S,2)];
    +0319 prob.vartype(allInt) = 'B';
    +0320 
    +0321 %We still do not know which of the presentMets that can be produced. Go
    +0322 %through them, force production, and see if the problem can be solved
    +0323 for i=1:numel(pmIndexes)
    +0324     prob.blc(numel(irrevModel.mets)-numel(pmIndexes)+i)=1;
    +0325     prob.lb=[prob.blx; prob.blc];
    +0326     res=optimizeProb(prob,params);
    +0327     isFeasible=checkSolution(res);
    +0328     if ~isFeasible
    +0329         %Reset the constraint again
    +0330         prob.blc(numel(irrevModel.mets)-numel(pmIndexes)+i)=0;
    +0331     else
    +0332         %Metabolite produced
    +0333         metProduction(pmIndexes(i))=1;
    +0334     end
    +0335 end
    +0336 prob.lb=[prob.blx; prob.blc];
    +0337 
    +0338 % solve problem
    +0339 res=optimizeProb(prob,params);
    +0340 
    +0341 %Problem should not be infeasible, but it is possible that the time limit
    +0342 %was reached before finding any solutions.
    +0343 if ~checkSolution(res)
    +0344     if res.hitTimeLimit
    +0345         EM='Time limit reached without finding a solution. Try increasing the TimeLimit parameter.';
    +0346     else
    +0347         EM='The problem is infeasible';
    +0348     end
    +0349     error('RAVEN:badInput', '%s', EM);
    +0350 end
    +0351 
    +0352 fValue=res.obj;
    +0353 
    +0354 %Get all reactions used in the irreversible model
    +0355 usedRxns=(nonEssentialIndex(res.full(nRxns+1:nRxns+nNonEssential)<0.1))';
    +0356 
    +0357 %Map to reversible model IDs
    +0358 usedRxns=[usedRxns(usedRxns<=numel(model.rxns));revRxns(usedRxns(usedRxns>numel(model.rxns))-numel(model.rxns))];
    +0359 
    +0360 %Then get the ones that are not used in either direction or is essential
    +0361 I=true(numel(model.rxns),1);
    +0362 I(usedRxns)=false;
    +0363 I(essentialIndex)=false;
    +0364 deletedRxns=model.rxns(I);
    +0365 outModel=removeReactions(model,I,true,true);
    +0366 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/alpha.png b/docs/html/alpha.png similarity index 100% rename from doc/alpha.png rename to docs/html/alpha.png diff --git a/docs/html/analysis/FSEOF.html b/docs/html/analysis/FSEOF.html new file mode 100644 index 00000000..9158b629 --- /dev/null +++ b/docs/html/analysis/FSEOF.html @@ -0,0 +1,269 @@ + + + + Description of FSEOF + + + + + + + + + +
    Home > analysis > FSEOF.m
    + + + +

    FSEOF +

    + +

    PURPOSE ^

    +
    FSEOF Flux Scanning based on Enforced Objective Flux.
    + +

    SYNOPSIS ^

    +
    function targets=FSEOF(model,biomassRxn,targetRxn,varargin)
    + +

    DESCRIPTION ^

    +
     FSEOF  Flux Scanning based on Enforced Objective Flux.
    +
    + Implements the Flux Scanning based on Enforced Objective Flux algorithm.
    + This function writes a tab-delimited file or prints to the command
    + window. If an output is specified (targets), it also returns a structure
    + indicating for each model reaction whether FSEOF identifies it as a target
    + and the slope of the reaction when switching from biomass formation to
    + product formation.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + biomassRxn : char
    +     reaction ID of the biomass formation or growth reaction.
    + targetRxn : char
    +     reaction ID of the target reaction.
    +
    + Name-Value Arguments
    + --------------------
    + iterations : double
    +     number of enforced target flux levels, equally spaced up to coefficient
    +     times the maximum target flux (default 10).
    + coefficient : double
    +     ratio of the maximum target reaction flux that is enforced at the last
    +     iteration, less than 1 (default 0.9).
    + outputFile : char
    +     output filename (default prints to command window).
    + corrThreshold : double
    +     minimum absolute Pearson correlation coefficient between the absolute
    +     flux of a reaction and the enforced product flux for the reaction to be
    +     selected as a target. Values are in [0, 1]. The filter keeps reactions
    +     whose flux is close to linear in the enforced product flux, and a higher
    +     value keeps fewer reactions (default 0.9).
    +
    + Returns
    + -------
    + targets : struct
    +     structure with information for identified targets, with fields:
    +
    +     - logical : logical array, true for amplification targets (identical to
    +       amplify).
    +     - amplify : logical array, reactions whose absolute flux increases
    +       with enforced product flux.
    +     - knockdown : logical array, reactions whose absolute flux decreases but
    +       stays above zero at the highest enforced product flux.
    +     - knockout : logical array, reactions whose absolute flux decreases to
    +       zero at the highest enforced product flux.
    +     - slope : numeric array with per-reaction regression slopes of
    +       absolute flux versus enforced product flux (for all reactions).
    +
    + Examples
    + --------
    +     targets = FSEOF(model, biomassRxn, targetRxn, iterations, ...
    +                     coefficient, outputFile);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function targets=FSEOF(model,biomassRxn,targetRxn,varargin)
    +0002 % FSEOF  Flux Scanning based on Enforced Objective Flux.
    +0003 %
    +0004 % Implements the Flux Scanning based on Enforced Objective Flux algorithm.
    +0005 % This function writes a tab-delimited file or prints to the command
    +0006 % window. If an output is specified (targets), it also returns a structure
    +0007 % indicating for each model reaction whether FSEOF identifies it as a target
    +0008 % and the slope of the reaction when switching from biomass formation to
    +0009 % product formation.
    +0010 %
    +0011 % Parameters
    +0012 % ----------
    +0013 % model : struct
    +0014 %     a model structure.
    +0015 % biomassRxn : char
    +0016 %     reaction ID of the biomass formation or growth reaction.
    +0017 % targetRxn : char
    +0018 %     reaction ID of the target reaction.
    +0019 %
    +0020 % Name-Value Arguments
    +0021 % --------------------
    +0022 % iterations : double
    +0023 %     number of enforced target flux levels, equally spaced up to coefficient
    +0024 %     times the maximum target flux (default 10).
    +0025 % coefficient : double
    +0026 %     ratio of the maximum target reaction flux that is enforced at the last
    +0027 %     iteration, less than 1 (default 0.9).
    +0028 % outputFile : char
    +0029 %     output filename (default prints to command window).
    +0030 % corrThreshold : double
    +0031 %     minimum absolute Pearson correlation coefficient between the absolute
    +0032 %     flux of a reaction and the enforced product flux for the reaction to be
    +0033 %     selected as a target. Values are in [0, 1]. The filter keeps reactions
    +0034 %     whose flux is close to linear in the enforced product flux, and a higher
    +0035 %     value keeps fewer reactions (default 0.9).
    +0036 %
    +0037 % Returns
    +0038 % -------
    +0039 % targets : struct
    +0040 %     structure with information for identified targets, with fields:
    +0041 %
    +0042 %     - logical : logical array, true for amplification targets (identical to
    +0043 %       amplify).
    +0044 %     - amplify : logical array, reactions whose absolute flux increases
    +0045 %       with enforced product flux.
    +0046 %     - knockdown : logical array, reactions whose absolute flux decreases but
    +0047 %       stays above zero at the highest enforced product flux.
    +0048 %     - knockout : logical array, reactions whose absolute flux decreases to
    +0049 %       zero at the highest enforced product flux.
    +0050 %     - slope : numeric array with per-reaction regression slopes of
    +0051 %       absolute flux versus enforced product flux (for all reactions).
    +0052 %
    +0053 % Examples
    +0054 % --------
    +0055 %     targets = FSEOF(model, biomassRxn, targetRxn, iterations, ...
    +0056 %                     coefficient, outputFile);
    +0057 
    +0058 biomassRxn=char(biomassRxn);
    +0059 targetRxn=char(targetRxn);
    +0060 
    +0061 p=parseRAVENargs(varargin, {'iterations',10; 'coefficient',0.9; 'outputFile',[]; 'corrThreshold',0.9});
    +0062 iterations=p.iterations;
    +0063 coefficient=p.coefficient;
    +0064 outputFile=p.outputFile;
    +0065 corrThreshold=p.corrThreshold;
    +0066 
    +0067 %Find out the maximum theoretical yield of target reaction
    +0068 model=setParam(model,'obj',targetRxn,1);
    +0069 sol=solveLP(model,1);
    +0070 targetMax=sol.f*coefficient;
    +0071 
    +0072 model=setParam(model,'obj',biomassRxn,1);
    +0073 
    +0074 nRxns=length(model.rxns);
    +0075 results=zeros(nRxns,iterations);
    +0076 enforcedFlux=(1:iterations)*targetMax/iterations;
    +0077 
    +0078 %Run pFBA at each enforced target flux level
    +0079 for i=1:iterations
    +0080     model=setParam(model,'lb',targetRxn,enforcedFlux(i));
    +0081     sol=solveLP(model,1);
    +0082     results(:,i)=sol.x;
    +0083 end
    +0084 
    +0085 %Compute per-reaction regression slope (FS4) and Pearson correlation (FS1)
    +0086 %on absolute flux values to treat forward/reverse symmetrically.
    +0087 absResults=abs(results);
    +0088 slope=zeros(nRxns,1);
    +0089 rCoeff=zeros(nRxns,1);
    +0090 hasVariation = iterations > 1 && std(enforcedFlux) > 0;
    +0091 if hasVariation
    +0092     for num=1:nRxns
    +0093         fluxTrack=absResults(num,:);
    +0094         if any(fluxTrack > 1e-10) && std(fluxTrack) > 0
    +0095             p_fit=polyfit(enforcedFlux,fluxTrack,1);
    +0096             slope(num)=p_fit(1);
    +0097             r=corrcoef(enforcedFlux,fluxTrack);
    +0098             rCoeff(num)=r(1,2);
    +0099         end
    +0100     end
    +0101 end
    +0102 
    +0103 %Classify targets: select reactions whose absolute flux is sufficiently
    +0104 %correlated with the enforced product flux (FS1), then split by direction
    +0105 %and whether any flux remains at maximum enforcement (FS2).
    +0106 isCorrelated = abs(rCoeff) >= corrThreshold;
    +0107 isAmplify    = isCorrelated & slope > 0;
    +0108 isKnockdown  = isCorrelated & slope < 0 & absResults(:,end) > 1e-8;
    +0109 isKnockout   = isCorrelated & slope < 0 & absResults(:,end) <= 1e-8;
    +0110 
    +0111 rxnDirection=sign(results(:,1));
    +0112 
    +0113 %Generating output
    +0114 formatSpec='%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n';
    +0115 if ~isempty(outputFile)
    +0116     outputFile=char(outputFile);
    +0117     fid=fopen(outputFile,'w');
    +0118     fprintf(fid,formatSpec,'Type','Slope','rowID','Enzyme ID','Enzyme Name','Subsystems','Direction','Gr Rule');
    +0119 else
    +0120     fid=-1;
    +0121     fprintf(formatSpec,'Type','Slope','rowID','Enzyme ID','Enzyme Name','Subsystems','Direction','Gr Rule');
    +0122 end
    +0123 
    +0124 targetTypes  = {'Amplify','Knockdown','Knockout'};
    +0125 targetArrays = {isAmplify, isKnockdown, isKnockout};
    +0126 
    +0127 for t=1:3
    +0128     flag=targetArrays{t};
    +0129     for num=1:nRxns
    +0130         if flag(num)
    +0131             A_type=targetTypes{t};
    +0132             A0=num2str(slope(num));
    +0133             A1=num2str(num);
    +0134             A2=char(model.rxns(num));
    +0135             A3=char(model.rxnNames(num));
    +0136             if isfield(model,'subSystems') && ~isempty(model.subSystems{num})
    +0137                 if ~any(cellfun(@(x) iscell(x), model.subSystems))
    +0138                     subSys=cellfun(@(x) {x}, model.subSystems, 'uni', 0);
    +0139                 else
    +0140                     subSys=model.subSystems;
    +0141                 end
    +0142                 A4=char(strjoin(subSys{num},';'));
    +0143             else
    +0144                 A4='';
    +0145             end
    +0146             A5=num2str(model.rev(num)*rxnDirection(num));
    +0147             A6=char(model.grRules(num));
    +0148             if fid == -1
    +0149                 fprintf(formatSpec,A_type,A0,A1,A2,A3,A4,A5,A6);
    +0150             else
    +0151                 fprintf(fid,formatSpec,A_type,A0,A1,A2,A3,A4,A5,A6);
    +0152             end
    +0153         end
    +0154     end
    +0155 end
    +0156 
    +0157 if fid ~= -1
    +0158     fclose(fid);
    +0159 end
    +0160 
    +0161 if nargout == 1
    +0162     targets.logical   = isAmplify;
    +0163     targets.amplify   = isAmplify;
    +0164     targets.knockdown = isKnockdown;
    +0165     targets.knockout  = isKnockout;
    +0166     targets.slope     = slope;
    +0167 end
    +0168 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/analysis/analyzeSampling.html b/docs/html/analysis/analyzeSampling.html new file mode 100644 index 00000000..62463b26 --- /dev/null +++ b/docs/html/analysis/analyzeSampling.html @@ -0,0 +1,263 @@ + + + + Description of analyzeSampling + + + + + + + + + +
    Home > analysis > analyzeSampling.m
    + + + +

    analyzeSampling +

    + +

    PURPOSE ^

    +
    analyzeSampling Compare flux change significance with expression change.
    + +

    SYNOPSIS ^

    +
    function scores=analyzeSampling(Tex, df, solutionsA, solutionsB, varargin)
    + +

    DESCRIPTION ^

    +
     analyzeSampling  Compare flux change significance with expression change.
    +
    + Compares the significance of change in flux between two conditions with
    + the significance of change in gene expression.
    +
    + Parameters
    + ----------
    + Tex : double
    +     a vector of t-scores for the change in gene expression for each
    +     reaction. The score is the Student t between the two conditions, or
    +     is calculated from a p-value by the inverse of the error function.
    +     Transcripts that increased in expression level have positive values
    +     and transcripts that decreased have negative values. A p-value does
    +     not give the direction of the change, so a score calculated from a
    +     p-value takes its sign from the direction of the expression change.
    + df : double
    +     the degrees of freedom in the t-test.
    + solutionsA : double
    +     random solutions for the reference condition (as generated by
    +     randomSampling).
    + solutionsB : double
    +     random solutions for the test condition (as generated by
    +     randomSampling).
    +
    + Name-Value Arguments
    + --------------------
    + printResults : logical
    +     true to print the 10 reactions with the highest probability in each
    +     category (default false).
    +
    + Returns
    + -------
    + scores : double
    +     a Nx3 column matrix with the probabilities of a reaction:
    +
    +     1.  changing both in flux and expression in the same direction
    +     2.  changing in expression but not in flux
    +     3.  changing in flux but not in expression or changing in opposed
    +         directions in flux and expression
    +
    + Examples
    + --------
    +     scores = analyzeSampling(Tex, df, solutionsA, solutionsB, ...
    +                              printResults);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • getFluxZ getFluxZ Calculate Z scores between two sets of random flux distributions.
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function scores=analyzeSampling(Tex, df, solutionsA, solutionsB, varargin)
    +0002 % analyzeSampling  Compare flux change significance with expression change.
    +0003 %
    +0004 % Compares the significance of change in flux between two conditions with
    +0005 % the significance of change in gene expression.
    +0006 %
    +0007 % Parameters
    +0008 % ----------
    +0009 % Tex : double
    +0010 %     a vector of t-scores for the change in gene expression for each
    +0011 %     reaction. The score is the Student t between the two conditions, or
    +0012 %     is calculated from a p-value by the inverse of the error function.
    +0013 %     Transcripts that increased in expression level have positive values
    +0014 %     and transcripts that decreased have negative values. A p-value does
    +0015 %     not give the direction of the change, so a score calculated from a
    +0016 %     p-value takes its sign from the direction of the expression change.
    +0017 % df : double
    +0018 %     the degrees of freedom in the t-test.
    +0019 % solutionsA : double
    +0020 %     random solutions for the reference condition (as generated by
    +0021 %     randomSampling).
    +0022 % solutionsB : double
    +0023 %     random solutions for the test condition (as generated by
    +0024 %     randomSampling).
    +0025 %
    +0026 % Name-Value Arguments
    +0027 % --------------------
    +0028 % printResults : logical
    +0029 %     true to print the 10 reactions with the highest probability in each
    +0030 %     category (default false).
    +0031 %
    +0032 % Returns
    +0033 % -------
    +0034 % scores : double
    +0035 %     a Nx3 column matrix with the probabilities of a reaction:
    +0036 %
    +0037 %     1.  changing both in flux and expression in the same direction
    +0038 %     2.  changing in expression but not in flux
    +0039 %     3.  changing in flux but not in expression or changing in opposed
    +0040 %         directions in flux and expression
    +0041 %
    +0042 % Examples
    +0043 % --------
    +0044 %     scores = analyzeSampling(Tex, df, solutionsA, solutionsB, ...
    +0045 %                              printResults);
    +0046 
    +0047 p=parseRAVENargs(varargin, {'printResults',false});
    +0048 printResults=p.printResults;
    +0049 
    +0050 nRxns=numel(Tex);
    +0051 pM=zeros(nRxns,1);
    +0052 pH=zeros(nRxns,1);
    +0053 pR=zeros(nRxns,1);
    +0054 
    +0055 %Check that the number of reactions is the same in both expression and flux
    +0056 if nRxns~=size(solutionsA,1)
    +0057     EM='The number of reactions must be the same in Tex as in solutionsA';
    +0058     error('RAVEN:badInput', '%s', EM);
    +0059 end
    +0060 
    +0061 %Get the Z-score and mean for the solutions
    +0062 mA=mean(solutionsA,2);
    +0063 mB=mean(solutionsB,2);
    +0064 Zf=getFluxZ(solutionsA, solutionsB);
    +0065 
    +0066 %Clear up the tex if there are elements that are NaN or +/- Inf.
    +0067 I=isnan(Tex) | isinf(Tex);
    +0068 if any(I)
    +0069     EM='There are t-scores that are NaN or +/- Inf. These values are changed to 0.0';
    +0070     warning('RAVEN:warning', '%s', EM);
    +0071 end
    +0072 Tex(I)=0;
    +0073 
    +0074 for i=1:nRxns
    +0075     %Check how the flux has changed. The means are needed because to
    +0076     %differentiate between a positive flux changing to a smaller flux and a
    +0077     %negative flux changing to a more negative flux (which is a larger
    +0078     %flux)
    +0079     I=mB(i)/mA(i);
    +0080     if I<0
    +0081         pM(i)=erf(abs(Zf(i)));
    +0082         pH(i)=(1-pM(i))*(2*tcdf(abs(Tex(i)),df)-1);
    +0083         pR(i)=0;
    +0084     else
    +0085         if mB(i)<0
    +0086             Zf(i)=Zf(i)*-1;
    +0087         end
    +0088     end
    +0089 
    +0090     I=Zf(i)/Tex(i);
    +0091     if I<0
    +0092         pM(i)=erf(abs(Zf(i)));
    +0093         pH(i)=(1-pM(i))*(2*tcdf(abs(Tex(i)),df)-1);
    +0094         pR(i)=0;
    +0095     else
    +0096         pR(i)=erf(abs(Zf(i)))*(2*tcdf(abs(Tex(i)),df)-1);
    +0097         pH(i)=(2*tcdf(abs(Tex(i)),df)-1)*(1-erf(abs(Zf(i))));
    +0098         pM(i)=erf(abs(Zf(i)))*(1-(2*tcdf(abs(Tex(i)),df)-1));
    +0099     end
    +0100 end
    +0101 
    +0102 scores=[pR pH pM];
    +0103 
    +0104 if printResults==true
    +0105     fprintf('TOP SCORING REACTIONS\n\n');
    +0106     %The top 10 hits in the first category
    +0107     [I, J]=sort(pR,'descend');
    +0108     fprintf('Reactions which change both in flux and expression in the same direction\nReaction\tProbability\n');
    +0109     for i=1:10
    +0110         fprintf([num2str(J(i)) '\t' num2str(I(i)) '\n']);
    +0111     end
    +0112 
    +0113     %The top 10 hits in the first category
    +0114     [I, J]=sort(pH,'descend');
    +0115     fprintf('\nReactions which change in expression but not in flux\nReaction\tProbability\n');
    +0116     for i=1:10
    +0117         fprintf([num2str(J(i)) '\t' num2str(I(i)) '\n']);
    +0118     end
    +0119 
    +0120     %The top 10 hits in the first category
    +0121     [I, J]=sort(pM,'descend');
    +0122     fprintf('\nReactions which change in flux but not in expression, or in opposed directions in flux and expression\nReaction\tProbability\n');
    +0123     for i=1:10
    +0124         fprintf([num2str(J(i)) '\t' num2str(I(i)) '\n']);
    +0125     end
    +0126 end
    +0127 end
    +0128 
    +0129 function p = tcdf(t,v,uflag)
    +0130 % TCDF Student T cumulative distribution function (cdf).
    +0131 %
    +0132 % P = TCDF(X,V) computes the cdf for Student T distribution
    +0133 % with V degrees of freedom, at the values in X. V must be a
    +0134 % scalar or have the same size as T.
    +0135 %
    +0136 % P = TCDF(X,V,"upper") computes it for the upper tail instead
    +0137 % of the lower tail.
    +0138 %
    +0139 % Alternative to the TCDF function in the Matlab statistics toolbox, for use
    +0140 % when that toolbox is not installed. Adapted from
    +0141 % https://github.com/fieldtrip/fieldtrip/blob/master/external/stats/tcdf.m,
    +0142 % originally by Gordon Smyth, University of Queensland.
    +0143 
    +0144 if v <= 0, error('Degrees of freedom must be positive.'); end
    +0145 
    +0146 % resize v if necessary
    +0147 if all(size(v)==1)
    +0148     v = ones(size(t))*v;
    +0149 end
    +0150 
    +0151 %check for NaNs - do not do calculations on them, give those out as NaNs
    +0152 if any( not(isfinite(t(:))) | not(isfinite(v(:))) )
    +0153     sel = find(isfinite(t) & isfinite(v));
    +0154     x=nan(size(t));
    +0155     p=nan(size(t));
    +0156     x(sel) = t(sel).^2 ./ (v(sel) + t(sel).^2) ;
    +0157     p(sel) = 0.5 .* ( 1 + sign(t(sel)) .* betainc( x(sel), 0.5, 0.5*v(sel) ) );
    +0158 else
    +0159     x = t.^2 ./ (v + t.^2) ;
    +0160     p = 0.5 .* ( 1 + sign(t) .* betainc( x, 0.5, 0.5*v ) );
    +0161 end
    +0162 
    +0163 if nargin==3
    +0164     if strcmp(uflag, 'upper')
    +0165         % compute it for the upper tail instead of the lower tail
    +0166         p = 1-p;
    +0167     else
    +0168         error('incorrect specification of uflag');
    +0169     end
    +0170 end
    +0171 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/analysis/compareFluxes.html b/docs/html/analysis/compareFluxes.html new file mode 100644 index 00000000..85235416 --- /dev/null +++ b/docs/html/analysis/compareFluxes.html @@ -0,0 +1,321 @@ + + + + Description of compareFluxes + + + + + + + + + +
    Home > analysis > compareFluxes.m
    + + + +

    compareFluxes +

    + +

    PURPOSE ^

    +
    compareFluxes Find the reactions whose flux differs between two flux vectors.
    + +

    SYNOPSIS ^

    +
    function result = compareFluxes(model, fluxes1, fluxes2, varargin)
    + +

    DESCRIPTION ^

    +
     compareFluxes  Find the reactions whose flux differs between two flux vectors.
    +
    + Compares two flux vectors from the same model and returns all reactions
    + that changed, sorted by absolute flux difference. Classifies reactions as
    + turned on, turned off, reversed direction, or changed magnitude.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + fluxes1 : double
    +     reference flux vector (condition A, such as wild-type).
    + fluxes2 : double
    +     comparison flux vector (condition B, such as a mutant or knock-in).
    +
    + Name-Value Arguments
    + --------------------
    + cutoff : double
    +     minimum |flux| for a reaction to be active, and minimum |flux2 - flux1|
    +     for a reaction to be changed (default 1e-8).
    + metaboliteList : cell
    +     cell array of metabolite names. Only reactions involving at least one
    +     of these metabolites are compared; all other reactions are excluded
    +     from every field of the result. Matching is against model.metNames and
    +     is case insensitive. A name that matches no metabolite is reported as
    +     a warning (default all reactions).
    + nMax : double
    +     maximum rows printed in the summary table (default 20).
    + verbose : logical
    +     print the summary table (default true).
    +
    + Returns
    + -------
    + result : struct
    +     structure with fields:
    +
    +     - turnedOn : cell array with the IDs of reactions that are active in
    +       fluxes2 and not in fluxes1.
    +     - turnedOff : cell array with the IDs of reactions that are active in
    +       fluxes1 and not in fluxes2.
    +     - flipped : cell array with the IDs of reactions that are active in
    +       both conditions and change sign.
    +     - changed : struct with fields for all reactions with |flux2 - flux1|
    +       above cutoff, sorted by |flux2 - flux1| descending:
    +
    +       - rxn : cell array with the reaction IDs.
    +       - flux1 : double array with the flux in condition A.
    +       - flux2 : double array with the flux in condition B.
    +       - absDelta : double array with |flux2 - flux1|.
    +       - relChange : double array with (flux2 - flux1)/|flux1|. NaN when
    +         |flux1| is below cutoff.
    +       - type : cell array with 'on', 'off', 'flip' or '' (changed
    +         magnitude) for each reaction.
    +
    + Examples
    + --------
    +     solA = solveLP(model);
    +     modelAna = setParam(model, 'ub', 'O2exchange', 0);
    +     solB = solveLP(modelAna);
    +     result = compareFluxes(model, solA.x, solB.x)
    +     result = compareFluxes(model, solA.x, solB.x, 'nMax', 50)
    +     result = compareFluxes(model, solA.x, solB.x, 'metaboliteList', {'ATP','NADH'})
    +
    + See also
    + --------
    + walkFluxes, traceFluxPath, modelSummary
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function result = compareFluxes(model, fluxes1, fluxes2, varargin)
    +0002 % compareFluxes  Find the reactions whose flux differs between two flux vectors.
    +0003 %
    +0004 % Compares two flux vectors from the same model and returns all reactions
    +0005 % that changed, sorted by absolute flux difference. Classifies reactions as
    +0006 % turned on, turned off, reversed direction, or changed magnitude.
    +0007 %
    +0008 % Parameters
    +0009 % ----------
    +0010 % model : struct
    +0011 %     a model structure.
    +0012 % fluxes1 : double
    +0013 %     reference flux vector (condition A, such as wild-type).
    +0014 % fluxes2 : double
    +0015 %     comparison flux vector (condition B, such as a mutant or knock-in).
    +0016 %
    +0017 % Name-Value Arguments
    +0018 % --------------------
    +0019 % cutoff : double
    +0020 %     minimum |flux| for a reaction to be active, and minimum |flux2 - flux1|
    +0021 %     for a reaction to be changed (default 1e-8).
    +0022 % metaboliteList : cell
    +0023 %     cell array of metabolite names. Only reactions involving at least one
    +0024 %     of these metabolites are compared; all other reactions are excluded
    +0025 %     from every field of the result. Matching is against model.metNames and
    +0026 %     is case insensitive. A name that matches no metabolite is reported as
    +0027 %     a warning (default all reactions).
    +0028 % nMax : double
    +0029 %     maximum rows printed in the summary table (default 20).
    +0030 % verbose : logical
    +0031 %     print the summary table (default true).
    +0032 %
    +0033 % Returns
    +0034 % -------
    +0035 % result : struct
    +0036 %     structure with fields:
    +0037 %
    +0038 %     - turnedOn : cell array with the IDs of reactions that are active in
    +0039 %       fluxes2 and not in fluxes1.
    +0040 %     - turnedOff : cell array with the IDs of reactions that are active in
    +0041 %       fluxes1 and not in fluxes2.
    +0042 %     - flipped : cell array with the IDs of reactions that are active in
    +0043 %       both conditions and change sign.
    +0044 %     - changed : struct with fields for all reactions with |flux2 - flux1|
    +0045 %       above cutoff, sorted by |flux2 - flux1| descending:
    +0046 %
    +0047 %       - rxn : cell array with the reaction IDs.
    +0048 %       - flux1 : double array with the flux in condition A.
    +0049 %       - flux2 : double array with the flux in condition B.
    +0050 %       - absDelta : double array with |flux2 - flux1|.
    +0051 %       - relChange : double array with (flux2 - flux1)/|flux1|. NaN when
    +0052 %         |flux1| is below cutoff.
    +0053 %       - type : cell array with 'on', 'off', 'flip' or '' (changed
    +0054 %         magnitude) for each reaction.
    +0055 %
    +0056 % Examples
    +0057 % --------
    +0058 %     solA = solveLP(model);
    +0059 %     modelAna = setParam(model, 'ub', 'O2exchange', 0);
    +0060 %     solB = solveLP(modelAna);
    +0061 %     result = compareFluxes(model, solA.x, solB.x)
    +0062 %     result = compareFluxes(model, solA.x, solB.x, 'nMax', 50)
    +0063 %     result = compareFluxes(model, solA.x, solB.x, 'metaboliteList', {'ATP','NADH'})
    +0064 %
    +0065 % See also
    +0066 % --------
    +0067 % walkFluxes, traceFluxPath, modelSummary
    +0068 
    +0069 p = parseRAVENargs(varargin, {'cutoff',1e-8; 'metaboliteList',[]; 'nMax',20; 'verbose',true});
    +0070 cutoff         = p.cutoff;
    +0071 metaboliteList = p.metaboliteList;
    +0072 nMax           = p.nMax;
    +0073 verbose        = p.verbose;
    +0074 
    +0075 f1    = fluxes1(:);
    +0076 f2    = fluxes2(:);
    +0077 nRxns = numel(model.rxns);
    +0078 
    +0079 if numel(f1) ~= nRxns || numel(f2) ~= nRxns
    +0080     error('RAVEN:badInput', ...
    +0081         'Flux vectors must have %d elements (numel(model.rxns)).', nRxns);
    +0082 end
    +0083 
    +0084 active1 = abs(f1) > cutoff;
    +0085 active2 = abs(f2) > cutoff;
    +0086 delta   = f2 - f1;
    +0087 
    +0088 % Restrict to reactions involving one of metaboliteList, if given. Reactions
    +0089 % left out are treated as unchanged, so they appear in no output field.
    +0090 inList = true(nRxns, 1);
    +0091 if ~isempty(metaboliteList)
    +0092     metaboliteList = convertCharArray(metaboliteList);
    +0093     inList = false(nRxns, 1);
    +0094     notFound = {};
    +0095     for i = 1:numel(metaboliteList)
    +0096         metIdx = find(strcmpi(metaboliteList{i}, model.metNames));
    +0097         if isempty(metIdx)
    +0098             notFound{end+1} = metaboliteList{i}; %#ok<AGROW>
    +0099         else
    +0100             inList(any(model.S(metIdx,:) ~= 0, 1)) = true;
    +0101         end
    +0102     end
    +0103     if ~isempty(notFound)
    +0104         warning('RAVEN:warning', '%s', ...
    +0105             ravenList('No metabolite in the model is named:', notFound));
    +0106     end
    +0107     active1(~inList) = false;
    +0108     active2(~inList) = false;
    +0109     delta(~inList)   = 0;
    +0110 end
    +0111 
    +0112 onMask   = ~active1 &  active2;
    +0113 offMask  =  active1 & ~active2;
    +0114 flipMask =  active1 &  active2 & sign(f1) ~= sign(f2);
    +0115 
    +0116 result.turnedOn  = model.rxns(onMask);
    +0117 result.turnedOff = model.rxns(offMask);
    +0118 result.flipped   = model.rxns(flipMask);
    +0119 
    +0120 % All changed reactions sorted by |delta| descending
    +0121 changedMask = abs(delta) > cutoff;
    +0122 [~, si]     = sort(abs(delta(changedMask)), 'descend');
    +0123 idx = find(changedMask);
    +0124 idx = idx(si);
    +0125 
    +0126 % Relative change: (f2-f1)/|f1|; NaN when reference flux is near-zero
    +0127 denom = abs(f1(idx));
    +0128 denom(denom < cutoff) = NaN;
    +0129 relChange = delta(idx) ./ denom;
    +0130 
    +0131 % Type label per changed reaction
    +0132 changedTypes = repmat({''}, numel(idx), 1);
    +0133 for k = 1:numel(idx)
    +0134     ri = idx(k);
    +0135     if onMask(ri)
    +0136         changedTypes{k} = 'on';
    +0137     elseif offMask(ri)
    +0138         changedTypes{k} = 'off';
    +0139     elseif flipMask(ri)
    +0140         changedTypes{k} = 'flip';
    +0141     end
    +0142 end
    +0143 
    +0144 result.changed.rxn       = model.rxns(idx);
    +0145 result.changed.flux1     = f1(idx);
    +0146 result.changed.flux2     = f2(idx);
    +0147 result.changed.absDelta  = abs(delta(idx));
    +0148 result.changed.relChange = relChange;
    +0149 result.changed.type      = changedTypes;
    +0150 
    +0151 if verbose
    +0152     printTable_(model, result, nMax, nnz(inList));
    +0153 end
    +0154 end
    +0155 
    +0156 %--------------------------------------------------------------------------
    +0157 function printTable_(model, r, nMax, nConsidered)
    +0158     W   = 72;
    +0159     bar = repmat('=', 1, W);
    +0160     sep = repmat('-', 1, W);
    +0161     nC  = numel(r.changed.rxn);
    +0162 
    +0163     fprintf('\n%s\n', bar);
    +0164     fprintf('  Flux comparison: %d of %d reactions changed\n', nC, nConsidered);
    +0165     nOn  = numel(r.turnedOn);
    +0166     nOff = numel(r.turnedOff);
    +0167     nFlp = numel(r.flipped);
    +0168     if nOn + nOff + nFlp > 0
    +0169         fprintf('  (on: %d  |  off: %d  |  reversed: %d)\n', nOn, nOff, nFlp);
    +0170     end
    +0171 
    +0172     if nC == 0
    +0173         fprintf('%s\n\n', bar);
    +0174         return
    +0175     end
    +0176 
    +0177     nShow    = min(nC, nMax);
    +0178     showRxns = r.changed.rxn(1:nShow);
    +0179     eqns     = constructEquations(model, showRxns);
    +0180 
    +0181     fprintf('%s\n', sep);
    +0182     fprintf('  %-20s  %10s  %10s  %10s  %7s  %-5s  %-25s  %s\n', ...
    +0183         'Reaction', 'Cond.1', 'Cond.2', '|dflux|', 'drel%', 'Type', 'Name', 'Equation');
    +0184     fprintf('  %s\n', sep);
    +0185 
    +0186     for i = 1:nShow
    +0187         rel = r.changed.relChange(i);
    +0188         if isnan(rel)
    +0189             relStr = '    new';
    +0190         else
    +0191             relStr = sprintf('%+6.0f%%', rel * 100);
    +0192         end
    +0193         ri = find(strcmp(model.rxns, showRxns{i}), 1);
    +0194         nm = '';
    +0195         if isfield(model,'rxnNames') && ri <= numel(model.rxnNames)
    +0196             nm = char(model.rxnNames{ri});
    +0197         end
    +0198         if numel(nm) > 25, nm = [nm(1:22) '...']; end
    +0199         fprintf('  %-20s  %+10.4g  %+10.4g  %10.4g  %7s  %-5s  %-25s  %s\n', ...
    +0200             showRxns{i}, r.changed.flux1(i), r.changed.flux2(i), ...
    +0201             r.changed.absDelta(i), relStr, r.changed.type{i}, nm, eqns{i});
    +0202     end
    +0203     if nC > nMax
    +0204         fprintf('  ... (%d more, use nMax to show all)\n', nC - nMax);
    +0205     end
    +0206     fprintf('%s\n\n', bar);
    +0207 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/analysis/findGeneDeletions.html b/docs/html/analysis/findGeneDeletions.html new file mode 100644 index 00000000..fa94b4df --- /dev/null +++ b/docs/html/analysis/findGeneDeletions.html @@ -0,0 +1,235 @@ + + + + Description of findGeneDeletions + + + + + + + + + +
    Home > analysis > findGeneDeletions.m
    + + + +

    findGeneDeletions +

    + +

    PURPOSE ^

    +
    findGeneDeletions Delete genes and track the resulting fluxes.
    + +

    SYNOPSIS ^

    +
    function [genes, fluxes, originalGenes, details, grRatioMuts]=findGeneDeletions(model,varargin)
    + +

    DESCRIPTION ^

    +
     findGeneDeletions  Delete genes and track the resulting fluxes.
    +
    + Deletes single genes or gene pairs, solves the model by FBA after each
    + deletion, and returns the resulting fluxes. The output identifies gene
    + deletion targets.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + testType : char
    +     type of gene deletion (default "sgd"):
    +
    +     - "sgd" : single gene deletion
    +     - "dgd" : double gene deletion
    +
    + Returns
    + -------
    + genes : double
    +     a matrix with the genes that were deleted in each simulation (the
    +     gene indexes in originalGenes). Each row corresponds to a column in
    +     fluxes.
    + fluxes : double
    +     a matrix with the resulting fluxes. Double deletions that result in
    +     an unsolvable problem have all zero flux. Single deletions that
    +     result in an unsolvable problem are indicated in details instead.
    + originalGenes : cell
    +     the genes in the input model.
    + details : double
    +     a vector with one element per gene in originalGenes that states
    +     whether the gene was deleted and why. Genes that only belong to
    +     dead-end reactions, for example, are not deleted:
    +
    +     - 1 : deleted, and the problem was solved (a lethal deletion
    +       with zero growth also gets 1)
    +     - 2 : the single-deletion problem had no optimal solution
    +       (infeasible or failed); fluxes and grRatioMuts are 0
    +     - 3 : redundant (not assigned)
    +     - 4 : involved in dead-end reaction
    + grRatioMuts : double
    +     growth rate ratio between mutated strain and wild type, matching the
    +     originalGenes(genes) mutants. The elements do not map directly to
    +     model.genes, unlike the output of COBRA getEssentialGenes. The
    +     mapping to model.genes is obtained with:
    +
    +         grRatio=zeros(1,numel(model.genes));
    +         grRatio(genes)=grRatioMuts;
    +
    + Examples
    + --------
    +     [genes, fluxes, originalGenes, details, grRatioMuts]=...
    +         findGeneDeletions(model,testType);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function [genes, fluxes, originalGenes, details, grRatioMuts]=findGeneDeletions(model,varargin)
    +0002 % findGeneDeletions  Delete genes and track the resulting fluxes.
    +0003 %
    +0004 % Deletes single genes or gene pairs, solves the model by FBA after each
    +0005 % deletion, and returns the resulting fluxes. The output identifies gene
    +0006 % deletion targets.
    +0007 %
    +0008 % Parameters
    +0009 % ----------
    +0010 % model : struct
    +0011 %     a model structure.
    +0012 %
    +0013 % Name-Value Arguments
    +0014 % --------------------
    +0015 % testType : char
    +0016 %     type of gene deletion (default "sgd"):
    +0017 %
    +0018 %     - "sgd" : single gene deletion
    +0019 %     - "dgd" : double gene deletion
    +0020 %
    +0021 % Returns
    +0022 % -------
    +0023 % genes : double
    +0024 %     a matrix with the genes that were deleted in each simulation (the
    +0025 %     gene indexes in originalGenes). Each row corresponds to a column in
    +0026 %     fluxes.
    +0027 % fluxes : double
    +0028 %     a matrix with the resulting fluxes. Double deletions that result in
    +0029 %     an unsolvable problem have all zero flux. Single deletions that
    +0030 %     result in an unsolvable problem are indicated in details instead.
    +0031 % originalGenes : cell
    +0032 %     the genes in the input model.
    +0033 % details : double
    +0034 %     a vector with one element per gene in originalGenes that states
    +0035 %     whether the gene was deleted and why. Genes that only belong to
    +0036 %     dead-end reactions, for example, are not deleted:
    +0037 %
    +0038 %     - 1 : deleted, and the problem was solved (a lethal deletion
    +0039 %       with zero growth also gets 1)
    +0040 %     - 2 : the single-deletion problem had no optimal solution
    +0041 %       (infeasible or failed); fluxes and grRatioMuts are 0
    +0042 %     - 3 : redundant (not assigned)
    +0043 %     - 4 : involved in dead-end reaction
    +0044 % grRatioMuts : double
    +0045 %     growth rate ratio between mutated strain and wild type, matching the
    +0046 %     originalGenes(genes) mutants. The elements do not map directly to
    +0047 %     model.genes, unlike the output of COBRA getEssentialGenes. The
    +0048 %     mapping to model.genes is obtained with:
    +0049 %
    +0050 %         grRatio=zeros(1,numel(model.genes));
    +0051 %         grRatio(genes)=grRatioMuts;
    +0052 %
    +0053 % Examples
    +0054 % --------
    +0055 %     [genes, fluxes, originalGenes, details, grRatioMuts]=...
    +0056 %         findGeneDeletions(model,testType);
    +0057 
    +0058 originalModel=model;
    +0059 p=parseRAVENargs(varargin, {'testType',[]});
    +0060 testType=p.testType;
    +0061 if isempty(testType)
    +0062     testType='sgd';
    +0063 else
    +0064     testType=char(testType);
    +0065 end
    +0066 
    +0067 %Check that the test type is correct
    +0068 if ~strcmpi(testType,'sgd') && ~strcmpi(testType,'dgd')
    +0069     EM='Incorrect test type';
    +0070     error('RAVEN:badInput', '%s', EM);
    +0071 end
    +0072 
    +0073 originalGenes=model.genes;
    +0074 details=zeros(numel(model.genes),1);
    +0075 
    +0076 %First simplify the model to reduce the size
    +0077 model=simplifyModel(model,true,false,true,true);
    +0078 model=removeReactions(model,{},true,true); %Removes unused genes
    +0079 details(~ismember(originalGenes,model.genes))=4;
    +0080 
    +0081 [~, geneMapping]=ismember(model.genes,originalGenes);
    +0082 growthWT=solveLP(model);
    +0083 growthWT=growthWT.f;
    +0084 
    +0085 %Do single deletion. This is done here since the double deletion depends
    +0086 %on which single deletions prove lethal (to reduce the size of the system)
    +0087 if strcmpi(testType,'sgd') || strcmpi(testType,'dgd')
    +0088     fluxes=zeros(numel(model.rxns),numel(model.genes));
    +0089     grRatioMuts=zeros(1,numel(model.genes));
    +0090     for i=1:numel(model.genes)
    +0091         %Constrain all reactions involving the gene to 0
    +0092         tempModel=removeGenes(model,i,false,false,false);
    +0093         sol=solveLP(tempModel);
    +0094 
    +0095         %If the optimization terminated successfully
    +0096         if sol.stat==1
    +0097             fluxes(:,i)=sol.x;
    +0098             grRatioMuts(i)=sol.f/growthWT;
    +0099             details(geneMapping(i))=1;
    +0100         else
    +0101             fluxes(:,i)=0;
    +0102             grRatioMuts(i)=0;
    +0103             details(geneMapping(i))=2;
    +0104         end
    +0105     end
    +0106     genes=geneMapping;
    +0107 end
    +0108 
    +0109 %For double deletions
    +0110 if strcmpi(testType,'dgd')
    +0111     %This is a little lazy but it is fine. Check which genes have already
    +0112     %been deleted in "sgd" analysis.
    +0113     [~, I]=ismember(originalGenes(details==1),model.genes);
    +0114     genesToModify=nchoosek(I,2);
    +0115     genes=geneMapping(genesToModify);
    +0116     grRatioMuts=zeros(1,size(genesToModify,1));
    +0117     fluxes=sparse(numel(model.rxns),size(genesToModify,1));
    +0118     for i=1:size(genesToModify,1)
    +0119         tempModel=removeGenes(model,genesToModify(i,:),false,false,false);
    +0120         sol=solveLP(tempModel);
    +0121 
    +0122         if sol.stat==1
    +0123             fluxes(:,i)=sol.x;
    +0124             grRatioMuts(i)=sol.f/growthWT;
    +0125         end
    +0126     end
    +0127 end
    +0128 
    +0129 %Map back to the old model
    +0130 [~, I]=ismember(model.rxns,originalModel.rxns);
    +0131 temp=fluxes;
    +0132 fluxes=sparse(numel(originalModel.rxns),size(temp,2));
    +0133 fluxes(I,:)=temp;
    +0134 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/analysis/getAllSubGraphs.html b/docs/html/analysis/getAllSubGraphs.html similarity index 91% rename from doc/analysis/getAllSubGraphs.html rename to docs/html/analysis/getAllSubGraphs.html index c887854b..ab39fa49 100644 --- a/doc/analysis/getAllSubGraphs.html +++ b/docs/html/analysis/getAllSubGraphs.html @@ -39,9 +39,9 @@

    DESCRIPTION ^SOURCE CODE ^% Returns 0012 % ------- 0013 % subGraphs : logical -0014 % a boolean matrix where the rows correspond to the metabolites and -0015 % the columns to which subgraph they are assigned to. The columns are -0016 % ordered so that larger subgraphs come first. +0014 % a logical matrix with one row per metabolite and one column per +0015 % subgraph. An element is true if the metabolite belongs to the +0016 % subgraph. The columns are ordered by decreasing subgraph size. 0017 % 0018 % Examples 0019 % -------- diff --git a/doc/analysis/getAllowedBounds.html b/docs/html/analysis/getAllowedBounds.html similarity index 80% rename from doc/analysis/getAllowedBounds.html rename to docs/html/analysis/getAllowedBounds.html index f3c75685..875148be 100644 --- a/doc/analysis/getAllowedBounds.html +++ b/docs/html/analysis/getAllowedBounds.html @@ -41,12 +41,11 @@

    DESCRIPTION ^DESCRIPTION ^SOURCE CODE ^% number of elements as reactions in the model, or a vector of 0014 % reaction indexes (default model.rxns). 0015 % runParallel : logical -0016 % speed up calculations by parallel processing. This is not beneficial -0017 % if allowed bounds are calculated for only a few reactions, as the -0018 % overhead of parallel processing will take longer. It requires MATLAB -0019 % Parallel Computing Toolbox. If this is not installed, the -0020 % calculations will not be parallelized, regardless of what is -0021 % indicated as runParallel (default true). -0022 % -0023 % Returns -0024 % ------- -0025 % minFluxes : double -0026 % minimal allowed fluxes. -0027 % maxFluxes : double -0028 % maximal allowed fluxes. -0029 % exitFlags : double -0030 % exit flags for min/max for each of the reactions. True if it was -0031 % possible to calculate a flux. +0016 % run the calculations in parallel (default true). Parallel processing +0017 % requires the MATLAB Parallel Computing Toolbox; without it, the +0018 % calculations run serially regardless of this setting. For a few +0019 % reactions, the overhead of parallel processing exceeds the time it +0020 % saves. +0021 % +0022 % Returns +0023 % ------- +0024 % minFluxes : double +0025 % minimal allowed fluxes. +0026 % maxFluxes : double +0027 % maximal allowed fluxes. +0028 % exitFlags : double +0029 % a matrix with one row per reaction. Column 1 holds the solver status +0030 % of the minimisation and column 2 the status of the maximisation +0031 % (1 if a flux could be calculated). 0032 % 0033 % Notes 0034 % ----- diff --git a/doc/analysis/getEssentialRxns.html b/docs/html/analysis/getEssentialRxns.html similarity index 100% rename from doc/analysis/getEssentialRxns.html rename to docs/html/analysis/getEssentialRxns.html diff --git a/doc/analysis/getFluxZ.html b/docs/html/analysis/getFluxZ.html similarity index 59% rename from doc/analysis/getFluxZ.html rename to docs/html/analysis/getFluxZ.html index d4d426f2..dbf4f9a3 100644 --- a/doc/analysis/getFluxZ.html +++ b/docs/html/analysis/getFluxZ.html @@ -41,9 +41,12 @@

    DESCRIPTION ^SOURCE CODE ^% Returns 0014 % ------- 0015 % Z : double -0016 % a vector with Z-scores that tells you for each reaction how likely it -0017 % is for its flux to have increased (positive sign) or decreased -0018 % (negative sign) in the second condition with respect to the first. -0019 % -0020 % Examples -0021 % -------- -0022 % Z = getFluxZ(solutionsA, solutionsB); -0023 % -0024 % See also -0025 % -------- -0026 % randomSampling -0027 -0028 nRxns=size(solutionsA,1); -0029 -0030 %Check that the number of reactions is the same in both cases -0031 if nRxns~=size(solutionsB,1) -0032 EM='The number of reactions must be the same in solutionsA as in solutionsB'; -0033 error('RAVEN:badInput', '%s', EM); -0034 end -0035 -0036 Z=zeros(nRxns,1); -0037 -0038 %Calculate the mean and standard deviation for the two cases -0039 mA=mean(solutionsA,2); -0040 mB=mean(solutionsB,2); -0041 -0042 %This can lead to OUT OF MEMORY, so do it in segments of 500 reactions -0043 varA=zeros(size(solutionsA,1),1); -0044 for i=1:500:size(solutionsA,1) -0045 varA(i:min(i+499,size(solutionsA,1)))=var(solutionsA(i:min(i+499,size(solutionsA,1)),:),0,2); -0046 end -0047 varB=zeros(size(solutionsB,1),1); -0048 for i=1:500:size(solutionsB,1) -0049 varB(i:min(i+499,size(solutionsB,1)))=var(solutionsB(i:min(i+499,size(solutionsB,1)),:),0,2); -0050 end -0051 -0052 %If the mean of both solutions are the same then the Z-score is zero -0053 toCheck=mA~=mB; +0016 % a vector with one Z-score per reaction: the difference between the +0017 % mean fluxes of solutionsB and solutionsA, divided by the square root +0018 % of the sum of their variances. A positive sign indicates an increase +0019 % of the flux in the second condition with respect to the first, a +0020 % negative sign a decrease. Values are limited to the range -100 to +0021 % 100. +0022 % +0023 % Examples +0024 % -------- +0025 % Z = getFluxZ(solutionsA, solutionsB); +0026 % +0027 % See also +0028 % -------- +0029 % randomSampling +0030 +0031 nRxns=size(solutionsA,1); +0032 +0033 %Check that the number of reactions is the same in both cases +0034 if nRxns~=size(solutionsB,1) +0035 EM='The number of reactions must be the same in solutionsA as in solutionsB'; +0036 error('RAVEN:badInput', '%s', EM); +0037 end +0038 +0039 Z=zeros(nRxns,1); +0040 +0041 %Calculate the mean and standard deviation for the two cases +0042 mA=mean(solutionsA,2); +0043 mB=mean(solutionsB,2); +0044 +0045 %This can lead to OUT OF MEMORY, so do it in segments of 500 reactions +0046 varA=zeros(size(solutionsA,1),1); +0047 for i=1:500:size(solutionsA,1) +0048 varA(i:min(i+499,size(solutionsA,1)))=var(solutionsA(i:min(i+499,size(solutionsA,1)),:),0,2); +0049 end +0050 varB=zeros(size(solutionsB,1),1); +0051 for i=1:500:size(solutionsB,1) +0052 varB(i:min(i+499,size(solutionsB,1)))=var(solutionsB(i:min(i+499,size(solutionsB,1)),:),0,2); +0053 end 0054 -0055 %If the variance is zero in both cases, then put a very large or very small -0056 %Z-score for the corresponding reactions -0057 I=find(varA==0 & varB==0 & toCheck==true); -0058 toCheck(I)=false; -0059 % J true where flux increased from A to B, matching the sign convention -0060 % the general branch below uses ((mB-mA)/... is positive when increased) -0061 J=mB(I)>mA(I); -0062 Z(I(J))=100; -0063 Z(I(~J))=-100; -0064 toCheck=find(toCheck); -0065 -0066 for i=1:numel(toCheck) -0067 Z(toCheck(i))=(mB(toCheck(i))-mA(toCheck(i)))/sqrt(varA(toCheck(i))+varB(toCheck(i))); -0068 end -0069 -0070 %Shrink very large values -0071 Z=min(Z,100); -0072 Z=max(Z,-100); -0073 end

    +0055 %If the mean of both solutions are the same then the Z-score is zero +0056 toCheck=mA~=mB; +0057 +0058 %If the variance is zero in both cases, then put a very large or very small +0059 %Z-score for the corresponding reactions +0060 I=find(varA==0 & varB==0 & toCheck==true); +0061 toCheck(I)=false; +0062 % J true where flux increased from A to B, matching the sign convention +0063 % the general branch below uses ((mB-mA)/... is positive when increased) +0064 J=mB(I)>mA(I); +0065 Z(I(J))=100; +0066 Z(I(~J))=-100; +0067 toCheck=find(toCheck); +0068 +0069 for i=1:numel(toCheck) +0070 Z(toCheck(i))=(mB(toCheck(i))-mA(toCheck(i)))/sqrt(varA(toCheck(i))+varB(toCheck(i))); +0071 end +0072 +0073 %Shrink very large values +0074 Z=min(Z,100); +0075 Z=max(Z,-100); +0076 end
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/analysis/getMinimalMedium.html b/docs/html/analysis/getMinimalMedium.html new file mode 100644 index 00000000..61acf538 --- /dev/null +++ b/docs/html/analysis/getMinimalMedium.html @@ -0,0 +1,295 @@ + + + + Description of getMinimalMedium + + + + + + + + + +
    Home > analysis > getMinimalMedium.m
    + + + +

    getMinimalMedium +

    + +

    PURPOSE ^

    +
    getMinimalMedium Find the minimal set of uptake reactions for growth.
    + +

    SYNOPSIS ^

    +
    function [medium, mediumIdx] = getMinimalMedium(model, varargin)
    + +

    DESCRIPTION ^

    +
     getMinimalMedium  Find the minimal set of uptake reactions for growth.
    +
    + Uses MILP to find the smallest set of exchange reactions that can carry
    + uptake flux (lb < 0) while the model achieves a target growth rate. The
    + MILP formulation gives a medium with the globally minimal number of
    + uptake reactions.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + minGrowth : double
    +     minimum objective value (growth rate) the medium must support. If
    +     empty, 10 % of the optimum objective value under the current bounds,
    +     which requires one extra FBA call. A value given explicitly avoids
    +     that call (for example 'minGrowth', 0.1) (default empty).
    + verbose : logical
    +     print the resulting minimal medium (default true).
    +
    + Returns
    + -------
    + medium : cell
    +     cell array of reaction IDs that form the minimal medium. Empty if
    +     no feasible medium can support the requested growth.
    + mediumIdx : double
    +     column vector of reaction indices corresponding to medium.
    +
    + Examples
    + --------
    +     [med, idx] = getMinimalMedium(model)
    +     [med, idx] = getMinimalMedium(model, 'minGrowth', 0.05)
    +     [med, idx] = getMinimalMedium(model, 'verbose', false)
    +
    + Notes
    + -----
    + Only exchange reactions with lb < 0 are candidate uptake reactions.
    + Exchange reactions with lb >= 0 are not included.
    +
    + The MILP formulation:
    +   minimise  sum(y_i)
    +   s.t.      S v = 0
    +             c' v >= minGrowth
    +             v_i >= lb_i * y_i   for each candidate uptake reaction i
    +             lb_j <= v_j <= ub_j for all reactions j
    +             y_i in {0, 1}
    +
    + A y_i = 0 forces reaction i to carry non-negative flux (no net uptake);
    + y_i = 1 allows it to carry flux as low as lb_i.
    +
    + The number of uptake reactions is minimal, but the medium is not
    + necessarily unique: when several media of the same size support the
    + growth rate, the solver returns one of them, and another solver or
    + solver version can return a different one.
    +
    + The MILP is solved with the solver set by setRavenSolver. RAVEN's bundled
    + GLPK solves it for small models. The RAVEN:glpkMILP warning issued with
    + GLPK states that GLPK works for small problems and that Gurobi is much
    + faster and more reliable for larger ones (setRavenSolver('gurobi')).
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [medium, mediumIdx] = getMinimalMedium(model, varargin)
    +0002 % getMinimalMedium  Find the minimal set of uptake reactions for growth.
    +0003 %
    +0004 % Uses MILP to find the smallest set of exchange reactions that can carry
    +0005 % uptake flux (lb < 0) while the model achieves a target growth rate. The
    +0006 % MILP formulation gives a medium with the globally minimal number of
    +0007 % uptake reactions.
    +0008 %
    +0009 % Parameters
    +0010 % ----------
    +0011 % model : struct
    +0012 %     a model structure.
    +0013 %
    +0014 % Name-Value Arguments
    +0015 % --------------------
    +0016 % minGrowth : double
    +0017 %     minimum objective value (growth rate) the medium must support. If
    +0018 %     empty, 10 % of the optimum objective value under the current bounds,
    +0019 %     which requires one extra FBA call. A value given explicitly avoids
    +0020 %     that call (for example 'minGrowth', 0.1) (default empty).
    +0021 % verbose : logical
    +0022 %     print the resulting minimal medium (default true).
    +0023 %
    +0024 % Returns
    +0025 % -------
    +0026 % medium : cell
    +0027 %     cell array of reaction IDs that form the minimal medium. Empty if
    +0028 %     no feasible medium can support the requested growth.
    +0029 % mediumIdx : double
    +0030 %     column vector of reaction indices corresponding to medium.
    +0031 %
    +0032 % Examples
    +0033 % --------
    +0034 %     [med, idx] = getMinimalMedium(model)
    +0035 %     [med, idx] = getMinimalMedium(model, 'minGrowth', 0.05)
    +0036 %     [med, idx] = getMinimalMedium(model, 'verbose', false)
    +0037 %
    +0038 % Notes
    +0039 % -----
    +0040 % Only exchange reactions with lb < 0 are candidate uptake reactions.
    +0041 % Exchange reactions with lb >= 0 are not included.
    +0042 %
    +0043 % The MILP formulation:
    +0044 %   minimise  sum(y_i)
    +0045 %   s.t.      S v = 0
    +0046 %             c' v >= minGrowth
    +0047 %             v_i >= lb_i * y_i   for each candidate uptake reaction i
    +0048 %             lb_j <= v_j <= ub_j for all reactions j
    +0049 %             y_i in {0, 1}
    +0050 %
    +0051 % A y_i = 0 forces reaction i to carry non-negative flux (no net uptake);
    +0052 % y_i = 1 allows it to carry flux as low as lb_i.
    +0053 %
    +0054 % The number of uptake reactions is minimal, but the medium is not
    +0055 % necessarily unique: when several media of the same size support the
    +0056 % growth rate, the solver returns one of them, and another solver or
    +0057 % solver version can return a different one.
    +0058 %
    +0059 % The MILP is solved with the solver set by setRavenSolver. RAVEN's bundled
    +0060 % GLPK solves it for small models. The RAVEN:glpkMILP warning issued with
    +0061 % GLPK states that GLPK works for small problems and that Gurobi is much
    +0062 % faster and more reliable for larger ones (setRavenSolver('gurobi')).
    +0063 
    +0064 p = parseRAVENargs(varargin, {'minGrowth',[]; 'verbose',true});
    +0065 minGrowth = p.minGrowth;
    +0066 verbose   = p.verbose;
    +0067 
    +0068 medium    = {};
    +0069 mediumIdx = zeros(0,1);
    +0070 
    +0071 % ---- Resolve minGrowth ----
    +0072 if isempty(minGrowth)
    +0073     sol0 = solveLP(model);
    +0074     if sol0.stat ~= 1
    +0075         error('RAVEN:infeasible', ...
    +0076             'getMinimalMedium: model is infeasible under current bounds.');
    +0077     end
    +0078     minGrowth = 0.1 * sol0.f;
    +0079 end
    +0080 
    +0081 if minGrowth <= 0
    +0082     warning('getMinimalMedium:zeroGrowth', ...
    +0083         'minGrowth <= 0; the MILP will accept a zero-growth solution.');
    +0084 end
    +0085 
    +0086 % ---- Identify candidate uptake reactions ----
    +0087 [~, exchIdx] = getExchangeRxns(model);
    +0088 % Only consider exchange reactions with lb < 0 (able to take up)
    +0089 lb_cap = max(model.lb(exchIdx), -1000);  % cap at -1000 for numerical safety
    +0090 isUptake = lb_cap < 0;
    +0091 uptakeIdx = exchIdx(isUptake);           % indices into model.rxns
    +0092 uptakeLb  = lb_cap(isUptake);           % effective lower bounds (negative)
    +0093 n_uptake  = numel(uptakeIdx);
    +0094 
    +0095 if n_uptake == 0
    +0096     if verbose
    +0097         fprintf('getMinimalMedium: no exchange reactions with lb < 0 found.\n');
    +0098     end
    +0099     return
    +0100 end
    +0101 
    +0102 % ---- Build MILP ----
    +0103 n_mets = numel(model.mets);
    +0104 n_rxns = numel(model.rxns);
    +0105 n_vars = n_rxns + n_uptake;   % [flux vars | binary uptake indicators]
    +0106 
    +0107 % Objective: minimise sum(y)
    +0108 obj = [zeros(n_rxns,1); ones(n_uptake,1)];
    +0109 
    +0110 % 1. Steady-state: S*v = 0   (n_mets rows)
    +0111 A_ss = [model.S, sparse(n_mets, n_uptake)];
    +0112 b_ss = zeros(n_mets, 1);
    +0113 
    +0114 % 2. Growth lower bound: c'*v >= minGrowth   (1 row)
    +0115 A_grw = [model.c', sparse(1, n_uptake)];
    +0116 b_grw = minGrowth;
    +0117 
    +0118 % 3. Uptake coupling: v_i - lb_i*y_k >= 0   (n_uptake rows)
    +0119 %    coefficient of v_i (in flux block) = 1
    +0120 %    coefficient of y_k (in binary block, k-th uptake var) = -lb_i (> 0)
    +0121 rows  = (1:n_uptake)';
    +0122 colsV = uptakeIdx;
    +0123 colsY = n_rxns + (1:n_uptake)';
    +0124 A_upt = sparse([rows; rows], [colsV; colsY], [ones(n_uptake,1); -uptakeLb], n_uptake, n_vars);
    +0125 b_upt = zeros(n_uptake, 1);
    +0126 
    +0127 % Assemble — prob.a is the "original" A (without solver slack columns) and
    +0128 % is used by optimizeProb to know how many variables to return in res.full.
    +0129 prob.A       = [A_ss; A_grw; A_upt];
    +0130 prob.a       = prob.A;
    +0131 prob.b       = [b_ss; b_grw; b_upt];
    +0132 prob.csense  = [repmat('E',1,n_mets), 'G', repmat('G',1,n_uptake)];
    +0133 prob.c       = obj;
    +0134 prob.osense  = 1;   % minimise
    +0135 lb_forProb              = model.lb;
    +0136 lb_forProb(uptakeIdx)   = uptakeLb;   % cap at -1000 for numerical safety
    +0137 prob.lb      = [lb_forProb; zeros(n_uptake,1)];
    +0138 prob.ub      = [model.ub; ones(n_uptake,1)];
    +0139 prob.vartype = [repmat('C', 1, n_rxns), repmat('I', 1, n_uptake)];
    +0140 
    +0141 % ---- Solve ----
    +0142 sol = optimizeProb(prob, [], false);
    +0143 isFeasible = checkSolution(sol);
    +0144 
    +0145 if ~isFeasible
    +0146     if verbose
    +0147         fprintf('getMinimalMedium: MILP returned no feasible solution.\n');
    +0148         fprintf('  The model may not be able to grow at minGrowth=%.4g with any medium.\n', minGrowth);
    +0149     end
    +0150     return
    +0151 end
    +0152 
    +0153 % ---- Extract result ----
    +0154 y_sol     = sol.full(n_rxns + 1 : end);
    +0155 inMedium  = y_sol > 0.5;
    +0156 mediumIdx = uptakeIdx(inMedium);
    +0157 medium    = model.rxns(mediumIdx);
    +0158 
    +0159 if verbose
    +0160     printMedium_(model, medium, mediumIdx, minGrowth, n_uptake);
    +0161 end
    +0162 end
    +0163 
    +0164 %--------------------------------------------------------------------------
    +0165 function printMedium_(model, medium, mediumIdx, minGrowth, nCandidates)
    +0166     W = 62;
    +0167     fprintf('\n%s\n', repmat('=', 1, W));
    +0168     fprintf('  Minimal medium  (%d of %d candidate uptake reactions)\n', ...
    +0169         numel(medium), nCandidates);
    +0170     fprintf('  Target growth: %.4g\n', minGrowth);
    +0171     fprintf('%s\n', repmat('-', 1, W));
    +0172     if isempty(medium)
    +0173         fprintf('  (none — model can grow without any uptake)\n');
    +0174     else
    +0175         for i = 1:numel(medium)
    +0176             ri  = mediumIdx(i);
    +0177             nm  = '';
    +0178             if isfield(model,'rxnNames') && ri <= numel(model.rxnNames)
    +0179                 nm = char(model.rxnNames{ri});
    +0180             end
    +0181             if numel(nm) > 35, nm = [nm(1:32) '...']; end
    +0182             fprintf('  %-20s  %s\n', medium{i}, nm);
    +0183         end
    +0184     end
    +0185     fprintf('%s\n\n', repmat('=', 1, W));
    +0186 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/analysis/haveFlux.html b/docs/html/analysis/haveFlux.html new file mode 100644 index 00000000..91f089fa --- /dev/null +++ b/docs/html/analysis/haveFlux.html @@ -0,0 +1,208 @@ + + + + Description of haveFlux + + + + + + + + + +
    Home > analysis > haveFlux.m
    + + + +

    haveFlux +

    + +

    PURPOSE ^

    +
    haveFlux Check which reactions can carry a flux.
    + +

    SYNOPSIS ^

    +
    function I=haveFlux(model,varargin)
    + +

    DESCRIPTION ^

    +
     haveFlux  Check which reactions can carry a flux.
    +
    + Checks which reactions can carry a (positive or negative) flux under the
    + model's current bounds; other media or bounds are not explored. Unlike
    + getAllowedBounds, which minimises and maximises every reaction, it skips a
    + reaction once an earlier solution has given it a flux, and minimises only
    + reversible reactions that still have none.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + cutOff : double
    +     the absolute flux value that a reaction has to exceed to be identified
    +     as carrying flux (default 10^-6).
    + rxns : cell or logical or double
    +     either a cell array of IDs, a logical vector with the same number of
    +     elements as reactions in the model, or a vector of indexes (default
    +     model.rxns).
    + seed : double
    +     seed for the random number generator that shuffles the order in
    +     which reactions are tested. The order is then reproducible across
    +     runs (default: the current rng state is left untouched).
    +
    + Returns
    + -------
    + I : logical
    +     logical array with true if the corresponding reaction can carry a
    +     flux under the tested bounds.
    +
    + Examples
    + --------
    +     I = haveFlux(model, cutOff, rxns);
    +
    + Notes
    + -----
    + Bounds of +/- Inf are replaced with +/- 10000 before solving.
    +
    + Dead-end reactions are removed (simplifyModel) before solving. A dead-end
    + reaction with a forced flux (lb > 0 or ub < 0) makes the model infeasible,
    + but it is removed like any other dead end, so the remaining reactions are
    + tested as if it were absent. A forced flux elsewhere in an infeasible
    + model gives false for all reactions.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function I=haveFlux(model,varargin)
    +0002 % haveFlux  Check which reactions can carry a flux.
    +0003 %
    +0004 % Checks which reactions can carry a (positive or negative) flux under the
    +0005 % model's current bounds; other media or bounds are not explored. Unlike
    +0006 % getAllowedBounds, which minimises and maximises every reaction, it skips a
    +0007 % reaction once an earlier solution has given it a flux, and minimises only
    +0008 % reversible reactions that still have none.
    +0009 %
    +0010 % Parameters
    +0011 % ----------
    +0012 % model : struct
    +0013 %     a model structure.
    +0014 %
    +0015 % Name-Value Arguments
    +0016 % --------------------
    +0017 % cutOff : double
    +0018 %     the absolute flux value that a reaction has to exceed to be identified
    +0019 %     as carrying flux (default 10^-6).
    +0020 % rxns : cell or logical or double
    +0021 %     either a cell array of IDs, a logical vector with the same number of
    +0022 %     elements as reactions in the model, or a vector of indexes (default
    +0023 %     model.rxns).
    +0024 % seed : double
    +0025 %     seed for the random number generator that shuffles the order in
    +0026 %     which reactions are tested. The order is then reproducible across
    +0027 %     runs (default: the current rng state is left untouched).
    +0028 %
    +0029 % Returns
    +0030 % -------
    +0031 % I : logical
    +0032 %     logical array with true if the corresponding reaction can carry a
    +0033 %     flux under the tested bounds.
    +0034 %
    +0035 % Examples
    +0036 % --------
    +0037 %     I = haveFlux(model, cutOff, rxns);
    +0038 %
    +0039 % Notes
    +0040 % -----
    +0041 % Bounds of +/- Inf are replaced with +/- 10000 before solving.
    +0042 %
    +0043 % Dead-end reactions are removed (simplifyModel) before solving. A dead-end
    +0044 % reaction with a forced flux (lb > 0 or ub < 0) makes the model infeasible,
    +0045 % but it is removed like any other dead end, so the remaining reactions are
    +0046 % tested as if it were absent. A forced flux elsewhere in an infeasible
    +0047 % model gives false for all reactions.
    +0048 
    +0049 p=parseRAVENargs(varargin, {'cutOff',10^-6; 'rxns',[]; 'seed',[]});
    +0050 if ~isempty(p.seed)
    +0051     rng(p.seed);
    +0052 end
    +0053 cutOff=p.cutOff;
    +0054 rxns=p.rxns;
    +0055 if isempty(cutOff)
    +0056     cutOff=10^-6;
    +0057 end
    +0058 if isempty(rxns)
    +0059     rxns=model.rxns;
    +0060 elseif ~islogical(rxns) && ~isnumeric(rxns)
    +0061     rxns=convertCharArray(rxns);
    +0062 end
    +0063 
    +0064 %This is since we're maximizing for the sum of fluxes, which isn't possible
    +0065 %when there are infinite bounds
    +0066 model.lb(model.lb==-inf)=-10000;
    +0067 model.ub(model.ub==inf)=10000;
    +0068 
    +0069 %Get the reaction IDs. A bit of an awkward way, but fine.
    +0070 indexes=getIndexes(model,rxns,'rxns');
    +0071 rxns=model.rxns(indexes);
    +0072 
    +0073 %First remove all dead-end reactions
    +0074 smallModel=simplifyModel(model,false,false,true,true);
    +0075 
    +0076 %Nothing is left to solve when every reaction is a dead end, and reactions
    +0077 %without metabolites are limited by their bounds only
    +0078 if isempty(smallModel.rxns) || isempty(smallModel.mets)
    +0079     I=ismember(rxns,smallModel.rxns(smallModel.ub>cutOff | smallModel.lb<-cutOff));
    +0080     return
    +0081 end
    +0082 
    +0083 %Get the indexes of the reactions in the connected model
    +0084 indexes=getIndexes(smallModel,intersect(smallModel.rxns,rxns),'rxns');
    +0085 J=false(numel(indexes),1);
    +0086 mixIndexes=indexes(randperm(numel(indexes)));
    +0087 
    +0088 %Maximize for all fluxes first to get fewer rxns to test
    +0089 smallModel.c=ones(numel(smallModel.c),1);
    +0090 sol=solveLP(smallModel);
    +0091 if ~isempty(sol.x)
    +0092     J(abs(sol.x(mixIndexes))>cutOff)=true;
    +0093 end
    +0094 
    +0095 %Loop through and maximize then minimize each rxn if it does not already
    +0096 %have a flux
    +0097 Z=zeros(numel(smallModel.c),1);
    +0098 hsSolOut=[];
    +0099 for i=[1 -1]
    +0100     for j=1:numel(J)
    +0101         if J(j)==false
    +0102             %Only minimize for reversible reactions
    +0103             if i==1 || smallModel.rev(mixIndexes(j))~=0
    +0104                 smallModel.c=Z;
    +0105                 smallModel.c(mixIndexes(j))=i;
    +0106                 [sol, hsSolOut]=solveLP(smallModel,0,[],hsSolOut);
    +0107                 if any(sol.x)
    +0108                     J(abs(sol.x(mixIndexes))>cutOff)=true;
    +0109                 end
    +0110             end
    +0111         end
    +0112     end
    +0113 end
    +0114 
    +0115 I=ismember(rxns,smallModel.rxns(mixIndexes(J)));
    +0116 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/analysis/index.html b/docs/html/analysis/index.html new file mode 100644 index 00000000..f06e4d8e --- /dev/null +++ b/docs/html/analysis/index.html @@ -0,0 +1,29 @@ + + + + Index for Directory analysis + + + + + + + + + + +
    < Master indexIndex for analysis >
    + +

    Index for analysis

    + +

    Matlab files in this directory:

    + +
     FSEOFFSEOF Flux Scanning based on Enforced Objective Flux.
     analyzeSamplinganalyzeSampling Compare flux change significance with expression change.
     compareFluxescompareFluxes Find the reactions whose flux differs between two flux vectors.
     findGeneDeletionsfindGeneDeletions Delete genes and track the resulting fluxes.
     getAllSubGraphsgetAllSubGraphs Get all metabolic subgraphs in a model.
     getAllowedBoundsgetAllowedBounds Return the minimal and maximal fluxes through reactions.
     getEssentialRxnsgetEssentialRxns Calculate the essential reactions for a solvable model.
     getFluxZgetFluxZ Calculate Z scores between two sets of random flux distributions.
     getMinimalMediumgetMinimalMedium Find the minimal set of uptake reactions for growth.
     haveFluxhaveFlux Check which reactions can carry a flux.
     randomSamplingrandomSampling Sample the flux solution space (entry point for all samplers).
     reporterMetabolitesreporterMetabolites Identify metabolites around which transcriptional changes occur.
     runDynamicFBArunDynamicFBA Perform dynamic FBA using the static optimisation approach.
     runPhenotypePhasePlanerunPhenotypePhasePlane Run phenotype phase plane analysis and plot the results.
     runProductionEnveloperunProductionEnvelope Calculate the byproduct secretion envelope.
     runRobustnessAnalysisrunRobustnessAnalysis Perform robustness analysis for a reaction and objective.
     runSimpleOptKnockrunSimpleOptKnock Find deletions that couple metabolite production to growth.
     sampleACHRsampleACHR Artificially Centered Hit-and-Run flux sampling.
     sampleCHRRsampleCHRR Coordinate Hit-and-Run with Rounding flux sampling.
     sampleChebyshevCentersampleChebyshevCenter Centre of the largest ball inscribed in a polytope.
     sampleMaxVolEllipsesampleMaxVolEllipse Maximum-volume inscribed ellipsoid of a polytope.
     sampleWarmupPointssampleWarmupPoints FVA warmup vertices for ACHR sampling.
     traceFluxPathtraceFluxPath Find the highest-flux-fraction path between two reactions.
     walkFluxeswalkFluxes Interactively navigate a flux distribution reaction by reaction.
    + + + + +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/analysis/randomSampling.html b/docs/html/analysis/randomSampling.html new file mode 100644 index 00000000..718157df --- /dev/null +++ b/docs/html/analysis/randomSampling.html @@ -0,0 +1,442 @@ + + + + Description of randomSampling + + + + + + + + + +
    Home > analysis > randomSampling.m
    + + + +

    randomSampling +

    + +

    PURPOSE ^

    +
    randomSampling Sample the flux solution space (entry point for all samplers).
    + +

    SYNOPSIS ^

    +
    function [solutions, goodRxns, info]=randomSampling(model,varargin)
    + +

    DESCRIPTION ^

    +
     randomSampling  Sample the flux solution space (entry point for all samplers).
    +
    + Dispatches to one of three sampling methods via the 'method' argument:
    +   'achr' (default)  Artificially Centered Hit-and-Run; near-uniform MCMC
    +                     sampling of the polytope interior (see sampleACHR).
    +   'chrr'            Coordinate Hit-and-Run with Rounding; near-uniform MCMC
    +                     with maximum-volume-ellipsoid rounding, which improves
    +                     mixing on thin or ill-conditioned polytopes such as
    +                     enzyme-constrained models (see sampleCHRR).
    +   'randomObjective' the random-objective vertex method of Bordel et al. (2010)
    +                     PLoS Comput Biol (doi:10.1371/journal.pcbi.1000859): each
    +                     sample optimises a random objective of nObjectives
    +                     reactions with random weights, returning a polytope
    +                     vertex.
    +
    + The 'achr' and 'chrr' methods draw from the (near-)uniform distribution
    + over the interior; the 'randomObjective' method draws vertices. Arguments
    + below marked [randomObjective] apply only to that method; 'thinning' and
    + 'nBurnin' apply only to the MCMC methods.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + nSamples : double
    +     the number of solutions to return (default 1000).
    + method : char
    +     sampling method: 'achr' (default), 'chrr', or 'randomObjective'.
    + thinning : double
    +     MCMC steps between recorded samples for 'achr' and 'chrr' (default 100).
    + nBurnin : double
    +     burn-in steps discarded before the first sample for 'chrr' (default 1000).
    + replaceBoundsWithInf : logical
    +     [randomObjective]
    +     replace the largest upper bounds with Inf and the smallest lower
    +     bounds with -Inf (the latter only if the smallest lower bound is
    +     negative). This is required to get solutions without loops if the
    +     model has arbitrarily large bounds, for example 1000 and -1000. A
    +     model with only biologically relevant bounds can use false
    +     (default true).
    + supressErrors : logical
    +     if false, randomSampling raises an error after 99 consecutive failed
    +     attempts to find a non-zero solution that is not involved in loops.
    +     Failures can result from constraints that are too relaxed (such as
    +     unlimited glucose uptake) or too strict (such as many narrow
    +     constraints). If true, the attempts continue without an error
    +     (default false).
    + runParallel : logical
    +     run the calculations in parallel (default true). Requires the MATLAB
    +     Parallel Computing Toolbox; without it, the calculations run serially
    +     regardless of this setting.
    + goodRxns : double
    +     vector of indexes of those reactions that are not involved in loops
    +     and can be used as random objective functions, as generated by a
    +     previous run of randomSampling on the same model (default empty).
    + minFlux : logical
    +     determines if a second optimisation is performed for each random
    +     sample, which minimises the sum of absolute fluxes to prevent loops
    +     (default false).
    + nObjectives : double
    +     number of reactions included in the random objective function per
    +     sample iteration (default 2).
    + seed : double
    +     random seed for reproducibility. If empty, the current MATLAB random
    +     state is used (default empty).
    +
    + Returns
    + -------
    + solutions : double
    +     matrix with the solutions.
    + goodRxns : double
    +     [randomObjective] vector of indexes of those reactions that are not
    +     involved in loops or always carry zero flux and can be used as random
    +     objective functions. Empty ([]) for the 'achr' and 'chrr' methods.
    + info : struct
    +     [chrr] the diagnostics from sampleCHRR, including mveConverged (whether
    +     the maximum-volume ellipsoid rounding reached tolerance). Empty ([])
    +     for the 'achr' and 'randomObjective' methods.
    +
    + Notes
    + -----
    + With 'randomObjective', the solutions are generated by optimising (with
    + random weights) a random set of nObjectives reactions (default 2). The
    + sign of each weight is chosen at random, which selects maximisation or
    + minimisation of the reaction; reactions with an upper bound below 0 always
    + receive a positive weight. The default 'achr' and the 'chrr' methods use
    + no random objectives; they take hit-and-run steps through the interior of
    + the solution space.
    +
    + If the model is a GECKO v3+ ecModel, then usage_prot reactions are not
    + selected as random objectives; the objectives are drawn from the
    + metabolic reactions.
    +
    + Examples
    + --------
    +     % Near-uniform interior sampling (default, ACHR):
    +     solutions = randomSampling(model, 1000);
    +     % Coordinate hit-and-run with rounding:
    +     solutions = randomSampling(model, 1000, 'method', 'chrr', 'seed', 1);
    +     % Random-objective vertex method:
    +     solutions = randomSampling(model, 1000, 'method', 'randomObjective');
    +
    + See also
    + --------
    + sampleACHR, sampleCHRR, sampleMaxVolEllipse
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • getAllowedBounds getAllowedBounds Return the minimal and maximal fluxes through reactions.
    • sampleACHR sampleACHR Artificially Centered Hit-and-Run flux sampling.
    • sampleCHRR sampleCHRR Coordinate Hit-and-Run with Rounding flux sampling.
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function [solutions, goodRxns, info]=randomSampling(model,varargin)
    +0002 % randomSampling  Sample the flux solution space (entry point for all samplers).
    +0003 %
    +0004 % Dispatches to one of three sampling methods via the 'method' argument:
    +0005 %   'achr' (default)  Artificially Centered Hit-and-Run; near-uniform MCMC
    +0006 %                     sampling of the polytope interior (see sampleACHR).
    +0007 %   'chrr'            Coordinate Hit-and-Run with Rounding; near-uniform MCMC
    +0008 %                     with maximum-volume-ellipsoid rounding, which improves
    +0009 %                     mixing on thin or ill-conditioned polytopes such as
    +0010 %                     enzyme-constrained models (see sampleCHRR).
    +0011 %   'randomObjective' the random-objective vertex method of Bordel et al. (2010)
    +0012 %                     PLoS Comput Biol (doi:10.1371/journal.pcbi.1000859): each
    +0013 %                     sample optimises a random objective of nObjectives
    +0014 %                     reactions with random weights, returning a polytope
    +0015 %                     vertex.
    +0016 %
    +0017 % The 'achr' and 'chrr' methods draw from the (near-)uniform distribution
    +0018 % over the interior; the 'randomObjective' method draws vertices. Arguments
    +0019 % below marked [randomObjective] apply only to that method; 'thinning' and
    +0020 % 'nBurnin' apply only to the MCMC methods.
    +0021 %
    +0022 % Parameters
    +0023 % ----------
    +0024 % model : struct
    +0025 %     a model structure.
    +0026 %
    +0027 % Name-Value Arguments
    +0028 % --------------------
    +0029 % nSamples : double
    +0030 %     the number of solutions to return (default 1000).
    +0031 % method : char
    +0032 %     sampling method: 'achr' (default), 'chrr', or 'randomObjective'.
    +0033 % thinning : double
    +0034 %     MCMC steps between recorded samples for 'achr' and 'chrr' (default 100).
    +0035 % nBurnin : double
    +0036 %     burn-in steps discarded before the first sample for 'chrr' (default 1000).
    +0037 % replaceBoundsWithInf : logical
    +0038 %     [randomObjective]
    +0039 %     replace the largest upper bounds with Inf and the smallest lower
    +0040 %     bounds with -Inf (the latter only if the smallest lower bound is
    +0041 %     negative). This is required to get solutions without loops if the
    +0042 %     model has arbitrarily large bounds, for example 1000 and -1000. A
    +0043 %     model with only biologically relevant bounds can use false
    +0044 %     (default true).
    +0045 % supressErrors : logical
    +0046 %     if false, randomSampling raises an error after 99 consecutive failed
    +0047 %     attempts to find a non-zero solution that is not involved in loops.
    +0048 %     Failures can result from constraints that are too relaxed (such as
    +0049 %     unlimited glucose uptake) or too strict (such as many narrow
    +0050 %     constraints). If true, the attempts continue without an error
    +0051 %     (default false).
    +0052 % runParallel : logical
    +0053 %     run the calculations in parallel (default true). Requires the MATLAB
    +0054 %     Parallel Computing Toolbox; without it, the calculations run serially
    +0055 %     regardless of this setting.
    +0056 % goodRxns : double
    +0057 %     vector of indexes of those reactions that are not involved in loops
    +0058 %     and can be used as random objective functions, as generated by a
    +0059 %     previous run of randomSampling on the same model (default empty).
    +0060 % minFlux : logical
    +0061 %     determines if a second optimisation is performed for each random
    +0062 %     sample, which minimises the sum of absolute fluxes to prevent loops
    +0063 %     (default false).
    +0064 % nObjectives : double
    +0065 %     number of reactions included in the random objective function per
    +0066 %     sample iteration (default 2).
    +0067 % seed : double
    +0068 %     random seed for reproducibility. If empty, the current MATLAB random
    +0069 %     state is used (default empty).
    +0070 %
    +0071 % Returns
    +0072 % -------
    +0073 % solutions : double
    +0074 %     matrix with the solutions.
    +0075 % goodRxns : double
    +0076 %     [randomObjective] vector of indexes of those reactions that are not
    +0077 %     involved in loops or always carry zero flux and can be used as random
    +0078 %     objective functions. Empty ([]) for the 'achr' and 'chrr' methods.
    +0079 % info : struct
    +0080 %     [chrr] the diagnostics from sampleCHRR, including mveConverged (whether
    +0081 %     the maximum-volume ellipsoid rounding reached tolerance). Empty ([])
    +0082 %     for the 'achr' and 'randomObjective' methods.
    +0083 %
    +0084 % Notes
    +0085 % -----
    +0086 % With 'randomObjective', the solutions are generated by optimising (with
    +0087 % random weights) a random set of nObjectives reactions (default 2). The
    +0088 % sign of each weight is chosen at random, which selects maximisation or
    +0089 % minimisation of the reaction; reactions with an upper bound below 0 always
    +0090 % receive a positive weight. The default 'achr' and the 'chrr' methods use
    +0091 % no random objectives; they take hit-and-run steps through the interior of
    +0092 % the solution space.
    +0093 %
    +0094 % If the model is a GECKO v3+ ecModel, then usage_prot reactions are not
    +0095 % selected as random objectives; the objectives are drawn from the
    +0096 % metabolic reactions.
    +0097 %
    +0098 % Examples
    +0099 % --------
    +0100 %     % Near-uniform interior sampling (default, ACHR):
    +0101 %     solutions = randomSampling(model, 1000);
    +0102 %     % Coordinate hit-and-run with rounding:
    +0103 %     solutions = randomSampling(model, 1000, 'method', 'chrr', 'seed', 1);
    +0104 %     % Random-objective vertex method:
    +0105 %     solutions = randomSampling(model, 1000, 'method', 'randomObjective');
    +0106 %
    +0107 % See also
    +0108 % --------
    +0109 % sampleACHR, sampleCHRR, sampleMaxVolEllipse
    +0110 
    +0111 p=parseRAVENargs(varargin, {'nSamples',[]; 'replaceBoundsWithInf',[]; 'supressErrors',[]; 'runParallel',[]; 'goodRxns',[]; 'minFlux',[]; 'nObjectives',[]; 'seed',[]; 'method',[]; 'thinning',[]; 'nBurnin',[]});
    +0112 nSamples=p.nSamples;
    +0113 replaceBoundsWithInf=p.replaceBoundsWithInf;
    +0114 supressErrors=p.supressErrors;
    +0115 runParallel=p.runParallel;
    +0116 goodRxns=p.goodRxns;
    +0117 minFlux=p.minFlux;
    +0118 nObjectives=p.nObjectives;
    +0119 seed=p.seed;
    +0120 method=p.method;
    +0121 thinning=p.thinning;
    +0122 nBurnin=p.nBurnin;
    +0123 info=[]; %only populated by the 'chrr' method
    +0124 if isempty(nSamples)
    +0125     nSamples=1000;
    +0126 end
    +0127 if isempty(method)
    +0128     method='achr';
    +0129 end
    +0130 
    +0131 % ---- Dispatch to the requested sampler ----
    +0132 % 'achr'/'chrr' are near-uniform MCMC samplers of the polytope interior,
    +0133 % implemented in sampleACHR / sampleCHRR. 'randomObjective' is the
    +0134 % random-objective vertex method (Bordel 2010), implemented below; goodRxns is
    +0135 % meaningful only for that method (empty otherwise).
    +0136 switch lower(method)
    +0137     case 'achr'
    +0138         solutions = sampleACHR(model, nSamples, thinning, [], seed);
    +0139         goodRxns = [];
    +0140         return;
    +0141     case 'chrr'
    +0142         [solutions, info] = sampleCHRR(model, nSamples, thinning, nBurnin, seed);
    +0143         goodRxns = [];
    +0144         return;
    +0145     case {'randomobjective', 'objective'}
    +0146         % fall through to the random-objective implementation below
    +0147     otherwise
    +0148         error('RAVEN:badInput', ...
    +0149             ['randomSampling: unknown method ''%s''; expected ''achr'', ' ...
    +0150              '''chrr'', or ''randomObjective''.'], method);
    +0151 end
    +0152 
    +0153 if isempty(replaceBoundsWithInf)
    +0154     replaceBoundsWithInf=true;
    +0155 end
    +0156 if isempty(supressErrors)
    +0157     supressErrors=false;
    +0158 end
    +0159 if isempty(runParallel)
    +0160     runParallel=true;
    +0161 end
    +0162 if isempty(minFlux)
    +0163     minFlux=false;
    +0164 end
    +0165 if isempty(nObjectives)
    +0166     nObjectives=2;
    +0167 end
    +0168 
    +0169 nW = parallelWorkersRAVEN(runParallel);
    +0170 
    +0171 nObjRxns=nObjectives;
    +0172 
    +0173 %Simplify the model to speed stuff up a little. Keep original mapping
    +0174 originalRxns=model.rxns;
    +0175 model=simplifyModel(model,false,false,true,true);
    +0176 [~, simpToOrig] = ismember(model.rxns, originalRxns);
    +0177 
    +0178 %goodRxns is part of the public API and stored as original-model indices.
    +0179 %Convert to simplified-model indices for use in the detection and sampling
    +0180 %loops below, dropping any reactions that were removed by simplification.
    +0181 if ~isempty(goodRxns)
    +0182     [tf, simpIdx] = ismember(originalRxns(goodRxns), model.rxns);
    +0183     goodRxns = double(simpIdx(tf));
    +0184 end
    +0185 
    +0186 %Check that the model is feasible given the constraints
    +0187 [sol,~]=solveLP(model);
    +0188 if isempty(sol.x)
    +0189     EM='The model has no feasible solution, likely due to incompatible constraints';
    +0190     error('RAVEN:badInput', '%s', EM);
    +0191 elseif sol.f==0
    +0192     warning('The model objective function cannot reach a non-zero value. This might be intended, so randomSampling will continue, but this could indicate problems with your model')
    +0193 end
    +0194 
    +0195 nRxns = numel(model.rxns);
    +0196 %Reactions which can be involved in loops should not be optimized for.
    +0197 %Check which reactions reach an arbitrary high upper bound. This is done
    +0198 %on the original bounded model so that loop detection is meaningful
    +0199 %(Inf-bounded reactions would always reach the threshold).
    +0200 if isempty(goodRxns)
    +0201     [minF, maxF] = getAllowedBounds(model, 'runParallel', runParallel);
    +0202     goodRxns = true(nRxns, 1);
    +0203     % Reactions that reach the model's own highest bound are involved in
    +0204     % loops. The threshold has to be derived from the model, as the bound
    +0205     % replacement below does: hardcoding ±1000 matches nothing on a model
    +0206     % whose bounds are on another scale (ecModels, 100-scale bounds), so no
    +0207     % reaction would be excluded and loops would be sampled while the
    +0208     % function reports success.
    +0209     loopUb = max(model.ub);
    +0210     loopLb = min(model.lb);
    +0211     goodRxns(maxF == Inf | maxF > loopUb*0.999) = false;
    +0212     if loopLb < 0
    +0213         goodRxns(minF == -Inf | minF < loopLb*0.999) = false;
    +0214     end
    +0215     % Reactions that cannot carry any non-zero flux
    +0216     goodRxns(~(maxF > 0 | minF < 0)) = false;
    +0217     % In ecModels do not sample from usage_prot reactions
    +0218     if isfield(model,'ec')
    +0219         goodRxns(startsWith(model.rxns,'usage_prot_')) = false;
    +0220     end
    +0221     goodRxns = find(goodRxns);
    +0222 end
    +0223 
    +0224 %Change the bounds to +/- Inf to prevent loops in the sampled solutions.
    +0225 %Applied after goodRxns detection so that loop-detection uses original bounds.
    +0226 if replaceBoundsWithInf==true
    +0227     model.ub(model.ub==max(model.ub))=Inf;
    +0228     if min(model.lb)<0 % Only negative lower bounds should be set to -Inf
    +0229         model.lb(model.lb==min(model.lb))=-Inf;
    +0230     end
    +0231 end
    +0232 
    +0233 %Seed the RNG here so that only the sampling loop is affected, not the
    +0234 %model simplification or goodRxns detection above.
    +0235 if ~isempty(seed)
    +0236     rng(seed);
    +0237 end
    +0238 
    +0239 %Reserve space for a solution matrix
    +0240 sols = zeros(numel(model.rxns),nSamples);
    +0241 sols = num2cell(sols,1);
    +0242 
    +0243 %Main loop
    +0244 if nSamples > 0
    +0245 
    +0246     PB = progressReport(nSamples,'Performing random sampling');
    +0247     parfor (i = 1:nSamples, nW)
    +0248         badSolutions = 1;
    +0249         tmpModel = model;
    +0250         while lt(0,badSolutions)
    +0251             rxns = goodRxns(randperm(numel(goodRxns), nObjRxns));
    +0252             tmpModel.c = zeros(numel(tmpModel.rxns),1);
    +0253 
    +0254             multipliers = 2*randi(2,nObjRxns,1)-3;  % random draws from {-1, 1}
    +0255             multipliers(tmpModel.ub(rxns)<0) = 1;
    +0256 
    +0257             tmpModel.c(rxns) = rand(nObjRxns,1).*multipliers;
    +0258 
    +0259             if true(minFlux)
    +0260                 sol=solveLP(tmpModel,1);
    +0261             else
    +0262                 sol=solveLP(tmpModel,0);
    +0263             end
    +0264             if any(sol.x) && abs(sol.f)>10^-8
    +0265                 sols{i} = sol.x;
    +0266                 badSolutions = 0;
    +0267             else
    +0268                 badSolutions = badSolutions+1;
    +0269                 %If it only finds bad solutions then throw an error.
    +0270                 if badSolutions == 100 && supressErrors==false
    +0271                     error('The program is having problems finding non-zero solutions (ignoring reactions that might be involved in loops). Review the constraints on your model. Set supressErrors to true to ignore this error');
    +0272                 end
    +0273             end
    +0274         end
    +0275         count(PB);
    +0276     end
    +0277 
    +0278     %Map to original model
    +0279     sols = cell2mat(sols);
    +0280     solutions=zeros(numel(originalRxns),nSamples);
    +0281     solutions(simpToOrig,:)=sols;
    +0282 else
    +0283     solutions=[];
    +0284 end
    +0285 %Return goodRxns as original-model indices so callers can pass them back
    +0286 %safely across separate calls (which each re-simplify the model internally).
    +0287 goodRxns = double(simpToOrig(goodRxns));
    +0288 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/analysis/reporterMetabolites.html b/docs/html/analysis/reporterMetabolites.html new file mode 100644 index 00000000..dc1f5dbb --- /dev/null +++ b/docs/html/analysis/reporterMetabolites.html @@ -0,0 +1,359 @@ + + + + Description of reporterMetabolites + + + + + + + + + +
    Home > analysis > reporterMetabolites.m
    + + + +

    reporterMetabolites +

    + +

    PURPOSE ^

    +
    reporterMetabolites Identify metabolites around which transcriptional changes occur.
    + +

    SYNOPSIS ^

    +
    function repMets=reporterMetabolites(model,genes,genePValues,varargin)
    + +

    DESCRIPTION ^

    +
     reporterMetabolites  Identify metabolites around which transcriptional changes occur.
    +
    + Applies the Reporter Metabolites algorithm, which scores each metabolite
    + by the differential expression of the genes around it.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + genes : cell
    +     a cell array of gene names (matching model.genes).
    + genePValues : double
    +     P-values for differential expression of the genes. Values must lie in
    +     [0, 1]; NaN values are ignored, any other value raises an error.
    +
    + Name-Value Arguments
    + --------------------
    + printResults : logical
    +     true to print the top 20 Reporter Metabolites to the screen
    +     (default false).
    + outputFile : char
    +     the results are printed to this file (default none).
    + geneFoldChanges : double
    +     log-fold changes for the genes. If supplied, Reporter Metabolites are
    +     also calculated for the upregulated genes (fold change >= 0) alone and
    +     for the downregulated genes (fold change < 0) alone, in addition to
    +     the full test (default none).
    +
    + Returns
    + -------
    + repMets : struct
    +     an array of structures with the following fields:
    +
    +     - test : a string that describes the genes used to calculate the
    +       Reporter Metabolites ("all", "only up", or "only down"). The tests
    +       "only up" and "only down" are only calculated if geneFoldChanges is
    +       supplied.
    +     - mets : a cell array of metabolite IDs for the metabolites for which
    +       a score could be calculated.
    +     - metNames : a cell array with the names of the metabolites in
    +       "mets".
    +     - metZScores : background-corrected Z-scores for differential
    +       expression around each metabolite in "mets". The entries of all
    +       fields are sorted by this score, highest first.
    +     - metPValues : one-sided (upper-tail) P-values of "metZScores".
    +     - metNGenes : number of neighbouring genes for each metabolite in
    +       "mets".
    +     - meanZ : average Z-scores for the genes around each metabolite in
    +       "mets".
    +     - stdZ : standard deviations of the Z-scores around each metabolite
    +       in "mets".
    +
    + Examples
    + --------
    +     repMets = reporterMetabolites(model, genes, genePValues, ...
    +         printResults, outputFile, geneFoldChanges);
    +
    + Notes
    + -----
    + For details about the algorithm, see Patil KR, Nielsen J, Uncovering
    + transcriptional regulation of metabolism by using metabolic network
    + topology. Proc. Natl Acad. Sci. USA 2005;102:2685-2689.
    +
    + The background correction uses the closed-form mean and standard
    + deviation of the aggregate Z-score of n genes drawn at random from all
    + scored genes, so the results are deterministic.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • reporterMetabolites reporterMetabolites Identify metabolites around which transcriptional changes occur.
    +This function is called by: +
      +
    • reporterMetabolites reporterMetabolites Identify metabolites around which transcriptional changes occur.
    + + + + +

    SOURCE CODE ^

    +
    0001 function repMets=reporterMetabolites(model,genes,genePValues,varargin)
    +0002 % reporterMetabolites  Identify metabolites around which transcriptional changes occur.
    +0003 %
    +0004 % Applies the Reporter Metabolites algorithm, which scores each metabolite
    +0005 % by the differential expression of the genes around it.
    +0006 %
    +0007 % Parameters
    +0008 % ----------
    +0009 % model : struct
    +0010 %     a model structure.
    +0011 % genes : cell
    +0012 %     a cell array of gene names (matching model.genes).
    +0013 % genePValues : double
    +0014 %     P-values for differential expression of the genes. Values must lie in
    +0015 %     [0, 1]; NaN values are ignored, any other value raises an error.
    +0016 %
    +0017 % Name-Value Arguments
    +0018 % --------------------
    +0019 % printResults : logical
    +0020 %     true to print the top 20 Reporter Metabolites to the screen
    +0021 %     (default false).
    +0022 % outputFile : char
    +0023 %     the results are printed to this file (default none).
    +0024 % geneFoldChanges : double
    +0025 %     log-fold changes for the genes. If supplied, Reporter Metabolites are
    +0026 %     also calculated for the upregulated genes (fold change >= 0) alone and
    +0027 %     for the downregulated genes (fold change < 0) alone, in addition to
    +0028 %     the full test (default none).
    +0029 %
    +0030 % Returns
    +0031 % -------
    +0032 % repMets : struct
    +0033 %     an array of structures with the following fields:
    +0034 %
    +0035 %     - test : a string that describes the genes used to calculate the
    +0036 %       Reporter Metabolites ("all", "only up", or "only down"). The tests
    +0037 %       "only up" and "only down" are only calculated if geneFoldChanges is
    +0038 %       supplied.
    +0039 %     - mets : a cell array of metabolite IDs for the metabolites for which
    +0040 %       a score could be calculated.
    +0041 %     - metNames : a cell array with the names of the metabolites in
    +0042 %       "mets".
    +0043 %     - metZScores : background-corrected Z-scores for differential
    +0044 %       expression around each metabolite in "mets". The entries of all
    +0045 %       fields are sorted by this score, highest first.
    +0046 %     - metPValues : one-sided (upper-tail) P-values of "metZScores".
    +0047 %     - metNGenes : number of neighbouring genes for each metabolite in
    +0048 %       "mets".
    +0049 %     - meanZ : average Z-scores for the genes around each metabolite in
    +0050 %       "mets".
    +0051 %     - stdZ : standard deviations of the Z-scores around each metabolite
    +0052 %       in "mets".
    +0053 %
    +0054 % Examples
    +0055 % --------
    +0056 %     repMets = reporterMetabolites(model, genes, genePValues, ...
    +0057 %         printResults, outputFile, geneFoldChanges);
    +0058 %
    +0059 % Notes
    +0060 % -----
    +0061 % For details about the algorithm, see Patil KR, Nielsen J, Uncovering
    +0062 % transcriptional regulation of metabolism by using metabolic network
    +0063 % topology. Proc. Natl Acad. Sci. USA 2005;102:2685-2689.
    +0064 %
    +0065 % The background correction uses the closed-form mean and standard
    +0066 % deviation of the aggregate Z-score of n genes drawn at random from all
    +0067 % scored genes, so the results are deterministic.
    +0068 
    +0069 genes=convertCharArray(genes);
    +0070 p=parseRAVENargs(varargin, {'printResults',false; 'outputFile',[]; 'geneFoldChanges',[]});
    +0071 printResults=p.printResults;
    +0072 outputFile=p.outputFile;
    +0073 geneFoldChanges=p.geneFoldChanges;
    +0074 
    +0075 %Check some stuff
    +0076 if numel(genes)~=numel(genePValues)
    +0077     EM='The number of genes and the number of Z-scores must be the same';
    +0078     error('RAVEN:badInput', '%s', EM);
    +0079 end
    +0080 if ~isfield(model,'rxnGeneMat')
    +0081     EM='The model structure must have a rxnGeneMat field';
    +0082     error('RAVEN:badInput', '%s', EM);
    +0083 end
    +0084 
    +0085 %All P-values that are given must lie in [0,1]; NaN means that no P-value
    +0086 %is available and is removed below
    +0087 badP=~isnan(genePValues) & (genePValues<0 | genePValues>1);
    +0088 if any(badP)
    +0089     firstBad=find(badP,1);
    +0090     EM=sprintf('The P-values must lie between 0 and 1, but %d of them do not. The first is %g (gene %s)',nnz(badP),genePValues(firstBad),genes{firstBad});
    +0091     error('RAVEN:badInput', '%s', EM);
    +0092 end
    +0093 
    +0094 %Remove the genes which are not in the model
    +0095 genePValues(~ismember(genes,model.genes))=[];
    +0096 if any(geneFoldChanges)
    +0097     geneFoldChanges(~ismember(genes,model.genes))=[];
    +0098 end
    +0099 genes(~ismember(genes,model.genes))=[];
    +0100 
    +0101 %Remove the genes that has NA P-value
    +0102 genes(isnan(genePValues))=[];
    +0103 if any(geneFoldChanges)
    +0104     geneFoldChanges(isnan(genePValues))=[];
    +0105 end
    +0106 genePValues(isnan(genePValues))=[];
    +0107 
    +0108 %Convert p-values to Z-scores. Uses base MATLAB erfcinv instead of the
    +0109 %Statistics Toolbox norminv: -norminv(p) == sqrt(2)*erfcinv(2*p).
    +0110 geneZScores=sqrt(2)*erfcinv(2*genePValues);
    +0111 
    +0112 %This is to prevent errors if the p-values are exactly 0 or 1
    +0113 geneZScores(geneZScores==-inf)=-15;
    +0114 geneZScores(geneZScores==inf)=15;
    +0115 
    +0116 %For each metabolite, calculate the aggregate Z-score and keep track of the
    +0117 %number of neighbouring genes
    +0118 metZScores=nan(numel(model.mets),1);
    +0119 %Sums are taken on Z-scores relative to their mean (found in two passes, the
    +0120 %second relative to the first gene's Z-score), which keeps the rounding error
    +0121 %proportional to the spread of the Z-scores instead of to their size
    +0122 metZShifted=nan(numel(model.mets),1);
    +0123 zCentre=0;
    +0124 if ~isempty(geneZScores)
    +0125     zCentre=geneZScores(1)+mean(geneZScores-geneZScores(1));
    +0126 end
    +0127 metNGenes=nan(numel(model.mets),1);
    +0128 meanZ=nan(numel(model.mets),1);
    +0129 stdZ=nan(numel(model.mets),1);
    +0130 %model.genes -> genes(filtered list) index, computed once rather than
    +0131 %re-scanning the whole "genes" list (via ismember) for every metabolite,
    +0132 %which is quadratic in a genome-scale model's mets x genes.
    +0133 [~,geneIdxInList]=ismember(model.genes,genes);
    +0134 for i=1:numel(model.mets)
    +0135     %Get the involved rxns
    +0136     I=model.S(i,:);
    +0137 
    +0138     %Get the involved genes
    +0139     [~, J]=find(model.rxnGeneMat(I~=0,:));
    +0140 
    +0141     %Find the genes in the gene list
    +0142     K=unique(geneIdxInList(J));
    +0143     K=K(K>0);
    +0144     
    +0145     %Calculate the aggregated Z-score for the metabolite
    +0146     if any(K)
    +0147         metZScores(i)=sum(geneZScores(K))/sqrt(numel(K));
    +0148         metZShifted(i)=sum(geneZScores(K)-zCentre)/sqrt(numel(K));
    +0149         meanZ(i)=mean(geneZScores(K));
    +0150         stdZ(i)=std(geneZScores(K));
    +0151         metNGenes(i)=numel(K);
    +0152     end
    +0153 end
    +0154 
    +0155 %Remove the metabolites which have no Z-scores, from all fields alike
    +0156 noScore=isnan(metZScores);
    +0157 mets=model.mets(~noScore);
    +0158 metNames=model.metNames(~noScore);
    +0159 metZShifted(noScore)=[];
    +0160 metZScores(noScore)=[];
    +0161 metNGenes(noScore)=[];
    +0162 meanZ(noScore)=[];
    +0163 stdZ(noScore)=[];
    +0164 
    +0165 %Correct for background using the closed-form moments of the sampling
    +0166 %distribution. For n genes drawn with replacement from the scored-gene pool
    +0167 %(mean mu, population std sigma), the aggregate Z = sum(z)/sqrt(n) has:
    +0168 %  E[Z] = sqrt(n)*mu  and  Std[Z] = sigma  (independent of n)
    +0169 %so the corrected score is (metZ - sqrt(n)*mu) / sigma. When sigma is 0 (all
    +0170 %scored genes have the same Z-score) no metabolite stands out from the
    +0171 %background, and the corrected score is 0 (P-value 0.5). The moments are
    +0172 %taken on Z-scores shifted by their mean, so that mu and the numerator keep
    +0173 %their precision when the Z-scores are close to each other or the first one is
    +0174 %an outlier. Identical Z-scores then give exactly 0 for the numerator, and the
    +0175 %test of the spread of the Z-scores (0 up to 100*eps(max(abs(Z)))) only
    +0176 %prevents a division of 0 by 0.
    +0177 zShifted = geneZScores - zCentre;
    +0178 muShifted = mean(zShifted);
    +0179 sigma = std(zShifted,1);
    +0180 if max(geneZScores)-min(geneZScores) > 100*eps(max(abs(geneZScores)))
    +0181     metZScores = (metZShifted - sqrt(metNGenes) .* muShifted) ./ sigma;
    +0182 else
    +0183     metZScores = zeros(size(metZScores));
    +0184 end
    +0185 
    +0186 %Calculate the P-values (upper-tail of the standard normal). Uses the base
    +0187 %MATLAB erfc so this does not depend on the Statistics Toolbox normcdf:
    +0188 %1 - normcdf(z) == 0.5*erfc(z/sqrt(2)).
    +0189 metPValues=0.5*erfc(metZScores/sqrt(2));
    +0190 
    +0191 %Sort the results
    +0192 [metZScores, I]=sort(metZScores,'descend');
    +0193 mets=mets(I);
    +0194 metNames=metNames(I);
    +0195 metPValues=metPValues(I);
    +0196 metNGenes=metNGenes(I);
    +0197 meanZ=meanZ(I);
    +0198 stdZ=stdZ(I);
    +0199 
    +0200 %Prepare the output
    +0201 repMets.mets=mets;
    +0202 repMets.metNames=metNames;
    +0203 repMets.metZScores=metZScores;
    +0204 repMets.metPValues=metPValues;
    +0205 repMets.metNGenes=metNGenes;
    +0206 repMets.meanZ=meanZ;
    +0207 repMets.stdZ=stdZ;
    +0208 
    +0209 %Call this function recursively if geneFoldChanges has been specified
    +0210 repMets(1).test='all';
    +0211 if any(geneFoldChanges)
    +0212     repMets=[repMets;reporterMetabolites(model,genes(geneFoldChanges>=0),genePValues(geneFoldChanges>=0))];
    +0213     repMets=[repMets;reporterMetabolites(model,genes(geneFoldChanges<0),genePValues(geneFoldChanges<0))];
    +0214     repMets(2).test='only up';
    +0215     repMets(3).test='only down';
    +0216 end
    +0217 
    +0218 %This is for printing results to the screen. For printing a full list of
    +0219 %all scores, specify a output file
    +0220 %Escapes metabolite text before it is spliced into an fprintf template,
    +0221 %since a literal "%" in e.g. a metabolite name would otherwise be misread
    +0222 %as a format directive and swallow the remainder of the line.
    +0223 esc=@(s) strrep(s,'%','%%');
    +0224 
    +0225 if printResults==true
    +0226     for i=1:numel(repMets)
    +0227         fprintf(['TOP 20 REPORTER METABOLITES\nTEST TYPE: ' repMets(i).test '\nID\tNAME\tP-VALUE\n']);
    +0228         for j=1:min(20,numel(repMets(i).mets))
    +0229             fprintf([esc(repMets(i).mets{j}) '\t' esc(repMets(i).metNames{j}) '\t' num2str(repMets(i).metPValues(j)) '\n']);
    +0230         end
    +0231         fprintf('\n');
    +0232     end
    +0233 end
    +0234 
    +0235 %Print results to file
    +0236 if any(outputFile)
    +0237     fid=fopen(outputFile,'w');
    +0238     for i=1:numel(repMets)
    +0239         fprintf(fid,['REPORTER METABOLITES USING TEST TYPE: ' repMets(i).test '\n']);
    +0240         fprintf(fid,'ID\tNAME\tZ-SCORE\tP-VALUE\tNUMBER OF NEIGHBOURS\tAVERAGE Z-SCORE\tSTD Z-SCORE\n');
    +0241         for j=1:numel(repMets(i).mets)
    +0242             fprintf(fid,[esc(repMets(i).mets{j}) '\t' esc(repMets(i).metNames{j}) '\t' num2str(repMets(i).metZScores(j)) '\t' num2str(repMets(i).metPValues(j)) '\t' num2str(repMets(i).metNGenes(j)) '\t' num2str(repMets(i).meanZ(j)) '\t' num2str(repMets(i).stdZ(j)) '\n']);
    +0243         end
    +0244     end
    +0245     fclose(fid);
    +0246 end
    +0247 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/analysis/runDynamicFBA.html b/docs/html/analysis/runDynamicFBA.html similarity index 56% rename from doc/analysis/runDynamicFBA.html rename to docs/html/analysis/runDynamicFBA.html index de3d3d9f..e3353ab0 100644 --- a/doc/analysis/runDynamicFBA.html +++ b/docs/html/analysis/runDynamicFBA.html @@ -4,7 +4,7 @@ Description of runDynamicFBA - + @@ -21,13 +21,13 @@

    runDynamicFBA

    PURPOSE ^

    -
    runDynamicFBA Perform dynamic FBA using the static optimization approach.
    +
    runDynamicFBA Perform dynamic FBA using the static optimisation approach.

    SYNOPSIS ^

    function [concentrationMatrix, excRxnNames, timeVec, biomassVec] = runDynamicFBA(model, substrateRxns, initConcentrations, initBiomass, timeStep, nSteps, plotRxns, exclUptakeRxns)

    DESCRIPTION ^

    -
     runDynamicFBA  Perform dynamic FBA using the static optimization approach.
    +
     runDynamicFBA  Perform dynamic FBA using the static optimisation approach.
     
      Parameters
      ----------
    @@ -35,8 +35,8 @@ 

    DESCRIPTION ^DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 
 <h2><a name=SOURCE CODE ^

    0001 function [concentrationMatrix, excRxnNames, timeVec, biomassVec] = runDynamicFBA(model, substrateRxns, initConcentrations, initBiomass, timeStep, nSteps, plotRxns, exclUptakeRxns)
    -0002 % runDynamicFBA  Perform dynamic FBA using the static optimization approach.
    +0002 % runDynamicFBA  Perform dynamic FBA using the static optimisation approach.
     0003 %
     0004 % Parameters
     0005 % ----------
    @@ -104,8 +103,8 @@ 

    SOURCE CODE ^% a model structure. 0008 % substrateRxns : cell 0009 % cell array with exchange reaction identifiers for substrates that are -0010 % initially in the media, whose concentration may change (e.g. not h2o -0011 % or co2). +0010 % initially in the media, whose concentration may change (for example +0011 % not h2o or co2). 0012 % initConcentrations : double 0013 % initial concentrations of substrates (matching substrateRxns). 0014 % initBiomass : double @@ -119,7 +118,7 @@

    SOURCE CODE ^% concentration should be plotted. 0023 % exclUptakeRxns : cell 0024 % cell array with exchange reaction identifiers for substrates whose -0025 % concentration does not change (e.g. co2, o2, h2o, h). +0025 % concentration does not change (for example co2, o2, h2o, h). 0026 % 0027 % Returns 0028 % ------- @@ -142,107 +141,106 @@

    SOURCE CODE ^% Notes 0046 % ----- 0047 % If no initial concentration is given for a substrate that has an open -0048 % uptake in the model (i.e. model.lb < 0) the concentration is assumed to -0049 % be high enough to not be limiting. If the uptake rate for a nutrient is -0050 % calculated to exceed the maximum uptake rate for that nutrient specified -0051 % in the model and the max uptake rate specified is > 0, the maximum uptake -0052 % rate specified in the model is used instead of the calculated uptake -0053 % rate. -0054 % -0055 % Modified from COBRA Toolbox dynamicFBA.m. -0056 -0057 % Find exchange rxns -0058 excRxnNames = getExchangeRxns(model); -0059 excRxnNames(ismember(excRxnNames,exclUptakeRxns))=[]; -0060 excInd = getIndexes(model,excRxnNames,'rxns'); -0061 % Figure out if substrate reactions are correct -0062 missingInd = find(~ismember(substrateRxns,excRxnNames)); -0063 if (~isempty(missingInd)) -0064 for i = 1:length(missingInd) -0065 fprintf('%s\n',substrateRxns{missingInd(i)}); -0066 end -0067 error('Invalid substrate uptake reaction!'); -0068 end -0069 -0070 % Initialize concentrations -0071 [~, substrateMatchInd] = ismember(substrateRxns,excRxnNames); -0072 concentrations = zeros(length(excRxnNames),1); -0073 concentrations(substrateMatchInd) = initConcentrations; -0074 -0075 % Deal with reactions for which there are no initial concentrations -0076 originalBound = -model.lb(excInd); -0077 noInitConcentration = (concentrations == 0 & originalBound > 0); -0078 concentrations(noInitConcentration) = 1000; -0079 -0080 biomass = initBiomass; -0081 -0082 % Initialize bounds -0083 uptakeBound = concentrations/(biomass*timeStep); -0084 -0085 % Make sure bounds are not higher than what are specified in the model -0086 aboveOriginal = (uptakeBound > originalBound) & (originalBound > 0); -0087 uptakeBound(aboveOriginal) = originalBound(aboveOriginal); -0088 model.lb(excInd) = -uptakeBound; -0089 -0090 concentrationMatrix = concentrations; -0091 biomassVec = biomass; -0092 timeVec(1) = 0; -0093 [~,hsSol]=solveLP(model,1); -0094 PB = progressReport(nSteps,'Running dynamic FBA analysis'); -0095 for stepNo = 1:nSteps -0096 % Run FBA -0097 [sol,hsSol] = solveLP(model,1,[],hsSol); -0098 mu = sol.f; -0099 if (sol.stat ~= 1 || mu == 0) -0100 fprintf('\nNo feasible solution - nutrients exhausted. Biomass:\t %f\n', biomass); -0101 break; -0102 end -0103 uptakeFlux = sol.x(excInd); -0104 biomass = biomass*exp(mu*timeStep); -0105 biomassVec(end+1) = biomass; -0106 -0107 % Update concentrations -0108 concentrations = concentrations - uptakeFlux/mu*biomass*(1-exp(mu*timeStep)); -0109 concentrations(concentrations <= 0) = 0; -0110 concentrationMatrix(:,end+1) = concentrations; -0111 -0112 % Update bounds for uptake reactions -0113 uptakeBound = concentrations/(biomass*timeStep); -0114 % This is to avoid any numerical issues -0115 uptakeBound(uptakeBound > 1000) = 1000; -0116 % Figure out if the computed bounds were above the original bounds -0117 aboveOriginal = (uptakeBound > originalBound) & (originalBound > 0); -0118 % Revert to original bounds if the rate was too high -0119 uptakeBound(aboveOriginal) = originalBound(aboveOriginal); -0120 uptakeBound(abs(uptakeBound) < 1e-9) = 0; -0121 -0122 model.lb(excInd) = -uptakeBound; -0123 timeVec(stepNo+1) = stepNo*timeStep; -0124 -0125 count(PB); -0126 end -0127 -0128 selNonZero = any(concentrationMatrix>0,2); -0129 concentrationMatrix = concentrationMatrix(selNonZero,:); -0130 excRxnNames = excRxnNames(selNonZero); -0131 selPlot = ismember(excRxnNames,plotRxns); -0132 -0133 % Plot concentrations as a function of time -0134 clf -0135 subplot(1,2,1); -0136 plot(timeVec,biomassVec); -0137 axis tight -0138 title('Biomass'); -0139 xlabel('Time (h)'); -0140 ylabel('Concentration (g/L)'); -0141 subplot(1,2,2); -0142 plot(timeVec,concentrationMatrix(selPlot,:)); -0143 axis tight -0144 title('Substrates and/or products'); -0145 xlabel('Time (h)'); -0146 ylabel('Concentration (mmol/L)'); -0147 legend(strrep(excRxnNames(selPlot),'EX_','')); -0148 end

    +0048 % uptake in the model (that is, model.lb < 0), the initial concentration is set +0049 % to 1000. If the uptake bound calculated from the concentration exceeds +0050 % the maximum uptake rate specified in the model for that nutrient, and the +0051 % maximum uptake rate specified is > 0, the maximum uptake rate specified +0052 % in the model is used as the uptake bound. +0053 % +0054 % Modified from COBRA Toolbox dynamicFBA.m. +0055 +0056 % Find exchange rxns +0057 excRxnNames = getExchangeRxns(model); +0058 excRxnNames(ismember(excRxnNames,exclUptakeRxns))=[]; +0059 excInd = getIndexes(model,excRxnNames,'rxns'); +0060 % Figure out if substrate reactions are correct +0061 missingInd = find(~ismember(substrateRxns,excRxnNames)); +0062 if (~isempty(missingInd)) +0063 for i = 1:length(missingInd) +0064 fprintf('%s\n',substrateRxns{missingInd(i)}); +0065 end +0066 error('Invalid substrate uptake reaction!'); +0067 end +0068 +0069 % Initialize concentrations +0070 [~, substrateMatchInd] = ismember(substrateRxns,excRxnNames); +0071 concentrations = zeros(length(excRxnNames),1); +0072 concentrations(substrateMatchInd) = initConcentrations; +0073 +0074 % Deal with reactions for which there are no initial concentrations +0075 originalBound = -model.lb(excInd); +0076 noInitConcentration = (concentrations == 0 & originalBound > 0); +0077 concentrations(noInitConcentration) = 1000; +0078 +0079 biomass = initBiomass; +0080 +0081 % Initialize bounds +0082 uptakeBound = concentrations/(biomass*timeStep); +0083 +0084 % Make sure bounds are not higher than what are specified in the model +0085 aboveOriginal = (uptakeBound > originalBound) & (originalBound > 0); +0086 uptakeBound(aboveOriginal) = originalBound(aboveOriginal); +0087 model.lb(excInd) = -uptakeBound; +0088 +0089 concentrationMatrix = concentrations; +0090 biomassVec = biomass; +0091 timeVec(1) = 0; +0092 [~,hsSol]=solveLP(model,1); +0093 PB = progressReport(nSteps,'Running dynamic FBA analysis'); +0094 for stepNo = 1:nSteps +0095 % Run FBA +0096 [sol,hsSol] = solveLP(model,1,[],hsSol); +0097 mu = sol.f; +0098 if (sol.stat ~= 1 || mu == 0) +0099 fprintf('\nNo feasible solution - nutrients exhausted. Biomass:\t %f\n', biomass); +0100 break; +0101 end +0102 uptakeFlux = sol.x(excInd); +0103 biomass = biomass*exp(mu*timeStep); +0104 biomassVec(end+1) = biomass; +0105 +0106 % Update concentrations +0107 concentrations = concentrations - uptakeFlux/mu*biomass*(1-exp(mu*timeStep)); +0108 concentrations(concentrations <= 0) = 0; +0109 concentrationMatrix(:,end+1) = concentrations; +0110 +0111 % Update bounds for uptake reactions +0112 uptakeBound = concentrations/(biomass*timeStep); +0113 % This is to avoid any numerical issues +0114 uptakeBound(uptakeBound > 1000) = 1000; +0115 % Figure out if the computed bounds were above the original bounds +0116 aboveOriginal = (uptakeBound > originalBound) & (originalBound > 0); +0117 % Revert to original bounds if the rate was too high +0118 uptakeBound(aboveOriginal) = originalBound(aboveOriginal); +0119 uptakeBound(abs(uptakeBound) < 1e-9) = 0; +0120 +0121 model.lb(excInd) = -uptakeBound; +0122 timeVec(stepNo+1) = stepNo*timeStep; +0123 +0124 count(PB); +0125 end +0126 +0127 selNonZero = any(concentrationMatrix>0,2); +0128 concentrationMatrix = concentrationMatrix(selNonZero,:); +0129 excRxnNames = excRxnNames(selNonZero); +0130 selPlot = ismember(excRxnNames,plotRxns); +0131 +0132 % Plot concentrations as a function of time +0133 clf +0134 subplot(1,2,1); +0135 plot(timeVec,biomassVec); +0136 axis tight +0137 title('Biomass'); +0138 xlabel('Time (h)'); +0139 ylabel('Concentration (g/L)'); +0140 subplot(1,2,2); +0141 plot(timeVec,concentrationMatrix(selPlot,:)); +0142 axis tight +0143 title('Substrates and/or products'); +0144 xlabel('Time (h)'); +0145 ylabel('Concentration (mmol/L)'); +0146 legend(strrep(excRxnNames(selPlot),'EX_','')); +0147 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/analysis/runPhenotypePhasePlane.html b/docs/html/analysis/runPhenotypePhasePlane.html similarity index 100% rename from doc/analysis/runPhenotypePhasePlane.html rename to docs/html/analysis/runPhenotypePhasePlane.html diff --git a/doc/analysis/runProductionEnvelope.html b/docs/html/analysis/runProductionEnvelope.html similarity index 54% rename from doc/analysis/runProductionEnvelope.html rename to docs/html/analysis/runProductionEnvelope.html index 53e5bec4..d1c4b005 100644 --- a/doc/analysis/runProductionEnvelope.html +++ b/docs/html/analysis/runProductionEnvelope.html @@ -41,14 +41,18 @@

    DESCRIPTION ^SOURCE CODE ^% Name-Value Arguments 0014 % -------------------- 0015 % nPts : double -0016 % number of points in the plot (default 20). -0017 % -0018 % Returns -0019 % ------- -0020 % biomassValues : double -0021 % biomass values for plotting. -0022 % targetValues : double -0023 % target upper and lower bounds for plotting. -0024 % -0025 % Examples -0026 % -------- -0027 % [biomassValues, targetValues] = runProductionEnvelope(model, ... -0028 % targetRxn, biomassRxn, nPts); -0029 % -0030 % Notes -0031 % ----- -0032 % Modified from COBRA Toolbox productionEnvelope.m. -0033 -0034 p=parseRAVENargs(varargin, {'nPts',20}); -0035 nPts=p.nPts; -0036 -0037 % Run FBA to get upper bound for biomass -0038 model = setParam(model,'obj',biomassRxn,1); -0039 [solMax,hsSol] = solveLP(model); -0040 solMax = solMax.x(logical(model.c)); -0041 model.c = -model.c; -0042 solMin = solveLP(model,0,[],hsSol); -0043 solMin = solMin.x(logical(model.c)); -0044 -0045 % Create biomass range vector -0046 biomassValues = linspace(solMin,solMax,nPts); -0047 targetUpperBound = nan(1,numel(biomassValues)); -0048 targetLowerBound = nan(1,numel(biomassValues)); -0049 -0050 PB = progressReport(length(biomassValues),'Creating production envelope'); -0051 % Max/min for target production -0052 model = setParam(model,'obj',targetRxn,1); -0053 for i = 1:length(biomassValues) -0054 model1 = setParam(model,'eq',biomassRxn,biomassValues(i)); -0055 sol = solveLP(model1,0,[],hsSol); -0056 if (sol.stat > 0) -0057 targetUpperBound(i) = sol.x(logical(model.c)); -0058 else -0059 targetUpperBound(i) = NaN; -0060 end -0061 model1.c = -model1.c; -0062 sol = solveLP(model1,0,[],hsSol); -0063 if (sol.stat > 0) -0064 targetLowerBound(i) = sol.x(logical(model1.c)); -0065 else -0066 targetLowerBound(i) = NaN; -0067 end -0068 count(PB); -0069 end -0070 -0071 % Plot results -0072 plot([biomassValues fliplr(biomassValues)],[targetUpperBound fliplr(targetLowerBound)],'blue','LineWidth',2); -0073 axis tight; -0074 ylabel([strrep(targetRxn,'_','-') ' (mmol/gDW h)']); -0075 xlabel('Growth rate (1/h)'); -0076 targetValues = [targetLowerBound; targetUpperBound]; -0077 end

    +0016 % number of biomass values at which the target bounds are calculated +0017 % (default 20). +0018 % +0019 % Returns +0020 % ------- +0021 % biomassValues : double +0022 % nPts biomass values, evenly spaced from the minimum to the maximum +0023 % biomass flux. +0024 % targetValues : double +0025 % a matrix with two rows: the lower bounds (row 1) and the upper bounds +0026 % (row 2) of the target reaction flux at each biomass value. NaN where +0027 % the problem has no solution. +0028 % +0029 % Examples +0030 % -------- +0031 % [biomassValues, targetValues] = runProductionEnvelope(model, ... +0032 % targetRxn, biomassRxn, nPts); +0033 % +0034 % Notes +0035 % ----- +0036 % Modified from COBRA Toolbox productionEnvelope.m. +0037 +0038 p=parseRAVENargs(varargin, {'nPts',20}); +0039 nPts=p.nPts; +0040 +0041 % Run FBA to get upper bound for biomass +0042 model = setParam(model,'obj',biomassRxn,1); +0043 [solMax,hsSol] = solveLP(model); +0044 solMax = solMax.x(logical(model.c)); +0045 model.c = -model.c; +0046 solMin = solveLP(model,0,[],hsSol); +0047 solMin = solMin.x(logical(model.c)); +0048 +0049 % Create biomass range vector +0050 biomassValues = linspace(solMin,solMax,nPts); +0051 targetUpperBound = nan(1,numel(biomassValues)); +0052 targetLowerBound = nan(1,numel(biomassValues)); +0053 +0054 PB = progressReport(length(biomassValues),'Creating production envelope'); +0055 % Max/min for target production +0056 model = setParam(model,'obj',targetRxn,1); +0057 for i = 1:length(biomassValues) +0058 model1 = setParam(model,'eq',biomassRxn,biomassValues(i)); +0059 sol = solveLP(model1,0,[],hsSol); +0060 if (sol.stat > 0) +0061 targetUpperBound(i) = sol.x(logical(model.c)); +0062 else +0063 targetUpperBound(i) = NaN; +0064 end +0065 model1.c = -model1.c; +0066 sol = solveLP(model1,0,[],hsSol); +0067 if (sol.stat > 0) +0068 targetLowerBound(i) = sol.x(logical(model1.c)); +0069 else +0070 targetLowerBound(i) = NaN; +0071 end +0072 count(PB); +0073 end +0074 +0075 % Plot results +0076 plot([biomassValues fliplr(biomassValues)],[targetUpperBound fliplr(targetLowerBound)],'blue','LineWidth',2); +0077 axis tight; +0078 ylabel([strrep(targetRxn,'_','-') ' (mmol/gDW h)']); +0079 xlabel('Growth rate (1/h)'); +0080 targetValues = [targetLowerBound; targetUpperBound]; +0081 end
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/analysis/runRobustnessAnalysis.html b/docs/html/analysis/runRobustnessAnalysis.html new file mode 100644 index 00000000..7214e60a --- /dev/null +++ b/docs/html/analysis/runRobustnessAnalysis.html @@ -0,0 +1,176 @@ + + + + Description of runRobustnessAnalysis + + + + + + + + + +
    Home > analysis > runRobustnessAnalysis.m
    + + + +

    runRobustnessAnalysis +

    + +

    PURPOSE ^

    +
    runRobustnessAnalysis Perform robustness analysis for a reaction and objective.
    + +

    SYNOPSIS ^

    +
    function [controlFlux, objFlux] = runRobustnessAnalysis(model, controlRxn, varargin)
    + +

    DESCRIPTION ^

    +
     runRobustnessAnalysis  Perform robustness analysis for a reaction and objective.
    +
    + Varies the flux of a reaction of interest from its minimum to its maximum
    + and calculates the optimal value of an objective of interest at each
    + flux.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + controlRxn : char
    +     reaction of interest whose value is to be controlled.
    +
    + Name-Value Arguments
    + --------------------
    + nPoints : double
    +     number of control flux values (default 20).
    + objRxn : char
    +     reaction identifier of the objective to be maximised (default: the
    +     objective defined in the model).
    + plotRedCost : logical
    +     whether the reduced cost of the control reaction is also plotted on
    +     the right axis (default false).
    +
    + Returns
    + -------
    + controlFlux : double
    +     flux values of the reaction of interest, ranging from its minimum to
    +     its maximum value.
    + objFlux : double
    +     optimal values of objective reaction at each control reaction flux
    +     value.
    +
    + Examples
    + --------
    +     [controlFlux, objFlux] = runRobustnessAnalysis(model, controlRxn, ...
    +         nPoints, objRxn);
    +
    + Notes
    + -----
    + Modified from COBRA Toolbox robustnessAnalysis.m.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function [controlFlux, objFlux] = runRobustnessAnalysis(model, controlRxn, varargin)
    +0002 % runRobustnessAnalysis  Perform robustness analysis for a reaction and objective.
    +0003 %
    +0004 % Varies the flux of a reaction of interest from its minimum to its maximum
    +0005 % and calculates the optimal value of an objective of interest at each
    +0006 % flux.
    +0007 %
    +0008 % Parameters
    +0009 % ----------
    +0010 % model : struct
    +0011 %     a model structure.
    +0012 % controlRxn : char
    +0013 %     reaction of interest whose value is to be controlled.
    +0014 %
    +0015 % Name-Value Arguments
    +0016 % --------------------
    +0017 % nPoints : double
    +0018 %     number of control flux values (default 20).
    +0019 % objRxn : char
    +0020 %     reaction identifier of the objective to be maximised (default: the
    +0021 %     objective defined in the model).
    +0022 % plotRedCost : logical
    +0023 %     whether the reduced cost of the control reaction is also plotted on
    +0024 %     the right axis (default false).
    +0025 %
    +0026 % Returns
    +0027 % -------
    +0028 % controlFlux : double
    +0029 %     flux values of the reaction of interest, ranging from its minimum to
    +0030 %     its maximum value.
    +0031 % objFlux : double
    +0032 %     optimal values of objective reaction at each control reaction flux
    +0033 %     value.
    +0034 %
    +0035 % Examples
    +0036 % --------
    +0037 %     [controlFlux, objFlux] = runRobustnessAnalysis(model, controlRxn, ...
    +0038 %         nPoints, objRxn);
    +0039 %
    +0040 % Notes
    +0041 % -----
    +0042 % Modified from COBRA Toolbox robustnessAnalysis.m.
    +0043 
    +0044 p=parseRAVENargs(varargin, {'nPoints',20; 'objRxn',[]; 'plotRedCost',false});
    +0045 nPoints=p.nPoints;
    +0046 objRxn=p.objRxn;
    +0047 plotRedCost=p.plotRedCost;
    +0048 if isempty(objRxn)
    +0049     baseModel = model;
    +0050 else
    +0051     baseModel = setParam(model,'obj',objRxn,1);
    +0052 end
    +0053 
    +0054 if any(ismember(model.rxns,controlRxn))
    +0055     controlRxnIdx = getIndexes(model,controlRxn,'rxns');
    +0056     tmpModel = setParam(model,'obj',controlRxnIdx,1);
    +0057     solMax = solveLP(tmpModel);
    +0058     solMax = solMax.x(logical(tmpModel.c));
    +0059     tmpModel.c = -tmpModel.c;
    +0060     solMin = solveLP(tmpModel);
    +0061     solMin = solMin.x(logical(tmpModel.c));
    +0062 else
    +0063     error('Control reaction does not exist!');
    +0064 end
    +0065 
    +0066 objFlux = zeros(nPoints,1);
    +0067 redCost = zeros(nPoints,1);
    +0068 controlFlux = linspace(solMin,solMax,nPoints)';
    +0069 
    +0070 
    +0071 PB = progressReport(length(controlFlux),'Running robustness analysis');
    +0072 for i=1:length(controlFlux)
    +0073     modelControlled = setParam(baseModel,'eq',controlRxnIdx,controlFlux(i));
    +0074     solControlled = solveLP(modelControlled);
    +0075     objFlux(i) = solControlled.x(logical(modelControlled.c));
    +0076     redCost(i) = solControlled.rCost(controlRxnIdx);
    +0077     count(PB);
    +0078 end
    +0079 
    +0080 if plotRedCost
    +0081     yyaxis right
    +0082     plot(controlFlux,redCost)
    +0083     ylabel([strrep(controlRxn,'_','-') ' reduced cost']);
    +0084     yyaxis left
    +0085 end
    +0086 plot(controlFlux,objFlux)
    +0087 xlabel(strrep(controlRxn,'_','-'));
    +0088 ylabel('Objective');
    +0089 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/analysis/runSimpleOptKnock.html b/docs/html/analysis/runSimpleOptKnock.html new file mode 100644 index 00000000..2479a938 --- /dev/null +++ b/docs/html/analysis/runSimpleOptKnock.html @@ -0,0 +1,228 @@ + + + + Description of runSimpleOptKnock + + + + + + + + + +
    Home > analysis > runSimpleOptKnock.m
    + + + +

    runSimpleOptKnock +

    + +

    PURPOSE ^

    +
    runSimpleOptKnock Find deletions that couple metabolite production to growth.
    + +

    SYNOPSIS ^

    +
    function out = runSimpleOptKnock(model, targetRxn, biomassRxn, varargin)
    + +

    DESCRIPTION ^

    +
     runSimpleOptKnock  Find deletions that couple metabolite production to growth.
    +
    + Checks all gene or reaction deletions for growth-coupled metabolite
    + production, by testing combinations of up to maxNumKO deletions. It tries
    + a deeper deletion only after a deletion whose growth rate is above
    + minGrowth. A combination is reported if the growth rate exceeds
    + minGrowth and the product of growth rate and production rate exceeds the
    + value without the last added deletion (the wild type for the first
    + deletion) by more than 1 %. The method is not defined as a MILP and is
    + therefore slow.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + targetRxn : char
    +     identifier of target reaction.
    + biomassRxn : char
    +     identifier of biomass reaction.
    +
    + Name-Value Arguments
    + --------------------
    + deletions : cell
    +     cell array with gene or reaction identifiers that should be
    +     considered for knockout (default model.rxns).
    + genesOrRxns : char
    +     string indicating whether deletions parameter is given with 'genes'
    +     or 'rxns' identifiers (default 'rxns').
    + maxNumKO : double
    +     maximum number of simultaneous knockouts (default 1).
    + minGrowth : double
    +     minimum growth rate (default 0.05).
    +
    + Returns
    + -------
    + out : struct
    +     structure with deletion strategies that result in growth-coupled
    +     production, with fields:
    +
    +     - KO : cell array with gene(s) or reaction(s) to be deleted.
    +     - growthRate : vector with growth rates after deletion.
    +     - prodRate : vector with production rates after deletion.
    +     - score : vector with the product of growth rate and production rate.
    +
    + Examples
    + --------
    +     out = runSimpleOptKnock(model, targetRxn, biomassRxn, deletions, ...
    +         genesOrRxns, maxNumKO, minGrowth);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function out = runSimpleOptKnock(model, targetRxn, biomassRxn, varargin)
    +0002 % runSimpleOptKnock  Find deletions that couple metabolite production to growth.
    +0003 %
    +0004 % Checks all gene or reaction deletions for growth-coupled metabolite
    +0005 % production, by testing combinations of up to maxNumKO deletions. It tries
    +0006 % a deeper deletion only after a deletion whose growth rate is above
    +0007 % minGrowth. A combination is reported if the growth rate exceeds
    +0008 % minGrowth and the product of growth rate and production rate exceeds the
    +0009 % value without the last added deletion (the wild type for the first
    +0010 % deletion) by more than 1 %. The method is not defined as a MILP and is
    +0011 % therefore slow.
    +0012 %
    +0013 % Parameters
    +0014 % ----------
    +0015 % model : struct
    +0016 %     a model structure.
    +0017 % targetRxn : char
    +0018 %     identifier of target reaction.
    +0019 % biomassRxn : char
    +0020 %     identifier of biomass reaction.
    +0021 %
    +0022 % Name-Value Arguments
    +0023 % --------------------
    +0024 % deletions : cell
    +0025 %     cell array with gene or reaction identifiers that should be
    +0026 %     considered for knockout (default model.rxns).
    +0027 % genesOrRxns : char
    +0028 %     string indicating whether deletions parameter is given with 'genes'
    +0029 %     or 'rxns' identifiers (default 'rxns').
    +0030 % maxNumKO : double
    +0031 %     maximum number of simultaneous knockouts (default 1).
    +0032 % minGrowth : double
    +0033 %     minimum growth rate (default 0.05).
    +0034 %
    +0035 % Returns
    +0036 % -------
    +0037 % out : struct
    +0038 %     structure with deletion strategies that result in growth-coupled
    +0039 %     production, with fields:
    +0040 %
    +0041 %     - KO : cell array with gene(s) or reaction(s) to be deleted.
    +0042 %     - growthRate : vector with growth rates after deletion.
    +0043 %     - prodRate : vector with production rates after deletion.
    +0044 %     - score : vector with the product of growth rate and production rate.
    +0045 %
    +0046 % Examples
    +0047 % --------
    +0048 %     out = runSimpleOptKnock(model, targetRxn, biomassRxn, deletions, ...
    +0049 %         genesOrRxns, maxNumKO, minGrowth);
    +0050 
    +0051 p=parseRAVENargs(varargin, {'deletions',[]; 'genesOrRxns','rxns'; 'maxNumKO',1; 'minGrowth',0.05});
    +0052 deletions=p.deletions;
    +0053 genesOrRxns=p.genesOrRxns;
    +0054 maxNumKO=p.maxNumKO;
    +0055 minGrowth=p.minGrowth;
    +0056 if isempty(deletions)
    +0057     params.deletions = model.rxns;
    +0058 else
    +0059     params.deletions = deletions;
    +0060 end
    +0061 params.genesOrRxns = genesOrRxns;
    +0062 params.maxNumKO = maxNumKO;
    +0063 params.minGrowth = minGrowth;
    +0064 
    +0065 % Number of deletions
    +0066 out.KO         = cell(0,params.maxNumKO); % The KO genes/rxns
    +0067 out.growthRate = zeros(0);
    +0068 out.prodRate   = zeros(0);
    +0069 out.score      = zeros(0);
    +0070 
    +0071 params.biomassIdx  = getIndexes(model,biomassRxn,'rxns');
    +0072 params.targetIdx   = getIndexes(model,targetRxn,'rxns');
    +0073 
    +0074 model = setParam(model,'obj',params.biomassIdx,1);
    +0075 [solWT, hsSol] = solveLP(model);
    +0076 WT.minScore = solWT.x(params.targetIdx)*solWT.f;
    +0077 
    +0078 params.PB = progressReport(numel(params.deletions),'Running simple OptKnock analysis');
    +0079 KO=zeros(1,params.maxNumKO);
    +0080 [~,~,out,~] = knockoutIteration(model,params,WT,out,params.maxNumKO,KO,[],hsSol);
    +0081 
    +0082 if size(out.KO,2)>1
    +0083     singleKO = cellfun(@isempty,out.KO(:,1));
    +0084     if any(singleKO)
    +0085         singleKO = out.KO{singleKO,2};
    +0086         singleKO = strcmp(out.KO(:,1),singleKO);
    +0087         out.KO(singleKO,:) = [];
    +0088         out.growthRate(singleKO,:) = [];
    +0089         out.prodRate(singleKO,:) = [];
    +0090         out.score(singleKO,:) = [];
    +0091     end
    +0092 end
    +0093 
    +0094 
    +0095 params.PB.done;
    +0096 end
    +0097 
    +0098 function [model,iteration,out,KO,hsSol] = knockoutIteration(model,params,WT,out,iteration,KO,minScore,hsSol)
    +0099 if nargin<7 || isempty(minScore)
    +0100     minScore = WT.minScore;
    +0101 end
    +0102 iteration = iteration - 1;
    +0103 for i = 1:numel(params.deletions)
    +0104     if iteration+1==params.maxNumKO
    +0105         params.PB.update(i);
    +0106     end
    +0107     KO(iteration+1)=i;
    +0108     switch params.genesOrRxns
    +0109         case 'rxns'
    +0110             modelKO = setParam(model,'eq',params.deletions{i},0);
    +0111         case 'genes'
    +0112             modelKO = removeGenes(model,params.deletions{i},false,false,false);
    +0113     end
    +0114     solKO = solveLP(modelKO,0,[],hsSol);
    +0115     if ~isempty(solKO.f)
    +0116         growthRate = solKO.f;
    +0117         prodRate   = solKO.x(params.targetIdx);
    +0118         prodRate(prodRate<1e-10)=0; % Filter out results from solver tolerance
    +0119         if growthRate > params.minGrowth
    +0120             if growthRate*prodRate > minScore*1.01
    +0121                 out.KO(end+1,find(KO)) = transpose(params.deletions(KO(find(KO))));
    +0122                 out.growthRate(end+1,1) = growthRate;
    +0123                 out.prodRate(end+1,1)   = prodRate;
    +0124                 out.score(end+1,1)      = growthRate*prodRate;
    +0125             end
    +0126             if iteration>0
    +0127                 [~,~,out] = knockoutIteration(modelKO,params,WT,out,iteration,KO,growthRate*prodRate,hsSol);
    +0128             end
    +0129         end
    +0130     end
    +0131 end
    +0132 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/analysis/sampleACHR.html b/docs/html/analysis/sampleACHR.html new file mode 100644 index 00000000..2d19b234 --- /dev/null +++ b/docs/html/analysis/sampleACHR.html @@ -0,0 +1,234 @@ + + + + Description of sampleACHR + + + + + + + + + +
    Home > analysis > sampleACHR.m
    + + + +

    sampleACHR +

    + +

    PURPOSE ^

    +
    sampleACHR Artificially Centered Hit-and-Run flux sampling.
    + +

    SYNOPSIS ^

    +
    function solutions = sampleACHR(model, nSamples, thinning, warmup, seed)
    + +

    DESCRIPTION ^

    +
     sampleACHR  Artificially Centered Hit-and-Run flux sampling.
    +
    + Draws nSamples flux vectors approximately uniformly from the flux polytope
    + {v : S*v = 0, lb <= v <= ub}, using the ACHR algorithm (Kaufman & Smith 1998).
    + No rounding is performed, so on strongly elongated polytopes (for example
    + enzyme-constrained models) mixing is slower than with sampleCHRR, which
    + rounds the polytope.
    + Reactions whose bounds or warmup values coincide never move. The remaining
    + reactions are corrected towards S*v = 0 every few steps and before each
    + recorded sample, with a sparse Cholesky factor of the row-normalised S*S'
    + (no dense null-space basis), so the rounding error in S*v does not grow with
    + the number of steps and samples stay inside the bounds. The guarantee
    + S*v = 0 holds for a well-conditioned S and warmup points that are feasible
    + for the model. When the largest residual of a recorded sample exceeds 1e-9
    + times max(1, max(abs(v))) (nearly dependent rows of S, a noisy or infeasible
    + warmup), sampleACHR issues the warning sampleACHR:steadyStateNotRestored.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + nSamples : double
    +     number of flux vectors to return (default 1000).
    + thinning : double
    +     Markov-chain steps between recorded samples (default 100).
    + warmup : double
    +     nPoints-by-nRxns warmup vertices (default: generated by sampleWarmupPoints).
    + seed : double
    +     random seed for reproducibility (default: current RNG state).
    +
    + Returns
    + -------
    + solutions : double
    +     nRxns-by-nSamples matrix of sampled flux distributions.
    +
    + See also
    + --------
    + randomSampling, sampleCHRR, sampleWarmupPoints
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    • randomSampling randomSampling Sample the flux solution space (entry point for all samplers).
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function solutions = sampleACHR(model, nSamples, thinning, warmup, seed)
    +0002 % sampleACHR  Artificially Centered Hit-and-Run flux sampling.
    +0003 %
    +0004 % Draws nSamples flux vectors approximately uniformly from the flux polytope
    +0005 % {v : S*v = 0, lb <= v <= ub}, using the ACHR algorithm (Kaufman & Smith 1998).
    +0006 % No rounding is performed, so on strongly elongated polytopes (for example
    +0007 % enzyme-constrained models) mixing is slower than with sampleCHRR, which
    +0008 % rounds the polytope.
    +0009 % Reactions whose bounds or warmup values coincide never move. The remaining
    +0010 % reactions are corrected towards S*v = 0 every few steps and before each
    +0011 % recorded sample, with a sparse Cholesky factor of the row-normalised S*S'
    +0012 % (no dense null-space basis), so the rounding error in S*v does not grow with
    +0013 % the number of steps and samples stay inside the bounds. The guarantee
    +0014 % S*v = 0 holds for a well-conditioned S and warmup points that are feasible
    +0015 % for the model. When the largest residual of a recorded sample exceeds 1e-9
    +0016 % times max(1, max(abs(v))) (nearly dependent rows of S, a noisy or infeasible
    +0017 % warmup), sampleACHR issues the warning sampleACHR:steadyStateNotRestored.
    +0018 %
    +0019 % Parameters
    +0020 % ----------
    +0021 % model : struct
    +0022 %     a model structure.
    +0023 % nSamples : double
    +0024 %     number of flux vectors to return (default 1000).
    +0025 % thinning : double
    +0026 %     Markov-chain steps between recorded samples (default 100).
    +0027 % warmup : double
    +0028 %     nPoints-by-nRxns warmup vertices (default: generated by sampleWarmupPoints).
    +0029 % seed : double
    +0030 %     random seed for reproducibility (default: current RNG state).
    +0031 %
    +0032 % Returns
    +0033 % -------
    +0034 % solutions : double
    +0035 %     nRxns-by-nSamples matrix of sampled flux distributions.
    +0036 %
    +0037 % See also
    +0038 % --------
    +0039 % randomSampling, sampleCHRR, sampleWarmupPoints
    +0040 
    +0041 if nargin < 2 || isempty(nSamples); nSamples = 1000; end
    +0042 if nargin < 3 || isempty(thinning); thinning = 100;  end
    +0043 if nargin < 4 || isempty(warmup);   warmup = sampleWarmupPoints(model); end
    +0044 if nargin < 5;                      seed = [];        end
    +0045 if ~isempty(seed)
    +0046     rng(seed);
    +0047 end
    +0048 
    +0049 nRxns = numel(model.rxns);
    +0050 lb = model.lb(:);
    +0051 ub = model.ub(:);
    +0052 nW = size(warmup, 1);
    +0053 
    +0054 % Reactions that cannot move: fixed bounds, or identical value in all warmup
    +0055 % points. Directions and corrections act on the other reactions only.
    +0056 spread = (max(warmup, [], 1) - min(warmup, [], 1))';
    +0057 free = (ub - lb > 1e-9) & (spread > 1e-9);
    +0058 proj = buildSteadyStateCorrection(model.S, free, lb, ub);
    +0059 corrEvery = min(thinning, 20);
    +0060 
    +0061 center = mean(warmup, 1)';             % nRxns-by-1
    +0062 prev = restoreSteadyState(center, proj);
    +0063 center = prev;
    +0064 nTotal = 0;
    +0065 
    +0066 solutions = zeros(nRxns, nSamples);
    +0067 rec = 0;
    +0068 worstResidual = 0;
    +0069 nSteps = thinning*nSamples;
    +0070 
    +0071 for stepno = 1:nSteps
    +0072     pidx = randi(nW);
    +0073     delta = warmup(pidx, :)' - center;
    +0074     delta(~free) = 0;
    +0075     dirTol = 1e-12*max(1, max(abs(delta)));
    +0076 
    +0077     posD = delta > dirTol;
    +0078     negD = delta < -dirTol;
    +0079     ubp = ub - prev;
    +0080     lbp = lb - prev;
    +0081     % alpha range keeping lb <= prev + alpha*delta <= ub
    +0082     upperLims = [ubp(posD)./delta(posD); lbp(negD)./delta(negD)];
    +0083     lowerLims = [lbp(posD)./delta(posD); ubp(negD)./delta(negD)];
    +0084     if isempty(upperLims); tHi = 0; else; tHi = min(upperLims); end
    +0085     if isempty(lowerLims); tLo = 0; else; tLo = max(lowerLims); end
    +0086 
    +0087     if tHi > tLo
    +0088         alpha = tLo + rand*(tHi - tLo);
    +0089         prev = min(max(prev + alpha*delta, lb), ub);
    +0090     end
    +0091 
    +0092     % Running centre (quasi-Markovian ACHR: centre converges and steadies).
    +0093     center = (nTotal*center + prev) / (nTotal + 1);
    +0094     nTotal = nTotal + 1;
    +0095 
    +0096     if mod(stepno, corrEvery) == 0 || mod(stepno, thinning) == 0
    +0097         prev = restoreSteadyState(prev, proj);
    +0098         center = restoreSteadyState(center, proj);
    +0099     end
    +0100 
    +0101     if mod(stepno, thinning) == 0
    +0102         rec = rec + 1;
    +0103         solutions(:, rec) = prev;
    +0104         worstResidual = max(worstResidual, ...
    +0105             max(abs(model.S*prev))/max(1, max(abs(prev))));
    +0106     end
    +0107 end
    +0108 if worstResidual > 1e-9
    +0109     warning('sampleACHR:steadyStateNotRestored', ...
    +0110         ['The largest residual of S*v over the samples is %.3g times max(1, max(abs(v))), ' ...
    +0111          'above 1e-9. The rows of S are nearly dependent or the warmup points are not ' ...
    +0112          'feasible for the model.'], worstResidual);
    +0113 end
    +0114 end
    +0115 
    +0116 function proj = buildSteadyStateCorrection(S, free, lb, ub)
    +0117 % Sparse factor of the row-normalised S(:,free)*S(:,free)' with a small ridge,
    +0118 % used to move a flux vector onto S*v = 0 through the free reactions only.
    +0119 Sf = S(:, free);
    +0120 rowNorm = sqrt(full(sum(Sf.^2, 2)));
    +0121 keep = rowNorm > 0;
    +0122 rowNorm = rowNorm(keep);
    +0123 Sn = spdiags(1./rowNorm, 0, numel(rowNorm), numel(rowNorm))*Sf(keep, :);
    +0124 A = Sn*Sn' + 1e-10*speye(size(Sn, 1));
    +0125 [R, flag, p] = chol(A, 'vector');
    +0126 if flag ~= 0
    +0127     error('sampleACHR:factorization', 'The stoichiometric matrix could not be factorized.');
    +0128 end
    +0129 proj = struct('S', S, 'free', free, 'keep', keep, 'rowNorm', rowNorm, ...
    +0130     'Sn', Sn, 'R', R, 'Rt', R', 'p', p, 'lb', lb, 'ub', ub);
    +0131 end
    +0132 
    +0133 function v = restoreSteadyState(v, proj)
    +0134 % Removes the residual of S*v through the free reactions (a few ridge-regularised
    +0135 % projection passes), then clips v to its bounds.
    +0136 tol = 1e-12*max(1, max(abs(v)));
    +0137 for it = 1:8
    +0138     r = proj.S*v;
    +0139     if max(abs(r)) <= tol
    +0140         break
    +0141     end
    +0142     rn = r(proj.keep) ./ proj.rowNorm;
    +0143     x = zeros(size(rn));
    +0144     x(proj.p) = proj.R \ (proj.Rt \ rn(proj.p));
    +0145     v(proj.free) = v(proj.free) - proj.Sn'*x;
    +0146 end
    +0147 v = min(max(v, proj.lb), proj.ub);
    +0148 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/analysis/sampleCHRR.html b/docs/html/analysis/sampleCHRR.html new file mode 100644 index 00000000..15b5ff11 --- /dev/null +++ b/docs/html/analysis/sampleCHRR.html @@ -0,0 +1,254 @@ + + + + Description of sampleCHRR + + + + + + + + + +
    Home > analysis > sampleCHRR.m
    + + + +

    sampleCHRR +

    + +

    PURPOSE ^

    +
    sampleCHRR Coordinate Hit-and-Run with Rounding flux sampling.
    + +

    SYNOPSIS ^

    +
    function [solutions, info] = sampleCHRR(model, nSamples, thinning, nBurnin, seed, fixedWidthTol)
    + +

    DESCRIPTION ^

    +
     sampleCHRR  Coordinate Hit-and-Run with Rounding flux sampling.
    +
    + Draws nSamples flux vectors approximately uniformly from the flux polytope
    + {v : S*v = 0, lb <= v <= ub}, using the CHRR algorithm (Haraldsdottir et al.
    + 2017, Bioinformatics 33:1741). The polytope is rounded before sampling,
    + which improves mixing on strongly elongated polytopes (for example
    + enzyme-constrained models) in comparison with sampleACHR, which does not
    + round.
    +
    + Constraints to condition on (for example a biomass lower bound or measured
    + exchange fluxes) are set on the model before the call.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + nSamples : double
    +     number of flux vectors to return (default 1000).
    + thinning : double
    +     Markov-chain steps between recorded samples (default 100).
    + nBurnin : double
    +     burn-in steps discarded before the first recorded sample (default 1000).
    + seed : double
    +     random seed for reproducibility (default: current RNG state).
    + fixedWidthTol : double
    +     a reaction whose allowed flux range is narrower than this is treated as
    +     stoichiometrically fixed and added to the equality system at the middle
    +     of its range, which keeps the reduced polytope full-dimensional
    +     (default 1e-7).
    +
    + Returns
    + -------
    + solutions : double
    +     nRxns-by-nSamples matrix of sampled flux distributions.
    + info : struct
    +     .nDimensions  : dimension of the sampled flux polytope
    +     .mveConverged : whether MVE rounding reached tolerance
    +     .fixedRxns    : IDs of reactions added to the equality system as fixed
    +
    + See also
    + --------
    + randomSampling, sampleACHR, sampleMaxVolEllipse
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    • randomSampling randomSampling Sample the flux solution space (entry point for all samplers).
    + + + + +

    SOURCE CODE ^

    +
    0001 function [solutions, info] = sampleCHRR(model, nSamples, thinning, nBurnin, seed, fixedWidthTol)
    +0002 % sampleCHRR  Coordinate Hit-and-Run with Rounding flux sampling.
    +0003 %
    +0004 % Draws nSamples flux vectors approximately uniformly from the flux polytope
    +0005 % {v : S*v = 0, lb <= v <= ub}, using the CHRR algorithm (Haraldsdottir et al.
    +0006 % 2017, Bioinformatics 33:1741). The polytope is rounded before sampling,
    +0007 % which improves mixing on strongly elongated polytopes (for example
    +0008 % enzyme-constrained models) in comparison with sampleACHR, which does not
    +0009 % round.
    +0010 %
    +0011 % Constraints to condition on (for example a biomass lower bound or measured
    +0012 % exchange fluxes) are set on the model before the call.
    +0013 %
    +0014 % Parameters
    +0015 % ----------
    +0016 % model : struct
    +0017 %     a model structure.
    +0018 % nSamples : double
    +0019 %     number of flux vectors to return (default 1000).
    +0020 % thinning : double
    +0021 %     Markov-chain steps between recorded samples (default 100).
    +0022 % nBurnin : double
    +0023 %     burn-in steps discarded before the first recorded sample (default 1000).
    +0024 % seed : double
    +0025 %     random seed for reproducibility (default: current RNG state).
    +0026 % fixedWidthTol : double
    +0027 %     a reaction whose allowed flux range is narrower than this is treated as
    +0028 %     stoichiometrically fixed and added to the equality system at the middle
    +0029 %     of its range, which keeps the reduced polytope full-dimensional
    +0030 %     (default 1e-7).
    +0031 %
    +0032 % Returns
    +0033 % -------
    +0034 % solutions : double
    +0035 %     nRxns-by-nSamples matrix of sampled flux distributions.
    +0036 % info : struct
    +0037 %     .nDimensions  : dimension of the sampled flux polytope
    +0038 %     .mveConverged : whether MVE rounding reached tolerance
    +0039 %     .fixedRxns    : IDs of reactions added to the equality system as fixed
    +0040 %
    +0041 % See also
    +0042 % --------
    +0043 % randomSampling, sampleACHR, sampleMaxVolEllipse
    +0044 
    +0045 if nargin < 2 || isempty(nSamples);      nSamples = 1000;     end
    +0046 if nargin < 3 || isempty(thinning);      thinning = 100;      end
    +0047 if nargin < 4 || isempty(nBurnin);       nBurnin = 1000;      end
    +0048 if nargin < 5;                           seed = [];           end
    +0049 if nargin < 6 || isempty(fixedWidthTol); fixedWidthTol = 1e-7; end
    +0050 if ~isempty(seed)
    +0051     rng(seed);
    +0052 end
    +0053 
    +0054 nRxns = numel(model.rxns);
    +0055 S  = full(model.S);
    +0056 lb = model.lb(:);
    +0057 ub = model.ub(:);
    +0058 
    +0059 % ---- Fold implicitly-fixed reactions into equalities ----
    +0060 % A reaction with ~zero flux range is determined by stoichiometry; keeping it as
    +0061 % a box constraint would make the reduced polytope lower-dimensional and the MVE
    +0062 % solve singular. Fix it at its (unique) value instead.
    +0063 [minF, maxF] = getAllowedBounds(model);
    +0064 minF = minF(:); maxF = maxF(:);
    +0065 width = maxF - minF;
    +0066 fixed = width < fixedWidthTol;
    +0067 fixedVals = (maxF + minF) / 2;
    +0068 fixedIdx = find(fixed);
    +0069 
    +0070 Aeq = S;
    +0071 beq = zeros(size(S, 1), 1);
    +0072 if ~isempty(fixedIdx)
    +0073     blk = zeros(numel(fixedIdx), nRxns);
    +0074     for k = 1:numel(fixedIdx)
    +0075         blk(k, fixedIdx(k)) = 1;
    +0076     end
    +0077     Aeq = [Aeq; blk];
    +0078     beq = [beq; fixedVals(fixedIdx)];
    +0079 end
    +0080 
    +0081 % Particular solution v0 and nullspace basis N: v = v0 + N*x.
    +0082 % v0 is ANY particular solution; the sampled flux distribution is invariant to
    +0083 % its choice and to the nullspace basis.
    +0084 % lsqminnorm rather than '\': Aeq is square whenever the number of metabolites
    +0085 % plus the number of folded-in fixed reactions equals the number of reactions,
    +0086 % and it is then rank-deficient, for which '\' returns NaN. Those NaNs travel
    +0087 % into the polytope and surface much later as a misleading "empty interior"
    +0088 % error from sampleChebyshevCenter.
    +0089 v0 = lsqminnorm(Aeq, beq);
    +0090 N  = null(Aeq);
    +0091 d  = size(N, 2);
    +0092 
    +0093 info = struct();
    +0094 info.fixedRxns = model.rxns(fixedIdx);
    +0095 
    +0096 if d == 0
    +0097     % Flux space is a single point — nothing to sample.
    +0098     solutions = repmat(v0, 1, nSamples);
    +0099     info.nDimensions = 0;
    +0100     info.mveConverged = true;
    +0101     return;
    +0102 end
    +0103 
    +0104 % ---- Build the full-dimensional polytope in x (free reactions only) ----
    +0105 free = ~fixed;
    +0106 Nf = N(free, :);
    +0107 A_full = [Nf; -Nf];
    +0108 b_full = [ub(free) - v0(free); v0(free) - lb(free)];
    +0109 % Drop rows whose direction vanished in the nullspace (constraint is constant).
    +0110 keep = sqrt(sum(A_full.^2, 2)) > 1e-10;
    +0111 A_full = A_full(keep, :);
    +0112 b_full = b_full(keep);
    +0113 
    +0114 % ---- Round with the MVE ----
    +0115 x0 = sampleChebyshevCenter(A_full, b_full);
    +0116 [center, E, converged] = sampleMaxVolEllipse(A_full, b_full, x0);
    +0117 info.nDimensions = d;
    +0118 info.mveConverged = converged;
    +0119 if ~converged
    +0120     warning('RAVEN:warning', '%s', ['The maximum-volume ellipsoid rounding ' ...
    +0121         'did not converge; the samples may be poorly mixed. Inspect the ' ...
    +0122         'mveConverged field of the second output.']);
    +0123 end
    +0124 
    +0125 % Rounded polytope {y : A_r*y <= b_r}, which contains the unit ball.
    +0126 A_r = A_full * E;
    +0127 b_r = b_full - A_full*center;
    +0128 
    +0129 % ---- Coordinate hit-and-run on the rounded polytope ----
    +0130 y = zeros(d, 1);
    +0131 s = b_r - A_r*y;                       % maintained slack, > 0 at start
    +0132 solutions = zeros(nRxns, nSamples);
    +0133 rec = 0;
    +0134 total = nBurnin + thinning*nSamples;
    +0135 colTol = 1e-12;
    +0136 
    +0137 for stepno = 1:total
    +0138     j = randi(d);
    +0139     col = A_r(:, j);
    +0140     pos = col > colTol;
    +0141     neg = col < -colTol;
    +0142     ratios = s ./ col;
    +0143     if any(pos); tHi = min(ratios(pos)); else; tHi = inf;  end
    +0144     if any(neg); tLo = max(ratios(neg)); else; tLo = -inf; end
    +0145     if ~isfinite(tHi) || ~isfinite(tLo) || tHi <= tLo
    +0146         continue;                      % unbounded/degenerate coordinate; skip
    +0147     end
    +0148     t = tLo + rand*(tHi - tLo);
    +0149     y(j) = y(j) + t;
    +0150     s = s - col*t;
    +0151 
    +0152     if stepno > nBurnin && mod(stepno - nBurnin, thinning) == 0
    +0153         rec = rec + 1;
    +0154         x = center + E*y;
    +0155         solutions(:, rec) = v0 + N*x;
    +0156     end
    +0157 end
    +0158 
    +0159 if rec < nSamples
    +0160     if rec >= 1
    +0161         solutions(:, rec+1:end) = repmat(solutions(:, rec), 1, nSamples - rec);
    +0162     else
    +0163         solutions = repmat(v0, 1, nSamples);
    +0164     end
    +0165 end
    +0166 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/analysis/sampleChebyshevCenter.html b/docs/html/analysis/sampleChebyshevCenter.html similarity index 98% rename from doc/analysis/sampleChebyshevCenter.html rename to docs/html/analysis/sampleChebyshevCenter.html index f37ba7f7..3b69e197 100644 --- a/doc/analysis/sampleChebyshevCenter.html +++ b/docs/html/analysis/sampleChebyshevCenter.html @@ -90,7 +90,7 @@

    SOURCE CODE ^% Variables [x (n, free); r (1, >=0)]; minimise -r (maximises the ball radius). -0029 prob.A = [A, norms]; +0029 prob.A = sparse([A, norms]); % Gurobi and SCIP require a sparse A 0030 prob.a = prob.A; 0031 prob.b = b; 0032 prob.csense = repmat('L', 1, m); diff --git a/doc/analysis/sampleMaxVolEllipse.html b/docs/html/analysis/sampleMaxVolEllipse.html similarity index 59% rename from doc/analysis/sampleMaxVolEllipse.html rename to docs/html/analysis/sampleMaxVolEllipse.html index 6e49cb8c..e4962bb4 100644 --- a/doc/analysis/sampleMaxVolEllipse.html +++ b/docs/html/analysis/sampleMaxVolEllipse.html @@ -34,7 +34,7 @@

    DESCRIPTION ^DESCRIPTION ^DESCRIPTION ^SOURCE CODE ^% {z : A*z <= b}, using the Zhang & Gao (2003) primal-dual interior-point 0007 % method (the regularised variant used in COBRA's CHRR sampler). This is the 0008 % rounding step of sampleCHRR: the returned transform E maps the polytope to a -0009 % well-conditioned body in which coordinate hit-and-run mixes rapidly. +0009 % well-conditioned body in which coordinate hit-and-run samples. 0010 % 0011 % This function is pure linear algebra (no LP solver) except when x0 is empty, 0012 % in which case a Chebyshev centre is obtained via sampleChebyshevCenter. @@ -124,8 +125,8 @@

    SOURCE CODE ^% tol : double (optional) 0027 % convergence tolerance (default 1e-6). 0028 % reg : double (optional) -0029 % diagonal / Levenberg regularisation keeping the Newton solves -0030 % well-conditioned (default 1e-8). +0029 % diagonal (Levenberg) regularisation of the Newton solves +0030 % (default 1e-8). 0031 % 0032 % Returns 0033 % ------- @@ -135,146 +136,147 @@

    SOURCE CODE ^% n-by-n lower-triangular rounding transform with E*E' = E2 (the SPD 0038 % ellipsoid matrix). The rounding substitution is z = x + E*y. 0039 % converged : logical -0040 % whether tol was reached within maxiter. A false value is not fatal — -0041 % the last iterate is still a valid (if looser) rounding. -0042 % -0043 % Notes -0044 % ----- -0045 % Validated against analytic cases: a box maps to the unit ball (E = I); a -0046 % sheared box {z : -1 <= M*z <= 1} gives E*E' = inv(M)*inv(M)'; a triangle -0047 % gives its Steiner inellipse. -0048 % -0049 % Based on Y. Zhang & L. Gao (2003), SIAM J. Optim. 14:53-76; as used in -0050 % Haraldsdottir et al. (2017) Bioinformatics 33:1741 (CHRR). -0051 % -0052 % See also -0053 % -------- -0054 % sampleCHRR, sampleChebyshevCenter, randomSampling -0055 -0056 if nargin < 4 || isempty(maxiter); maxiter = 150; end -0057 if nargin < 5 || isempty(tol); tol = 1e-6; end -0058 if nargin < 6 || isempty(reg); reg = 1e-8; end -0059 -0060 [m, n] = size(A); -0061 b = b(:); -0062 % bnrm is the norm of the ORIGINAL b (before the row-normalisation below sets -0063 % b <- ones). This matches COBRA's chrrSampler reference; do not "correct" it to -0064 % sqrt(m), or convergence scaling diverges from the reference. -0065 bnrm = norm(b); -0066 minmu = 1e-8; -0067 tau0 = 0.75; -0068 -0069 if nargin < 3 || isempty(x0) -0070 x0 = sampleChebyshevCenter(A, b); -0071 end -0072 x0 = x0(:); -0073 -0074 bmAx0 = b - A*x0; -0075 if any(bmAx0 <= 0) -0076 error('RAVEN:sampling', 'sampleMaxVolEllipse: x0 is not strictly interior (A*x0 < b).'); -0077 end -0078 -0079 % Row-normalise so the constraint RHS is all ones; solve in shifted coords. -0080 A = A ./ bmAx0; -0081 b = ones(m, 1); -0082 -0083 x = zeros(n, 1); -0084 y = ones(m, 1); -0085 bmAx = b; -0086 z = zeros(m, 1); -0087 astep = 0; -0088 Adx = zeros(n, 1); -0089 converged = false; -0090 E2 = eye(n); -0091 -0092 for it = 1:maxiter -0093 if it > 1 -0094 bmAx = bmAx - astep*Adx; -0095 end -0096 -0097 AtYA = A' * (A .* y); % A' * diag(y) * A (n-by-n) -0098 E2 = inv(AtYA); %#ok<MINV> explicit inverse mirrors the reference -0099 Q = A * E2 * A'; % m-by-m -0100 h = sqrt(max(diag(Q), 0)); -0101 -0102 if it == 1 -0103 t = min(bmAx ./ h); -0104 y = y / t^2; -0105 h = t * h; -0106 z = max(1e-1, bmAx - h); -0107 Q = t^2 * Q; -0108 end -0109 -0110 yz = y .* z; -0111 yh = y .* h; -0112 gap = sum(yz) / m; -0113 rmu = max(min(0.5, gap)*gap, minmu); -0114 -0115 R1 = -A' * yh; % dual residual (n-by-1) -0116 R2 = bmAx - h - z; % primal/slack (m-by-1) -0117 R3 = rmu - yz; % complementarity (m-by-1) -0118 res = max([max(abs(R1)), max(abs(R2)), max(abs(R3))]); -0119 -0120 if res < tol*(1 + bnrm) && rmu <= minmu -0121 x = x + x0; -0122 converged = true; -0123 break; -0124 end -0125 -0126 YQ = y .* Q; % diag(y) * Q -0127 YQQY = YQ .* YQ'; % Hadamard product -0128 y2h = 2 * yh; -0129 G = YQQY + diag(max(reg, y2h .* z)); -0130 YA = y .* A; % diag(y) * A -0131 T = G \ ((h + z) .* YA); % m-by-n -0132 ATP = (y2h .* T - YA)'; % n-by-m -0133 R3Dy = R3 ./ y; -0134 R23 = R2 - R3Dy; -0135 ATP_A = ATP*A + reg*eye(n); -0136 dx = ATP_A \ (R1 + ATP*R23); -0137 Adx = A * dx; -0138 dyDy = G \ (y2h .* (Adx - R23)); -0139 dy = y .* dyDy; -0140 dz = R3Dy - z .* dyDy; -0141 -0142 ax = -1 / min([-Adx ./ bmAx; -0.5]); -0143 ay = -1 / min([ dyDy; -0.5]); -0144 az = -1 / min([ dz ./ z; -0.5]); -0145 tau = max(tau0, 1 - res); -0146 astep = tau * min([1, ax, ay, az]); -0147 -0148 x = x + astep*dx; -0149 y = y + astep*dy; -0150 z = z + astep*dz; -0151 end -0152 -0153 if ~converged -0154 x = x + x0; -0155 end -0156 -0157 E = nearestSPDchol(E2); -0158 end -0159 +0040 % whether tol was reached within maxiter. If false, the last iterate +0041 % is returned as the centre and transform, which is a valid but looser +0042 % rounding. +0043 % +0044 % Notes +0045 % ----- +0046 % Analytic cases, tested in tSampling: a box maps to the unit ball (E = I); +0047 % a sheared box {z : -1 <= M*z <= 1} gives E*E' = inv(M)*inv(M)'; a +0048 % triangle gives its Steiner inellipse. +0049 % +0050 % Based on Y. Zhang & L. Gao (2003), SIAM J. Optim. 14:53-76; as used in +0051 % Haraldsdottir et al. (2017) Bioinformatics 33:1741 (CHRR). +0052 % +0053 % See also +0054 % -------- +0055 % sampleCHRR, sampleChebyshevCenter, randomSampling +0056 +0057 if nargin < 4 || isempty(maxiter); maxiter = 150; end +0058 if nargin < 5 || isempty(tol); tol = 1e-6; end +0059 if nargin < 6 || isempty(reg); reg = 1e-8; end +0060 +0061 [m, n] = size(A); +0062 b = b(:); +0063 % bnrm is the norm of the ORIGINAL b (before the row-normalisation below sets +0064 % b <- ones). This matches COBRA's chrrSampler reference; do not "correct" it to +0065 % sqrt(m), or convergence scaling diverges from the reference. +0066 bnrm = norm(b); +0067 minmu = 1e-8; +0068 tau0 = 0.75; +0069 +0070 if nargin < 3 || isempty(x0) +0071 x0 = sampleChebyshevCenter(A, b); +0072 end +0073 x0 = x0(:); +0074 +0075 bmAx0 = b - A*x0; +0076 if any(bmAx0 <= 0) +0077 error('RAVEN:sampling', 'sampleMaxVolEllipse: x0 is not strictly interior (A*x0 < b).'); +0078 end +0079 +0080 % Row-normalise so the constraint RHS is all ones; solve in shifted coords. +0081 A = A ./ bmAx0; +0082 b = ones(m, 1); +0083 +0084 x = zeros(n, 1); +0085 y = ones(m, 1); +0086 bmAx = b; +0087 z = zeros(m, 1); +0088 astep = 0; +0089 Adx = zeros(n, 1); +0090 converged = false; +0091 E2 = eye(n); +0092 +0093 for it = 1:maxiter +0094 if it > 1 +0095 bmAx = bmAx - astep*Adx; +0096 end +0097 +0098 AtYA = A' * (A .* y); % A' * diag(y) * A (n-by-n) +0099 E2 = inv(AtYA); %#ok<MINV> explicit inverse mirrors the reference +0100 Q = A * E2 * A'; % m-by-m +0101 h = sqrt(max(diag(Q), 0)); +0102 +0103 if it == 1 +0104 t = min(bmAx ./ h); +0105 y = y / t^2; +0106 h = t * h; +0107 z = max(1e-1, bmAx - h); +0108 Q = t^2 * Q; +0109 end +0110 +0111 yz = y .* z; +0112 yh = y .* h; +0113 gap = sum(yz) / m; +0114 rmu = max(min(0.5, gap)*gap, minmu); +0115 +0116 R1 = -A' * yh; % dual residual (n-by-1) +0117 R2 = bmAx - h - z; % primal/slack (m-by-1) +0118 R3 = rmu - yz; % complementarity (m-by-1) +0119 res = max([max(abs(R1)), max(abs(R2)), max(abs(R3))]); +0120 +0121 if res < tol*(1 + bnrm) && rmu <= minmu +0122 x = x + x0; +0123 converged = true; +0124 break; +0125 end +0126 +0127 YQ = y .* Q; % diag(y) * Q +0128 YQQY = YQ .* YQ'; % Hadamard product +0129 y2h = 2 * yh; +0130 G = YQQY + diag(max(reg, y2h .* z)); +0131 YA = y .* A; % diag(y) * A +0132 T = G \ ((h + z) .* YA); % m-by-n +0133 ATP = (y2h .* T - YA)'; % n-by-m +0134 R3Dy = R3 ./ y; +0135 R23 = R2 - R3Dy; +0136 ATP_A = ATP*A + reg*eye(n); +0137 dx = ATP_A \ (R1 + ATP*R23); +0138 Adx = A * dx; +0139 dyDy = G \ (y2h .* (Adx - R23)); +0140 dy = y .* dyDy; +0141 dz = R3Dy - z .* dyDy; +0142 +0143 ax = -1 / min([-Adx ./ bmAx; -0.5]); +0144 ay = -1 / min([ dyDy; -0.5]); +0145 az = -1 / min([ dz ./ z; -0.5]); +0146 tau = max(tau0, 1 - res); +0147 astep = tau * min([1, ax, ay, az]); +0148 +0149 x = x + astep*dx; +0150 y = y + astep*dy; +0151 z = z + astep*dz; +0152 end +0153 +0154 if ~converged +0155 x = x + x0; +0156 end +0157 +0158 E = nearestSPDchol(E2); +0159 end 0160 -0161 function E = nearestSPDchol(M) -0162 % Lower-triangular Cholesky factor of the nearest SPD matrix to M, so E*E' = M. -0163 M = (M + M') / 2; -0164 [E, p] = chol(M, 'lower'); -0165 if p == 0 -0166 return; -0167 end -0168 ev = eig(M); -0169 jitter = max(-min(ev), 0) + 1e-12; -0170 nn = size(M, 1); -0171 for k = 1:30 -0172 [E, p] = chol(M + jitter*eye(nn), 'lower'); -0173 if p == 0 -0174 return; -0175 end -0176 jitter = jitter * 10; -0177 end -0178 E = chol(M + jitter*eye(nn), 'lower'); -0179 end +0161 +0162 function E = nearestSPDchol(M) +0163 % Lower-triangular Cholesky factor of the nearest SPD matrix to M, so E*E' = M. +0164 M = (M + M') / 2; +0165 [E, p] = chol(M, 'lower'); +0166 if p == 0 +0167 return; +0168 end +0169 ev = eig(M); +0170 jitter = max(-min(ev), 0) + 1e-12; +0171 nn = size(M, 1); +0172 for k = 1:30 +0173 [E, p] = chol(M + jitter*eye(nn), 'lower'); +0174 if p == 0 +0175 return; +0176 end +0177 jitter = jitter * 10; +0178 end +0179 E = chol(M + jitter*eye(nn), 'lower'); +0180 end
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/analysis/sampleWarmupPoints.html b/docs/html/analysis/sampleWarmupPoints.html similarity index 54% rename from doc/analysis/sampleWarmupPoints.html rename to docs/html/analysis/sampleWarmupPoints.html index 5da0488e..ba6d7b99 100644 --- a/doc/analysis/sampleWarmupPoints.html +++ b/docs/html/analysis/sampleWarmupPoints.html @@ -31,18 +31,19 @@

    DESCRIPTION ^SOURCE CODE ^% 0004 % Generates warmup points for sampleACHR by maximising and minimising each 0005 % reaction in turn and storing the full flux distribution at each optimum. -0006 % These vertices span the flux polytope and seed the hit-and-run directions. -0007 % -0008 % Parameters -0009 % ---------- -0010 % model : struct -0011 % a RAVEN model structure. -0012 % -0013 % Name-Value Arguments -0014 % -------------------- -0015 % runParallel : logical -0016 % speed up calculations by parallel processing, as in getAllowedBounds, -0017 % which solves the same per-reaction min/max LPs (default true). -0018 % -0019 % Returns -0020 % ------- -0021 % warmup : double -0022 % nPoints-by-nRxns matrix; each row is a vertex flux distribution. Duplicate -0023 % rows are removed. -0024 % -0025 % See also -0026 % -------- -0027 % sampleACHR, randomSampling, getAllowedBounds -0028 -0029 p=parseRAVENargs(varargin, {'runParallel',true}); -0030 runParallel=p.runParallel; -0031 -0032 nRxns = numel(model.rxns); -0033 nW = parallelWorkersRAVEN(runParallel); -0034 fixedRxn = (model.ub - model.lb) < 1e-9; % fixed reaction — no warmup direction -0035 -0036 %Maximise and minimise each reaction in turn. Each direction is its own -0037 %fixed-size (rxns x rxns) buffer (NaN where a reaction is fixed or -0038 %infeasible) so both parfor loops write to statically-indexed rows, -0039 %mirroring getAllowedBounds' own min/max-per-reaction parfor. -0040 ptsMax = NaN(nRxns, nRxns); -0041 ptsMin = NaN(nRxns, nRxns); -0042 -0043 PB = progressReport(2*nRxns,'Running sampleWarmupPoints'); -0044 parfor (i = 1:nRxns, nW) -0045 if ~fixedRxn(i) -0046 tmp = model; -0047 tmp.c = zeros(nRxns, 1); -0048 tmp.c(i) = 1; % solveLP maximises c'*v -0049 sol = solveLP(tmp); -0050 if ~isempty(sol.x) && sol.stat > 0 -0051 ptsMax(i,:) = sol.x'; -0052 end -0053 end -0054 count(PB) -0055 end -0056 parfor (i = 1:nRxns, nW) -0057 if ~fixedRxn(i) -0058 tmp = model; -0059 tmp.c = zeros(nRxns, 1); -0060 tmp.c(i) = -1; -0061 sol = solveLP(tmp); -0062 if ~isempty(sol.x) && sol.stat > 0 -0063 ptsMin(i,:) = sol.x'; -0064 end -0065 end -0066 count(PB) -0067 end -0068 -0069 pts = [ptsMax; ptsMin]; -0070 warmup = pts(~any(isnan(pts),2), :); -0071 if isempty(warmup) -0072 error('RAVEN:sampling', ... -0073 'sampleWarmupPoints: could not generate any warmup points; check model feasibility.'); -0074 end -0075 warmup = unique(warmup, 'rows'); -0076 if size(warmup, 1) < 2 -0077 error('RAVEN:sampling', ... -0078 'sampleWarmupPoints: flux cone collapses to a single point; cannot sample.'); -0079 end -0080 end +0006 % These vertices span the flux polytope and define the hit-and-run +0007 % directions. +0008 % +0009 % Parameters +0010 % ---------- +0011 % model : struct +0012 % a model structure. +0013 % +0014 % Name-Value Arguments +0015 % -------------------- +0016 % runParallel : logical +0017 % run the calculations in parallel, as in getAllowedBounds, which solves +0018 % the same per-reaction minimisation and maximisation LPs (default true). +0019 % +0020 % Returns +0021 % ------- +0022 % warmup : double +0023 % nPoints-by-nRxns matrix; each row is a vertex flux distribution. Duplicate +0024 % rows are removed. +0025 % +0026 % See also +0027 % -------- +0028 % sampleACHR, randomSampling, getAllowedBounds +0029 +0030 p=parseRAVENargs(varargin, {'runParallel',true}); +0031 runParallel=p.runParallel; +0032 +0033 nRxns = numel(model.rxns); +0034 nW = parallelWorkersRAVEN(runParallel); +0035 fixedRxn = (model.ub - model.lb) < 1e-9; % fixed reaction — no warmup direction +0036 +0037 %Maximise and minimise each reaction in turn. Each direction is its own +0038 %fixed-size (rxns x rxns) buffer (NaN where a reaction is fixed or +0039 %infeasible) so both parfor loops write to statically-indexed rows, +0040 %mirroring getAllowedBounds' own min/max-per-reaction parfor. +0041 ptsMax = NaN(nRxns, nRxns); +0042 ptsMin = NaN(nRxns, nRxns); +0043 +0044 PB = progressReport(2*nRxns,'Running sampleWarmupPoints'); +0045 parfor (i = 1:nRxns, nW) +0046 if ~fixedRxn(i) +0047 tmp = model; +0048 tmp.c = zeros(nRxns, 1); +0049 tmp.c(i) = 1; % solveLP maximises c'*v +0050 sol = solveLP(tmp); +0051 if ~isempty(sol.x) && sol.stat > 0 +0052 ptsMax(i,:) = sol.x'; +0053 end +0054 end +0055 count(PB) +0056 end +0057 parfor (i = 1:nRxns, nW) +0058 if ~fixedRxn(i) +0059 tmp = model; +0060 tmp.c = zeros(nRxns, 1); +0061 tmp.c(i) = -1; +0062 sol = solveLP(tmp); +0063 if ~isempty(sol.x) && sol.stat > 0 +0064 ptsMin(i,:) = sol.x'; +0065 end +0066 end +0067 count(PB) +0068 end +0069 +0070 pts = [ptsMax; ptsMin]; +0071 warmup = pts(~any(isnan(pts),2), :); +0072 if isempty(warmup) +0073 error('RAVEN:sampling', ... +0074 'sampleWarmupPoints: could not generate any warmup points; check model feasibility.'); +0075 end +0076 warmup = unique(warmup, 'rows'); +0077 if size(warmup, 1) < 2 +0078 error('RAVEN:sampling', ... +0079 'sampleWarmupPoints: flux cone collapses to a single point; cannot sample.'); +0080 end +0081 end
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/analysis/traceFluxPath.html b/docs/html/analysis/traceFluxPath.html new file mode 100644 index 00000000..cc57b130 --- /dev/null +++ b/docs/html/analysis/traceFluxPath.html @@ -0,0 +1,519 @@ + + + + Description of traceFluxPath + + + + + + + + + +
    Home > analysis > traceFluxPath.m
    + + + +

    traceFluxPath +

    + +

    PURPOSE ^

    +
    traceFluxPath Find the highest-flux-fraction path between two reactions.
    + +

    SYNOPSIS ^

    +
    function [pathRxns, pathMets, cumFrac] = traceFluxPath(model, fluxes, fromRxn, toRxn, varargin)
    + +

    DESCRIPTION ^

    +
     traceFluxPath  Find the highest-flux-fraction path between two reactions.
    +
    + Traces how flux flows from one reaction to another through the metabolic
    + network, quantifying what fraction of the source reaction's flux reaches
    + the target. At each metabolite junction the flux is split proportionally
    + among all consuming reactions; the path with the highest cumulative
    + fraction is returned, and printed if verbose is true.
    +
    + By default the search excludes currency metabolites (for example ATP,
    + NAD, H2O), which makes the returned path follow material flow. The
    + setting traceMaterial=false disables this.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + fluxes : double
    +     flux vector (length = numel(model.rxns)).
    + fromRxn : char
    +     source reaction ID.
    + toRxn : char
    +     target reaction ID.
    +
    + Name-Value Arguments
    + --------------------
    + cutoff : double
    +     minimum net flux through a metabolite (|S_ij * v_j|) for a consuming
    +     reaction to be considered (default 1e-8).
    + maxHops : double
    +     paths of more than maxHops reactions are not extended further, so a
    +     returned path has at most maxHops + 1 reactions (default 30).
    + verbose : logical
    +     print the path to the command window (default true).
    + traceMaterial : logical
    +     exclude common currency and cofactor metabolites (ATP, ADP, NAD, NADH,
    +     H2O, CoA, Pi, CO2 and others); the path then follows material flux
    +     (default true).
    + carbonOnly : logical
    +     additionally require each intermediate metabolite to contain at least
    +     one carbon atom, inferred from model.metFormulas. Drops H+, H2O, Pi,
    +     O2, NH3, CO2 that are not already caught by the currency list. Ignored
    +     with a warning when model.metFormulas is absent or empty
    +     (default false).
    + excludeMets : cell of char
    +     extra metabolite names or IDs (after stripping compartment suffix) to
    +     exclude, in addition to the built-in currency list (which applies when
    +     traceMaterial is true) (default {}).
    +
    + Returns
    + -------
    + pathRxns : cell
    +     cell array of reaction IDs on the best path, including fromRxn and
    +     toRxn. Empty if no path is found.
    + pathMets : cell
    +     metabolite IDs at each junction (numel = numel(pathRxns)-1).
    + cumFrac : double
    +     fraction of fromRxn's flux that passes through this exact path
    +     (0 = no path found, 1 = all flux reaches toRxn directly).
    +
    + Examples
    + --------
    +     [p, m, f] = traceFluxPath(model, sol.x, 'PFK', 'ATPS4rpp')
    +     % disable material filter to see all connections including ATP and ADP:
    +     [p, m, f] = traceFluxPath(model, sol.x, 'PFK', 'ATPS4rpp', 'traceMaterial', false)
    +     % add model-specific cofactors to the exclusion list:
    +     [p, m, f] = traceFluxPath(model, sol.x, 'PFK', 'CS', 'excludeMets', {'acetyl-CoA'})
    +
    + Notes
    + -----
    + The algorithm follows metabolites in the forward direction only: it
    + traces the metabolites that fromRxn produces and finds which consuming
    + reactions eventually reach toRxn. If fromRxn and toRxn are not connected
    + in the forward flux direction (for example they are in parallel branches), an
    + empty path is returned. A best-first search, which expands the entry with
    + the highest cumulative fraction first, finds the highest-fraction path;
    + a path does not revisit a reaction.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [pathRxns, pathMets, cumFrac] = traceFluxPath(model, fluxes, fromRxn, toRxn, varargin)
    +0002 % traceFluxPath  Find the highest-flux-fraction path between two reactions.
    +0003 %
    +0004 % Traces how flux flows from one reaction to another through the metabolic
    +0005 % network, quantifying what fraction of the source reaction's flux reaches
    +0006 % the target. At each metabolite junction the flux is split proportionally
    +0007 % among all consuming reactions; the path with the highest cumulative
    +0008 % fraction is returned, and printed if verbose is true.
    +0009 %
    +0010 % By default the search excludes currency metabolites (for example ATP,
    +0011 % NAD, H2O), which makes the returned path follow material flow. The
    +0012 % setting traceMaterial=false disables this.
    +0013 %
    +0014 % Parameters
    +0015 % ----------
    +0016 % model : struct
    +0017 %     a model structure.
    +0018 % fluxes : double
    +0019 %     flux vector (length = numel(model.rxns)).
    +0020 % fromRxn : char
    +0021 %     source reaction ID.
    +0022 % toRxn : char
    +0023 %     target reaction ID.
    +0024 %
    +0025 % Name-Value Arguments
    +0026 % --------------------
    +0027 % cutoff : double
    +0028 %     minimum net flux through a metabolite (|S_ij * v_j|) for a consuming
    +0029 %     reaction to be considered (default 1e-8).
    +0030 % maxHops : double
    +0031 %     paths of more than maxHops reactions are not extended further, so a
    +0032 %     returned path has at most maxHops + 1 reactions (default 30).
    +0033 % verbose : logical
    +0034 %     print the path to the command window (default true).
    +0035 % traceMaterial : logical
    +0036 %     exclude common currency and cofactor metabolites (ATP, ADP, NAD, NADH,
    +0037 %     H2O, CoA, Pi, CO2 and others); the path then follows material flux
    +0038 %     (default true).
    +0039 % carbonOnly : logical
    +0040 %     additionally require each intermediate metabolite to contain at least
    +0041 %     one carbon atom, inferred from model.metFormulas. Drops H+, H2O, Pi,
    +0042 %     O2, NH3, CO2 that are not already caught by the currency list. Ignored
    +0043 %     with a warning when model.metFormulas is absent or empty
    +0044 %     (default false).
    +0045 % excludeMets : cell of char
    +0046 %     extra metabolite names or IDs (after stripping compartment suffix) to
    +0047 %     exclude, in addition to the built-in currency list (which applies when
    +0048 %     traceMaterial is true) (default {}).
    +0049 %
    +0050 % Returns
    +0051 % -------
    +0052 % pathRxns : cell
    +0053 %     cell array of reaction IDs on the best path, including fromRxn and
    +0054 %     toRxn. Empty if no path is found.
    +0055 % pathMets : cell
    +0056 %     metabolite IDs at each junction (numel = numel(pathRxns)-1).
    +0057 % cumFrac : double
    +0058 %     fraction of fromRxn's flux that passes through this exact path
    +0059 %     (0 = no path found, 1 = all flux reaches toRxn directly).
    +0060 %
    +0061 % Examples
    +0062 % --------
    +0063 %     [p, m, f] = traceFluxPath(model, sol.x, 'PFK', 'ATPS4rpp')
    +0064 %     % disable material filter to see all connections including ATP and ADP:
    +0065 %     [p, m, f] = traceFluxPath(model, sol.x, 'PFK', 'ATPS4rpp', 'traceMaterial', false)
    +0066 %     % add model-specific cofactors to the exclusion list:
    +0067 %     [p, m, f] = traceFluxPath(model, sol.x, 'PFK', 'CS', 'excludeMets', {'acetyl-CoA'})
    +0068 %
    +0069 % Notes
    +0070 % -----
    +0071 % The algorithm follows metabolites in the forward direction only: it
    +0072 % traces the metabolites that fromRxn produces and finds which consuming
    +0073 % reactions eventually reach toRxn. If fromRxn and toRxn are not connected
    +0074 % in the forward flux direction (for example they are in parallel branches), an
    +0075 % empty path is returned. A best-first search, which expands the entry with
    +0076 % the highest cumulative fraction first, finds the highest-fraction path;
    +0077 % a path does not revisit a reaction.
    +0078 
    +0079 p = parseRAVENargs(varargin, {'cutoff',1e-8; 'maxHops',30; 'verbose',true; ...
    +0080     'traceMaterial',true; 'carbonOnly',false; 'excludeMets',{}});
    +0081 cutoff        = p.cutoff;
    +0082 maxHops       = p.maxHops;
    +0083 verbose       = p.verbose;
    +0084 traceMaterial = p.traceMaterial;
    +0085 carbonOnly    = p.carbonOnly;
    +0086 excludeMets   = p.excludeMets;
    +0087 if ischar(excludeMets), excludeMets = {excludeMets}; end
    +0088 
    +0089 % ---- Build exclusion set ----
    +0090 if traceMaterial
    +0091     currencyList = [defaultCurrencyMets_(), excludeMets(:)'];
    +0092 else
    +0093     currencyList = excludeMets(:)';
    +0094 end
    +0095 
    +0096 % Pre-compute carbon counts per metabolite (for carbonOnly filter)
    +0097 hasCarbonFilter = false;
    +0098 carbonCounts    = [];
    +0099 if carbonOnly
    +0100     if isfield(model,'metFormulas') && ~isempty(model.metFormulas)
    +0101         carbonCounts    = cellfun(@countCarbon_, model.metFormulas);
    +0102         hasCarbonFilter = true;
    +0103     else
    +0104         warning('traceFluxPath:noFormulas', ...
    +0105             'carbonOnly requires model.metFormulas; the option is ignored.');
    +0106     end
    +0107 end
    +0108 
    +0109 % ---- Resolve reactions ----
    +0110 pathRxns = {};
    +0111 pathMets = {};
    +0112 cumFrac  = 0;
    +0113 
    +0114 fromIdx = resolveRxn_(model, fromRxn);
    +0115 toIdx   = resolveRxn_(model, toRxn);
    +0116 
    +0117 if fromIdx == toIdx
    +0118     pathRxns = model.rxns(fromIdx);
    +0119     pathMets = {};
    +0120     cumFrac  = 1;
    +0121     if verbose, printPath_(model, fluxes, pathRxns, pathMets, cumFrac, ...
    +0122             fromRxn, toRxn, traceMaterial || ~isempty(excludeMets) || carbonOnly); end
    +0123     return
    +0124 end
    +0125 
    +0126 nRxns = numel(model.rxns);
    +0127 
    +0128 % ---- Best-first search (highest cumulative fraction first) ----
    +0129 % Queue entries are structs: .rxn (idx), .frac (cumFrac), .rpath, .mpath
    +0130 bestAtNode = zeros(nRxns,1);
    +0131 bestAtNode(fromIdx) = 1.0;
    +0132 
    +0133 queue = makeEntry_(fromIdx, 1.0, fromIdx, zeros(0,1));
    +0134 
    +0135 bestFrac  = 0;
    +0136 bestRPath = [];
    +0137 bestMPath = [];
    +0138 
    +0139 while ~isempty(queue)
    +0140     % Pop highest-fraction entry
    +0141     entry = queue(1);
    +0142     queue(1) = [];
    +0143 
    +0144     cur   = entry.rxn;
    +0145     frac  = entry.frac;
    +0146     rpath = entry.rpath;
    +0147     mpath = entry.mpath;
    +0148 
    +0149     if cur == toIdx
    +0150         if frac > bestFrac
    +0151             bestFrac  = frac;
    +0152             bestRPath = rpath;
    +0153             bestMPath = mpath;
    +0154         end
    +0155         continue
    +0156     end
    +0157 
    +0158     if numel(rpath) > maxHops, continue; end
    +0159 
    +0160     % Find metabolites net-produced by cur. Iterate only the structurally
    +0161     % nonzero entries of the (sparse) column instead of every metabolite
    +0162     % in the model, most of which a reaction never touches.
    +0163     col  = model.S(:, cur);
    +0164     fcur = fluxes(cur);
    +0165     for m = find(col)'
    +0166         if col(m) * fcur <= 0, continue; end   % skip consumed or zero-contribution mets
    +0167 
    +0168         % ---- Material-flux filters ----
    +0169         if ~isempty(currencyList) && isCurrencyMet_(model, m, currencyList)
    +0170             continue
    +0171         end
    +0172         if hasCarbonFilter && carbonCounts(m) == 0
    +0173             continue
    +0174         end
    +0175 
    +0176         % Reactions that net-consume this metabolite
    +0177         row  = full(model.S(m, :));
    +0178         fVec = fluxes(:)';
    +0179         consumers = find(row .* fVec < -cutoff);
    +0180         consumers = consumers(~ismember(consumers, rpath));  % no revisiting
    +0181         if isempty(consumers), continue; end
    +0182 
    +0183         totalCons = sum(abs(row(consumers) .* fVec(consumers)));
    +0184 
    +0185         for ci = 1:numel(consumers)
    +0186             nr      = consumers(ci);
    +0187             frac_r  = abs(row(nr) * fluxes(nr)) / totalCons;
    +0188             newFrac = frac * frac_r;
    +0189 
    +0190             if newFrac > bestAtNode(nr) || nr == toIdx
    +0191                 bestAtNode(nr) = max(bestAtNode(nr), newFrac);
    +0192                 newEntry = makeEntry_(nr, newFrac, [rpath, nr], [mpath; m]);
    +0193 
    +0194                 % Insert in decreasing-fraction order
    +0195                 if isempty(queue)
    +0196                     queue = newEntry;
    +0197                 else
    +0198                     fracs = [queue.frac];
    +0199                     pos   = find(fracs < newFrac, 1);
    +0200                     if isempty(pos)
    +0201                         queue(end+1) = newEntry; %#ok<AGROW>
    +0202                     else
    +0203                         queue = [queue(1:pos-1), newEntry, queue(pos:end)];
    +0204                     end
    +0205                 end
    +0206             end
    +0207         end
    +0208     end
    +0209 end
    +0210 
    +0211 % ---- Package and display ----
    +0212 cumFrac = bestFrac;
    +0213 if ~isempty(bestRPath)
    +0214     pathRxns = model.rxns(bestRPath);
    +0215     pathMets = model.mets(bestMPath);
    +0216 end
    +0217 
    +0218 if verbose
    +0219     materialMode = traceMaterial || ~isempty(excludeMets) || carbonOnly;
    +0220     printPath_(model, fluxes, pathRxns, pathMets, cumFrac, fromRxn, toRxn, materialMode);
    +0221 end
    +0222 end
    +0223 
    +0224 %--------------------------------------------------------------------------
    +0225 function e = makeEntry_(rxn, frac, rpath, mpath)
    +0226     e.rxn   = rxn;
    +0227     e.frac  = frac;
    +0228     e.rpath = rpath;
    +0229     e.mpath = mpath;
    +0230 end
    +0231 
    +0232 function printPath_(model, fluxes, pathRxns, pathMets, cumFrac, fromRxn, toRxn, materialMode)
    +0233     W   = 66;
    +0234     bar = repmat('=', 1, W);
    +0235     fprintf('\n%s\n', bar);
    +0236     fprintf('  Flux path: %s \x2192 %s\n', fromRxn, toRxn);
    +0237     if materialMode
    +0238         fprintf('  (material flux — currency metabolites excluded)\n');
    +0239     end
    +0240     fprintf('%s\n', bar);
    +0241 
    +0242     if isempty(pathRxns)
    +0243         fprintf('  No forward flux path found between these reactions.\n');
    +0244         if materialMode
    +0245             fprintf('  Tip: try traceMaterial=false to include cofactor connections,\n');
    +0246             fprintf('       or excludeMets={} to see what the filter removed.\n');
    +0247         else
    +0248             fprintf('  (They may be in parallel branches or connected only in reverse.)\n');
    +0249         end
    +0250         fprintf('%s\n\n', bar);
    +0251         return
    +0252     end
    +0253 
    +0254     % One-line path with junction fractions
    +0255     parts = pathRxns{1};
    +0256     for i = 2:numel(pathRxns)
    +0257         mi = find(strcmp(model.mets, pathMets{i-1}));
    +0258         row = full(model.S(mi, :));
    +0259         consumers = find(row .* fluxes' < -1e-12);
    +0260         if isempty(consumers)
    +0261             pct = NaN;
    +0262         else
    +0263             totalCons = sum(abs(row(consumers) .* fluxes(consumers)'));
    +0264             ni = find(strcmp(model.rxns, pathRxns{i}));
    +0265             pct = abs(row(ni) * fluxes(ni)) / totalCons * 100;
    +0266         end
    +0267         mLbl = getMetLabel_(model, mi);
    +0268         if isnan(pct)
    +0269             parts = [parts sprintf(' \x2192[%s]\x2192 %s', mLbl, pathRxns{i})]; %#ok<AGROW>
    +0270         else
    +0271             parts = [parts sprintf(' \x2192[%s: %.0f%%]\x2192 %s', mLbl, pct, pathRxns{i})]; %#ok<AGROW>
    +0272         end
    +0273     end
    +0274     fprintf('\n  %s\n\n', parts);
    +0275 
    +0276     fprintf('  Cumulative fraction: %.2f%%\n', cumFrac*100);
    +0277     fprintf('  Path length:         %d reactions\n', numel(pathRxns));
    +0278 
    +0279     fprintf('\n  Reactions:\n');
    +0280     eqns = constructEquations(model, pathRxns);
    +0281     for i = 1:numel(pathRxns)
    +0282         ri  = find(strcmp(model.rxns, pathRxns{i}));
    +0283         nm  = '';
    +0284         if isfield(model,'rxnNames') && ri <= numel(model.rxnNames)
    +0285             nm = char(model.rxnNames{ri});
    +0286         end
    +0287         if numel(nm) > 25, nm = [nm(1:22) '...']; end
    +0288         fprintf('  %2d. %-14s  flux: %+10.4g   %-25s  %s\n', i, pathRxns{i}, fluxes(ri), nm, eqns{i});
    +0289     end
    +0290     fprintf('\n%s\n\n', bar);
    +0291 end
    +0292 
    +0293 function idx = resolveRxn_(model, rxn)
    +0294     if isnumeric(rxn)
    +0295         idx = rxn;
    +0296     else
    +0297         idx = find(strcmp(model.rxns, char(rxn)));
    +0298         if isempty(idx)
    +0299             error('Reaction ''%s'' not found in model.', rxn);
    +0300         end
    +0301         idx = idx(1);
    +0302     end
    +0303 end
    +0304 
    +0305 function s = getMetLabel_(model, metIdx)
    +0306     if isfield(model,'metNames') && ~isempty(model.metNames{metIdx})
    +0307         s = model.metNames{metIdx};
    +0308     else
    +0309         s = model.mets{metIdx};
    +0310     end
    +0311 end
    +0312 
    +0313 function tf = isCurrencyMet_(model, m, currencyList)
    +0314 % Returns true if metabolite m matches any entry in currencyList.
    +0315 % Matches against metNames (case-insensitive exact) and against the
    +0316 % metabolite ID after stripping compartment suffixes like [c], [m], _c.
    +0317     targets = {};
    +0318     if isfield(model,'metNames') && m <= numel(model.metNames) && ~isempty(model.metNames{m})
    +0319         targets{end+1} = lower(strtrim(model.metNames{m}));
    +0320     end
    +0321     % Strip compartment from ID: [c], (c), _c, _C at end
    +0322     rawId = model.mets{m};
    +0323     stripped = regexprep(rawId, '[\[\(][^\]\)]*[\]\)]$|_[a-zA-Z]\d*$', '');
    +0324     targets{end+1} = lower(stripped);
    +0325 
    +0326     for i = 1:numel(currencyList)
    +0327         p = lower(strtrim(currencyList{i}));
    +0328         for j = 1:numel(targets)
    +0329             if strcmp(targets{j}, p)
    +0330                 tf = true;
    +0331                 return
    +0332             end
    +0333         end
    +0334     end
    +0335     tf = false;
    +0336 end
    +0337 
    +0338 function nC = countCarbon_(formula)
    +0339 % Count carbon atoms in a molecular formula string like 'C6H12O6'.
    +0340     if isempty(formula)
    +0341         nC = 0; return
    +0342     end
    +0343     tok = regexp(char(formula), 'C(\d*)', 'tokens', 'once');
    +0344     if isempty(tok)
    +0345         nC = 0;
    +0346     elseif isempty(tok{1})
    +0347         nC = 1;
    +0348     else
    +0349         nC = str2double(tok{1});
    +0350     end
    +0351 end
    +0352 
    +0353 function lst = defaultCurrencyMets_()
    +0354 % Common currency / cofactor metabolites matched case-insensitively.
    +0355 % Bare comment lines inside {} act as row separators in older MATLAB, so
    +0356 % all comments are placed after ... on data lines instead.
    +0357     lst = { ...
    +0358         'ATP','ADP','AMP','dATP','dADP','dAMP', ... % adenosine phosphates
    +0359         'atp','adp','amp','datp','dadp','damp', ...
    +0360         'GTP','GDP','GMP','dGTP','dGDP','dGMP', ... % other NTPs/NDPs/NMPs
    +0361         'CTP','CDP','CMP','dCTP','dCDP','dCMP', ...
    +0362         'UTP','UDP','UMP','dUTP','dUDP','dUMP', ...
    +0363         'TTP','TDP','TMP','dTTP','dTDP','dTMP', ...
    +0364         'gtp','gdp','gmp','dgtp','dgdp','dgmp', ...
    +0365         'ctp','cdp','cmp','dctp','dcdp','dcmp', ...
    +0366         'utp','udp','ump','dutp','dudp','dump', ...
    +0367         'ttp','tdp','tmp','dttp','dtdp','dtmp', ...
    +0368         'NAD','NADH','NADP','NADPH','NAD+','NADP+', ... % nicotinamide cofactors
    +0369         'nad','nadh','nadp','nadph', ...
    +0370         'FAD','FADH2','FMN','FMNH2', ... % flavin cofactors
    +0371         'fad','fadh2','fmn','fmnh2', ...
    +0372         'CoA','coenzyme A','coenzyme a','Coenzyme A', ... % coenzyme A
    +0373         'coa', ...
    +0374         'H2O','water','H+','proton','OH-','hydroxide', ... % water / proton
    +0375         'h2o','h','oh1', ...
    +0376         'phosphate','orthophosphate','pyrophosphate','diphosphate', ... % inorganic phosphate
    +0377         'pi','ppi','pp', ...
    +0378         'CO2','carbon dioxide','bicarbonate','HCO3-', ... % carbon dioxide
    +0379         'co2','hco3', ...
    +0380         'O2','oxygen','NH3','ammonia','NH4+','ammonium','nitrogen', ... % oxygen / nitrogen
    +0381         'o2','nh3','nh4', ...
    +0382         'sulfate','sulfite','sulfide','thiosulfate', ... % sulfur species
    +0383         'thioredoxin','thioredoxin-SH','thioredoxin-S2', ... % redox carriers
    +0384         'trdrd','trdox', ...
    +0385         'ferredoxin','ferredoxin reduced','ferredoxin oxidized', ...
    +0386         'fdred','fdox', ...
    +0387         'ubiquinol','ubiquinone','menaquinol','menaquinone', ...
    +0388         'q8h2','q8','mqn8','mql8', ...
    +0389         'lipoamide','dihydrolipoamide', ...
    +0390         'S-adenosyl-L-methionine','S-adenosylhomocysteine', ... % one-carbon donors
    +0391         'sam','sah','amet','ahcys', ...
    +0392         '5-methyltetrahydrofolate','tetrahydrofolate','dihydrofolate', ...
    +0393         'thf','dhf','mlthf','methf' ...
    +0394     };
    +0395 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/analysis/walkFluxes.html b/docs/html/analysis/walkFluxes.html new file mode 100644 index 00000000..7d0214e6 --- /dev/null +++ b/docs/html/analysis/walkFluxes.html @@ -0,0 +1,290 @@ + + + + Description of walkFluxes + + + + + + + + + +
    Home > analysis > walkFluxes.m
    + + + +

    walkFluxes +

    + +

    PURPOSE ^

    +
    walkFluxes Interactively navigate a flux distribution reaction by reaction.
    + +

    SYNOPSIS ^

    +
    function walkFluxes(model, fluxes, startRxn, varargin)
    + +

    DESCRIPTION ^

    +
     walkFluxes  Interactively navigate a flux distribution reaction by reaction.
    +
    + Starting from a chosen reaction, displays the flux-carrying reactions
    + connected through shared metabolites, grouped by metabolite and labelled
    + with each neighbour's role (produces or consumes). Entering the number of
    + a neighbour steps to it. The visited reactions are kept in a history, and
    + 'b' steps back through it.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + fluxes : double
    +     flux vector (length = numel(model.rxns)).
    + startRxn : char or double
    +     starting reaction ID (string) or reaction index.
    +
    + Name-Value Arguments
    + --------------------
    + cutoff : double
    +     minimum net flux through a metabolite (|S_ij * v_j|) for the metabolite
    +     to be listed for the current reaction and for a reaction to be
    +     displayed as a neighbour (default 1e-8).
    + maxPerMet : double
    +     maximum neighbour reactions displayed per metabolite, selected by
    +     decreasing absolute flux (default 8).
    +
    + Examples
    + --------
    +     sol = solveLP(model);
    +     walkFluxes(model, sol.x, 'BIOMASS_Ec_iJO1366_core_53p95M')
    +     walkFluxes(model, sol.x, 'PFK', 'cutoff', 1e-6)
    +
    + Notes
    + -----
    + Navigation commands:
    +   <number>  : step to that neighbour reaction
    +   b         : go back to the previous reaction
    +   q         : quit
    +
    + This function is interactive and reads from the terminal at every step.
    + It raises an error under -batch and in deployed code.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function walkFluxes(model, fluxes, startRxn, varargin)
    +0002 % walkFluxes  Interactively navigate a flux distribution reaction by reaction.
    +0003 %
    +0004 % Starting from a chosen reaction, displays the flux-carrying reactions
    +0005 % connected through shared metabolites, grouped by metabolite and labelled
    +0006 % with each neighbour's role (produces or consumes). Entering the number of
    +0007 % a neighbour steps to it. The visited reactions are kept in a history, and
    +0008 % 'b' steps back through it.
    +0009 %
    +0010 % Parameters
    +0011 % ----------
    +0012 % model : struct
    +0013 %     a model structure.
    +0014 % fluxes : double
    +0015 %     flux vector (length = numel(model.rxns)).
    +0016 % startRxn : char or double
    +0017 %     starting reaction ID (string) or reaction index.
    +0018 %
    +0019 % Name-Value Arguments
    +0020 % --------------------
    +0021 % cutoff : double
    +0022 %     minimum net flux through a metabolite (|S_ij * v_j|) for the metabolite
    +0023 %     to be listed for the current reaction and for a reaction to be
    +0024 %     displayed as a neighbour (default 1e-8).
    +0025 % maxPerMet : double
    +0026 %     maximum neighbour reactions displayed per metabolite, selected by
    +0027 %     decreasing absolute flux (default 8).
    +0028 %
    +0029 % Examples
    +0030 % --------
    +0031 %     sol = solveLP(model);
    +0032 %     walkFluxes(model, sol.x, 'BIOMASS_Ec_iJO1366_core_53p95M')
    +0033 %     walkFluxes(model, sol.x, 'PFK', 'cutoff', 1e-6)
    +0034 %
    +0035 % Notes
    +0036 % -----
    +0037 % Navigation commands:
    +0038 %   <number>  : step to that neighbour reaction
    +0039 %   b         : go back to the previous reaction
    +0040 %   q         : quit
    +0041 %
    +0042 % This function is interactive and reads from the terminal at every step.
    +0043 % It raises an error under -batch and in deployed code.
    +0044 
    +0045 isBatch = false;
    +0046 try
    +0047     isBatch = batchStartupOptionUsed;
    +0048 catch
    +0049 end
    +0050 if isBatch || isdeployed
    +0051     error('walkFluxes:nonInteractive', ...
    +0052         ['walkFluxes is interactive and reads from the terminal at every step, ' ...
    +0053          'so it cannot run under -batch or in deployed code. Run it in an ' ...
    +0054          'interactive MATLAB session instead.']);
    +0055 end
    +0056 
    +0057 p = parseRAVENargs(varargin, {'cutoff',1e-8; 'maxPerMet',8});
    +0058 cutoff    = p.cutoff;
    +0059 maxPerMet = p.maxPerMet;
    +0060 
    +0061 rxnIdx  = resolveRxn_(model, startRxn);
    +0062 history = zeros(0,1);
    +0063 W       = 68;
    +0064 
    +0065 while true
    +0066     % ---- Header ----
    +0067     fprintf('\n%s\n', repmat('=', 1, W));
    +0068     f   = fluxes(rxnIdx);
    +0069     rID = model.rxns{rxnIdx};
    +0070     rName = getRxnName_(model, rxnIdx);
    +0071     if isempty(rName)
    +0072         fprintf('  [%s]  flux: %+.6g\n', rID, f);
    +0073     else
    +0074         fprintf('  [%s]  %s\n  flux: %+.6g\n', rID, rName, f);
    +0075     end
    +0076     eqn = constructEquations(model, model.rxns(rxnIdx));
    +0077     fprintf('  %s\n', eqn{1});
    +0078     if isfield(model,'grRules') && rxnIdx <= numel(model.grRules) ...
    +0079             && ~isempty(model.grRules{rxnIdx})
    +0080         fprintf('  genes: %s\n', model.grRules{rxnIdx});
    +0081     end
    +0082     fprintf('%s\n', repmat('=', 1, W));
    +0083 
    +0084     % ---- Collect neighbours grouped by metabolite ----
    +0085     col         = full(model.S(:, rxnIdx));
    +0086     nonzeroMets = find(abs(col) > 0);
    +0087 
    +0088     % One number per unique reaction across all metabolite groups
    +0089     seenRxns   = containers.Map('KeyType','double','ValueType','double');
    +0090     allNeighbors = [];
    +0091 
    +0092     for mi = 1:numel(nonzeroMets)
    +0093         m    = nonzeroMets(mi);
    +0094         netF = col(m) * f;   % >0: current rxn produces m; <0: consumes m
    +0095 
    +0096         if abs(netF) < cutoff, continue; end
    +0097 
    +0098         if netF < 0
    +0099             role = 'consumed';
    +0100         else
    +0101             role = 'produced';
    +0102         end
    +0103 
    +0104         % Other flux-carrying reactions involving this metabolite
    +0105         row   = full(model.S(m, :));
    +0106         carry = find(abs(row .* fluxes') > cutoff);
    +0107         carry = carry(carry ~= rxnIdx);
    +0108         if isempty(carry), continue; end
    +0109 
    +0110         [~, si] = sort(abs(fluxes(carry)), 'descend');
    +0111         carry   = carry(si(1:min(end, maxPerMet)));
    +0112 
    +0113         mLbl = getMetLabel_(model, m);
    +0114         fprintf('\n  %s  [%s, %.4g]\n', mLbl, role, abs(netF));
    +0115 
    +0116         for ci = 1:numel(carry)
    +0117             nr         = carry(ci);
    +0118             neighNetF  = row(nr) * fluxes(nr);  % >0: nr produces m; <0: consumes m
    +0119             neighRole  = 'produces';
    +0120             if neighNetF < 0, neighRole = 'consumes'; end
    +0121 
    +0122             nrName = getRxnName_(model, nr);
    +0123             if numel(nrName) > 28, nrName = [nrName(1:25) '...']; end
    +0124 
    +0125             if isKey(seenRxns, nr)
    +0126                 n = seenRxns(nr);   % re-use existing number; don't re-add
    +0127             else
    +0128                 n = numel(allNeighbors) + 1;
    +0129                 seenRxns(nr)     = n;
    +0130                 allNeighbors(end+1) = nr; %#ok<AGROW>
    +0131             end
    +0132 
    +0133             fprintf('  %3d. %-14s  %+9.4g  %-8s  %s\n', ...
    +0134                 n, model.rxns{nr}, fluxes(nr), neighRole, nrName);
    +0135         end
    +0136     end
    +0137 
    +0138     fprintf('\n');
    +0139     nN = numel(allNeighbors);
    +0140 
    +0141     if nN == 0
    +0142         fprintf('  (no flux-carrying neighbours at this cutoff)\n');
    +0143         fprintf('  b: go back   q: quit\n');
    +0144         resp = strtrim(input('  > ', 's'));
    +0145     else
    +0146         fprintf('  1-%d: step to reaction   b: go back   q: quit\n', nN);
    +0147         resp = strtrim(input('  > ', 's'));
    +0148     end
    +0149 
    +0150     if strcmpi(resp, 'q')
    +0151         fprintf('Navigator closed.\n');
    +0152         return
    +0153 
    +0154     elseif strcmpi(resp, 'b')
    +0155         if isempty(history)
    +0156             fprintf('  (no history)\n');
    +0157         else
    +0158             rxnIdx     = history(end);
    +0159             history(end) = [];
    +0160         end
    +0161 
    +0162     else
    +0163         n = str2double(resp);
    +0164         if ~isnan(n) && n >= 1 && n <= nN && n == round(n)
    +0165             history(end+1) = rxnIdx; %#ok<AGROW>
    +0166             rxnIdx         = allNeighbors(n);
    +0167         else
    +0168             fprintf('  Enter a number 1-%d, ''b'', or ''q''.\n', max(nN,1));
    +0169         end
    +0170     end
    +0171 end
    +0172 end
    +0173 
    +0174 %--------------------------------------------------------------------------
    +0175 function idx = resolveRxn_(model, rxn)
    +0176     if isnumeric(rxn)
    +0177         idx = rxn;
    +0178     else
    +0179         idx = find(strcmp(model.rxns, char(rxn)));
    +0180         if isempty(idx)
    +0181             error('Reaction ''%s'' not found in model.', rxn);
    +0182         end
    +0183         idx = idx(1);
    +0184     end
    +0185 end
    +0186 
    +0187 function s = getRxnName_(model, rxnIdx)
    +0188     s = '';
    +0189     if isfield(model,'rxnNames') && rxnIdx <= numel(model.rxnNames)
    +0190         s = char(model.rxnNames{rxnIdx});
    +0191     end
    +0192 end
    +0193 
    +0194 function s = getMetLabel_(model, metIdx)
    +0195     if isfield(model,'metNames') && ~isempty(model.metNames{metIdx})
    +0196         s = model.metNames{metIdx};
    +0197     else
    +0198         s = model.mets{metIdx};
    +0199     end
    +0200 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/annotation/assignSBOterms.html b/docs/html/annotation/assignSBOterms.html new file mode 100644 index 00000000..0f81feac --- /dev/null +++ b/docs/html/annotation/assignSBOterms.html @@ -0,0 +1,293 @@ + + + + Description of assignSBOterms + + + + + + + + + +
    Home > annotation > assignSBOterms.m
    + + + +

    assignSBOterms +

    + +

    PURPOSE ^

    +
    assignSBOterms Assign SBO terms to metabolites and reactions.
    + +

    SYNOPSIS ^

    +
    function model = assignSBOterms(model, varargin)
    + +

    DESCRIPTION ^

    +
     assignSBOterms  Assign SBO terms to metabolites and reactions.
    +
    + Assigns SBO terms to metabolites and reactions following a generic rule
    + set that opts parameterises. The function is the MATLAB counterpart of
    + yeast-GEM's addSBOterms.
    +
    + SBO is written via editMiriam(..., 'fill'), which preserves pre-existing
    + SBO annotations. The terms are stored in model.metMiriams and
    + model.rxnMiriams under the namespace 'sbo' (there is no separate SBO
    + field); they can be read with extractMiriam(model.rxnMiriams, 'sbo').
    + exportModel writes them as the SBML sboTerm attribute.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + opts : struct
    +     structure with any of the following fields. Missing or empty fields
    +     take the defaults shown:
    +
    +     - biomassMetNames : {'biomass','DNA','RNA','protein','carbohydrate',
    +       'lipid','cofactor','ion'}
    +     - biomassMetSuffixes : {' backbone',' chain'}
    +     - biomassRxnName : 'biomass pseudoreaction'
    +     - ngamRxnName : 'non-growth associated maintenance reaction'
    +     - pseudoreactionSubstrings : {'pseudoreaction','SLIME rxn'}
    +     - onlyLastReactionForPseudo : false. If true, the biomass, NGAM and
    +       pseudoreaction terms are applied only to the last reaction of the
    +       model. This reproduces yeast-GEM's addSBOterms.m, which applies the
    +       pseudoreaction rule to the last reaction only, because of a typo in
    +       the loop header (for i=numel(...)).
    +     - transportRxnIdxs : indexes of the transport reactions (default:
    +       the reactions identified by getTransportRxns).
    +
    + Returns
    + -------
    + model : struct
    +     model structure with the SBO terms added to model.metMiriams and
    +     model.rxnMiriams.
    +
    + Examples
    + --------
    +     model = assignSBOterms(model);
    +     model = assignSBOterms(model, struct('onlyLastReactionForPseudo', true));
    +
    + Notes
    + -----
    + Metabolites:
    +
    +     SBO:0000649 (Biomass) when the metabolite name is in
    +     opts.biomassMetNames, or ends with any of opts.biomassMetSuffixes.
    +     Otherwise SBO:0000247 (Simple chemical).
    +
    + Reactions (default → override → pseudoreaction override):
    +
    +     SBO:0000176 (Metabolic reaction) default.
    +     Single-reactant reactions become:
    +         SBO:0000627 (exchange) if the lone metabolite is extracellular
    +             (compartment 'e' or compartment name 'extracellular'),
    +         SBO:0000632 (sink) if coef < 0,
    +         SBO:0000628 (demand) otherwise.
    +     Transport reactions (opts.transportRxnIdxs or, by default, the
    +         reactions identified by getTransportRxns: the same metabolite
    +         name more than once in a single reaction) → SBO:0000655.
    +     Reactions whose name matches opts.biomassRxnName → SBO:0000629.
    +     Reactions whose name matches opts.ngamRxnName → SBO:0000630.
    +     Reactions whose name contains any of opts.pseudoreactionSubstrings
    +         → SBO:0000395.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • editMiriam editMiriam Change MIRIAM annotation fields, one type at a time.
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function model = assignSBOterms(model, varargin)
    +0002 % assignSBOterms  Assign SBO terms to metabolites and reactions.
    +0003 %
    +0004 % Assigns SBO terms to metabolites and reactions following a generic rule
    +0005 % set that opts parameterises. The function is the MATLAB counterpart of
    +0006 % yeast-GEM's addSBOterms.
    +0007 %
    +0008 % SBO is written via editMiriam(..., 'fill'), which preserves pre-existing
    +0009 % SBO annotations. The terms are stored in model.metMiriams and
    +0010 % model.rxnMiriams under the namespace 'sbo' (there is no separate SBO
    +0011 % field); they can be read with extractMiriam(model.rxnMiriams, 'sbo').
    +0012 % exportModel writes them as the SBML sboTerm attribute.
    +0013 %
    +0014 % Parameters
    +0015 % ----------
    +0016 % model : struct
    +0017 %     a model structure.
    +0018 %
    +0019 % Name-Value Arguments
    +0020 % --------------------
    +0021 % opts : struct
    +0022 %     structure with any of the following fields. Missing or empty fields
    +0023 %     take the defaults shown:
    +0024 %
    +0025 %     - biomassMetNames : {'biomass','DNA','RNA','protein','carbohydrate',
    +0026 %       'lipid','cofactor','ion'}
    +0027 %     - biomassMetSuffixes : {' backbone',' chain'}
    +0028 %     - biomassRxnName : 'biomass pseudoreaction'
    +0029 %     - ngamRxnName : 'non-growth associated maintenance reaction'
    +0030 %     - pseudoreactionSubstrings : {'pseudoreaction','SLIME rxn'}
    +0031 %     - onlyLastReactionForPseudo : false. If true, the biomass, NGAM and
    +0032 %       pseudoreaction terms are applied only to the last reaction of the
    +0033 %       model. This reproduces yeast-GEM's addSBOterms.m, which applies the
    +0034 %       pseudoreaction rule to the last reaction only, because of a typo in
    +0035 %       the loop header (for i=numel(...)).
    +0036 %     - transportRxnIdxs : indexes of the transport reactions (default:
    +0037 %       the reactions identified by getTransportRxns).
    +0038 %
    +0039 % Returns
    +0040 % -------
    +0041 % model : struct
    +0042 %     model structure with the SBO terms added to model.metMiriams and
    +0043 %     model.rxnMiriams.
    +0044 %
    +0045 % Examples
    +0046 % --------
    +0047 %     model = assignSBOterms(model);
    +0048 %     model = assignSBOterms(model, struct('onlyLastReactionForPseudo', true));
    +0049 %
    +0050 % Notes
    +0051 % -----
    +0052 % Metabolites:
    +0053 %
    +0054 %     SBO:0000649 (Biomass) when the metabolite name is in
    +0055 %     opts.biomassMetNames, or ends with any of opts.biomassMetSuffixes.
    +0056 %     Otherwise SBO:0000247 (Simple chemical).
    +0057 %
    +0058 % Reactions (default → override → pseudoreaction override):
    +0059 %
    +0060 %     SBO:0000176 (Metabolic reaction) default.
    +0061 %     Single-reactant reactions become:
    +0062 %         SBO:0000627 (exchange) if the lone metabolite is extracellular
    +0063 %             (compartment 'e' or compartment name 'extracellular'),
    +0064 %         SBO:0000632 (sink) if coef < 0,
    +0065 %         SBO:0000628 (demand) otherwise.
    +0066 %     Transport reactions (opts.transportRxnIdxs or, by default, the
    +0067 %         reactions identified by getTransportRxns: the same metabolite
    +0068 %         name more than once in a single reaction) → SBO:0000655.
    +0069 %     Reactions whose name matches opts.biomassRxnName → SBO:0000629.
    +0070 %     Reactions whose name matches opts.ngamRxnName → SBO:0000630.
    +0071 %     Reactions whose name contains any of opts.pseudoreactionSubstrings
    +0072 %         → SBO:0000395.
    +0073 
    +0074 p=parseRAVENargs(varargin, {'opts',[]});
    +0075 opts=p.opts;
    +0076 if isempty(opts)
    +0077     opts = struct();
    +0078 end
    +0079 opts = applyDefaults(opts);
    +0080 
    +0081 % Metabolite SBO ------------------------------------------------------
    +0082 metsSBO = cell(size(model.mets));
    +0083 for i = 1:length(model.mets)
    +0084     metName = model.metNames{i};
    +0085     if any(strcmp(opts.biomassMetNames, metName)) || endsWithAny(metName, opts.biomassMetSuffixes)
    +0086         metsSBO{i} = 'SBO:0000649';
    +0087     else
    +0088         metsSBO{i} = 'SBO:0000247';
    +0089     end
    +0090 end
    +0091 
    +0092 % Reaction SBO --------------------------------------------------------
    +0093 rxnSBO = cell(size(model.rxns));
    +0094 rxnSBO(:) = {'SBO:0000176'};
    +0095 
    +0096 % Single-reactant reactions
    +0097 reactantNumber = sum(model.S ~= 0, 1);
    +0098 singleRxns = find(reactantNumber == 1);
    +0099 for k = 1:numel(singleRxns)
    +0100     idx = singleRxns(k);
    +0101     metRow = find(model.S(:, idx));
    +0102     compName = model.compNames{model.metComps(metRow)};
    +0103     compShort = model.comps{model.metComps(metRow)};
    +0104     if strcmp(compShort, 'e') || strcmp(compName, 'extracellular')
    +0105         rxnSBO{idx} = 'SBO:0000627';
    +0106     elseif sum(model.S(:, idx)) < 0
    +0107         rxnSBO{idx} = 'SBO:0000632';
    +0108     else
    +0109         rxnSBO{idx} = 'SBO:0000628';
    +0110     end
    +0111 end
    +0112 
    +0113 % Transport reactions
    +0114 if isfield(opts, 'transportRxnIdxs') && ~isempty(opts.transportRxnIdxs)
    +0115     transportIdxs = opts.transportRxnIdxs;
    +0116 else
    +0117     transportIdxs = getTransportRxns(model);
    +0118 end
    +0119 rxnSBO(transportIdxs) = {'SBO:0000655'};
    +0120 
    +0121 % Pseudoreaction overrides
    +0122 if opts.onlyLastReactionForPseudo
    +0123     pseudoTargets = numel(model.rxns);
    +0124 else
    +0125     pseudoTargets = 1:numel(model.rxns);
    +0126 end
    +0127 for ii = pseudoTargets
    +0128     name = model.rxnNames{ii};
    +0129     if strcmp(name, opts.biomassRxnName)
    +0130         rxnSBO{ii} = 'SBO:0000629';
    +0131     elseif strcmp(name, opts.ngamRxnName)
    +0132         rxnSBO{ii} = 'SBO:0000630';
    +0133     else
    +0134         for k = 1:numel(opts.pseudoreactionSubstrings)
    +0135             if contains(name, opts.pseudoreactionSubstrings{k})
    +0136                 rxnSBO{ii} = 'SBO:0000395';
    +0137                 break;
    +0138             end
    +0139         end
    +0140     end
    +0141 end
    +0142 
    +0143 model = editMiriam(model, 'met', 'all', 'sbo', metsSBO, 'fill');
    +0144 model = editMiriam(model, 'rxn', 'all', 'sbo', rxnSBO, 'fill');
    +0145 end
    +0146 
    +0147 
    +0148 function opts = applyDefaults(opts)
    +0149 defaults = struct( ...
    +0150     'biomassMetNames', {{'biomass','DNA','RNA','protein','carbohydrate','lipid','cofactor','ion'}}, ...
    +0151     'biomassMetSuffixes', {{' backbone',' chain'}}, ...
    +0152     'biomassRxnName', 'biomass pseudoreaction', ...
    +0153     'ngamRxnName', 'non-growth associated maintenance reaction', ...
    +0154     'pseudoreactionSubstrings', {{'pseudoreaction','SLIME rxn'}}, ...
    +0155     'onlyLastReactionForPseudo', false);
    +0156 fields = fieldnames(defaults);
    +0157 for k = 1:numel(fields)
    +0158     f = fields{k};
    +0159     if ~isfield(opts, f) || isempty(opts.(f))
    +0160         opts.(f) = defaults.(f);
    +0161     end
    +0162 end
    +0163 end
    +0164 
    +0165 
    +0166 function tf = endsWithAny(s, suffixes)
    +0167 tf = false;
    +0168 for i = 1:numel(suffixes)
    +0169     if endsWith(s, suffixes{i})
    +0170         tf = true;
    +0171         return;
    +0172     end
    +0173 end
    +0174 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/annotation/editMiriam.html b/docs/html/annotation/editMiriam.html new file mode 100644 index 00000000..67474ea5 --- /dev/null +++ b/docs/html/annotation/editMiriam.html @@ -0,0 +1,223 @@ + + + + Description of editMiriam + + + + + + + + + +
    Home > annotation > editMiriam.m
    + + + +

    editMiriam +

    + +

    PURPOSE ^

    +
    editMiriam Change MIRIAM annotation fields, one type at a time.
    + +

    SYNOPSIS ^

    +
    function model=editMiriam(model,type,object,miriamName,miriams,keep)
    + +

    DESCRIPTION ^

    +
     editMiriam  Change MIRIAM annotation fields, one type at a time.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + type : char
    +     'met', 'rxn', 'gene' or 'comp', the type of object that the
    +     annotations are assigned to.
    + object : cell or logical or double or char
    +     either a cell array of IDs, a logical vector with the same number of
    +     elements as objects of that type in the model, a vector of indexes,
    +     or 'all'.
    + miriamName : char
    +     string specifying the namespace of the identifier, for instance
    +     'bigg.metabolite'. A valid prefix from identifiers.org is expected
    +     (for example https://registry.identifiers.org/registry/bigg.metabolite).
    + miriams : char or cell
    +     string or cell array of strings with annotation identifiers, for example
    +     '12dgr161'.
    + keep : char
    +     one of the following strings, specifying what should be done if an
    +     object already has existing MIRIAM annotations with the same
    +     miriamName:
    +
    +     - 'replace' : discard all existing annotations with the same
    +       miriamName; they are overwritten, even if the new annotation is an
    +       empty field.
    +     - 'fill' : add annotations only to those objects that have no
    +       annotation with that miriamName. Otherwise, the existing annotation
    +       is kept, even if it differs from the new annotation.
    +     - 'add' : keep all existing annotations, and add any new
    +       annotations, after removing duplicates.
    +     - 'remove' : remove all annotations with the given miriamName from
    +       the specified objects. The miriams argument is ignored.
    +
    + Returns
    + -------
    + model : struct
    +     model structure with updated MIRIAM annotation field.
    +
    + Examples
    + --------
    +     model = editMiriam(model, type, object, miriamName, miriams, keep);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • extractMiriam extractMiriam Unpack MIRIAM annotations into a table.
    +This function is called by: +
      +
    • assignSBOterms assignSBOterms Assign SBO terms to metabolites and reactions.
    + + + + +

    SOURCE CODE ^

    +
    0001 function model=editMiriam(model,type,object,miriamName,miriams,keep)
    +0002 % editMiriam  Change MIRIAM annotation fields, one type at a time.
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % model : struct
    +0007 %     a model structure.
    +0008 % type : char
    +0009 %     'met', 'rxn', 'gene' or 'comp', the type of object that the
    +0010 %     annotations are assigned to.
    +0011 % object : cell or logical or double or char
    +0012 %     either a cell array of IDs, a logical vector with the same number of
    +0013 %     elements as objects of that type in the model, a vector of indexes,
    +0014 %     or 'all'.
    +0015 % miriamName : char
    +0016 %     string specifying the namespace of the identifier, for instance
    +0017 %     'bigg.metabolite'. A valid prefix from identifiers.org is expected
    +0018 %     (for example https://registry.identifiers.org/registry/bigg.metabolite).
    +0019 % miriams : char or cell
    +0020 %     string or cell array of strings with annotation identifiers, for example
    +0021 %     '12dgr161'.
    +0022 % keep : char
    +0023 %     one of the following strings, specifying what should be done if an
    +0024 %     object already has existing MIRIAM annotations with the same
    +0025 %     miriamName:
    +0026 %
    +0027 %     - 'replace' : discard all existing annotations with the same
    +0028 %       miriamName; they are overwritten, even if the new annotation is an
    +0029 %       empty field.
    +0030 %     - 'fill' : add annotations only to those objects that have no
    +0031 %       annotation with that miriamName. Otherwise, the existing annotation
    +0032 %       is kept, even if it differs from the new annotation.
    +0033 %     - 'add' : keep all existing annotations, and add any new
    +0034 %       annotations, after removing duplicates.
    +0035 %     - 'remove' : remove all annotations with the given miriamName from
    +0036 %       the specified objects. The miriams argument is ignored.
    +0037 %
    +0038 % Returns
    +0039 % -------
    +0040 % model : struct
    +0041 %     model structure with updated MIRIAM annotation field.
    +0042 %
    +0043 % Examples
    +0044 % --------
    +0045 %     model = editMiriam(model, type, object, miriamName, miriams, keep);
    +0046 miriamName=char(miriamName);
    +0047 miriams=convertCharArray(miriams);
    +0048 
    +0049 %Check 'keep' input
    +0050 keep=char(keep);
    +0051 if ~any(strcmp(keep,{'replace','fill','add','remove'}))
    +0052     error('Invalid ''keep'', should be ''replace'',''fill'',''add'',''remove''.')
    +0053 end
    +0054 %Check 'type' input
    +0055 type=char(type);
    +0056 if ~any(strcmp(type,{'met','gene','rxn','comp'}))
    +0057     error('Invalid ''type'', should be ''met'', ''gene'', ''rxn'' or ''comp''.')
    +0058 end
    +0059 %Check 'object' input
    +0060 if islogical(object)
    +0061     idxInModel=find(object);
    +0062 elseif isnumeric(object)
    +0063     idxInModel=object;
    +0064 else
    +0065     object=convertCharArray(object);
    +0066     if numel(object)==1 && strcmp(object{1},'all')
    +0067         idxInModel=1:numel(model.([type,'s']));
    +0068     else
    +0069         idxInModel=getIndexes(model,object,[type,'s']);
    +0070         if ~all(idxInModel)
    +0071             error('RAVEN:badInput', '%s', ravenList('The following objects cannot be found in the model: ', object(~idxInModel)))
    +0072         end
    +0073     end
    +0074 end
    +0075 %Check 'miriams' input (skip for 'remove' — miriams argument is not used)
    +0076 if ~strcmp(keep,'remove')
    +0077     if numel(miriams)==1 && numel(idxInModel)~=1
    +0078         miriams=repmat(miriams,numel(idxInModel),1);
    +0079     elseif numel(miriams)~=numel(idxInModel)
    +0080         error('The number of annotations does not match the number of objects.')
    +0081     end
    +0082 end
    +0083 
    +0084 miriamFieldName=[type,'Miriams'];
    +0085 
    +0086 if isfield(model,miriamFieldName)
    +0087     [extractedMiriams,extractedMiriamNames]=extractMiriam(model.(miriamFieldName)(idxInModel),'all');
    +0088 else
    +0089     extractedMiriams=cell(numel(idxInModel),1);
    +0090     extractedMiriamNames={miriamName};
    +0091 end
    +0092 
    +0093 [~, midx]=ismember(miriamName,extractedMiriamNames);
    +0094 if strcmp(keep,'remove')
    +0095     if midx>0
    +0096         extractedMiriams(:,midx)={''};
    +0097     end
    +0098     % Nothing to do when midx==0: miriamName not present
    +0099 elseif midx==0
    +0100     midx=numel(extractedMiriamNames)+1;
    +0101     extractedMiriamNames(end+1)={miriamName};
    +0102     extractedMiriams(:,end+1)=miriams;
    +0103 elseif strcmp(keep,'replace')
    +0104     extractedMiriams(:,midx)=miriams;
    +0105 else
    +0106     noMiriam=cellfun(@isempty,extractedMiriams(:,midx));
    +0107     extractedMiriams(noMiriam,midx)=miriams(noMiriam);
    +0108     if strcmp(keep,'add') % Skipped when only filling empty entries
    +0109         existingMiriams=[split(extractedMiriams(~noMiriam,midx),'; '), miriams(~noMiriam)];
    +0110         uniqueMiriams=cell(size(existingMiriams,1),1);
    +0111         for i=1:size(existingMiriams,1)
    +0112             uniqueMiriams{i,1}=strjoin(unique(existingMiriams(i,:)), {'; '});
    +0113         end
    +0114         extractedMiriams(~noMiriam,midx)=uniqueMiriams;
    +0115     end
    +0116 end
    +0117 
    +0118 % Make Miriam field again
    +0119 for i=1:numel(idxInModel)
    +0120     miriam.name=cell(1,1);
    +0121     miriam.value=cell(1,1);
    +0122     for j=1:numel(extractedMiriamNames)
    +0123         if ~isempty(extractedMiriams{i,j})
    +0124             values=strsplit(extractedMiriams{i,j},'; ');
    +0125             miriam.name(end+1:end+numel(values),1)=extractedMiriamNames(j);
    +0126             miriam.value(end+1:end+numel(values),1)=values;
    +0127         end
    +0128     end
    +0129     miriam.name(1)=[];
    +0130     miriam.value(1)=[];
    +0131     model.(miriamFieldName){idxInModel(i),1}=miriam;
    +0132 end
    +0133 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/annotation/extractMiriam.html b/docs/html/annotation/extractMiriam.html new file mode 100644 index 00000000..2dc7382c --- /dev/null +++ b/docs/html/annotation/extractMiriam.html @@ -0,0 +1,166 @@ + + + + Description of extractMiriam + + + + + + + + + +
    Home > annotation > extractMiriam.m
    + + + +

    extractMiriam +

    + +

    PURPOSE ^

    +
    extractMiriam Unpack MIRIAM annotations into a table.
    + +

    SYNOPSIS ^

    +
    function [miriams,extractedMiriamNames]=extractMiriam(modelMiriams,varargin)
    + +

    DESCRIPTION ^

    +
     extractMiriam  Unpack MIRIAM annotations into a table.
    +
    + Unpacks metMiriams, rxnMiriams, geneMiriams or compMiriams into a cell
    + array with one column per miriam name.
    +
    + Parameters
    + ----------
    + modelMiriams : cell
    +     a miriam structure (for example model.metMiriams) for one or more
    +     metabolites, reactions, genes or compartments.
    +
    + Name-Value Arguments
    + --------------------
    + miriamNames : cell or char
    +     cell array with miriam names to be extracted (default 'all', which
    +     extracts the annotation for all miriam names found in modelMiriams).
    +
    + Returns
    + -------
    + miriams : cell
    +     a cell array with extracted miriams, with one row per entity and one
    +     column per extracted miriam name. If several ids are available for
    +     the same entity (metabolite, gene, reaction or compartment) and miriam
    +     name, they are concatenated into one cell, separated by '; '.
    + extractedMiriamNames : cell
    +     cell array with extracted miriam names.
    +
    + Examples
    + --------
    +     [miriams, extractedMiriamNames] = extractMiriam(modelMiriams, miriamNames);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    • editMiriam editMiriam Change MIRIAM annotation fields, one type at a time.
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [miriams,extractedMiriamNames]=extractMiriam(modelMiriams,varargin)
    +0002 % extractMiriam  Unpack MIRIAM annotations into a table.
    +0003 %
    +0004 % Unpacks metMiriams, rxnMiriams, geneMiriams or compMiriams into a cell
    +0005 % array with one column per miriam name.
    +0006 %
    +0007 % Parameters
    +0008 % ----------
    +0009 % modelMiriams : cell
    +0010 %     a miriam structure (for example model.metMiriams) for one or more
    +0011 %     metabolites, reactions, genes or compartments.
    +0012 %
    +0013 % Name-Value Arguments
    +0014 % --------------------
    +0015 % miriamNames : cell or char
    +0016 %     cell array with miriam names to be extracted (default 'all', which
    +0017 %     extracts the annotation for all miriam names found in modelMiriams).
    +0018 %
    +0019 % Returns
    +0020 % -------
    +0021 % miriams : cell
    +0022 %     a cell array with extracted miriams, with one row per entity and one
    +0023 %     column per extracted miriam name. If several ids are available for
    +0024 %     the same entity (metabolite, gene, reaction or compartment) and miriam
    +0025 %     name, they are concatenated into one cell, separated by '; '.
    +0026 % extractedMiriamNames : cell
    +0027 %     cell array with extracted miriam names.
    +0028 %
    +0029 % Examples
    +0030 % --------
    +0031 %     [miriams, extractedMiriamNames] = extractMiriam(modelMiriams, miriamNames);
    +0032 
    +0033 p=parseRAVENargs(varargin, {'miriamNames',[]});
    +0034 miriamNames=p.miriamNames;
    +0035 if isempty(miriamNames) || (ischar(miriamNames) && strcmp(miriamNames,'all'))
    +0036     extractAllTypes=true;
    +0037 else
    +0038     extractAllTypes=false;
    +0039     miriamNames=convertCharArray(miriamNames);
    +0040 end
    +0041 
    +0042 %The annotation for all miriam names should be extracted
    +0043 if extractAllTypes
    +0044     miriamNames={''};
    +0045     for i=1:numel(modelMiriams)
    +0046         if ~isempty(modelMiriams{i,1})
    +0047             for j=1:numel(modelMiriams{i,1}.name)
    +0048                 miriamNames{numel(miriamNames)+1,1}=modelMiriams{i,1}.name{j};
    +0049             end
    +0050         end
    +0051     end
    +0052     miriamNames=sort(unique(miriamNames));
    +0053 end
    +0054 
    +0055 %Aggregate the final cell array table with extracted miriam information
    +0056 miriams=cell(numel(modelMiriams),0);
    +0057 for i=1:numel(miriamNames)
    +0058     miriams=[miriams, extractMiriamType(modelMiriams,miriamNames{i})];
    +0059 end
    +0060 extractedMiriamNames=miriamNames;
    +0061 
    +0062 end
    +0063 
    +0064 function miriams=extractMiriamType(modelMiriams,miriamName)
    +0065 %Create an empty cell array for ids vector
    +0066 tempMiriams = cell([size(modelMiriams,1) 1]);
    +0067 %Obtain the list of relevant miriam ids. Several entries may have
    +0068 %several miriam ids, such ids are kept in additional columns
    +0069 for i=1:numel(modelMiriams)
    +0070     if (~isempty(modelMiriams{i,1})) && any(strcmp(modelMiriams{i,1}.name,miriamName))
    +0071         for j=1:numel(modelMiriams{i,1}.name)
    +0072             if strcmp(modelMiriams{i,1}.name(j),miriamName)
    +0073                 tempMiriams(i,j) = modelMiriams{i,1}.value(j);
    +0074             end
    +0075         end
    +0076     else
    +0077         modelMiriams{i,1} = '';
    +0078     end
    +0079 end
    +0080 
    +0081 %Concatenate multiple ids per miriam name in one column with semicolon as
    +0082 %separator
    +0083 miriams = cell([size(tempMiriams,1) 1]);
    +0084 notEmpty=~cellfun(@isempty,tempMiriams);
    +0085 for i=1:size(miriams,1)
    +0086     miriams{i}=strjoin(tempMiriams(i,notEmpty(i,:)),{'; '});
    +0087 end
    +0088 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/annotation/index.html b/docs/html/annotation/index.html similarity index 79% rename from doc/annotation/index.html rename to docs/html/annotation/index.html index 09466180..981bdd1a 100644 --- a/doc/annotation/index.html +++ b/docs/html/annotation/index.html @@ -19,7 +19,7 @@

    Index for annotation

    Matlab files in this directory:

    -
     assignSBOtermsassignSBOterms Assign SBO terms to metabolites and reactions.
     editMiriameditMiriam Change MIRIAM annotation fields, one type at a time.
     extractMiriamextractMiriam Unpack MIRIAM annotations into a human-readable table.
     loadDeltaGCSVloadDeltaGCSV Load metDeltaG and rxnDeltaG from CSV files.
     saveDeltaGCSVsaveDeltaGCSV Save metDeltaG and rxnDeltaG to CSV files.
    + assignSBOtermsassignSBOterms Assign SBO terms to metabolites and reactions.  editMiriameditMiriam Change MIRIAM annotation fields, one type at a time.  extractMiriamextractMiriam Unpack MIRIAM annotations into a table.  loadDeltaGCSVloadDeltaGCSV Load metDeltaG and rxnDeltaG from CSV files.  saveDeltaGCSVsaveDeltaGCSV Save metDeltaG and rxnDeltaG to CSV files. diff --git a/doc/annotation/loadDeltaGCSV.html b/docs/html/annotation/loadDeltaGCSV.html similarity index 61% rename from doc/annotation/loadDeltaGCSV.html rename to docs/html/annotation/loadDeltaGCSV.html index bc2c535e..cef7a0f9 100644 --- a/doc/annotation/loadDeltaGCSV.html +++ b/docs/html/annotation/loadDeltaGCSV.html @@ -30,29 +30,30 @@

    DESCRIPTION ^
     loadDeltaGCSV  Load metDeltaG and rxnDeltaG from CSV files.
     
      Each CSV is a two-column table: identifier, deltaG. On load, rows whose
    - identifier is not in the model are silently skipped, and every matched
    - value is stored exactly as it appears in the CSV -- including yeast-GEM's
    - own "no measurement" placeholder (10000000.0). Callers that need to treat
    - a particular value as absent should filter it themselves after loading;
    - this function does not interpret CSV values. Pass '' for either metCsv
    - or rxnCsv to skip that side.
    + identifier is not in the model are skipped without a message, and every
    + matched value is stored as it appears in the CSV, including yeast-GEM's
    + "no measurement" placeholder (10000000.0). The function does not
    + interpret CSV values, so a value that is to be treated as absent has to
    + be filtered after loading. An empty string ('') for metCsv or rxnCsv
    + skips that side.
     
      Parameters
      ----------
      model : struct
    -     RAVEN model struct.
    +     a model structure.
     
      Name-Value Arguments
      --------------------
      metCsv : char
    -     Path to the metabolite deltaG CSV, or '' to skip.
    +     path to the metabolite deltaG CSV, or '' to skip (default '').
      rxnCsv : char
    -     Path to the reaction deltaG CSV, or '' to skip.
    +     path to the reaction deltaG CSV, or '' to skip (default '').
     
      Returns
      -------
      model : struct
    -     Updated model struct.
    +     model structure. The metDeltaG field is set only if metCsv is given and
    +     the rxnDeltaG field only if rxnCsv is given.
     
      Examples
      --------
    @@ -78,60 +79,61 @@ 

    SOURCE CODE ^% loadDeltaGCSV Load metDeltaG and rxnDeltaG from CSV files. 0003 % 0004 % Each CSV is a two-column table: identifier, deltaG. On load, rows whose -0005 % identifier is not in the model are silently skipped, and every matched -0006 % value is stored exactly as it appears in the CSV -- including yeast-GEM's -0007 % own "no measurement" placeholder (10000000.0). Callers that need to treat -0008 % a particular value as absent should filter it themselves after loading; -0009 % this function does not interpret CSV values. Pass '' for either metCsv -0010 % or rxnCsv to skip that side. +0005 % identifier is not in the model are skipped without a message, and every +0006 % matched value is stored as it appears in the CSV, including yeast-GEM's +0007 % "no measurement" placeholder (10000000.0). The function does not +0008 % interpret CSV values, so a value that is to be treated as absent has to +0009 % be filtered after loading. An empty string ('') for metCsv or rxnCsv +0010 % skips that side. 0011 % 0012 % Parameters 0013 % ---------- 0014 % model : struct -0015 % RAVEN model struct. +0015 % a model structure. 0016 % 0017 % Name-Value Arguments 0018 % -------------------- 0019 % metCsv : char -0020 % Path to the metabolite deltaG CSV, or '' to skip. +0020 % path to the metabolite deltaG CSV, or '' to skip (default ''). 0021 % rxnCsv : char -0022 % Path to the reaction deltaG CSV, or '' to skip. +0022 % path to the reaction deltaG CSV, or '' to skip (default ''). 0023 % 0024 % Returns 0025 % ------- 0026 % model : struct -0027 % Updated model struct. -0028 % -0029 % Examples -0030 % -------- -0031 % model = loadDeltaGCSV(model, 'data/met_dG.csv', 'data/rxn_dG.csv'); -0032 % model = loadDeltaGCSV(model, 'metCsv', 'data/met_dG.csv'); -0033 -0034 p=parseRAVENargs(varargin, {'metCsv',''; 'rxnCsv',''}); -0035 metCsv=p.metCsv; -0036 rxnCsv=p.rxnCsv; -0037 -0038 model=loadField(model, 'metDeltaG', model.mets, metCsv); -0039 model=loadField(model, 'rxnDeltaG', model.rxns, rxnCsv); -0040 end -0041 -0042 function model=loadField(model, fieldname, ids, csvPath) -0043 if isempty(csvPath); return; end -0044 if isfield(model, fieldname) -0045 disp(['Existing ' fieldname ' field will be overwritten.']) -0046 else -0047 model.(fieldname)=nan(numel(ids),1); -0048 end -0049 G=readtable(csvPath); -0050 values=G.(G.Properties.VariableNames{2}); -0051 [a, b]=ismember(ids, G.(G.Properties.VariableNames{1})); -0052 model.(fieldname)(a)=values(b(a)); -0053 if any(~a) -0054 fprintf(['Not all identifiers are matched to %s; the latter\n' ... -0055 'file might have to be supplemented with deltaG values for new entries.\n'], ... -0056 csvPath); -0057 end -0058 end

    +0027 % model structure. The metDeltaG field is set only if metCsv is given and +0028 % the rxnDeltaG field only if rxnCsv is given. +0029 % +0030 % Examples +0031 % -------- +0032 % model = loadDeltaGCSV(model, 'data/met_dG.csv', 'data/rxn_dG.csv'); +0033 % model = loadDeltaGCSV(model, 'metCsv', 'data/met_dG.csv'); +0034 +0035 p=parseRAVENargs(varargin, {'metCsv',''; 'rxnCsv',''}); +0036 metCsv=p.metCsv; +0037 rxnCsv=p.rxnCsv; +0038 +0039 model=loadField(model, 'metDeltaG', model.mets, metCsv); +0040 model=loadField(model, 'rxnDeltaG', model.rxns, rxnCsv); +0041 end +0042 +0043 function model=loadField(model, fieldname, ids, csvPath) +0044 if isempty(csvPath); return; end +0045 if isfield(model, fieldname) +0046 disp(['Existing ' fieldname ' field will be overwritten.']) +0047 else +0048 model.(fieldname)=nan(numel(ids),1); +0049 end +0050 G=readtable(csvPath); +0051 values=G.(G.Properties.VariableNames{2}); +0052 [a, b]=ismember(ids, G.(G.Properties.VariableNames{1})); +0053 model.(fieldname)(a)=values(b(a)); +0054 if any(~a) +0055 fprintf(['Not all identifiers are matched to %s; the latter\n' ... +0056 'file might have to be supplemented with deltaG values for new entries.\n'], ... +0057 csvPath); +0058 end +0059 end
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/annotation/saveDeltaGCSV.html b/docs/html/annotation/saveDeltaGCSV.html similarity index 89% rename from doc/annotation/saveDeltaGCSV.html rename to docs/html/annotation/saveDeltaGCSV.html index 7573b70c..c376e7e1 100644 --- a/doc/annotation/saveDeltaGCSV.html +++ b/docs/html/annotation/saveDeltaGCSV.html @@ -29,11 +29,11 @@

    SYNOPSIS ^DESCRIPTION ^

     saveDeltaGCSV  Save metDeltaG and rxnDeltaG to CSV files.
     
    - Writes a two-column table (identifier, deltaG) per side, one row per
    - entity, in model order. An entity with no value stored is written as
    - NaN, unchanged from whatever the model field holds -- this function does
    - not interpret or substitute values. Pass '' for either metCsv or rxnCsv
    - to skip that side.
    + Writes a two-column table (identifier, deltaG) for metabolites and one for
    + reactions, one row per entity, in model order. Values are written as the
    + model field holds them, so an entity with no value stored is written as
    + NaN. A file is skipped when its path is '' or when the model lacks the
    + corresponding field.
     
      Parameters
      ----------
    @@ -72,11 +72,11 @@ 

    SOURCE CODE ^
    0001 function saveDeltaGCSV(model, varargin)
     0002 % saveDeltaGCSV  Save metDeltaG and rxnDeltaG to CSV files.
     0003 %
    -0004 % Writes a two-column table (identifier, deltaG) per side, one row per
    -0005 % entity, in model order. An entity with no value stored is written as
    -0006 % NaN, unchanged from whatever the model field holds -- this function does
    -0007 % not interpret or substitute values. Pass '' for either metCsv or rxnCsv
    -0008 % to skip that side.
    +0004 % Writes a two-column table (identifier, deltaG) for metabolites and one for
    +0005 % reactions, one row per entity, in model order. Values are written as the
    +0006 % model field holds them, so an entity with no value stored is written as
    +0007 % NaN. A file is skipped when its path is '' or when the model lacks the
    +0008 % corresponding field.
     0009 %
     0010 % Parameters
     0011 % ----------
    diff --git a/docs/html/biomass/fitParameters.html b/docs/html/biomass/fitParameters.html
    new file mode 100644
    index 00000000..11bb4aa6
    --- /dev/null
    +++ b/docs/html/biomass/fitParameters.html
    @@ -0,0 +1,257 @@
    +
    +
    +
    +  Description of fitParameters
    +  
    +  
    +  
    +  
    +  
    +  
    +
    +
    +
    +
    Home > biomass > fitParameters.m
    + + + +

    fitParameters +

    + +

    PURPOSE ^

    +
    fitParameters Fit stoichiometric parameters such as maintenance ATP to measured data.
    + +

    SYNOPSIS ^

    +
    function [parameters, fitnessScore, exitFlag, newModel]=fitParameters(model,xRxns,xValues,rxnsToFit,valuesToFit,parameterPositions,varargin)
    + +

    DESCRIPTION ^

    +
     fitParameters  Fit stoichiometric parameters such as maintenance ATP to measured data.
    +
    + Searches for the parameter values that minimise the residual sum of squares
    + between the fluxes the model predicts and the measured ones, using
    + fminsearch (Nelder-Mead). Each candidate parameter set is written into the
    + S matrix at the positions given by parameterPositions, and the model is
    + solved once per data point with the xRxns bounds fixed to the values in
    + xValues. The fitted parameters are returned as absolute values.
    +
    + The function derives a value from measurements. To apply a known
    + growth-associated maintenance value to a biomass pseudoreaction, use setGAM.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + xRxns : cell
    +     cell array with the IDs of the reactions that will be fixed for each
    +     data point.
    + xValues : double
    +     matrix with the corresponding values for each xRxns (columns are
    +     reactions).
    + rxnsToFit : cell
    +     cell array with the IDs of reactions that will be fitted to.
    + valuesToFit : double
    +     matrix with the corresponding values for each rxnsToFit (columns are
    +     reactions).
    + parameterPositions : struct
    +     structure that determines where the parameters are in the
    +     stoichiometric matrix, with fields:
    +
    +     - position : cell array of vectors where each element contains the
    +       positions in the S-matrix for that parameter
    +     - isNegative : cell array of vectors where the elements are true if
    +       that position is set to the negative of the fitted value (to
    +       distinguish production from consumption)
    +
    + Name-Value Arguments
    + --------------------
    + fitToRatio : logical
    +     true to fit the ratio of simulated to measured values, false to fit
    +     the absolute difference. The ratio limits the influence of large
    +     fluxes on the residual sum of squares (default true).
    + initialGuess : double
    +     initial guess of the parameters (default ones).
    + plotFitting : logical
    +     true to plot the measured values and the fitted fluxes against
    +     xValues (default false).
    +
    + Returns
    + -------
    + parameters : double
    +     fitted parameters (absolute values) in the same order as in
    +     parameterPositions.
    + fitnessScore : double
    +     the corresponding residual sum of squares.
    + exitFlag : double
    +     exit status returned by fminsearch.
    + newModel : struct
    +     updated model structure with the fitted parameters. It is assigned
    +     only when plotFitting is true.
    +
    + Examples
    + --------
    +     [parameters, fitnessScore, exitFlag, newModel]=fitParameters(model,...
    +         xRxns,xValues,rxnsToFit,valuesToFit,parameterPositions,fitToRatio,...
    +         initialGuess,plotFitting);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [parameters, fitnessScore, exitFlag, newModel]=fitParameters(model,xRxns,xValues,rxnsToFit,valuesToFit,parameterPositions,varargin)
    +0002 % fitParameters  Fit stoichiometric parameters such as maintenance ATP to measured data.
    +0003 %
    +0004 % Searches for the parameter values that minimise the residual sum of squares
    +0005 % between the fluxes the model predicts and the measured ones, using
    +0006 % fminsearch (Nelder-Mead). Each candidate parameter set is written into the
    +0007 % S matrix at the positions given by parameterPositions, and the model is
    +0008 % solved once per data point with the xRxns bounds fixed to the values in
    +0009 % xValues. The fitted parameters are returned as absolute values.
    +0010 %
    +0011 % The function derives a value from measurements. To apply a known
    +0012 % growth-associated maintenance value to a biomass pseudoreaction, use setGAM.
    +0013 %
    +0014 % Parameters
    +0015 % ----------
    +0016 % model : struct
    +0017 %     a model structure.
    +0018 % xRxns : cell
    +0019 %     cell array with the IDs of the reactions that will be fixed for each
    +0020 %     data point.
    +0021 % xValues : double
    +0022 %     matrix with the corresponding values for each xRxns (columns are
    +0023 %     reactions).
    +0024 % rxnsToFit : cell
    +0025 %     cell array with the IDs of reactions that will be fitted to.
    +0026 % valuesToFit : double
    +0027 %     matrix with the corresponding values for each rxnsToFit (columns are
    +0028 %     reactions).
    +0029 % parameterPositions : struct
    +0030 %     structure that determines where the parameters are in the
    +0031 %     stoichiometric matrix, with fields:
    +0032 %
    +0033 %     - position : cell array of vectors where each element contains the
    +0034 %       positions in the S-matrix for that parameter
    +0035 %     - isNegative : cell array of vectors where the elements are true if
    +0036 %       that position is set to the negative of the fitted value (to
    +0037 %       distinguish production from consumption)
    +0038 %
    +0039 % Name-Value Arguments
    +0040 % --------------------
    +0041 % fitToRatio : logical
    +0042 %     true to fit the ratio of simulated to measured values, false to fit
    +0043 %     the absolute difference. The ratio limits the influence of large
    +0044 %     fluxes on the residual sum of squares (default true).
    +0045 % initialGuess : double
    +0046 %     initial guess of the parameters (default ones).
    +0047 % plotFitting : logical
    +0048 %     true to plot the measured values and the fitted fluxes against
    +0049 %     xValues (default false).
    +0050 %
    +0051 % Returns
    +0052 % -------
    +0053 % parameters : double
    +0054 %     fitted parameters (absolute values) in the same order as in
    +0055 %     parameterPositions.
    +0056 % fitnessScore : double
    +0057 %     the corresponding residual sum of squares.
    +0058 % exitFlag : double
    +0059 %     exit status returned by fminsearch.
    +0060 % newModel : struct
    +0061 %     updated model structure with the fitted parameters. It is assigned
    +0062 %     only when plotFitting is true.
    +0063 %
    +0064 % Examples
    +0065 % --------
    +0066 %     [parameters, fitnessScore, exitFlag, newModel]=fitParameters(model,...
    +0067 %         xRxns,xValues,rxnsToFit,valuesToFit,parameterPositions,fitToRatio,...
    +0068 %         initialGuess,plotFitting);
    +0069 
    +0070 p=parseRAVENargs(varargin, {'fitToRatio',true; 'initialGuess',[]; 'plotFitting',false});
    +0071 fitToRatio=p.fitToRatio;
    +0072 initialGuess=p.initialGuess;
    +0073 if isempty(initialGuess)
    +0074     initialGuess=ones(numel(parameterPositions.position),1);
    +0075 end
    +0076 plotFitting=p.plotFitting;
    +0077 
    +0078 xRxns=convertCharArray(xRxns);
    +0079 rxnsToFit=convertCharArray(rxnsToFit);
    +0080 
    +0081 %Find the indexes of reactions that will be fitted
    +0082 [I, rxnsToFitIndexes]=ismember(rxnsToFit,model.rxns);
    +0083 
    +0084 if ~all(I)
    +0085     EM='Could not find all reactions in rxnsToFit';
    +0086     error('RAVEN:badInput', '%s', EM);
    +0087 end
    +0088 
    +0089 %Find the indexes of reactions that will be used for constraints.
    +0090 [I, xRxnsIndexes]=ismember(xRxns,model.rxns);
    +0091 
    +0092 if ~all(I)
    +0093     EM='Could not find all reactions in xRxns';
    +0094     error('RAVEN:badInput', '%s', EM);
    +0095 end
    +0096 
    +0097 [parameters, fitnessScore, exitFlag]=fminsearch(@(parameters) getRSS(parameters,model,xRxnsIndexes,xValues,rxnsToFitIndexes,valuesToFit,parameterPositions,fitToRatio),initialGuess);
    +0098 
    +0099 parameters=abs(parameters);
    +0100 
    +0101 if plotFitting==true
    +0102     %Set the resulting parameters
    +0103     [~, resultingFluxes, newModel]=getRSS(parameters,model,xRxnsIndexes,xValues,rxnsToFitIndexes,valuesToFit,parameterPositions,true);
    +0104     plot(xValues,valuesToFit,'o',xValues,resultingFluxes,'-*');
    +0105 end
    +0106 end
    +0107 
    +0108 function [rss, resultingFluxes, newModel]=getRSS(parameters,model,xRxnsIndexes,xValues,rxnsToFitIndexes,valuesToFit,parameterPositions,fitToRatio)
    +0109 parameters=abs(parameters);
    +0110 
    +0111 %Set the parameters at the positions specified in parameterPositions
    +0112 for i=1:numel(parameterPositions.position)
    +0113     %Set positive
    +0114     model.S(parameterPositions.position{i}(parameterPositions.isNegative{i}==false))=parameters(i);
    +0115     
    +0116     %Set negative
    +0117     model.S(parameterPositions.position{i}(parameterPositions.isNegative{i}==true))=parameters(i)*-1;
    +0118 end
    +0119 
    +0120 %Also return an updated model
    +0121 newModel=model;
    +0122 
    +0123 %Loop through each data point, set xRxns to xValues and calculate the sum
    +0124 %of squares for the rxnsToFit
    +0125 rss=0;
    +0126 resultingFluxes=[];
    +0127 for i=1:size(xValues,1)
    +0128     model.lb(xRxnsIndexes)=xValues(i,:);
    +0129     model.ub(xRxnsIndexes)=xValues(i,:);
    +0130     
    +0131     sol=solveLP(model);
    +0132     
    +0133     %Calculate the rss
    +0134     if fitToRatio==false
    +0135         rs=sol.x(rxnsToFitIndexes)'-valuesToFit(i,:);
    +0136     else
    +0137         rs=sol.x(rxnsToFitIndexes)'./valuesToFit(i,:)-ones(1,size(valuesToFit,2));
    +0138     end
    +0139     rss=rss+rs*rs';
    +0140     resultingFluxes=[resultingFluxes sol.x(rxnsToFitIndexes)];
    +0141 end
    +0142 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/biomass/getBiomassFractions.html b/docs/html/biomass/getBiomassFractions.html new file mode 100644 index 00000000..6f78d373 --- /dev/null +++ b/docs/html/biomass/getBiomassFractions.html @@ -0,0 +1,240 @@ + + + + Description of getBiomassFractions + + + + + + + + + +
    Home > biomass > getBiomassFractions.m
    + + + +

    getBiomassFractions +

    + +

    PURPOSE ^

    +
    getBiomassFractions Compute mass fraction per biomass component.
    + +

    SYNOPSIS ^

    +
    function fractions = getBiomassFractions(model, biomassConfig)
    + +

    DESCRIPTION ^

    +
     getBiomassFractions  Compute mass fraction per biomass component.
    +
    + Compute the mass fraction (g/gDW) per biomass component plus the total.
    + The MATLAB counterpart of yeast-GEM's legacy sumBioMass.
    +
    + The biomassConfig struct describes the per-organism biomass layout (see
    + Parameters). A component contributes 0 when its pseudoreaction is missing
    + from the model or has no substrates. For a component whose pseudoreaction
    + exists and has substrates, and whose mass_strategy is not 'grams', an error
    + is raised when the molecular weight of a substrate is 0 (an empty
    + metFormula field, or a formula that consists only of R), when its formula
    + contains an element outside the built-in element table, or when
    + mass_strategy is unknown. A component with an unknown mass_strategy whose
    + pseudoreaction is missing or has no substrates contributes 0 without an
    + error.
    +
    + Parameters
    + ----------
    + model : struct
    +     RAVEN model struct.
    + biomassConfig : struct
    +     Struct describing the biomass layout, with fields:
    +
    +     - biomass_rxn : rxn id of the top-level biomass pseudoreaction.
    +     - proton_met : met id of cytosolic H+ (used by
    +       scaleBiomassPseudoreaction, not by getBiomassFractions).
    +     - components : cell array of component structs, each with fields:
    +
    +       - name : component name (for example 'protein').
    +       - pseudoreaction_name : model.rxnNames entry to identify the
    +         pseudoreaction.
    +       - mass_strategy : 'mw', 'mw_minus_2h', 'mw_minus_water' or
    +         'grams' (see Notes).
    +
    + Returns
    + -------
    + fractions : struct
    +     Struct with one field per component name and a field 'total'
    +     (for example fractions.protein and fractions.RNA). All values are in
    +     g/gDW.
    +
    + Examples
    + --------
    +     fractions = getBiomassFractions(model, biomassConfig);
    +
    + Notes
    + -----
    + mass_strategy values:
    +
    +     'mw'              MW from chemical formula
    +     'mw_minus_2h'     MW - 2.016 g/mol (two protons released per charged
    +                       tRNA; protein pseudoreaction substrates)
    +     'mw_minus_water'  MW - 18.015 g/mol (water released per
    +                       polymerisation step; RNA and DNA)
    +     'grams'           stoichiometry is already in g/gDW (lipid backbone)
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: + + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function fractions = getBiomassFractions(model, biomassConfig)
    +0002 % getBiomassFractions  Compute mass fraction per biomass component.
    +0003 %
    +0004 % Compute the mass fraction (g/gDW) per biomass component plus the total.
    +0005 % The MATLAB counterpart of yeast-GEM's legacy sumBioMass.
    +0006 %
    +0007 % The biomassConfig struct describes the per-organism biomass layout (see
    +0008 % Parameters). A component contributes 0 when its pseudoreaction is missing
    +0009 % from the model or has no substrates. For a component whose pseudoreaction
    +0010 % exists and has substrates, and whose mass_strategy is not 'grams', an error
    +0011 % is raised when the molecular weight of a substrate is 0 (an empty
    +0012 % metFormula field, or a formula that consists only of R), when its formula
    +0013 % contains an element outside the built-in element table, or when
    +0014 % mass_strategy is unknown. A component with an unknown mass_strategy whose
    +0015 % pseudoreaction is missing or has no substrates contributes 0 without an
    +0016 % error.
    +0017 %
    +0018 % Parameters
    +0019 % ----------
    +0020 % model : struct
    +0021 %     RAVEN model struct.
    +0022 % biomassConfig : struct
    +0023 %     Struct describing the biomass layout, with fields:
    +0024 %
    +0025 %     - biomass_rxn : rxn id of the top-level biomass pseudoreaction.
    +0026 %     - proton_met : met id of cytosolic H+ (used by
    +0027 %       scaleBiomassPseudoreaction, not by getBiomassFractions).
    +0028 %     - components : cell array of component structs, each with fields:
    +0029 %
    +0030 %       - name : component name (for example 'protein').
    +0031 %       - pseudoreaction_name : model.rxnNames entry to identify the
    +0032 %         pseudoreaction.
    +0033 %       - mass_strategy : 'mw', 'mw_minus_2h', 'mw_minus_water' or
    +0034 %         'grams' (see Notes).
    +0035 %
    +0036 % Returns
    +0037 % -------
    +0038 % fractions : struct
    +0039 %     Struct with one field per component name and a field 'total'
    +0040 %     (for example fractions.protein and fractions.RNA). All values are in
    +0041 %     g/gDW.
    +0042 %
    +0043 % Examples
    +0044 % --------
    +0045 %     fractions = getBiomassFractions(model, biomassConfig);
    +0046 %
    +0047 % Notes
    +0048 % -----
    +0049 % mass_strategy values:
    +0050 %
    +0051 %     'mw'              MW from chemical formula
    +0052 %     'mw_minus_2h'     MW - 2.016 g/mol (two protons released per charged
    +0053 %                       tRNA; protein pseudoreaction substrates)
    +0054 %     'mw_minus_water'  MW - 18.015 g/mol (water released per
    +0055 %                       polymerisation step; RNA and DNA)
    +0056 %     'grams'           stoichiometry is already in g/gDW (lipid backbone)
    +0057 
    +0058 fractions = struct();
    +0059 total = 0;
    +0060 for i = 1:numel(biomassConfig.components)
    +0061     comp = biomassConfig.components{i};
    +0062     f = computeComponentFraction(model, comp);
    +0063     fractions.(comp.name) = f;
    +0064     total = total + f;
    +0065 end
    +0066 fractions.total = total;
    +0067 end
    +0068 
    +0069 function f = computeComponentFraction(model, comp)
    +0070 rxnPos = strcmp(model.rxnNames, comp.pseudoreaction_name);
    +0071 if ~any(rxnPos)
    +0072     f = 0;
    +0073     return;
    +0074 end
    +0075 S_col = model.S(:, rxnPos);
    +0076 isSub = find(S_col < 0);
    +0077 if isempty(isSub)
    +0078     f = 0;
    +0079     return;
    +0080 end
    +0081 if strcmp(comp.mass_strategy, 'grams')
    +0082     f = full(-sum(S_col(isSub)));
    +0083     return;
    +0084 end
    +0085 offset = mwOffset(comp.mass_strategy);
    +0086 formulas = model.metFormulas(isSub);
    +0087 MWs = zeros(numel(formulas), 1);
    +0088 for i = 1:numel(formulas)
    +0089     MWs(i) = computeFormulaMW(formulas{i});
    +0090 end
    +0091 zeroMW = MWs == 0;
    +0092 if any(zeroMW)
    +0093     error('getBiomassFractions:emptyFormula', ...
    +0094         'Biomass metabolite %s has an empty metFormula field.', ...
    +0095         model.mets{isSub(find(zeroMW, 1))});
    +0096 end
    +0097 MWs = MWs + offset;
    +0098 f = full(-sum(S_col(isSub) .* MWs) / 1000);
    +0099 end
    +0100 
    +0101 function offset = mwOffset(strategy)
    +0102 switch strategy
    +0103     case 'mw'
    +0104         offset = 0;
    +0105     case 'mw_minus_2h'
    +0106         offset = -2.016;
    +0107     case 'mw_minus_water'
    +0108         offset = -18.015;
    +0109     otherwise
    +0110         error('getBiomassFractions:unknownStrategy', ...
    +0111             'Unknown mass_strategy: %s', strategy);
    +0112 end
    +0113 end
    +0114 
    +0115 function mw = computeFormulaMW(formula)
    +0116 % Molecular weight in g/mol from a Hill-style chemical formula. Same
    +0117 % element table as the legacy yeast-GEM sumBioMass.
    +0118 tokens = regexp(formula, '([A-Z][a-z]*)(\d*)', 'tokens');
    +0119 if isempty(tokens)
    +0120     mw = 0;
    +0121     return;
    +0122 end
    +0123 tokensMatrix = vertcat(tokens{:});
    +0124 tokensMatrix(cellfun(@isempty, tokensMatrix(:,2)), 2) = {'1'};
    +0125 elements = tokensMatrix(:, 1);
    +0126 counts = str2double(tokensMatrix(:, 2));
    +0127 elem = {'C', 12.01; 'H', 1.008; 'N', 14.007; 'O', 15.999; ...
    +0128         'P', 30.974; 'S', 32.06; 'R', 0; ...
    +0129         'Fe', 55.845; 'K', 39.098; 'Na', 22.99; 'Cl', 35.45; ...
    +0130         'Mn', 54.938; 'Zn', 65.38; 'Ca', 40.078; 'Mg', 24.305; 'Cu', 63.546};
    +0131 [~, elemMatch] = ismember(elements, elem(:,1));
    +0132 if any(elemMatch == 0)
    +0133     error('getBiomassFractions:unknownElement', ...
    +0134         'Unknown element in formula %s', formula);
    +0135 end
    +0136 mw = sum(counts .* transpose([elem{elemMatch, 2}]), 'all');
    +0137 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/biomass/index.html b/docs/html/biomass/index.html similarity index 100% rename from doc/biomass/index.html rename to docs/html/biomass/index.html diff --git a/docs/html/biomass/scaleBiomassFraction.html b/docs/html/biomass/scaleBiomassFraction.html new file mode 100644 index 00000000..c7a450de --- /dev/null +++ b/docs/html/biomass/scaleBiomassFraction.html @@ -0,0 +1,178 @@ + + + + Description of scaleBiomassFraction + + + + + + + + + +
    Home > biomass > scaleBiomassFraction.m
    + + + +

    scaleBiomassFraction +

    + +

    PURPOSE ^

    +
    scaleBiomassFraction Rescale a biomass component to a target value.
    + +

    SYNOPSIS ^

    +
    function model = scaleBiomassFraction(model, biomassConfig, componentName, newValue, varargin)
    + +

    DESCRIPTION ^

    +
     scaleBiomassFraction  Rescale a biomass component to a target value.
    +
    + Rescale a biomass component to a target g/gDW value, optionally
    + rescaling a second component so that the biomass total becomes
    + targetTotal (1 g/gDW by default), whatever the total was before.
    + The MATLAB counterpart of yeast-GEM's legacy scaleBioMass.
    +
    + Parameters
    + ----------
    + model : struct
    +     RAVEN model struct.
    + biomassConfig : struct
    +     Biomass layout struct, as described in getBiomassFractions.
    + componentName : char
    +     Name of the component to rescale. An error is raised if its current
    +     fraction is 0.
    + newValue : double
    +     Target fraction in g/gDW.
    +
    + Name-Value Arguments
    + --------------------
    + balanceOut : char
    +     Second component name to adjust so the biomass total becomes
    +     targetTotal. An error is raised if its fraction would become zero or
    +     negative, or if it is 0 to begin with. No balancing is done when
    +     empty or omitted (default '').
    + targetTotal : double
    +     Biomass total in g/gDW that balanceOut brings the components to
    +     (default 1).
    +
    + Returns
    + -------
    + model : struct
    +     Model with the rescaled pseudoreactions.
    +
    + Examples
    + --------
    +     model = scaleBiomassFraction(model, biomassConfig, 'protein', 0.5, 'carbohydrate');
    +     model = scaleBiomassFraction(model, biomassConfig, 'protein', 0.5, ...
    +         'balanceOut', 'carbohydrate', 'targetTotal', 0.95);
    +
    + See also
    + --------
    + getBiomassFractions
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function model = scaleBiomassFraction(model, biomassConfig, componentName, newValue, varargin)
    +0002 % scaleBiomassFraction  Rescale a biomass component to a target value.
    +0003 %
    +0004 % Rescale a biomass component to a target g/gDW value, optionally
    +0005 % rescaling a second component so that the biomass total becomes
    +0006 % targetTotal (1 g/gDW by default), whatever the total was before.
    +0007 % The MATLAB counterpart of yeast-GEM's legacy scaleBioMass.
    +0008 %
    +0009 % Parameters
    +0010 % ----------
    +0011 % model : struct
    +0012 %     RAVEN model struct.
    +0013 % biomassConfig : struct
    +0014 %     Biomass layout struct, as described in getBiomassFractions.
    +0015 % componentName : char
    +0016 %     Name of the component to rescale. An error is raised if its current
    +0017 %     fraction is 0.
    +0018 % newValue : double
    +0019 %     Target fraction in g/gDW.
    +0020 %
    +0021 % Name-Value Arguments
    +0022 % --------------------
    +0023 % balanceOut : char
    +0024 %     Second component name to adjust so the biomass total becomes
    +0025 %     targetTotal. An error is raised if its fraction would become zero or
    +0026 %     negative, or if it is 0 to begin with. No balancing is done when
    +0027 %     empty or omitted (default '').
    +0028 % targetTotal : double
    +0029 %     Biomass total in g/gDW that balanceOut brings the components to
    +0030 %     (default 1).
    +0031 %
    +0032 % Returns
    +0033 % -------
    +0034 % model : struct
    +0035 %     Model with the rescaled pseudoreactions.
    +0036 %
    +0037 % Examples
    +0038 % --------
    +0039 %     model = scaleBiomassFraction(model, biomassConfig, 'protein', 0.5, 'carbohydrate');
    +0040 %     model = scaleBiomassFraction(model, biomassConfig, 'protein', 0.5, ...
    +0041 %         'balanceOut', 'carbohydrate', 'targetTotal', 0.95);
    +0042 %
    +0043 % See also
    +0044 % --------
    +0045 % getBiomassFractions
    +0046 
    +0047 p=parseRAVENargs(varargin, {'balanceOut',''; 'targetTotal',1});
    +0048 balanceOut=p.balanceOut;
    +0049 targetTotal=p.targetTotal;
    +0050 
    +0051 fractions = getBiomassFractions(model, biomassConfig);
    +0052 if ~isfield(fractions, componentName)
    +0053     error('scaleBiomassFraction:unknownComponent', ...
    +0054         'biomassConfig has no component named %s', componentName);
    +0055 end
    +0056 current = fractions.(componentName);
    +0057 if current == 0
    +0058     error('scaleBiomassFraction:zeroCurrent', ...
    +0059         ['Cannot scale %s to %g: current fraction is 0 ' ...
    +0060          '(pseudoreaction missing or empty).'], componentName, newValue);
    +0061 end
    +0062 factor = newValue / current;
    +0063 model = scaleBiomassPseudoreaction(model, biomassConfig, componentName, factor);
    +0064 
    +0065 if ~isempty(balanceOut)
    +0066     fractions = getBiomassFractions(model, biomassConfig);
    +0067     total = fractions.total;
    +0068     if ~isfield(fractions, balanceOut)
    +0069         error('scaleBiomassFraction:unknownComponent', ...
    +0070             'biomassConfig has no component named %s', balanceOut);
    +0071     end
    +0072     balanceCurrent = fractions.(balanceOut);
    +0073     if balanceCurrent == 0
    +0074         error('scaleBiomassFraction:zeroCurrent', ...
    +0075             ['Cannot balance with %s: current fraction is 0 ' ...
    +0076              '(pseudoreaction missing or empty).'], balanceOut);
    +0077     end
    +0078     balanceNew = balanceCurrent + (targetTotal - total);
    +0079     if balanceNew <= 0
    +0080         error('scaleBiomassFraction:negativeBalance', ...
    +0081             ['Cannot balance with %s: its fraction would become %g g/gDW. ' ...
    +0082              'It is %g g/gDW, but the other components total %g g/gDW.'], ...
    +0083             balanceOut, balanceNew, balanceCurrent, total - balanceCurrent);
    +0084     end
    +0085     balanceFactor = balanceNew / balanceCurrent;
    +0086     model = scaleBiomassPseudoreaction(model, biomassConfig, balanceOut, balanceFactor);
    +0087 end
    +0088 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/biomass/scaleBiomassPseudoreaction.html b/docs/html/biomass/scaleBiomassPseudoreaction.html similarity index 50% rename from doc/biomass/scaleBiomassPseudoreaction.html rename to docs/html/biomass/scaleBiomassPseudoreaction.html index ef9c3787..66be1718 100644 --- a/doc/biomass/scaleBiomassPseudoreaction.html +++ b/docs/html/biomass/scaleBiomassPseudoreaction.html @@ -34,10 +34,12 @@

    DESCRIPTION ^DESCRIPTION ^SOURCE CODE ^% The MATLAB counterpart of yeast-GEM's legacy rescalePseudoReaction. 0007 % 0008 % "Substrate" means every metabolite in the pseudoreaction whose -0009 % metabolite name does NOT match the component name (the component's -0010 % product is left untouched). After rescaling, the coefficient of -0011 % biomassConfig.proton_met is recomputed so the pseudoreaction's total -0012 % ionic charge sums to zero. -0013 % -0014 % Parameters -0015 % ---------- -0016 % model : struct -0017 % RAVEN model struct. -0018 % biomassConfig : struct -0019 % Struct (see getBiomassFractions). -0020 % componentName : char -0021 % Name of the component to rescale (must match -0022 % biomassConfig.components{i}.name for some i, AND be the -0023 % model.metNames of the produced metabolite in the matching -0024 % pseudoreaction). -0025 % factor : double -0026 % Multiplicative factor. -0027 % -0028 % Returns -0029 % ------- -0030 % model : struct -0031 % Modified model. -0032 % -0033 % Examples -0034 % -------- -0035 % model = scaleBiomassPseudoreaction(model, biomassConfig, 'protein', 0.9); -0036 % -0037 % See also -0038 % -------- -0039 % getBiomassFractions -0040 -0041 comp = findComponent(biomassConfig, componentName); -0042 rxnPos = find(strcmp(model.rxnNames, comp.pseudoreaction_name)); -0043 if isempty(rxnPos) -0044 error('scaleBiomassPseudoreaction:missingPseudoreaction', ... -0045 'No reaction named %s in model.', comp.pseudoreaction_name); -0046 end -0047 -0048 for i = 1:length(model.mets) -0049 S_ir = model.S(i, rxnPos); -0050 isProd = strcmp(model.metNames{i}, componentName); -0051 if S_ir ~= 0 && ~isProd -0052 model.S(i, rxnPos) = factor * S_ir; -0053 end -0054 end -0055 -0056 % Rebalance H+ to keep charge neutrality. -0057 Hc = find(strcmp(model.mets, biomassConfig.proton_met)); -0058 model.S(Hc, rxnPos) = 0; -0059 model.S(Hc, rxnPos) = -chargeResidual(model, rxnPos); -0060 end -0061 -0062 function residual = chargeResidual(model, rxnPos) -0063 % Sum of charges over the metabolites that take part in a reaction. Only the -0064 % participants may be summed: S is sparse and 0*NaN is NaN, so any unset -0065 % charge in the model would otherwise poison the result. Summing with -0066 % 'omitnan' instead would treat an unset charge as neutral and silently -0067 % rebalance the reaction to the wrong coefficient. -0068 metIdx = find(model.S(:, rxnPos)); -0069 if ~isfield(model, 'metCharges') -0070 error('scaleBiomassPseudoreaction:noCharges', ... -0071 ['Cannot rebalance charge: the model has no metCharges field. ' ... -0072 'Provide metabolite charges, or rescale without charge balancing.']); -0073 end -0074 unknown = metIdx(isnan(model.metCharges(metIdx))); -0075 if ~isempty(unknown) -0076 error('scaleBiomassPseudoreaction:unknownCharge', ... -0077 ['Cannot rebalance charge of %s: the following metabolites have no ' ... -0078 'charge, so the charge balance is unknown rather than zero:\n\t%s'], ... -0079 model.rxns{rxnPos}, strjoin(model.mets(unknown), ', ')); -0080 end -0081 residual = sum(full(model.S(metIdx, rxnPos)) .* model.metCharges(metIdx)); -0082 end -0083 -0084 function comp = findComponent(cfg, name) -0085 for i = 1:numel(cfg.components) -0086 if strcmp(cfg.components{i}.name, name) -0087 comp = cfg.components{i}; -0088 return; -0089 end -0090 end -0091 error('scaleBiomassPseudoreaction:unknownComponent', ... -0092 'biomassConfig has no component named %s', name); -0093 end

    +0009 % metabolite name differs from the component name; the coefficient of the +0010 % component's product is unchanged. After rescaling, the coefficient of +0011 % biomassConfig.proton_met is recomputed so that the total charge of the +0012 % pseudoreaction is zero. An error is raised if the model has no +0013 % metCharges field or if a metabolite other than proton_met in the +0014 % pseudoreaction has an undefined (NaN) charge. +0015 % +0016 % Parameters +0017 % ---------- +0018 % model : struct +0019 % RAVEN model struct. +0020 % biomassConfig : struct +0021 % Struct (see getBiomassFractions). +0022 % componentName : char +0023 % Name of the component to rescale. It must match +0024 % biomassConfig.components{i}.name for some i and the model.metNames +0025 % entry of the produced metabolite in the matching pseudoreaction. +0026 % factor : double +0027 % Factor by which the substrate coefficients are multiplied. +0028 % +0029 % Returns +0030 % ------- +0031 % model : struct +0032 % Modified model. +0033 % +0034 % Examples +0035 % -------- +0036 % model = scaleBiomassPseudoreaction(model, biomassConfig, 'protein', 0.9); +0037 % +0038 % See also +0039 % -------- +0040 % getBiomassFractions +0041 +0042 comp = findComponent(biomassConfig, componentName); +0043 rxnPos = find(strcmp(model.rxnNames, comp.pseudoreaction_name)); +0044 if isempty(rxnPos) +0045 error('scaleBiomassPseudoreaction:missingPseudoreaction', ... +0046 'No reaction named %s in model.', comp.pseudoreaction_name); +0047 end +0048 +0049 Hc = find(strcmp(model.mets, biomassConfig.proton_met)); +0050 if isempty(Hc) +0051 error('scaleBiomassPseudoreaction:missingProton', ... +0052 'Proton metabolite %s not found in model.', biomassConfig.proton_met); +0053 end +0054 +0055 for i = 1:length(model.mets) +0056 S_ir = model.S(i, rxnPos); +0057 isProd = strcmp(model.metNames{i}, componentName); +0058 if S_ir ~= 0 && ~isProd +0059 model.S(i, rxnPos) = factor * S_ir; +0060 end +0061 end +0062 +0063 % Rebalance H+ to keep charge neutrality. +0064 model.S(Hc, rxnPos) = 0; +0065 model.S(Hc, rxnPos) = -chargeResidual(model, rxnPos); +0066 end +0067 +0068 function residual = chargeResidual(model, rxnPos) +0069 % Sum of charges over the metabolites that take part in a reaction. Only the +0070 % participants may be summed: S is sparse and 0*NaN is NaN, so any unset +0071 % charge in the model would otherwise poison the result. Summing with +0072 % 'omitnan' instead would treat an unset charge as neutral and silently +0073 % rebalance the reaction to the wrong coefficient. +0074 metIdx = find(model.S(:, rxnPos)); +0075 if ~isfield(model, 'metCharges') +0076 error('scaleBiomassPseudoreaction:noCharges', ... +0077 ['Cannot rebalance charge: the model has no metCharges field. ' ... +0078 'Provide metabolite charges, or rescale without charge balancing.']); +0079 end +0080 unknown = metIdx(isnan(model.metCharges(metIdx))); +0081 if ~isempty(unknown) +0082 error('scaleBiomassPseudoreaction:unknownCharge', ... +0083 ['Cannot rebalance charge of %s: the following metabolites have no ' ... +0084 'charge, so the charge balance is unknown rather than zero:\n\t%s'], ... +0085 model.rxns{rxnPos}, strjoin(model.mets(unknown), ', ')); +0086 end +0087 residual = sum(full(model.S(metIdx, rxnPos)) .* model.metCharges(metIdx)); +0088 end +0089 +0090 function comp = findComponent(cfg, name) +0091 for i = 1:numel(cfg.components) +0092 if strcmp(cfg.components{i}.name, name) +0093 comp = cfg.components{i}; +0094 return; +0095 end +0096 end +0097 error('scaleBiomassPseudoreaction:unknownComponent', ... +0098 'biomassConfig has no component named %s', name); +0099 end
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/biomass/setGAM.html b/docs/html/biomass/setGAM.html new file mode 100644 index 00000000..b9530942 --- /dev/null +++ b/docs/html/biomass/setGAM.html @@ -0,0 +1,188 @@ + + + + Description of setGAM + + + + + + + + + +
    Home > biomass > setGAM.m
    + + + +

    setGAM +

    + +

    PURPOSE ^

    +
    setGAM Set the growth-associated maintenance (GAM) coefficient.
    + +

    SYNOPSIS ^

    +
    function model = setGAM(model, value, biomassRxn, cofactorMetNames, varargin)
    + +

    DESCRIPTION ^

    +
     setGAM  Set the growth-associated maintenance (GAM) coefficient.
    +
    + Set the growth-associated maintenance (GAM) coefficient in the biomass
    + pseudoreaction, and optionally fix the non-growth maintenance (NGAM)
    + reaction's bounds. The MATLAB counterpart of yeast-GEM's legacy
    + changeGAM.
    +
    + For every metabolite in the biomass pseudoreaction whose model.metNames
    + entry is in cofactorMetNames, the stoichiometric coefficient is set to
    + value with the sign of the current coefficient. An error is raised
    + before the model is changed if ngamRxn is given and is not in the model,
    + and if ngamValue is given without ngamRxn (RAVEN:badInput).
    + For yeast-GEM, the cofactors are ATP, ADP, H2O, H+ and phosphate (ATP and
    + H2O are substrates; ADP, H+ and phosphate are products).
    +
    + Parameters
    + ----------
    + model : struct
    +     RAVEN model struct.
    + value : double
    +     New GAM value (mmol ATP / gDW per growth unit).
    + biomassRxn : char
    +     Reaction id of the biomass pseudoreaction.
    + cofactorMetNames : cell
    +     Cell array of metabolite names (matched against model.metNames, not
    +     metabolite ids) to rescale, for example
    +     {'ATP','ADP','H2O','H+','phosphate'}.
    +
    + Name-Value Arguments
    + --------------------
    + ngamRxn : char
    +     NGAM reaction id. Required when ngamValue is supplied.
    + ngamValue : double
    +     NGAM flux to fix. Both bounds of the NGAM reaction are set to
    +     ngamValue.
    +
    + Returns
    + -------
    + model : struct
    +     Modified model.
    +
    + Examples
    + --------
    +     model = setGAM(model, 80, 'r_4041', {'ATP','ADP','H2O','H+','phosphate'});
    +
    + See also
    + --------
    + fitParameters : derives such a value from measured fluxes; setGAM then
    +     applies it.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function model = setGAM(model, value, biomassRxn, cofactorMetNames, varargin)
    +0002 % setGAM  Set the growth-associated maintenance (GAM) coefficient.
    +0003 %
    +0004 % Set the growth-associated maintenance (GAM) coefficient in the biomass
    +0005 % pseudoreaction, and optionally fix the non-growth maintenance (NGAM)
    +0006 % reaction's bounds. The MATLAB counterpart of yeast-GEM's legacy
    +0007 % changeGAM.
    +0008 %
    +0009 % For every metabolite in the biomass pseudoreaction whose model.metNames
    +0010 % entry is in cofactorMetNames, the stoichiometric coefficient is set to
    +0011 % value with the sign of the current coefficient. An error is raised
    +0012 % before the model is changed if ngamRxn is given and is not in the model,
    +0013 % and if ngamValue is given without ngamRxn (RAVEN:badInput).
    +0014 % For yeast-GEM, the cofactors are ATP, ADP, H2O, H+ and phosphate (ATP and
    +0015 % H2O are substrates; ADP, H+ and phosphate are products).
    +0016 %
    +0017 % Parameters
    +0018 % ----------
    +0019 % model : struct
    +0020 %     RAVEN model struct.
    +0021 % value : double
    +0022 %     New GAM value (mmol ATP / gDW per growth unit).
    +0023 % biomassRxn : char
    +0024 %     Reaction id of the biomass pseudoreaction.
    +0025 % cofactorMetNames : cell
    +0026 %     Cell array of metabolite names (matched against model.metNames, not
    +0027 %     metabolite ids) to rescale, for example
    +0028 %     {'ATP','ADP','H2O','H+','phosphate'}.
    +0029 %
    +0030 % Name-Value Arguments
    +0031 % --------------------
    +0032 % ngamRxn : char
    +0033 %     NGAM reaction id. Required when ngamValue is supplied.
    +0034 % ngamValue : double
    +0035 %     NGAM flux to fix. Both bounds of the NGAM reaction are set to
    +0036 %     ngamValue.
    +0037 %
    +0038 % Returns
    +0039 % -------
    +0040 % model : struct
    +0041 %     Modified model.
    +0042 %
    +0043 % Examples
    +0044 % --------
    +0045 %     model = setGAM(model, 80, 'r_4041', {'ATP','ADP','H2O','H+','phosphate'});
    +0046 %
    +0047 % See also
    +0048 % --------
    +0049 % fitParameters : derives such a value from measured fluxes; setGAM then
    +0050 %     applies it.
    +0051 
    +0052 if nargin < 4
    +0053     error('setGAM:missingArgs', ...
    +0054         'biomassRxn and cofactorMetNames are required.');
    +0055 end
    +0056 
    +0057 p=parseRAVENargs(varargin, {'ngamRxn',[]; 'ngamValue',[]});
    +0058 ngamRxn=p.ngamRxn;
    +0059 ngamValue=p.ngamValue;
    +0060 %An empty id counts as not given
    +0061 if ~isempty(ngamRxn)
    +0062     ngamRxn=cellstr(ngamRxn);
    +0063     ngamRxn=ngamRxn(~cellfun(@isempty,ngamRxn));
    +0064 end
    +0065 if ~isempty(ngamValue) && isempty(ngamRxn)
    +0066     error('RAVEN:badInput', ...
    +0067         'ngamValue is given without ngamRxn. Give the NGAM reaction id as ''ngamRxn''.');
    +0068 end
    +0069 
    +0070 bioPos = strcmp(model.rxns, biomassRxn);
    +0071 if ~any(bioPos)
    +0072     error('setGAM:missingBiomassRxn', ...
    +0073         'Reaction %s not found in model.', biomassRxn);
    +0074 end
    +0075 
    +0076 setNgam = ~isempty(ngamRxn) && ~isempty(ngamValue);
    +0077 if ~isempty(ngamRxn) && ~all(ismember(ngamRxn, model.rxns))
    +0078     error('setGAM:missingNgamRxn', ...
    +0079         'NGAM reaction %s not found in model.', strjoin(ngamRxn, ', '));
    +0080 end
    +0081 
    +0082 for i = 1:length(model.mets)
    +0083     S_ix = model.S(i, bioPos);
    +0084     isCofactor = any(strcmp(cofactorMetNames, model.metNames{i}));
    +0085     if S_ix ~= 0 && isCofactor
    +0086         model.S(i, bioPos) = sign(S_ix) * value;
    +0087     end
    +0088 end
    +0089 
    +0090 if setNgam
    +0091     model = setParam(model, 'eq', ngamRxn, ngamValue);
    +0092 end
    +0093 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/c++.png b/docs/html/c++.png similarity index 100% rename from doc/c++.png rename to docs/html/c++.png diff --git a/doc/c.png b/docs/html/c.png similarity index 100% rename from doc/c.png rename to docs/html/c.png diff --git a/docs/html/comparison/compareMultipleModels.html b/docs/html/comparison/compareMultipleModels.html new file mode 100644 index 00000000..405e9e0e --- /dev/null +++ b/docs/html/comparison/compareMultipleModels.html @@ -0,0 +1,684 @@ + + + + Description of compareMultipleModels + + + + + + + + + +
    Home > comparison > compareMultipleModels.m
    + + + +

    compareMultipleModels +

    + +

    PURPOSE ^

    +
    compareMultipleModels Compare two or more condition-specific models.
    + +

    SYNOPSIS ^

    +
    function compStruct = compareMultipleModels(models,varargin)
    + +

    DESCRIPTION ^

    +
     compareMultipleModels  Compare two or more condition-specific models.
    +
    + Compares two or more models on overlap of reactions, metabolites, genes,
    + EC-codes, metabolite names and reaction equations (with and without
    + compartmentalisation), and on high-dimensional comparisons of the
    + reaction content: subsystem coverage, structural similarity, a
    + low-dimensional structural projection (skipped when neither tsne nor
    + mdscale is available), and an optional functional (task-based) comparison.
    +
    + Parameters
    + ----------
    + models : cell
    +     cell array of two or more models.
    +
    + Name-Value Arguments
    + --------------------
    + printResults : logical
    +     true to print the results to the command window (default false).
    + plotResults : logical
    +     true to plot the results (default false).
    + groupVector : double or cell
    +     numeric vector or cell array that assigns each model to a group, for
    +     example by tissue. The groups colour the points of the structural
    +     projection plot (default [], no grouping).
    + funcCompare : logical
    +     true to run the functional comparison, which requires taskFile
    +     (default false).
    + taskFile : char
    +     name of the task file for the functional comparison, in a format that
    +     parseTaskList reads. Required when funcCompare is true.
    +
    + Returns
    + -------
    + compStruct : struct
    +     structure that contains the comparison results, with fields:
    +
    +     - modelIDs : cell array of model ids
    +     - rxns, mets, genes, eccodes, metNames, equ, uEqu : the overlap for
    +       each field, each with sub-fields comparison (binary matrix where each
    +       row indicates which models are included in the comparison) and
    +       nElements (vector with the number of elements for each comparison).
    +       "equ" are the reaction equations after sorting and "uEqu" are the
    +       equations without compartments. The fields rxns, mets and genes
    +       compare identifiers; eccodes, metNames, equ and uEqu compare EC
    +       codes, metabolite names and reaction equations, which allows
    +       overlap between models that use different identifiers.
    +     - subsystems : substructure containing subsystem information, with
    +       fields matrix (number of reactions per subsystem, subsystems in rows
    +       and models in columns) and ID (cell array with the names of all
    +       subsystems). Only the first subsystem of each reaction is counted.
    +       The field is absent when a model lacks the subSystems field.
    +     - reactions : substructure containing reaction information, with
    +       fields matrix (binary matrix with reactions in rows and models in
    +       columns, used as the input for the model comparisons) and IDs
    +       (list of the reactions contained in the reaction matrix)
    +     - structComp : matrix with pairwise comparisons of model structure
    +       based on (1 - Hamming distance) between models
    +     - structCompMap : matrix with the 3D tSNE (or MDS) mapping of model
    +       structures based on Hamming distances. It is empty when neither
    +       tsne nor mdscale is available.
    +     - funcComp : substructure containing function comparison results, with
    +       fields matrix (1 for a passed task and 0 for a failed task, tasks in
    +       rows and models in columns) and tasks (the descriptions of all
    +       tasks). It is present only when funcCompare is true.
    +
    + Examples
    + --------
    +     compStruct = compareMultipleModels(models, printResults, ...
    +         plotResults, groupVector, funcCompare, taskFile);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function compStruct = compareMultipleModels(models,varargin)
    +0002 % compareMultipleModels  Compare two or more condition-specific models.
    +0003 %
    +0004 % Compares two or more models on overlap of reactions, metabolites, genes,
    +0005 % EC-codes, metabolite names and reaction equations (with and without
    +0006 % compartmentalisation), and on high-dimensional comparisons of the
    +0007 % reaction content: subsystem coverage, structural similarity, a
    +0008 % low-dimensional structural projection (skipped when neither tsne nor
    +0009 % mdscale is available), and an optional functional (task-based) comparison.
    +0010 %
    +0011 % Parameters
    +0012 % ----------
    +0013 % models : cell
    +0014 %     cell array of two or more models.
    +0015 %
    +0016 % Name-Value Arguments
    +0017 % --------------------
    +0018 % printResults : logical
    +0019 %     true to print the results to the command window (default false).
    +0020 % plotResults : logical
    +0021 %     true to plot the results (default false).
    +0022 % groupVector : double or cell
    +0023 %     numeric vector or cell array that assigns each model to a group, for
    +0024 %     example by tissue. The groups colour the points of the structural
    +0025 %     projection plot (default [], no grouping).
    +0026 % funcCompare : logical
    +0027 %     true to run the functional comparison, which requires taskFile
    +0028 %     (default false).
    +0029 % taskFile : char
    +0030 %     name of the task file for the functional comparison, in a format that
    +0031 %     parseTaskList reads. Required when funcCompare is true.
    +0032 %
    +0033 % Returns
    +0034 % -------
    +0035 % compStruct : struct
    +0036 %     structure that contains the comparison results, with fields:
    +0037 %
    +0038 %     - modelIDs : cell array of model ids
    +0039 %     - rxns, mets, genes, eccodes, metNames, equ, uEqu : the overlap for
    +0040 %       each field, each with sub-fields comparison (binary matrix where each
    +0041 %       row indicates which models are included in the comparison) and
    +0042 %       nElements (vector with the number of elements for each comparison).
    +0043 %       "equ" are the reaction equations after sorting and "uEqu" are the
    +0044 %       equations without compartments. The fields rxns, mets and genes
    +0045 %       compare identifiers; eccodes, metNames, equ and uEqu compare EC
    +0046 %       codes, metabolite names and reaction equations, which allows
    +0047 %       overlap between models that use different identifiers.
    +0048 %     - subsystems : substructure containing subsystem information, with
    +0049 %       fields matrix (number of reactions per subsystem, subsystems in rows
    +0050 %       and models in columns) and ID (cell array with the names of all
    +0051 %       subsystems). Only the first subsystem of each reaction is counted.
    +0052 %       The field is absent when a model lacks the subSystems field.
    +0053 %     - reactions : substructure containing reaction information, with
    +0054 %       fields matrix (binary matrix with reactions in rows and models in
    +0055 %       columns, used as the input for the model comparisons) and IDs
    +0056 %       (list of the reactions contained in the reaction matrix)
    +0057 %     - structComp : matrix with pairwise comparisons of model structure
    +0058 %       based on (1 - Hamming distance) between models
    +0059 %     - structCompMap : matrix with the 3D tSNE (or MDS) mapping of model
    +0060 %       structures based on Hamming distances. It is empty when neither
    +0061 %       tsne nor mdscale is available.
    +0062 %     - funcComp : substructure containing function comparison results, with
    +0063 %       fields matrix (1 for a passed task and 0 for a failed task, tasks in
    +0064 %       rows and models in columns) and tasks (the descriptions of all
    +0065 %       tasks). It is present only when funcCompare is true.
    +0066 %
    +0067 % Examples
    +0068 % --------
    +0069 %     compStruct = compareMultipleModels(models, printResults, ...
    +0070 %         plotResults, groupVector, funcCompare, taskFile);
    +0071 
    +0072 % The core comparison (structComp) runs in base MATLAB. The low-dimensional
    +0073 % projection (structCompMap) and the heatmap clustering additionally use the
    +0074 % Statistics and Machine Learning Toolbox (tsne/mdscale, linkage); these
    +0075 % degrade gracefully and are skipped when the toolbox is not available.
    +0076 %
    +0077 % See also
    +0078 % --------
    +0079 % diffModels : reports the differing values (stoichiometry, bounds, grRules)
    +0080 %     between two models.
    +0081 
    +0082 %% Set up input defaults
    +0083 p=parseRAVENargs(varargin, {'printResults',false; 'plotResults',false; 'groupVector',[]; 'funcCompare',false; 'taskFile',[]});
    +0084 printResults=p.printResults;
    +0085 plotResults=p.plotResults;
    +0086 groupVector=p.groupVector;
    +0087 if isempty(groupVector)
    +0088     groupVector = [];
    +0089 elseif ~isnumeric(groupVector)
    +0090     % convert strings to numeric groups
    +0091     [groupNames,~,groupVector] = unique(groupVector);
    +0092 else
    +0093     % generate group names for vector of numbers
    +0094     groupNames = arrayfun(@num2str,unique(groupVector),'UniformOutput',false);
    +0095 end
    +0096 funcCompare=p.funcCompare;
    +0097 taskFile=p.taskFile;
    +0098 if ~isempty(taskFile)
    +0099     taskFile=char(taskFile);
    +0100 end
    +0101 if numel(models) <= 1
    +0102     EM = 'Cannot compare only one model. Use printModelStats if you want a summary of a model';
    +0103     error('RAVEN:badInput', '%s', EM);
    +0104 end
    +0105 if isempty(taskFile) && funcCompare
    +0106     EM = 'Cannot perform the functional comparison without a task file. Specify taskFile or set funcCompare to FALSE.';
    +0107     error('RAVEN:badInput', '%s', EM);
    +0108 end
    +0109 
    +0110 %% Set up model ID structure
    +0111 compStruct.modelIDs = {};
    +0112 fprintf('\n Getting model IDs \n')
    +0113 for i = 1:numel(models)
    +0114     if ~ischar(models{i}.id)  % to deal with non-character IDs (cells, strings, etc)
    +0115         compStruct.modelIDs{i,1} = models{i}.id{1};
    +0116     else
    +0117         compStruct.modelIDs{i,1} = models{i}.id;
    +0118     end
    +0119     % Equations are computed once here (with and without compartmentalization)
    +0120     % so the overlap comparison below can treat them like any other field.
    +0121     models{i}.equ=constructEquations(models{i},models{i}.rxns,true,true,true);
    +0122     models{i}.uEqu=constructEquations(models{i},models{i}.rxns,false,true,true);
    +0123 end
    +0124 fprintf('*** Done \n\n')
    +0125 
    +0126 %% Compare overlap of reactions, metabolites, genes, EC-codes, metabolite
    +0127 % names and equations (with and without compartmentalization)
    +0128 fprintf('\n Comparing overlap of reactions, genes, metabolites and compartments \n')
    +0129 overlapFields={'rxns','mets','genes','eccodes','metNames','equ','uEqu'};
    +0130 overlapLabels={'reaction IDs','metabolite IDs','gene IDs','ec-numbers', ...
    +0131     'metabolite names','equations with compartment','equations without compartment'};
    +0132 for i=1:numel(overlapFields)
    +0133     field=overlapFields{i};
    +0134     compStruct.(field).comparison=getToCheck(models,field);
    +0135     compStruct.(field).nElements=checkStuff(getElements(models,field),compStruct.(field).comparison);
    +0136     if printResults==true
    +0137         fprintf(['*** Comparison of ' overlapLabels{i} ':\n']);
    +0138         printList(models,compStruct.(field).comparison,compStruct.(field).nElements);
    +0139         fprintf('\n\n');
    +0140     end
    +0141 end
    +0142 fprintf('*** Done \n\n')
    +0143 
    +0144 %% Compare models structure & function based on high-dimensional methods
    +0145 % Compare number of reactions in each subsystem in each model using a heatmap
    +0146 field = 'subSystems';
    +0147 fprintf('\n Comparing subsystem utilization \n')
    +0148 if any(~cellfun(@(m) isfield(m,field),models))
    +0149     fprintf('\nWARNING: At least one model does not contain the field "subSystems". \n')
    +0150     fprintf('         Skipping subsystem comparison. \n\n')
    +0151 else
    +0152     %% Flatten model subSystems field
    +0153     % Convert from cell array of cells to cell array of strings
    +0154     % NOTE: this function currently only recognizes one subSystem per reaction;
    +0155     %       additional subSystems will be ignored!
    +0156     for i = 1:numel(models)
    +0157         if any(cellfun(@(x) iscell(x), models{i}.subSystems));
    +0158             cells = cellfun(@iscell,models{i}.subSystems);
    +0159             models{i}.subSystems(cells) = cellfun(@(s) s{1}, models{i}.subSystems(cells), 'UniformOutput', false);
    +0160         end
    +0161     end
    +0162     [id,compMat] = compareModelField(models,field);
    +0163     compStruct.subsystems.ID = id;
    +0164     compStruct.subsystems.matrix = compMat;
    +0165     fprintf('*** Done \n\n')
    +0166 
    +0167     if printResults
    +0168         % This could use some cleaning up
    +0169         fprintf('*** Comparison of reaction subsystem populations:\n\n');
    +0170 
    +0171         nrow = min([15,numel(compStruct.subsystems.ID)]);
    +0172         ncol = min([10,numel(compStruct.modelIDs)]);
    +0173         summaryArray = [{field}, compStruct.modelIDs(1:ncol)'];
    +0174         summaryArray = [summaryArray; [compStruct.subsystems.ID(1:nrow), ...
    +0175             arrayfun(@num2str,compStruct.subsystems.matrix(1:nrow,1:ncol),'UniformOutput',false)]];
    +0176 
    +0177         charArray = [];
    +0178         for i = 1:size(summaryArray,2)
    +0179             charArray = [charArray, char(strcat(summaryArray(:,i),{'   '}))];
    +0180         end
    +0181         disp(charArray);
    +0182         if numel(compStruct.subsystems.ID) > 15
    +0183             fprintf('...\n');
    +0184         end
    +0185         fprintf('\n\n');
    +0186     end
    +0187 
    +0188     if plotResults==true
    +0189         % Plot all subsystems
    +0190         figure;
    +0191         plottingData = (compStruct.subsystems.matrix - mean(compStruct.subsystems.matrix,2))./mean(compStruct.subsystems.matrix,2);
    +0192         color_map = redblue(length(0:.01:2));
    +0193         h = genHeatMap(plottingData',compStruct.subsystems.ID,compStruct.modelIDs,'both','euclidean',color_map,[-1,1]);
    +0194         title('Subsystem Coverage - all subsystems','FontSize',18,'FontWeight','bold')
    +0195 
    +0196         % Plot only subsystems with deviation from mean
    +0197         keepSubs = (sum(plottingData~=0,2) ~= 0);
    +0198         if sum(keepSubs) > 1
    +0199             figure;
    +0200             h_small = genHeatMap(plottingData(keepSubs,:)',compStruct.subsystems.ID(keepSubs),...
    +0201                 compStruct.modelIDs,'both','euclidean',color_map,[-1,1]);
    +0202             title('Subsystem Coverage','FontSize',18,'FontWeight','bold')
    +0203 
    +0204             % Plot enrichment in subsystems with deviation from mean
    +0205             figure;
    +0206             color_map_bw = [1 1 1;0 0 0];
    +0207             h_enriched = genHeatMap(plottingData(keepSubs,:)',compStruct.subsystems.ID(keepSubs),...
    +0208                 compStruct.modelIDs,'both','euclidean',color_map_bw,[-10^-4,10^-4]);
    +0209             title('Subsystem Enrichment','FontSize',18,'FontWeight','bold')
    +0210         end
    +0211     end
    +0212 
    +0213 end
    +0214 
    +0215 % Compare overall reaction structure across all models using a heatmap
    +0216 field = 'rxns';
    +0217 fprintf('\n Comparing model reaction correlations \n')
    +0218 
    +0219 % Create binary matrix of reactions
    +0220 [id,binary_matrix] = compareModelField(models,field);
    +0221 compStruct.reactions.IDs = id;
    +0222 compStruct.reactions.matrix = binary_matrix;
    +0223 
    +0224 % calculate hamming similarity (1 - Hamming distance) between models. This
    +0225 % is done in base MATLAB so the Statistics Toolbox is not required: for
    +0226 % binary columns the agreement fraction equals 1 minus the Hamming distance.
    +0227 B = double(binary_matrix);
    +0228 compStruct.structComp = (B'*B + (1-B)'*(1-B)) / size(B,1);
    +0229 fprintf('*** Done \n\n')
    +0230 if plotResults == true
    +0231     color_map = [ones(100,1) linspace(1,0,100)' linspace(1,0,100)'];
    +0232     figure;
    +0233     h = genHeatMap(compStruct.structComp,compStruct.modelIDs,compStruct.modelIDs,'both','euclidean',color_map);
    +0234     title('Structural Similarity','FontSize',18,'FontWeight','bold')
    +0235 end
    +0236 
    +0237 % Compare overall reaction structure across all models using a 3D tSNE (or
    +0238 % MDS) projection. Both require the Statistics and Machine Learning Toolbox;
    +0239 % if neither is available the projection is skipped and structCompMap is left
    +0240 % empty.
    +0241 rng(42) % For consistency
    +0242 fprintf('\n Comparing model reaction structures \n')
    +0243 proj_coords = [];
    +0244 axis_labels = {};
    +0245 if exist('tsne','file') > 0
    +0246     proj_coords = tsne(double(binary_matrix'),'Distance','hamming','NumDimensions',3); % 3D
    +0247     axis_labels = {'tSNE 1';'tSNE 2';'tSNE 3'};
    +0248 elseif exist('mdscale','file') > 0
    +0249     proj_coords = mdscale(pdist(double(binary_matrix'),'hamming'),3);
    +0250     axis_labels = {'MDS 1';'MDS 2';'MDS 3'};
    +0251 else
    +0252     fprintf(['   Skipping low-dimensional projection: requires tsne or ' ...
    +0253         'mdscale from the Statistics and Machine Learning Toolbox\n']);
    +0254 end
    +0255 compStruct.structCompMap = proj_coords;
    +0256 fprintf('*** Done \n\n')
    +0257 
    +0258 % plot structure comparison results
    +0259 if plotResults == true && ~isempty(proj_coords)
    +0260     figure; hold on;
    +0261     if ~isempty(groupVector)
    +0262         color_data = groupVector;
    +0263         if length(groupNames) <= 7
    +0264             % "lines" colormap only has 7 unique colors
    +0265             color_palette = lines(length(groupNames));
    +0266         else
    +0267             color_palette = parula(length(groupNames));
    +0268         end
    +0269         colormap(color_palette);
    +0270     else
    +0271         color_data = 'k';
    +0272     end
    +0273     scatter3(proj_coords(:,1),proj_coords(:,2),proj_coords(:,3),35,color_data,'filled');
    +0274     view(135,25);  % to make it obvious that it is a 3D plot
    +0275     xlabel(axis_labels{1}); ylabel(axis_labels{2}); zlabel(axis_labels{3});
    +0276     set(gca,'FontSize',14,'LineWidth',1.25);
    +0277     title('Structural Comparison','FontSize',18,'FontWeight','bold')
    +0278 
    +0279     % add legend
    +0280     if ~isempty(groupVector)
    +0281         for i = 1:length(groupNames)
    +0282             h(i) = scatter3([],[],[],35,color_palette(i,:),'filled');
    +0283         end
    +0284         legend(h,groupNames);
    +0285     end
    +0286 end
    +0287 
    +0288 % Compare model functions by assessing their capacity to perform tasks
    +0289 if funcCompare == true && ~isempty(taskFile)
    +0290     fprintf('\n Checking model performance on specified tasks. \n')
    +0291     taskStructure=parseTaskList(taskFile);
    +0292     for i = 1:numel(models)
    +0293         fprintf('\n Checking model # %.0f \n',i)
    +0294         taskReport{i} = checkTasks(models{i},[],false,false,false,taskStructure);
    +0295     end
    +0296 
    +0297     % Save results
    +0298     taskMatrix = zeros(length(taskReport{1}.ok),numel(taskReport));
    +0299         for i = 1:numel(taskReport)
    +0300             taskMatrix(:,i) = taskReport{i}.ok;
    +0301         end
    +0302     compStruct.funcComp.matrix = taskMatrix;
    +0303     compStruct.funcComp.tasks = taskReport{1}.description;
    +0304     fprintf('*** Done \n\n')
    +0305 
    +0306     % Plot results
    +0307     if plotResults == true
    +0308         figure;
    +0309         color_map_bw = [1 1 1;0 0 0];
    +0310         h_enriched = genHeatMap(taskMatrix,compStruct.modelIDs,...
    +0311             taskReport{1}.description,'both','euclidean',color_map_bw,[0,1]);
    +0312         title('Functional Comparison - All Tasks','FontSize',18,'FontWeight','bold')
    +0313 
    +0314         figure;
    +0315         color_map_bw = [1 1 1;0 0 0];
    +0316         h_enriched = genHeatMap(taskMatrix(intersect(find(sum(taskMatrix,2)~=numel(models)),find(sum(taskMatrix,2)~=0)),:),...
    +0317             compStruct.modelIDs,taskReport{1}.description(intersect(find(sum(taskMatrix,2)~=numel(models)),find(sum(taskMatrix,2)~=0))),...
    +0318             'both','euclidean',color_map_bw,[0,1]);
    +0319         title('Functional Similarity','FontSize',18,'FontWeight','bold')
    +0320     end
    +0321 end
    +0322 
    +0323 end
    +0324 
    +0325 %% Additional Functions
    +0326 
    +0327 function [id,compMat] = compareModelField(models,field)
    +0328     % Generates a list of unique field entries and a matrix quantifying the
    +0329     % number of appearances of each field entry in each model
    +0330 
    +0331     % get unique list of field entries
    +0332     hasfield = cellfun(@(m) isfield(m,field),models);
    +0333     id = cellfun(@(m) m.(field),models(hasfield),'UniformOutput',false);
    +0334     id = unique(vertcat(id{:}));
    +0335 
    +0336     % assemble matrix comparing frequency of each entry in each model
    +0337     compMat = zeros(numel(id),numel(models));
    +0338     for i = 1:numel(models)
    +0339         [~,entryIndex] = ismember(models{i}.(field),id);  % get index of each field entry in the unique id list
    +0340         compMat(:,i) = histcounts(entryIndex, 0.5:1:(numel(id)+0.5));  % determine the frequency at which each index appears
    +0341     end
    +0342 end
    +0343 
    +0344 function A=getElements(models,field)
    +0345 %Gathers the raw field content (e.g. rxns, eccodes, equ) for each model that
    +0346 %has that field, for use as the universe of elements in checkStuff.
    +0347 A={};
    +0348 for i=1:numel(models)
    +0349     if isfield(models{i},field)
    +0350         A=[A;{models{i}.(field)}];
    +0351     end
    +0352 end
    +0353 end
    +0354 
    +0355 function toCheck=getToCheck(models,field)
    +0356 %Get all the combinations that should be checked for overlap (including the
    +0357 %single ones)
    +0358 toCheckA=[];
    +0359 I=find(cellfun(@checkField,models));
    +0360 nI=numel(I);
    +0361 for i=nI:-1:1
    +0362     combs=nchoosek(1:nI,i);
    +0363     toAdd=false(size(combs,1),nI);
    +0364     for j=1:size(combs,1)
    +0365         toAdd(j,combs(j,:))=true;
    +0366     end
    +0367     toCheckA=[toCheckA;toAdd];
    +0368 end
    +0369 
    +0370 %If not all of the models have the required field
    +0371 toCheck=false(size(toCheckA,1),numel(models));
    +0372 toCheck(:,I)=toCheckA;
    +0373 
    +0374 %Ugly thing to get around parameters
    +0375     function I=checkField(A)
    +0376         I=isfield(A,field);
    +0377     end
    +0378 end
    +0379 
    +0380 function printList(models,toCheck,nElements)
    +0381 %To guess how many spaces that are needed to align
    +0382 firstLen=[];
    +0383 for i=1:size(toCheck,1)
    +0384     label=[];
    +0385     I=find(toCheck(i,:));
    +0386     for j=1:numel(I)
    +0387         label=[label models{I(j)}.id '/'];
    +0388     end
    +0389     if i==1
    +0390         firstLen=numel(label);
    +0391     end
    +0392     nSpaces=firstLen-numel(label);
    +0393     fprintf([label(1:end-1) '  ' repmat(sprintf(' '),1,nSpaces) num2str(nElements(i)) '\n']);
    +0394 end
    +0395 end
    +0396 
    +0397 function nElements=checkStuff(A,toCheck)
    +0398 %Now loop through the toCheck matrix, starting with the combination with
    +0399 %the most models. Only elements that were not in iteration n are considered
    +0400 %in iteration n+1.
    +0401 nElements=zeros(size(toCheck,1),1);
    +0402 alreadyChecked=[];
    +0403 for i=1:size(toCheck,1)
    +0404     I=find(toCheck(i,:));
    +0405     inCommon=setdiff(A{I(1)},alreadyChecked);
    +0406     for j=2:numel(I)
    +0407         inCommon=intersect(inCommon,A{I(j)});
    +0408     end
    +0409     alreadyChecked=union(alreadyChecked,inCommon);
    +0410     nElements(i)=numel(inCommon);
    +0411 end
    +0412 end
    +0413 
    +0414 function h = genHeatMap(data,colnames,rownames,clust_dim,clust_dist,col_map,col_bounds,grid_color)
    +0415 %genHeatMap  Generate a heatmap for a given matrix of data.
    +0416 %
    +0417 % Usage:
    +0418 %
    +0419 %   genHeatMap(data,colnames,rownames,clust_dim,clust_dist,col_map,col_bounds,grid_color);
    +0420 %
    +0421 % Inputs:
    +0422 %
    +0423 % data        Numerical matrix.
    +0424 %
    +0425 % colnames    Cell array of data column names.
    +0426 %
    +0427 % rownames    Cell array of data row names.
    +0428 %
    +0429 % clust_dim   "none" - the data will be plotted as provided (DEFAULT)
    +0430 %             "rows" - cluster/rearrange the rows based on distance
    +0431 %             "cols" - cluster/rearrange the columns based on distance
    +0432 %             "both" - cluster/rearrange rows and columns based on distance
    +0433 %
    +0434 % clust_dist  Distance metric to be used for clustering, ignored if
    +0435 %             clust_dim is "none". Options are the same as those for
    +0436 %             distance in pdist (for example "euclidean", "hamming" and
    +0437 %             other metrics).
    +0438 %             (DEFAULT = "euclidean")
    +0439 %
    +0440 % col_map     Colormap, provided as string (for example "parula" or "hot")
    +0441 %             or an Nx3 RGB matrix of N colors.
    +0442 %             (DEFAULT = "hot")
    +0443 %
    +0444 % col_bounds  A 2-element vector with min and max values, to manually set
    +0445 %             the bounds of the colormap.
    +0446 %             (DEFAULT = min/max of data).
    +0447 %
    +0448 % grid_color  Color of the grid surrounding the heatmap cells.
    +0449 %             (DEFAULT = "none")
    +0450 %
    +0451 %
    +0452 
    +0453 % handle input arguments
    +0454 if nargin < 4 || isempty(clust_dim)
    +0455     clust_dim = 'none';
    +0456 elseif ~ismember(clust_dim,{'none','rows','cols','both'})
    +0457     error('%s is not a valid CLUST_DIM option. Choose "none", "rows", "cols", or "both".',clust_dim);
    +0458 end
    +0459 if nargin < 5 || isempty(clust_dist)
    +0460     clust_dist = 'euclidean';
    +0461 end
    +0462 if nargin < 6 || isempty(col_map)
    +0463     col_map = 'hot';
    +0464 end
    +0465 if nargin < 7 || isempty(col_bounds)
    +0466     col_bounds = [min(data(:)),max(data(:))];
    +0467 end
    +0468 if nargin < 8
    +0469     grid_color = 'none';
    +0470 end
    +0471 
    +0472 % perform hierarchical clustering to sort rows/columns (if specified).
    +0473 % Clustering needs the Statistics and Machine Learning Toolbox (linkage,
    +0474 % optimalleaforder, pdist); without it the heatmap is drawn unordered.
    +0475 linkage_method = 'average';
    +0476 if ~strcmp(clust_dim,'none') && ~(exist('linkage','file') && ...
    +0477         exist('optimalleaforder','file') && exist('pdist','file'))
    +0478     fprintf(['   Skipping heatmap clustering: requires the Statistics and ' ...
    +0479         'Machine Learning Toolbox\n']);
    +0480     clust_dim = 'none';
    +0481 end
    +0482 if ismember(clust_dim,{'rows','both'})
    +0483     L = linkage(data,linkage_method,clust_dist);
    +0484     row_ind = optimalleaforder(L,pdist(data,clust_dist));
    +0485 else
    +0486     row_ind = 1:size(data,1);
    +0487 end
    +0488 % perform hierarchical clustering to sort columns (if specified)
    +0489 if ismember(clust_dim,{'cols','both'})
    +0490     L = linkage(data',linkage_method,clust_dist);
    +0491     col_ind = optimalleaforder(L,pdist(data',clust_dist));
    +0492 else
    +0493     col_ind = 1:size(data,2);
    +0494 end
    +0495 
    +0496 % reorder data matrix according to clustering results
    +0497 sortdata = data(row_ind,col_ind);
    +0498 sortrows = rownames(row_ind);
    +0499 sortcols = colnames(col_ind);
    +0500 
    +0501 % check if data is square matrix with identical row and column names
    +0502 if (length(colnames) == length(rownames)) && all(strcmp(colnames,rownames))
    +0503     % flip data so the diagonal is from upper left to lower right
    +0504     sortdata = fliplr(sortdata);
    +0505     sortcols = flipud(sortcols);
    +0506 end
    +0507 
    +0508 % pad data matrix with zeros (pcolor cuts off last row and column)
    +0509 sortdata(end+1,end+1) = 0;
    +0510 
    +0511 % generate pcolor plot
    +0512 a = axes;
    +0513 set(a,'YAxisLocation','Right','XTick',[],'YTick', (1:size(sortdata,1))+0.5,'YTickLabels',sortrows);
    +0514 set(a,'TickLength',[0 0],'XLim',[1 size(sortdata,2)],'YLim',[1 size(sortdata,1)]);
    +0515 hold on
    +0516 
    +0517 h = pcolor(sortdata);
    +0518 set(h,'EdgeColor',grid_color);
    +0519 set(gca,'XTick', (1:size(sortdata,2))+0.5);
    +0520 set(gca,'YTick', (1:size(sortdata,1))+0.5);
    +0521 set(gca,'XTickLabels',sortcols,'YTickLabels',sortrows);
    +0522 set(gca,'XTickLabelRotation',90);
    +0523 colormap(col_map);
    +0524 
    +0525 if ~isempty(col_bounds)
    +0526     caxis(col_bounds);
    +0527 end
    +0528 
    +0529 end
    +0530 
    +0531 
    +0532 function c = redblue(m)
    +0533 %REDBLUE    Shades of red and blue color map
    +0534 %   REDBLUE(M), is an M-by-3 matrix that defines a colormap.
    +0535 %   The colors begin with bright blue, range through shades of
    +0536 %   blue to white, and then through shades of red to bright red.
    +0537 %   REDBLUE, by itself, is the same length as the current figure
    +0538 %   colormap. If no figure exists, MATLAB creates one.
    +0539 %
    +0540 %   For example, to reset the colormap of the current figure:
    +0541 %
    +0542 %             colormap(redblue)
    +0543 %
    +0544 %   See also HSV, GRAY, HOT, BONE, COPPER, PINK, FLAG,
    +0545 %   COLORMAP, RGBPLOT.
    +0546 if nargin < 1, m = size(get(gcf,'colormap'),1); end
    +0547 
    +0548 if (mod(m,2) == 0)
    +0549     % From [0 0 1] to [1 1 1], then [1 1 1] to [1 0 0];
    +0550     m1 = m*0.5;
    +0551     r = (0:m1-1)'/max(m1-1,1);
    +0552     g = r;
    +0553     r = [r; ones(m1,1)];
    +0554     g = [g; flipud(g)];
    +0555     b = flipud(r);
    +0556 else
    +0557     % From [0 0 1] to [1 1 1] to [1 0 0];
    +0558     m1 = floor(m*0.5);
    +0559     r = (0:m1-1)'/max(m1,1);
    +0560     g = r;
    +0561     r = [r; ones(m1+1,1)];
    +0562     g = [g; 1; flipud(g)];
    +0563     b = flipud(r);
    +0564 end
    +0565 
    +0566 c = [r g b];
    +0567 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/comparison/diffModels.html b/docs/html/comparison/diffModels.html new file mode 100644 index 00000000..12a9d1b1 --- /dev/null +++ b/docs/html/comparison/diffModels.html @@ -0,0 +1,362 @@ + + + + Description of diffModels + + + + + + + + + +
    Home > comparison > diffModels.m
    + + + +

    diffModels +

    + +

    PURPOSE ^

    +
    diffModels Report the semantic differences between two models.
    + +

    SYNOPSIS ^

    +
    function report = diffModels(modelA, modelB, varargin)
    + +

    DESCRIPTION ^

    +
     diffModels  Report the semantic differences between two models.
    +
    + Compares two models entry by entry, keyed by identifier, and returns a
    + report listing every difference found. Reactions, metabolites and genes
    + are matched by id and reported when present in only one model. For the
    + ids present in both models, the reaction stoichiometry, bounds,
    + objective coefficient, grRule and EC codes, and the metabolite formula,
    + charge and compartment are compared.
    +
    + grRules are compared by logical content: each rule is expanded to
    + disjunctive normal form, and the genes within each isozyme and the
    + isozymes themselves are sorted before comparison, so "a and b" equals
    + "b and a" and "(a or b)" equals "(b or a)".
    +
    + Parameters
    + ----------
    + modelA : struct
    +     the first model.
    + modelB : struct
    +     the second model.
    +
    + Name-Value Arguments
    + --------------------
    + stoichTol : double
    +     absolute tolerance for stoichiometric coefficient differences
    +     (default 1e-9).
    + maxPerCategory : double
    +     maximum number of entries in report.differences per category. Further
    +     differences of that category are replaced by a single summary line
    +     (default 50).
    + printResults : logical
    +     true to print the report to the command window (default false).
    +
    + Returns
    + -------
    + report : struct
    +     difference report with fields:
    +
    +     - equal : true if no differences were found.
    +     - differences : cell array of human-readable difference strings, each
    +       naming the entity affected.
    +     - rxnsOnlyInA and rxnsOnlyInB : reaction ids present in only one
    +       model.
    +     - metsOnlyInA and metsOnlyInB : metabolite ids present in only one
    +       model.
    +     - genesOnlyInA and genesOnlyInB : gene ids present in only one model.
    +       Both are empty when either model lacks the genes field.
    +
    + See also
    + --------
    + compareMultipleModels : overlap counts, similarity and clustering across
    +     two or more models.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function report = diffModels(modelA, modelB, varargin)
    +0002 % diffModels  Report the semantic differences between two models.
    +0003 %
    +0004 % Compares two models entry by entry, keyed by identifier, and returns a
    +0005 % report listing every difference found. Reactions, metabolites and genes
    +0006 % are matched by id and reported when present in only one model. For the
    +0007 % ids present in both models, the reaction stoichiometry, bounds,
    +0008 % objective coefficient, grRule and EC codes, and the metabolite formula,
    +0009 % charge and compartment are compared.
    +0010 %
    +0011 % grRules are compared by logical content: each rule is expanded to
    +0012 % disjunctive normal form, and the genes within each isozyme and the
    +0013 % isozymes themselves are sorted before comparison, so "a and b" equals
    +0014 % "b and a" and "(a or b)" equals "(b or a)".
    +0015 %
    +0016 % Parameters
    +0017 % ----------
    +0018 % modelA : struct
    +0019 %     the first model.
    +0020 % modelB : struct
    +0021 %     the second model.
    +0022 %
    +0023 % Name-Value Arguments
    +0024 % --------------------
    +0025 % stoichTol : double
    +0026 %     absolute tolerance for stoichiometric coefficient differences
    +0027 %     (default 1e-9).
    +0028 % maxPerCategory : double
    +0029 %     maximum number of entries in report.differences per category. Further
    +0030 %     differences of that category are replaced by a single summary line
    +0031 %     (default 50).
    +0032 % printResults : logical
    +0033 %     true to print the report to the command window (default false).
    +0034 %
    +0035 % Returns
    +0036 % -------
    +0037 % report : struct
    +0038 %     difference report with fields:
    +0039 %
    +0040 %     - equal : true if no differences were found.
    +0041 %     - differences : cell array of human-readable difference strings, each
    +0042 %       naming the entity affected.
    +0043 %     - rxnsOnlyInA and rxnsOnlyInB : reaction ids present in only one
    +0044 %       model.
    +0045 %     - metsOnlyInA and metsOnlyInB : metabolite ids present in only one
    +0046 %       model.
    +0047 %     - genesOnlyInA and genesOnlyInB : gene ids present in only one model.
    +0048 %       Both are empty when either model lacks the genes field.
    +0049 %
    +0050 % See also
    +0051 % --------
    +0052 % compareMultipleModels : overlap counts, similarity and clustering across
    +0053 %     two or more models.
    +0054 
    +0055 p = parseRAVENargs(varargin, {'stoichTol', 1e-9; 'maxPerCategory', 50; ...
    +0056     'printResults', false});
    +0057 stoichTol = p.stoichTol;
    +0058 maxPerCategory = p.maxPerCategory;
    +0059 printResults = p.printResults;
    +0060 
    +0061 diffs = {};                 % collected difference strings
    +0062 counts = struct();          % per-category counters, for truncation
    +0063 
    +0064 report = struct();
    +0065 report.equal = true;
    +0066 report.differences = {};
    +0067 
    +0068 % --- Identifier sets --------------------------------------------------
    +0069 [report.rxnsOnlyInA, report.rxnsOnlyInB, commonRxns] = idSets(modelA.rxns, modelB.rxns);
    +0070 [report.metsOnlyInA, report.metsOnlyInB, commonMets] = idSets(modelA.mets, modelB.mets);
    +0071 if isfield(modelA,'genes') && isfield(modelB,'genes')
    +0072     [report.genesOnlyInA, report.genesOnlyInB] = idSets(modelA.genes, modelB.genes);
    +0073 else
    +0074     report.genesOnlyInA = {}; report.genesOnlyInB = {};
    +0075 end
    +0076 
    +0077 reportSet('reactions', report.rxnsOnlyInA, report.rxnsOnlyInB);
    +0078 reportSet('metabolites', report.metsOnlyInA, report.metsOnlyInB);
    +0079 reportSet('genes', report.genesOnlyInA, report.genesOnlyInB);
    +0080 
    +0081 % --- Reaction content, on the shared ids ------------------------------
    +0082 [~, ia] = ismember(commonRxns, modelA.rxns);
    +0083 [~, ib] = ismember(commonRxns, modelB.rxns);
    +0084 for k = 1:numel(commonRxns)
    +0085     rxn = commonRxns{k};
    +0086     ja = ia(k); jb = ib(k);
    +0087 
    +0088     % Stoichiometry: compare the metabolite id -> coefficient maps.
    +0089     [ma, ca] = rxnStoich(modelA, ja);
    +0090     [mb, cb] = rxnStoich(modelB, jb);
    +0091     if ~isequal(sort(ma), sort(mb))
    +0092         push('stoich', sprintf('%s: stoichiometry involves different metabolites', rxn));
    +0093     else
    +0094         [~, locb] = ismember(ma, mb);
    +0095         for m = 1:numel(ma)
    +0096             if abs(ca(m) - cb(locb(m))) > stoichTol
    +0097                 push('stoich', sprintf('%s: coef[%s] A=%g B=%g', rxn, ma{m}, ca(m), cb(locb(m))));
    +0098             end
    +0099         end
    +0100     end
    +0101 
    +0102     % Bounds.
    +0103     if modelA.lb(ja) ~= modelB.lb(jb) || modelA.ub(ja) ~= modelB.ub(jb)
    +0104         push('bounds', sprintf('%s: bounds A=[%g,%g] B=[%g,%g]', rxn, ...
    +0105             modelA.lb(ja), modelA.ub(ja), modelB.lb(jb), modelB.ub(jb)));
    +0106     end
    +0107 
    +0108     % Objective coefficient.
    +0109     if fieldAt(modelA,'c',ja,0) ~= fieldAt(modelB,'c',jb,0)
    +0110         push('objective', sprintf('%s: objective A=%g B=%g', rxn, ...
    +0111             fieldAt(modelA,'c',ja,0), fieldAt(modelB,'c',jb,0)));
    +0112     end
    +0113 
    +0114     % grRule, compared as logic rather than text.
    +0115     ga = canonicalGpr(cellAt(modelA,'grRules',ja));
    +0116     gb = canonicalGpr(cellAt(modelB,'grRules',jb));
    +0117     if ~strcmp(ga, gb)
    +0118         push('gpr', sprintf('%s: grRule A="%s" B="%s"', rxn, ...
    +0119             cellAt(modelA,'grRules',ja), cellAt(modelB,'grRules',jb)));
    +0120     end
    +0121 
    +0122     % EC codes (RAVEN's first-class annotation field).
    +0123     ea = cellAt(modelA,'eccodes',ja);
    +0124     eb = cellAt(modelB,'eccodes',jb);
    +0125     if ~strcmp(ea, eb)
    +0126         push('eccodes', sprintf('%s: eccodes A="%s" B="%s"', rxn, ea, eb));
    +0127     end
    +0128 end
    +0129 
    +0130 % --- Metabolite content, on the shared ids ----------------------------
    +0131 [~, ia] = ismember(commonMets, modelA.mets);
    +0132 [~, ib] = ismember(commonMets, modelB.mets);
    +0133 for k = 1:numel(commonMets)
    +0134     met = commonMets{k};
    +0135     ja = ia(k); jb = ib(k);
    +0136 
    +0137     fa = cellAt(modelA,'metFormulas',ja);
    +0138     fb = cellAt(modelB,'metFormulas',jb);
    +0139     if ~strcmp(fa, fb)
    +0140         push('formula', sprintf('%s: formula A="%s" B="%s"', met, fa, fb));
    +0141     end
    +0142 
    +0143     if ~isequaln(fieldAt(modelA,'metCharges',ja,NaN), fieldAt(modelB,'metCharges',jb,NaN))
    +0144         push('charge', sprintf('%s: charge A=%g B=%g', met, ...
    +0145             fieldAt(modelA,'metCharges',ja,NaN), fieldAt(modelB,'metCharges',jb,NaN)));
    +0146     end
    +0147 
    +0148     cpA = metComp(modelA, ja);
    +0149     cpB = metComp(modelB, jb);
    +0150     if ~strcmp(cpA, cpB)
    +0151         push('compartment', sprintf('%s: compartment A="%s" B="%s"', met, cpA, cpB));
    +0152     end
    +0153 end
    +0154 
    +0155 report.equal = isempty(diffs);
    +0156 report.differences = diffs(:);
    +0157 
    +0158 if printResults
    +0159     if report.equal
    +0160         fprintf('Models are semantically equal.\n');
    +0161     else
    +0162         fprintf('Models differ (%d differences):\n', numel(diffs));
    +0163         fprintf('  - %s\n', diffs{:});
    +0164     end
    +0165 end
    +0166 
    +0167     % --- nested helpers (share diffs / counts / maxPerCategory) --------
    +0168     function push(category, message)
    +0169         if ~isfield(counts, category)
    +0170             counts.(category) = 0;
    +0171         end
    +0172         counts.(category) = counts.(category) + 1;
    +0173         if counts.(category) <= maxPerCategory
    +0174             diffs{end+1,1} = message; %#ok<AGROW>
    +0175         elseif counts.(category) == maxPerCategory + 1
    +0176             diffs{end+1,1} = sprintf('... (%s category truncated at %d entries)', ...
    +0177                 category, maxPerCategory); %#ok<AGROW>
    +0178         end
    +0179     end
    +0180 
    +0181     function reportSet(label, onlyA, onlyB)
    +0182         if ~isempty(onlyA)
    +0183             push([label '_only_in_a'], sprintf('%d %s only in A: %s', ...
    +0184                 numel(onlyA), label, joinTruncated(onlyA)));
    +0185         end
    +0186         if ~isempty(onlyB)
    +0187             push([label '_only_in_b'], sprintf('%d %s only in B: %s', ...
    +0188                 numel(onlyB), label, joinTruncated(onlyB)));
    +0189         end
    +0190     end
    +0191 end
    +0192 
    +0193 % --- local helpers ----------------------------------------------------
    +0194 function [onlyA, onlyB, common] = idSets(a, b)
    +0195 onlyA = setdiff(a, b);
    +0196 onlyB = setdiff(b, a);
    +0197 common = sort(intersect(a, b));
    +0198 onlyA = sort(onlyA(:))'; onlyB = sort(onlyB(:))';
    +0199 onlyA = onlyA(:); onlyB = onlyB(:);
    +0200 end
    +0201 
    +0202 function [mets, coeffs] = rxnStoich(model, j)
    +0203 nz = find(model.S(:, j));
    +0204 mets = model.mets(nz);
    +0205 mets = mets(:)';
    +0206 coeffs = full(model.S(nz, j))';
    +0207 end
    +0208 
    +0209 function v = fieldAt(model, field, j, default)
    +0210 if isfield(model, field) && numel(model.(field)) >= j
    +0211     v = model.(field)(j);
    +0212     if iscell(v), v = v{1}; end
    +0213     if isempty(v), v = default; end
    +0214 else
    +0215     v = default;
    +0216 end
    +0217 end
    +0218 
    +0219 function s = cellAt(model, field, j)
    +0220 % A char scalar from a cell-array field, '' when absent or empty.
    +0221 if isfield(model, field) && numel(model.(field)) >= j
    +0222     s = model.(field){j};
    +0223     if isempty(s), s = ''; end
    +0224 else
    +0225     s = '';
    +0226 end
    +0227 end
    +0228 
    +0229 function cp = metComp(model, j)
    +0230 if isfield(model, 'metComps') && isfield(model, 'comps')
    +0231     cp = model.comps{model.metComps(j)};
    +0232 else
    +0233     cp = '';
    +0234 end
    +0235 end
    +0236 
    +0237 function s = canonicalGpr(rule)
    +0238 % A canonical string for a grRule under which isozymes and their subunits
    +0239 % are order-insensitive. Empty rule -> ''.
    +0240 if isempty(rule)
    +0241     s = '';
    +0242     return;
    +0243 end
    +0244 clauses = grRuleToDNF(rule);
    +0245 parts = cell(1, numel(clauses));
    +0246 for i = 1:numel(clauses)
    +0247     genes = sort(unique(clauses{i}));
    +0248     parts{i} = strjoin(genes(:)', ' and ');
    +0249 end
    +0250 parts = sort(parts);
    +0251 s = strjoin(parts, ' or ');
    +0252 end
    +0253 
    +0254 function s = joinTruncated(ids)
    +0255 % Join up to the first 10 ids, appending a count of the remainder.
    +0256 ids = ids(:)';
    +0257 if numel(ids) > 10
    +0258     s = [strjoin(ids(1:10), ', ') sprintf(', ... (%d more)', numel(ids) - 10)];
    +0259 else
    +0260     s = strjoin(ids, ', ');
    +0261 end
    +0262 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/comparison/index.html b/docs/html/comparison/index.html similarity index 100% rename from doc/comparison/index.html rename to docs/html/comparison/index.html diff --git a/docs/html/conditions/applyCondition.html b/docs/html/conditions/applyCondition.html new file mode 100644 index 00000000..c9e9cb4c --- /dev/null +++ b/docs/html/conditions/applyCondition.html @@ -0,0 +1,282 @@ + + + + Description of applyCondition + + + + + + + + + +
    Home > conditions > applyCondition.m
    + + + +

    applyCondition +

    + +

    PURPOSE ^

    +
    applyCondition Apply a deterministic condition to a model.
    + +

    SYNOPSIS ^

    +
    function model = applyCondition(model, condition)
    + +

    DESCRIPTION ^

    +
     applyCondition  Apply a deterministic condition to a model.
    +
    + The steps of a condition are applied in this order: a prelude that resets
    + exchange bounds, metabolite removals from a pseudoreaction with automatic
    + charge rebalancing, a change of biomass stoichiometry, and per-reaction
    + bound changes. A final check compares the number of uptake bounds with an
    + expected count. The schema covers only these steps, so a condition can be
    + reviewed as data.
    +
    + The schema applies to any GEM whose condition presets are kept as data.
    + Project-specific extensions, for example yeast-GEM's amino_acid_ratio
    + step that rewrites the stoichiometry of a protein pseudoreaction from a
    + separate TSV file, are applied by the caller before or after this
    + function.
    +
    + Parameters
    + ----------
    + model : struct
    +     RAVEN model struct.
    + condition : char or struct
    +     Path to a YAML condition file, or a struct as produced by parseYAML.
    +     The schema is as follows (all keys optional). reset_exchanges is
    +     passed as the reaction type to getExchangeRxns; the lower bound of
    +     the matching reactions is set to 0 and the upper bound to 1000.
    +     charge_balance_met receives the coefficient that makes the charge of
    +     rxn_id zero, which requires the metCharges field; a participant of
    +     rxn_id other than charge_balance_met with a NaN charge raises
    +     applyCondition:unknownCharge. add entries are
    +     added to the existing coefficients. A bounds entry whose rxn is not
    +     in the model is skipped with a warning. expected_uptake_count is
    +     compared with the number of bounds entries with lb of -1000, and a
    +     warning is issued when they differ.
    +
    +         prelude:
    +           reset_exchanges: out
    +
    +         cofactor_pseudoreaction:
    +           rxn_id: r_4598
    +           remove_mets:
    +             - { met: s_3714 }
    +           charge_balance_met: s_0794
    +
    +         biomass_stoichiometry_delta:
    +           rxn_id: r_4041
    +           add:
    +             - { met: s_0689, coef:  0.08 }
    +             - { met: s_0687, coef: -0.08 }
    +             - { met: s_0794, coef: -0.16 }
    +
    +         bounds:
    +           - { rxn: r_1654, lb: -1000 }
    +           - { rxn: r_1992, lb: 0 }
    +           - { rxn: r_1663, lb: 0, ub: 0 }
    +
    +         expected_uptake_count: 15
    +
    + Returns
    + -------
    + model : struct
    +     Model with the condition applied.
    +
    + Examples
    + --------
    +     model = applyCondition(model, 'data/conditions/anaerobic.yml');
    +     model = applyCondition(model, parseYAML('data/conditions/anaerobic.yml'));
    +
    + See also
    + --------
    + parseYAML
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function model = applyCondition(model, condition)
    +0002 % applyCondition  Apply a deterministic condition to a model.
    +0003 %
    +0004 % The steps of a condition are applied in this order: a prelude that resets
    +0005 % exchange bounds, metabolite removals from a pseudoreaction with automatic
    +0006 % charge rebalancing, a change of biomass stoichiometry, and per-reaction
    +0007 % bound changes. A final check compares the number of uptake bounds with an
    +0008 % expected count. The schema covers only these steps, so a condition can be
    +0009 % reviewed as data.
    +0010 %
    +0011 % The schema applies to any GEM whose condition presets are kept as data.
    +0012 % Project-specific extensions, for example yeast-GEM's amino_acid_ratio
    +0013 % step that rewrites the stoichiometry of a protein pseudoreaction from a
    +0014 % separate TSV file, are applied by the caller before or after this
    +0015 % function.
    +0016 %
    +0017 % Parameters
    +0018 % ----------
    +0019 % model : struct
    +0020 %     RAVEN model struct.
    +0021 % condition : char or struct
    +0022 %     Path to a YAML condition file, or a struct as produced by parseYAML.
    +0023 %     The schema is as follows (all keys optional). reset_exchanges is
    +0024 %     passed as the reaction type to getExchangeRxns; the lower bound of
    +0025 %     the matching reactions is set to 0 and the upper bound to 1000.
    +0026 %     charge_balance_met receives the coefficient that makes the charge of
    +0027 %     rxn_id zero, which requires the metCharges field; a participant of
    +0028 %     rxn_id other than charge_balance_met with a NaN charge raises
    +0029 %     applyCondition:unknownCharge. add entries are
    +0030 %     added to the existing coefficients. A bounds entry whose rxn is not
    +0031 %     in the model is skipped with a warning. expected_uptake_count is
    +0032 %     compared with the number of bounds entries with lb of -1000, and a
    +0033 %     warning is issued when they differ.
    +0034 %
    +0035 %         prelude:
    +0036 %           reset_exchanges: out
    +0037 %
    +0038 %         cofactor_pseudoreaction:
    +0039 %           rxn_id: r_4598
    +0040 %           remove_mets:
    +0041 %             - { met: s_3714 }
    +0042 %           charge_balance_met: s_0794
    +0043 %
    +0044 %         biomass_stoichiometry_delta:
    +0045 %           rxn_id: r_4041
    +0046 %           add:
    +0047 %             - { met: s_0689, coef:  0.08 }
    +0048 %             - { met: s_0687, coef: -0.08 }
    +0049 %             - { met: s_0794, coef: -0.16 }
    +0050 %
    +0051 %         bounds:
    +0052 %           - { rxn: r_1654, lb: -1000 }
    +0053 %           - { rxn: r_1992, lb: 0 }
    +0054 %           - { rxn: r_1663, lb: 0, ub: 0 }
    +0055 %
    +0056 %         expected_uptake_count: 15
    +0057 %
    +0058 % Returns
    +0059 % -------
    +0060 % model : struct
    +0061 %     Model with the condition applied.
    +0062 %
    +0063 % Examples
    +0064 % --------
    +0065 %     model = applyCondition(model, 'data/conditions/anaerobic.yml');
    +0066 %     model = applyCondition(model, parseYAML('data/conditions/anaerobic.yml'));
    +0067 %
    +0068 % See also
    +0069 % --------
    +0070 % parseYAML
    +0071 
    +0072 if ischar(condition) || isstring(condition)
    +0073     cond = parseYAML(char(condition));
    +0074 elseif isstruct(condition)
    +0075     cond = condition;
    +0076 else
    +0077     error('applyCondition:invalidCondition', ...
    +0078         'condition must be a YAML file path or a struct.');
    +0079 end
    +0080 
    +0081 % --- Step 1: prelude ---------------------------------------------------
    +0082 if isfield(cond, 'prelude') && isfield(cond.prelude, 'reset_exchanges')
    +0083     [~, exchangeRxns] = getExchangeRxns(model, cond.prelude.reset_exchanges);
    +0084     model.lb(exchangeRxns) = 0;
    +0085     model.ub(exchangeRxns) = 1000;
    +0086 end
    +0087 
    +0088 % --- Step 2: cofactor pseudoreaction edits ----------------------------
    +0089 if isfield(cond, 'cofactor_pseudoreaction')
    +0090     cp = cond.cofactor_pseudoreaction;
    +0091     cofacIdx = getIndexes(model, cp.rxn_id, 'rxns');
    +0092     if isfield(cp, 'remove_mets')
    +0093         for i = 1:numel(cp.remove_mets)
    +0094             metIdx = getIndexes(model, cp.remove_mets{i}.met, 'mets');
    +0095             model.S(metIdx, cofacIdx) = 0;
    +0096         end
    +0097     end
    +0098     if isfield(cp, 'charge_balance_met')
    +0099         balanceIdx = find(strcmp(model.mets, cp.charge_balance_met));
    +0100         model.S(balanceIdx, cofacIdx) = 0;
    +0101         % Only the participating metabolites may be summed: S is sparse and
    +0102         % 0*NaN is NaN, while 'omitnan' would treat an unset charge as
    +0103         % neutral and silently write the wrong balancing coefficient.
    +0104         metIdx = find(model.S(:, cofacIdx));
    +0105         if ~isfield(model, 'metCharges')
    +0106             error('applyCondition:noCharges', ...
    +0107                 ['Cannot charge balance %s: the model has no metCharges ' ...
    +0108                  'field.'], cp.rxn_id);
    +0109         end
    +0110         unknown = metIdx(isnan(model.metCharges(metIdx)));
    +0111         if ~isempty(unknown)
    +0112             error('applyCondition:unknownCharge', ...
    +0113                 ['Cannot charge balance %s: the following metabolites have ' ...
    +0114                  'no charge, so the charge balance is unknown rather than ' ...
    +0115                  'zero:\n\t%s'], cp.rxn_id, strjoin(model.mets(unknown), ', '));
    +0116         end
    +0117         model.S(balanceIdx, cofacIdx) = ...
    +0118             -sum(full(model.S(metIdx, cofacIdx)) .* model.metCharges(metIdx));
    +0119     end
    +0120 end
    +0121 
    +0122 % --- Step 3: biomass stoichiometry delta ------------------------------
    +0123 if isfield(cond, 'biomass_stoichiometry_delta')
    +0124     delta = cond.biomass_stoichiometry_delta;
    +0125     bioIdx = getIndexes(model, delta.rxn_id, 'rxns');
    +0126     if isfield(delta, 'add')
    +0127         for i = 1:numel(delta.add)
    +0128             entry = delta.add{i};
    +0129             metIdx = getIndexes(model, entry.met, 'mets');
    +0130             model.S(metIdx, bioIdx) = full(model.S(metIdx, bioIdx)) + entry.coef;
    +0131         end
    +0132     end
    +0133 end
    +0134 
    +0135 % --- Step 4: bounds ---------------------------------------------------
    +0136 nUptake = 0;
    +0137 if isfield(cond, 'bounds')
    +0138     for i = 1:numel(cond.bounds)
    +0139         b = cond.bounds{i};
    +0140         rxnIdx = find(strcmp(model.rxns, b.rxn));
    +0141         if isempty(rxnIdx)
    +0142             warning('applyCondition:missingRxn', ...
    +0143                 'Reaction %s not found in model; skipping.', b.rxn);
    +0144             continue;
    +0145         end
    +0146         if isfield(b, 'lb')
    +0147             model.lb(rxnIdx) = b.lb;
    +0148             if b.lb == -1000
    +0149                 nUptake = nUptake + 1;
    +0150             end
    +0151         end
    +0152         if isfield(b, 'ub')
    +0153             model.ub(rxnIdx) = b.ub;
    +0154         end
    +0155     end
    +0156 end
    +0157 
    +0158 % --- Step 5: uptake sanity check --------------------------------------
    +0159 if isfield(cond, 'expected_uptake_count')
    +0160     if nUptake ~= cond.expected_uptake_count
    +0161         warning('applyCondition:uptakeCountMismatch', ...
    +0162             'Expected %d uptake reactions, applied %d. Some may be missing from the model.', ...
    +0163             cond.expected_uptake_count, nUptake);
    +0164     end
    +0165 end
    +0166 
    +0167 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/conditions/index.html b/docs/html/conditions/index.html similarity index 100% rename from doc/conditions/index.html rename to docs/html/conditions/index.html diff --git a/docs/html/conversion/addIdentifierPrefix.html b/docs/html/conversion/addIdentifierPrefix.html new file mode 100644 index 00000000..307cb0cc --- /dev/null +++ b/docs/html/conversion/addIdentifierPrefix.html @@ -0,0 +1,172 @@ + + + + Description of addIdentifierPrefix + + + + + + + + + +
    Home > conversion > addIdentifierPrefix.m
    + + + +

    addIdentifierPrefix +

    + +

    PURPOSE ^

    +
    addIdentifierPrefix Add identifier prefixes required by SBML.
    + +

    SYNOPSIS ^

    +
    function [model, hasChanged]=addIdentifierPrefix(model,varargin)
    + +

    DESCRIPTION ^

    +
     addIdentifierPrefix  Add identifier prefixes required by SBML.
    +
    + If any reaction, metabolite, compartment, gene or model identifier does
    + not start with a letter or _, which conflicts with SBML specifications,
    + a prefix is added to all identifiers in the respective model field.
    + The prefixes are:
    +
    +     "R_" for model.rxns,
    +     "M_" for model.mets and model.id,
    +     "C_" for model.comps;
    +     "G_" for model.genes (and also represented in model.grRules)
    +
    + Parameters
    + ----------
    + model : struct
    +     model whose identifiers should be modified.
    +
    + Name-Value Arguments
    + --------------------
    + fields : cell
    +     cell array with the names of the model fields to check for the need
    +     of a prefix, possible values: 'rxns', 'mets', 'comps', 'genes', 'id'
    +     (default all of these fields).
    +
    + Returns
    + -------
    + model : struct
    +     modified model.
    + hasChanged : cell
    +     cell array of char with one entry per field that received a prefix,
    +     for example 'model.rxns (R_ prefix)'.
    +
    + Examples
    + --------
    +     [model, hasChanged] = addIdentifierPrefix(model, fields);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [model, hasChanged]=addIdentifierPrefix(model,varargin)
    +0002 % addIdentifierPrefix  Add identifier prefixes required by SBML.
    +0003 %
    +0004 % If any reaction, metabolite, compartment, gene or model identifier does
    +0005 % not start with a letter or _, which conflicts with SBML specifications,
    +0006 % a prefix is added to all identifiers in the respective model field.
    +0007 % The prefixes are:
    +0008 %
    +0009 %     "R_" for model.rxns,
    +0010 %     "M_" for model.mets and model.id,
    +0011 %     "C_" for model.comps;
    +0012 %     "G_" for model.genes (and also represented in model.grRules)
    +0013 %
    +0014 % Parameters
    +0015 % ----------
    +0016 % model : struct
    +0017 %     model whose identifiers should be modified.
    +0018 %
    +0019 % Name-Value Arguments
    +0020 % --------------------
    +0021 % fields : cell
    +0022 %     cell array with the names of the model fields to check for the need
    +0023 %     of a prefix, possible values: 'rxns', 'mets', 'comps', 'genes', 'id'
    +0024 %     (default all of these fields).
    +0025 %
    +0026 % Returns
    +0027 % -------
    +0028 % model : struct
    +0029 %     modified model.
    +0030 % hasChanged : cell
    +0031 %     cell array of char with one entry per field that received a prefix,
    +0032 %     for example 'model.rxns (R_ prefix)'.
    +0033 %
    +0034 % Examples
    +0035 % --------
    +0036 %     [model, hasChanged] = addIdentifierPrefix(model, fields);
    +0037 
    +0038 p=parseRAVENargs(varargin, {'fields',[]});
    +0039 fields=p.fields;
    +0040 if isempty(fields)
    +0041     fields = {'rxns','mets','comps','genes','id'};
    +0042 end
    +0043 
    +0044 modelFields = {'rxns','R_';
    +0045                'mets','M_';
    +0046                'comps','C_';
    +0047                'genes','G_';
    +0048                'id','M_'};
    +0049 
    +0050 toChangeIdx = find(ismember(modelFields(:,1),fields));
    +0051 hasChanged  = false(numel(modelFields(:,1)),1);
    +0052 for i=1:numel(toChangeIdx)
    +0053     currName    = modelFields{toChangeIdx(i),1};
    +0054     currPrefix  = modelFields{toChangeIdx(i),2};
    +0055     if isfield(model,currName)
    +0056         currField   = model.(currName);
    +0057     else
    +0058         continue;
    +0059     end
    +0060     if ~all(startsWith(currField,regexpPattern('^[a-zA-Z_]')))
    +0061         currField = strcat(currPrefix, currField);
    +0062         hasChanged(toChangeIdx(i)) = true;
    +0063 
    +0064         if strcmp(currName,'genes') && isfield(model,'grRules')
    +0065             model.grRules = cellfun(@(r) prefixGenes(r,currPrefix), model.grRules, 'UniformOutput', false);
    +0066         end
    +0067         model.(currName) = currField;
    +0068     end
    +0069 end
    +0070 
    +0071 hasChanged = modelFields(hasChanged,:);
    +0072 hasChanged = append('model.', hasChanged(:,1), ' (', hasChanged(:,2), ' prefix)');
    +0073 end
    +0074 
    +0075 
    +0076 function rule = prefixGenes(rule, prefix)
    +0077 % Prefix every gene in a grRule, leaving "and", "or" and brackets alone. A
    +0078 % gene is everything between spaces and brackets, so ids that contain
    +0079 % "-", "." or ":" get one prefix, not one per word.
    +0080 [tokens, separators] = regexp(rule, '[^\s()]+', 'match', 'split');
    +0081 out = separators{1};
    +0082 for i = 1:numel(tokens)
    +0083     if ~any(strcmpi(tokens{i}, {'and','or'}))
    +0084         tokens{i} = [prefix tokens{i}];
    +0085     end
    +0086     out = [out tokens{i} separators{i+1}]; %#ok<AGROW>
    +0087 end
    +0088 rule = out;
    +0089 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/conversion/index.html b/docs/html/conversion/index.html similarity index 100% rename from doc/conversion/index.html rename to docs/html/conversion/index.html diff --git a/docs/html/conversion/ravenCobraWrapper.html b/docs/html/conversion/ravenCobraWrapper.html new file mode 100644 index 00000000..cf6cd078 --- /dev/null +++ b/docs/html/conversion/ravenCobraWrapper.html @@ -0,0 +1,551 @@ + + + + Description of ravenCobraWrapper + + + + + + + + + +
    Home > conversion > ravenCobraWrapper.m
    + + + +

    ravenCobraWrapper +

    + +

    PURPOSE ^

    +
    ravenCobraWrapper Convert between RAVEN and COBRA structures.
    + +

    SYNOPSIS ^

    +
    function newModel=ravenCobraWrapper(model)
    + +

    DESCRIPTION ^

    +
     ravenCobraWrapper  Convert between RAVEN and COBRA structures.
    +
    + A structure with the field 'rules' is treated as a COBRA Toolbox
    + structure and converted to RAVEN; a structure without it is treated as a
    + RAVEN structure and converted to COBRA. If a COBRA structure also has a
    + grRules field, that field is used and rules is not parsed.
    +
    + Parameters
    + ----------
    + model : struct
    +     a RAVEN or COBRA model structure.
    +
    + Returns
    + -------
    + newModel : struct
    +     the model structure in the other format: COBRA for a RAVEN input and
    +     RAVEN for a COBRA input.
    +
    + Examples
    + --------
    +     newModel = ravenCobraWrapper(model);
    +
    + Notes
    + -----
    + During the RAVEN -> COBRA -> RAVEN conversion cycle the following fields
    + are lost: annotation, compOutside, compMiriams, rxnComps, geneComps,
    + unconstrained. Boundary metabolites are lost, because the COBRA
    + structure has no boundary metabolites, and are removed using
    + simplifyModel before the RAVEN -> COBRA conversion. The field 'rev' is
    + also partially lost; during the COBRA -> RAVEN conversion it is
    + reconstructed from the lower bounds (a reaction is reversible if its
    + lower bound is below zero).
    +
    + During the COBRA -> RAVEN -> COBRA conversion cycle the following fields
    + are lost: geneEntrezID, modelVersion, proteins.
    +
    + The information about mandatory RAVEN fields was taken from the
    + checkModelStruct function, whereas the corresponding information about
    + COBRA fields was taken from the verifyModel function.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function newModel=ravenCobraWrapper(model)
    +0002 % ravenCobraWrapper  Convert between RAVEN and COBRA structures.
    +0003 %
    +0004 % A structure with the field 'rules' is treated as a COBRA Toolbox
    +0005 % structure and converted to RAVEN; a structure without it is treated as a
    +0006 % RAVEN structure and converted to COBRA. If a COBRA structure also has a
    +0007 % grRules field, that field is used and rules is not parsed.
    +0008 %
    +0009 % Parameters
    +0010 % ----------
    +0011 % model : struct
    +0012 %     a RAVEN or COBRA model structure.
    +0013 %
    +0014 % Returns
    +0015 % -------
    +0016 % newModel : struct
    +0017 %     the model structure in the other format: COBRA for a RAVEN input and
    +0018 %     RAVEN for a COBRA input.
    +0019 %
    +0020 % Examples
    +0021 % --------
    +0022 %     newModel = ravenCobraWrapper(model);
    +0023 %
    +0024 % Notes
    +0025 % -----
    +0026 % During the RAVEN -> COBRA -> RAVEN conversion cycle the following fields
    +0027 % are lost: annotation, compOutside, compMiriams, rxnComps, geneComps,
    +0028 % unconstrained. Boundary metabolites are lost, because the COBRA
    +0029 % structure has no boundary metabolites, and are removed using
    +0030 % simplifyModel before the RAVEN -> COBRA conversion. The field 'rev' is
    +0031 % also partially lost; during the COBRA -> RAVEN conversion it is
    +0032 % reconstructed from the lower bounds (a reaction is reversible if its
    +0033 % lower bound is below zero).
    +0034 %
    +0035 % During the COBRA -> RAVEN -> COBRA conversion cycle the following fields
    +0036 % are lost: geneEntrezID, modelVersion, proteins.
    +0037 %
    +0038 % The information about mandatory RAVEN fields was taken from the
    +0039 % checkModelStruct function, whereas the corresponding information about
    +0040 % COBRA fields was taken from the verifyModel function.
    +0041 
    +0042 if isfield(model,'rules')
    +0043     isRaven=false;
    +0044 else
    +0045     isRaven=true;
    +0046 end
    +0047 
    +0048 ravenPath=findRAVENroot();
    +0049 
    +0050 % Load COBRA field information
    +0051 fid             = fopen(fullfile(ravenPath,'src','conversion','COBRA_structure_fields.csv')); % Taken from https://github.com/opencobra/cobratoolbox/blob/develop/src/base/io/definitions/COBRA_structure_fields.csv
    +0052 fieldFile       = textscan(fid,repmat('%s',1,15),'Delimiter','\t','HeaderLines',1);
    +0053 dbFields        = ~cellfun(@isempty,fieldFile{5}); % Only keep fields with database annotations that should be translated to xxxMiriams
    +0054 dbFields        = dbFields & ~contains(fieldFile{1},{'metInChIString','metKEGGID','metPubChemID','rxnECNumbers'});
    +0055 COBRAnamespace  = fieldFile{5}(dbFields);
    +0056 COBRAnamespace  = regexprep(COBRAnamespace,';.*',''); % Only keep first suggested namespace
    +0057 COBRAfields     = fieldFile{1}(dbFields);
    +0058 fclose(fid);
    +0059 
    +0060 % Load conversion between additional COBRA fields and namespaces:
    +0061 fid             = fopen(fullfile(ravenPath,'src','conversion','cobraNamespaces.csv'));
    +0062 fieldFile       = textscan(fid,'%s %s','Delimiter',',','HeaderLines',0);
    +0063 COBRAfields     = [COBRAfields; fieldFile{1}];
    +0064 COBRAnamespace  = [COBRAnamespace; fieldFile{2}];
    +0065 rxnCOBRAfields  = COBRAfields(startsWith(COBRAfields,'rxn'));
    +0066 rxnNamespaces   = COBRAnamespace(startsWith(COBRAfields,'rxn'));
    +0067 metCOBRAfields  = COBRAfields(startsWith(COBRAfields,'met'));
    +0068 metNamespaces   = COBRAnamespace(startsWith(COBRAfields,'met'));
    +0069 geneCOBRAfields = COBRAfields(startsWith(COBRAfields,'gene'));
    +0070 geneNamespaces  = COBRAnamespace(startsWith(COBRAfields,'gene'));
    +0071 fclose(fid);
    +0072 
    +0073 if isRaven
    +0074     %Remove boundary metabolites
    +0075     model=simplifyModel(model);
    +0076 end
    +0077 
    +0078 % Keep fields that have identical names and content
    +0079 newModel.S=model.S;
    +0080 newModel.lb=model.lb;
    +0081 newModel.ub=model.ub;
    +0082 if isfield(model,'c')
    +0083     newModel.c=model.c;
    +0084 else
    +0085     newModel.c=zeros(numel(model.rxns),1);
    +0086 end
    +0087 newModel.rxns=model.rxns;
    +0088 optFields = {'rxnNames','rxnNotes','rxnConfidenceScores','rxnDeltaG',...
    +0089     'rxnGeneMat','rev','subSystems','comps','compNames','metCharges',...
    +0090     'metDeltaG','metFormulas','metNotes','metSmiles','genes','proteins'};
    +0091 for i=1:length(optFields)
    +0092     if isfield(model,optFields{i})
    +0093         newModel.(optFields{i})=model.(optFields{i});
    +0094     end
    +0095 end
    +0096     
    +0097 % Convert unique fields
    +0098 if isRaven
    +0099     fprintf('Converting RAVEN structure to COBRA..\n');
    +0100     %Convert from RAVEN to COBRA structure
    +0101     
    +0102     %Mandatory COBRA fields
    +0103     newModel.rxns=model.rxns;
    +0104     if all(~cellfun(@isempty,regexp(model.mets,'\[[^\]]+\]$')))
    +0105         newModel.mets=model.mets;
    +0106     else
    +0107         %Check if model has compartment info as "met_c" suffix in all metabolites:
    +0108         BiGGformat = false(size(model.mets));
    +0109         for i=1:numel(model.comps)
    +0110             compPos=model.metComps==i;
    +0111             BiGGformat(compPos)=~cellfun(@isempty,regexp(model.mets(compPos),['_' model.comps{i} '$']));
    +0112         end
    +0113         if all(BiGGformat)
    +0114             newModel.mets=model.mets;
    +0115             for i=1:numel(model.comps)
    +0116                 newModel.mets=regexprep(newModel.mets,['_' model.comps{i} '$'],['[' model.comps{i} ']']);
    +0117             end
    +0118         else
    +0119             newModel.mets=strcat(model.mets,'[',model.comps(model.metComps),']');
    +0120         end
    +0121     end
    +0122 
    +0123     %b, csense, osenseStr, genes, rules are also mandatory, but defined
    +0124     %later to match the order of fields
    +0125     
    +0126     %Optional COBRA fields
    +0127     if isfield(model,'id')
    +0128         newModel.modelID=model.id;
    +0129     end
    +0130     if isfield(model,'name')
    +0131         newModel.modelName=model.name;
    +0132     end
    +0133     if isfield(model,'eccodes')
    +0134         newModel.rxnECNumbers=model.eccodes;
    +0135     end
    +0136     if isfield(model,'rxnMiriams')
    +0137         [miriams,extractedMiriamNames]=extractMiriam(model.rxnMiriams);
    +0138         for i = 1:length(rxnCOBRAfields)
    +0139             j=ismember(extractedMiriamNames,rxnNamespaces{i});
    +0140             if any(j)
    +0141                 newModel.(rxnCOBRAfields{i}) = miriams(:,j);
    +0142             end
    +0143         end
    +0144     end
    +0145     if isfield(model,'rxnReferences') % Concatenate model.rxnReferences to those extracted from model.rxnMiriams
    +0146         if isfield(newModel,'rxnReferences')
    +0147             newModel.rxnReferences = strcat(newModel.rxnReferences,{'; '},model.rxnReferences);
    +0148             newModel.rxnReferences = regexprep(newModel.rxnReferences,'^; $','');
    +0149         else
    +0150             newModel.rxnReferences = model.rxnReferences;
    +0151         end
    +0152     end
    +0153     if isfield(model,'metNames')
    +0154         newModel.metNames=strcat(model.metNames,' [',model.compNames(model.metComps),']');
    +0155     end
    +0156     if isfield(model,'metMiriams')
    +0157         [miriams,extractedMiriamNames]=extractMiriam(model.metMiriams);
    +0158         %Shorten miriam names for KEGG and PubChem. These shorter names
    +0159         %will be used later to concatenate KEGG COMPOUND/GLYCAN and PubChem
    +0160         %Compound/Substance, into corresponding COBRA model fields
    +0161         extractedMiriamNames=regexprep(extractedMiriamNames,'^kegg\..+','kegg');
    +0162         extractedMiriamNames=regexprep(extractedMiriamNames,'^pubchem\..+','pubchem');
    +0163         i=ismember(extractedMiriamNames,'kegg');
    +0164         if any(i) % Combine KEGG compounds and glycans
    +0165             for j=1:length(i)
    +0166                 if i(j) && isfield(newModel,'metKEGGID')~=1
    +0167                     newModel.metKEGGID=miriams(:,j);
    +0168                 elseif i(j)
    +0169                     newModel.metKEGGID=strcat(newModel.metKEGGID,';',miriams(:,j));
    +0170                 end
    +0171             end
    +0172             newModel.metKEGGID=regexprep(newModel.metKEGGID,'^;|;$','');
    +0173         end
    +0174         i=ismember(extractedMiriamNames,'pubchem');
    +0175         if any(i) % Combine Pubchem compounds and substances
    +0176             for j=1:length(i)
    +0177                 if i(j) && isfield(newModel,'metPubChemID')~=1
    +0178                     newModel.metPubChemID=miriams(:,j);
    +0179                 elseif i(j)
    +0180                     newModel.metPubChemID=strcat(newModel.metPubChemID,';',miriams(:,j));
    +0181                 end
    +0182             end
    +0183             newModel.metPubChemID=regexprep(newModel.metPubChemID,'^;|;$','');
    +0184         end
    +0185         %All other Miriams can be directly parsed with no modifications:
    +0186         for i = 1:length(metCOBRAfields)
    +0187             j=ismember(extractedMiriamNames,metNamespaces{i});
    +0188             if any(j)
    +0189                 newModel.(metCOBRAfields{i}) = miriams(:,j);
    +0190             end
    +0191         end
    +0192     end
    +0193     if isfield(model,'inchis')
    +0194         newModel.metInChIString=regexprep(strcat('InChI=', model.inchis),'^InChI=$','');
    +0195     end
    +0196     newModel.b=zeros(numel(model.mets),1);
    +0197     newModel.csense=repmat('E',size(model.mets));
    +0198     if isfield(model,'geneMiriams')
    +0199         [miriams,extractedMiriamNames]=extractMiriam(model.geneMiriams);
    +0200         for i = 1:length(geneCOBRAfields)
    +0201             j=ismember(extractedMiriamNames,geneNamespaces{i});
    +0202             if any(j)
    +0203                 newModel.(geneCOBRAfields{i}) = miriams(:,j);
    +0204             end
    +0205         end
    +0206     end
    +0207     if isfield(model,'geneShortNames')
    +0208         newModel.geneNames=model.geneShortNames;
    +0209     end
    +0210     if isfield(model,'genes')
    +0211         newModel.rules=grrulesToRules(model);
    +0212     else
    +0213         fprintf('WARNING: no genes detected. The model therefore may not be exportable to SBML file with writeCbModel\n');
    +0214     end
    +0215     % If needed, normalise subSystems to cell-of-cells format
    +0216     if isfield(newModel,'subSystems') && ~isempty(newModel.subSystems)
    +0217         if ~iscell(newModel.subSystems{1})
    +0218             newModel.subSystems = cellfun(@(x) {x}, newModel.subSystems, 'UniformOutput', false);
    +0219         end
    +0220     end
    +0221 else
    +0222     fprintf('Converting COBRA structure to RAVEN..\n');
    +0223     %Convert from COBRA to RAVEN structure
    +0224 
    +0225     %Mandatory RAVEN fields
    +0226     newModel.mets=model.mets;
    +0227     if ~isfield(model,'comps')
    +0228         %Since 'comps' field is not mandatory in COBRA, it may be required
    +0229         %to obtain the non-redundant list of comps from metabolite ids, if
    +0230         %'comps' field is not available
    +0231         newModel.comps = unique(regexprep(model.mets,'.*\[([^\]]+)\]$','$1'));
    +0232         newModel.compNames = newModel.comps;
    +0233     end
    +0234     for i=1:numel(newModel.comps)
    +0235         newModel.mets=regexprep(newModel.mets,['\[', newModel.comps{i}, '\]$'],'');
    +0236         newModel.mets=regexprep(newModel.mets,['\[', newModel.compNames{i}, '\]$'],'');
    +0237     end
    +0238     
    +0239     %In some cases (e.g. any model that uses BiGG ids as main ids), there
    +0240     %may be overlapping mets due to removal of compartment info. To avoid
    +0241     %this, we change compartments from e.g. [c] into _c
    +0242     if numel(unique(newModel.mets))~=numel(model.mets)
    +0243         newModel.mets=model.mets;
    +0244         for i=1:numel(newModel.comps)
    +0245             newModel.mets=regexprep(newModel.mets,['\[' newModel.comps{i} '\]$'],['_' newModel.comps{i}]);
    +0246         end
    +0247     end
    +0248     %Since COBRA no longer contains rev field it is assumed that rxn is
    +0249     %reversible if its lower bound is set below zero
    +0250     if ~isfield(model,'rev')
    +0251         for i=1:numel(model.rxns)
    +0252             if model.lb(i)<0
    +0253                 newModel.rev(i,1)=1;
    +0254             else
    +0255                 newModel.rev(i,1)=0;
    +0256             end
    +0257         end
    +0258     end
    +0259     newModel.b=zeros(numel(model.mets),1);
    +0260    
    +0261     %metComps is also mandatory, but defined later to match the order of
    +0262     %fields
    +0263     
    +0264     %Fields 'name' and 'id' are also considered as mandatory, but
    +0265     %these are added to the model during exportModel/exportToExcelFormat
    +0266     %anyway, so there is no point to add this information here
    +0267     
    +0268     %Optional RAVEN fields
    +0269     if isfield(model,'modelID')
    +0270         newModel.id=model.modelID;
    +0271     end
    +0272     if isfield(model,'modelName')
    +0273         newModel.name=model.modelName;
    +0274     end
    +0275     if isfield(model,'rules') && ~isfield(model,'grRules')
    +0276         model.grRules        = rulesTogrrules(model);
    +0277     end
    +0278     if isfield(model,'grRules')
    +0279         [grRules,rxnGeneMat] = standardizeGrRules(model,true);
    +0280         newModel.grRules     = grRules;
    +0281         newModel.rxnGeneMat  = rxnGeneMat;
    +0282     end
    +0283     if isfield(model,'rxnECNumbers')
    +0284         newModel.eccodes=regexprep(model.rxnECNumbers,'EC|EC:','');
    +0285     end
    +0286     if any(isfield(model,rxnCOBRAfields))
    +0287         for i=1:numel(model.rxns)
    +0288             counter=1;
    +0289             newModel.rxnMiriams{i,1}=[];
    +0290             if isfield(model,'rxnReferences')
    +0291                 if ~isempty(model.rxnReferences{i})
    +0292                     pmids = model.rxnReferences{i};
    +0293                     pmids = strsplit(pmids,'; ');
    +0294                     nonPmids = cellfun(@isempty,regexp(pmids,'^\d+$','match','once'));
    +0295                     if any(nonPmids) %Not a pubmed id, keep in rxnReferences instead
    +0296                         newModel.rxnReferences{i,1} = strjoin(pmids(nonPmids),', ');
    +0297                         pmids(nonPmids)=[];
    +0298                     end
    +0299                     for j = 1:length(pmids)
    +0300                         newModel.rxnMiriams{i,1}.name{counter,1} = 'pubmed';
    +0301                         newModel.rxnMiriams{i,1}.value{counter,1} = pmids{j};
    +0302                         counter=counter+1;
    +0303                     end
    +0304                 end
    +0305             end
    +0306             for j = 2:length(rxnCOBRAfields) %Start from 2, as 1 is rxnReferences
    +0307                 if isfield(model,rxnCOBRAfields{j})
    +0308                     rxnAnnotation = model.(rxnCOBRAfields{j}){i};
    +0309                     if ~isempty(rxnAnnotation)
    +0310                         rxnAnnotation = strtrim(strsplit(rxnAnnotation,';'));
    +0311                         for a=1:length(rxnAnnotation)
    +0312                             newModel.rxnMiriams{i,1}.name{counter,1} = rxnNamespaces{j};
    +0313                             newModel.rxnMiriams{i,1}.value{counter,1} = rxnAnnotation{a};
    +0314                             counter=counter+1;
    +0315                         end
    +0316                     end
    +0317                 end
    +0318             end
    +0319         end
    +0320     end
    +0321     if isfield(newModel,'rxnReferences')
    +0322         emptyEntry = cellfun(@isempty,newModel.rxnReferences);
    +0323         newModel.rxnReferences(emptyEntry)={''};
    +0324         diffNumel = numel(newModel.rxns) - numel(newModel.rxnReferences);
    +0325         if diffNumel > 0
    +0326             newModel.rxnReferences(end+1:end+diffNumel) = {''};
    +0327         end
    +0328     end
    +0329     if any(isfield(model,geneCOBRAfields))
    +0330         for i=1:numel(model.genes)
    +0331             counter=1;
    +0332             newModel.geneMiriams{i,1}=[];
    +0333             for j = 1:length(geneCOBRAfields)
    +0334                 if isfield(model,geneCOBRAfields{j})
    +0335                     geneAnnotation = model.(geneCOBRAfields{j}){i};
    +0336                     if ~isempty(geneAnnotation)
    +0337                         geneAnnotation = strtrim(strsplit(geneAnnotation,';'));
    +0338                         for a=1:length(geneAnnotation)
    +0339                             newModel.geneMiriams{i,1}.name{counter,1} = geneNamespaces{j};
    +0340                             newModel.geneMiriams{i,1}.value{counter,1} = geneAnnotation{a};
    +0341                             counter=counter+1;
    +0342                         end
    +0343                     end
    +0344                 end
    +0345             end
    +0346         end
    +0347     end
    +0348     if isfield(model,'geneNames')
    +0349         newModel.geneShortNames=model.geneNames;
    +0350     end
    +0351     newModel.metNames=model.metNames;
    +0352     for i=1:numel(newModel.comps)
    +0353         newModel.metNames=regexprep(newModel.metNames,['\[', newModel.comps{i}, '\]$'],'');
    +0354         newModel.metNames=regexprep(newModel.metNames,['\[', newModel.compNames{i}, '\]$'],'');
    +0355     end
    +0356     newModel.metNames=deblank(newModel.metNames);
    +0357     newModel.metComps=regexprep(model.mets,'^.+\[','');
    +0358     newModel.metComps=regexprep(newModel.metComps,'\]$','');
    +0359     [~, newModel.metComps]=ismember(newModel.metComps,newModel.comps);
    +0360     if isfield(model,'metInChIString')
    +0361         newModel.inchis=regexprep(model.metInChIString,'^InChI=','');
    +0362     end
    +0363     printWarning=false;
    +0364     if any(isfield(model,[metCOBRAfields;'metKEGGID';'metPubChemID']))
    +0365         for i=1:numel(model.mets)
    +0366             counter=1;
    +0367             newModel.metMiriams{i,1}=[];
    +0368             if isfield(model,'metKEGGID')
    +0369                 if ~isempty(model.metKEGGID{i})
    +0370                     if strcmp(model.metKEGGID{i}(1),'C')
    +0371                         newModel.metMiriams{i,1}.name{counter,1} = 'kegg.compound';
    +0372                         newModel.metMiriams{i,1}.value{counter,1} = model.metKEGGID{i};
    +0373                         counter=counter+1;
    +0374                     elseif strcmp(model.metKEGGID{i}(1),'G')
    +0375                         newModel.metMiriams{i,1}.name{counter,1} = 'kegg.glycan';
    +0376                         newModel.metMiriams{i,1}.value{counter,1} = model.metKEGGID{i};
    +0377                         counter=counter+1;
    +0378                     end
    +0379                 end
    +0380             end
    +0381             if isfield(model,'metPubChemID')
    +0382                 if ~isempty(model.metPubChemID{i})
    +0383                     if length(model.metPubChemID{i})>3 && strcmp(model.metPubChemID{i}(1:4),'CID:')
    +0384                         newModel.metMiriams{i,1}.name{counter,1} = 'pubchem.compound';
    +0385                         newModel.metMiriams{i,1}.value{counter,1} = model.metPubChemID{i};
    +0386                         counter=counter+1;
    +0387                     elseif length(model.metPubChemID{i})>3 && strcmp(model.metPubChemID{i}(1:4),'SID:')
    +0388                         newModel.metMiriams{i,1}.name{counter,1} = 'pubchem.substance';
    +0389                         newModel.metMiriams{i,1}.value{counter,1} = model.metPubChemID{i};
    +0390                         counter=counter+1;
    +0391                     else
    +0392                         newModel.metMiriams{i,1}.name{counter,1} = 'pubchem.compound';
    +0393                         newModel.metMiriams{i,1}.value{counter,1} = model.metPubChemID{i};
    +0394                         counter=counter+1;
    +0395                         printWarning=true;
    +0396                     end
    +0397                 end
    +0398             end            
    +0399             for j = 1:length(metCOBRAfields)
    +0400                 if isfield(model,metCOBRAfields{j})
    +0401                     metAnnotation = model.(metCOBRAfields{j}){i};
    +0402                     if ~isempty(metAnnotation)
    +0403                         metAnnotation = strtrim(strsplit(metAnnotation,';'));
    +0404                         for a=1:length(metAnnotation)
    +0405                             newModel.metMiriams{i,1}.name{counter,1} = metNamespaces{j};
    +0406                             newModel.metMiriams{i,1}.value{counter,1} = metAnnotation{a};
    +0407                             counter=counter+1;
    +0408                         end
    +0409                     end
    +0410                 end
    +0411             end
    +0412         end
    +0413     end
    +0414     % Flatten subSystems if every reaction has exactly one subsystem.
    +0415     if isfield(newModel,'subSystems') && ~isempty(newModel.subSystems)
    +0416         if all(cellfun(@(x) iscell(x) && isscalar(x), newModel.subSystems))
    +0417             newModel.subSystems = transpose([newModel.subSystems{:}]);
    +0418         end
    +0419     end
    +0420     if printWarning
    +0421         fprintf('Could not determine whether PubChemIDs are compounds (CID)\n or substances (SID). All annotated PubChemIDs will therefore \n be assigned as compounds (CID).\n');
    +0422     end
    +0423 end
    +0424 
    +0425 % Order fields
    +0426 newModel=standardizeModelFieldOrder(newModel); % Corrects for both RAVEN and COBRA models
    +0427 end
    +0428 
    +0429 function rules=grrulesToRules(model)
    +0430 %Changes all gene names in grRules to 'x(geneNumber)' and changes 'or' and
    +0431 %'and' relations to the corresponding symbols
    +0432 replacingGenes=cell([size(model.genes,1) 1]);
    +0433 for i=1:numel(replacingGenes)
    +0434     replacingGenes{i}=strcat('x(',num2str(i),')');
    +0435 end
    +0436 rules = strcat({' '},model.grRules,{' '});
    +0437 for i=1:length(model.genes)
    +0438     %Escaped: a gene id is arbitrary model text, not a regex pattern, and
    +0439     %commonly contains metacharacters (e.g. "ENSG00000141510.16", where an
    +0440     %unescaped "." would match any character instead of a literal dot).
    +0441     escapedGene=regexptranslate('escape',model.genes{i});
    +0442     rules=regexprep(rules,[' ' escapedGene ' '],[' ' replacingGenes{i} ' ']);
    +0443     rules=regexprep(rules,['(' escapedGene ' '],['(' replacingGenes{i} ' ']);
    +0444     rules=regexprep(rules,[' ' escapedGene ')'],[' ' replacingGenes{i} ')']);
    +0445 end
    +0446 rules=regexprep(rules,' and ',' & ');
    +0447 rules=regexprep(rules,' or ',' | ');
    +0448 rules=strtrim(rules);
    +0449 end
    +0450 
    +0451 function grRules=rulesTogrrules(model)
    +0452 %Replaces &/| with and/or, replaces the x(i) format with the actual gene
    +0453 %ID, and strips the extra whitespace and redundant parenthesis introduced
    +0454 %by COBRA, to create grRules.
    +0455 grRules = strrep(model.rules,'&','and');
    +0456 grRules = strrep(grRules,'|','or');
    +0457 for i = 1:length(model.genes)
    +0458     grRules = strrep(grRules,['x(' num2str(i) ')'],model.genes{i});
    +0459 end
    +0460 grRules = strrep(grRules,'( ','(');
    +0461 grRules = strrep(grRules,' )',')');
    +0462 grRules = regexprep(grRules,'^(',''); %rules that start with a "("
    +0463 grRules = regexprep(grRules,')$',''); %rules that end with a ")"
    +0464 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/conversion/removeIdentifierPrefix.html b/docs/html/conversion/removeIdentifierPrefix.html similarity index 78% rename from doc/conversion/removeIdentifierPrefix.html rename to docs/html/conversion/removeIdentifierPrefix.html index ba99e7a1..11639553 100644 --- a/doc/conversion/removeIdentifierPrefix.html +++ b/docs/html/conversion/removeIdentifierPrefix.html @@ -31,15 +31,15 @@

    DESCRIPTION ^DESCRIPTION ^SOURCE CODE ^% 0004 % Removes identifier prefixes: 0005 % -0006 % "R_" for model.rxns, model.rxnNames and model.id, -0007 % "M_" for model.mets and model.metNames, +0006 % "R_" for model.rxns and model.rxnNames, +0007 % "M_" for model.mets, model.metNames and model.id, 0008 % "C_" for model.comps; 0009 % "G_" for model.genes (and also represented in model.grRules). 0010 % 0011 % By default, the prefixes are only removed if all entries in a particular -0012 % field have the prefix. The prefixes might have been present because one -0013 % or more identifiers do not start with a letter or _, which conflicts -0014 % with SBML specifications. +0012 % field have the prefix. The prefixes are required when one or more +0013 % identifiers do not start with a letter or _, which conflicts with SBML +0014 % specifications. 0015 % 0016 % Parameters 0017 % ---------- @@ -104,20 +104,20 @@

    SOURCE CODE ^% Name-Value Arguments 0022 % -------------------- 0023 % fields : cell -0024 % cell array with model field names from which the identifiers should -0025 % be removed, possible values: 'rxns', 'mets', 'comps', 'genes', -0026 % 'metNames', 'rxnNames', 'id' (default all listed model fields will -0027 % be checked). -0028 % forceRemove : logical -0029 % if prefixes should be removed even if not all entries in a model -0030 % field have the prefix (default false). -0031 % -0032 % Returns -0033 % ------- -0034 % model : struct -0035 % modified model. -0036 % hasChanged : cell -0037 % cell array with fields and prefixes that are removed. +0024 % cell array with the names of the model fields from which the prefix +0025 % is removed, possible values: 'rxns', 'mets', 'comps', 'genes', +0026 % 'metNames', 'rxnNames', 'id' (default all of these fields). +0027 % forceRemove : logical +0028 % true to remove the prefix from the entries that have it when not all +0029 % entries in a model field have the prefix (default false). +0030 % +0031 % Returns +0032 % ------- +0033 % model : struct +0034 % modified model. +0035 % hasChanged : cell +0036 % cell array of char with one entry per field from which a prefix is +0037 % removed, for example 'model.rxns (R_ prefix)'. 0038 % 0039 % Examples 0040 % -------- diff --git a/docs/html/conversion/standardizeModelFieldOrder.html b/docs/html/conversion/standardizeModelFieldOrder.html new file mode 100644 index 00000000..13a7bd13 --- /dev/null +++ b/docs/html/conversion/standardizeModelFieldOrder.html @@ -0,0 +1,153 @@ + + + + Description of standardizeModelFieldOrder + + + + + + + + + +
    Home > conversion > standardizeModelFieldOrder.m
    + + + +

    standardizeModelFieldOrder +

    + +

    PURPOSE ^

    +
    standardizeModelFieldOrder Order RAVEN model structure fields.
    + +

    SYNOPSIS ^

    +
    function orderedModel=standardizeModelFieldOrder(model)
    + +

    DESCRIPTION ^

    +
     standardizeModelFieldOrder  Order RAVEN model structure fields.
    +
    + Orders the fields of a RAVEN or COBRA model structure. The RAVEN order is
    + the one listed in orderRavenFields.csv, described at
    + https://raven-docs.readthedocs.io/en/latest/guide/model-structure.html
    + A structure with the field 'rules' is ordered as a COBRA structure, using
    + COBRA_structure_fields.csv. Fields that are not listed in the file are
    + placed last, in alphabetical order.
    +
    + The content of the fields is unchanged. To convert model fields between
    + RAVEN and COBRA format, use ravenCobraWrapper().
    +
    + Parameters
    + ----------
    + model : struct
    +     model structure, either RAVEN or COBRA format.
    +
    + Returns
    + -------
    + orderedModel : struct
    +     model structure with ordered fields.
    +
    + Examples
    + --------
    +     orderedModel = standardizeModelFieldOrder(model);
    +
    + See also
    + --------
    + ravenCobraWrapper
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: + + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function orderedModel=standardizeModelFieldOrder(model)
    +0002 % standardizeModelFieldOrder  Order RAVEN model structure fields.
    +0003 %
    +0004 % Orders the fields of a RAVEN or COBRA model structure. The RAVEN order is
    +0005 % the one listed in orderRavenFields.csv, described at
    +0006 % https://raven-docs.readthedocs.io/en/latest/guide/model-structure.html
    +0007 % A structure with the field 'rules' is ordered as a COBRA structure, using
    +0008 % COBRA_structure_fields.csv. Fields that are not listed in the file are
    +0009 % placed last, in alphabetical order.
    +0010 %
    +0011 % The content of the fields is unchanged. To convert model fields between
    +0012 % RAVEN and COBRA format, use ravenCobraWrapper().
    +0013 %
    +0014 % Parameters
    +0015 % ----------
    +0016 % model : struct
    +0017 %     model structure, either RAVEN or COBRA format.
    +0018 %
    +0019 % Returns
    +0020 % -------
    +0021 % orderedModel : struct
    +0022 %     model structure with ordered fields.
    +0023 %
    +0024 % Examples
    +0025 % --------
    +0026 %     orderedModel = standardizeModelFieldOrder(model);
    +0027 %
    +0028 % See also
    +0029 % --------
    +0030 % ravenCobraWrapper
    +0031 
    +0032 ravenPath=findRAVENroot();
    +0033 
    +0034 if ~isfield(model,'rules') % Check if model is RAVEN
    +0035     fid = fopen(fullfile(ravenPath,'src','conversion','orderRavenFields.csv'));
    +0036     fields = textscan(fid,'%s','Delimiter',',','HeaderLines',0);
    +0037     fields = fields{1};
    +0038     fclose(fid);
    +0039 else % If model is COBRA
    +0040     fid = fopen(fullfile(ravenPath,'src','conversion','COBRA_structure_fields.csv')); % Taken from https://github.com/opencobra/cobratoolbox/blob/develop/src/base/io/definitions/COBRA_structure_fields.csv
    +0041     fields = textscan(fid,repmat('%s',1,15),'Delimiter','\t','HeaderLines',1);
    +0042     fields = fields{1};
    +0043     fclose(fid);
    +0044 end
    +0045 
    +0046 modelfields = fieldnames(model);
    +0047 order = fields(ismember(fields(:,1),modelfields));
    +0048 remainingOrder = sort(setdiff(modelfields,order));
    +0049 overallOrder = [columnVector(order);columnVector(remainingOrder)];
    +0050 orderedModel = orderfields(model,overallOrder);
    +0051 end
    +0052 
    +0053 function vecT = columnVector(vec)
    +0054 % Converts a vector to a column vector
    +0055 %
    +0056 % USAGE:
    +0057 %
    +0058 %   vecT = columnVector(vec)
    +0059 %
    +0060 % INPUT:
    +0061 %   vec:     a vector
    +0062 %
    +0063 % OUTPUT:
    +0064 %   vecT:    a column vector
    +0065 %
    +0066 % .. Authors:
    +0067 %     - Markus Herrgard (COBRA Toolbox)
    +0068 
    +0069 [n, m] = size(vec);
    +0070 
    +0071 if (m ~= 1 && n < m) || n == 1
    +0072     vecT = vec';
    +0073 else
    +0074     vecT = vec;
    +0075 end
    +0076 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/curation/curateModelFromTables.html b/docs/html/curation/curateModelFromTables.html new file mode 100644 index 00000000..5193baae --- /dev/null +++ b/docs/html/curation/curateModelFromTables.html @@ -0,0 +1,900 @@ + + + + Description of curateModelFromTables + + + + + + + + + +
    Home > curation > curateModelFromTables.m
    + + + +

    curateModelFromTables +

    + +

    PURPOSE ^

    +
    curateModelFromTables Curate or add mets, rxns and genes from tables.
    + +

    SYNOPSIS ^

    +
    function newModel=curateModelFromTables(model,metsInfo,varargin)
    + +

    DESCRIPTION ^

    +
     curateModelFromTables  Curate or add mets, rxns and genes from tables.
    +
    + Curates existing metabolites, reactions and genes and adds new ones from
    + tabular data files.
    +
    + If the *.tsv files contain metabolites, reactions or genes that are
    + already present in the model, the information in the model is
    + overwritten. This includes empty annotations in the *.tsv files, unless
    + 'overwriteWithEmpty' is false. Metabolites are matched by
    + metaboliteName[comp]; reactions by the stoichiometry of their reactants
    + and products; genes by their gene name. Entities that are not present in
    + the model are added.
    +
    + Columns are identified by their header (case-insensitive, and spaces around
    + a header are ignored, also inside its quotes), so their order
    + does not matter and optional columns can be left out. Every field that a
    + table supplies is written to new and existing entities alike; a field the
    + model does not have yet is created, with its default value for the other
    + entities, unless all supplied values for it are empty. An empty grRules
    + cell leaves the rule of an existing reaction unchanged, and so does an
    + empty lb, ub or rev cell: an empty bound means that no bound is given.
    + A new reaction without a lb gets a lower bound of -Inf, and one without an
    + ub gets model.annotation.defaultUB (Inf if the model has none).
    +
    + Parameters
    + ----------
    + model : struct
    +     RAVEN model structure to be curated.
    + metsInfo : char
    +     Path to a *.tsv file with metabolite information, or 'none' to skip
    +     metabolite curation. Columns: metNames, comps (both required),
    +     formula, charge, inchi, metNotes, and any number of MIRIAM-namespace
    +     columns. Singular forms and the RAVEN field names (metFormulas,
    +     metCharges, inchis) are accepted as headers too.
    +
    + Name-Value Arguments
    + --------------------
    + genesInfo : char
    +     Path to a *.tsv file with gene information, or 'none'. Columns:
    +     genes (required), geneShortNames (or geneShortName), and MIRIAM.
    + rxnsCoeffs : char
    +     Path to a *.tsv file with reaction stoichiometric coefficients, or
    +     'none'. Columns (all required): rxnIdx (or index), rxnNames,
    +     metNames, comps, coefficient. One row per (reaction, metabolite)
    +     pair.
    + rxnsInfo : char
    +     Path to a *.tsv file with reaction information, or 'none'. Columns:
    +     rxnIdx (or index), rxnNames (both required), grRules, lb, ub, rev,
    +     subSystems, eccodes, rxnNotes, rxnReferences, rxnConfidenceScores,
    +     and MIRIAM. An index appears in at most one row of this table,
    +     otherwise RAVEN:badInput is raised (the rxnsCoeffs file has one row per
    +     metabolite of the reaction and repeats it). The two reaction tables
    +     are matched on index and name, and reactions or indices that appear
    +     in only one of them raise RAVEN:badInput.
    + metPrefix : char
    +     Prefix used to create new metabolite ids (for example 's_' for
    +     yeast-GEM, 'M_' for the BiGG default) (default 'M_').
    + rxnPrefix : char
    +     Prefix used to create new reaction ids (default 'R_').
    + overwriteWithEmpty : logical
    +     if true, an empty cell in a row of an existing metabolite, gene or
    +     reaction empties the corresponding field of the model. If false, an
    +     empty cell leaves the existing value unchanged and only the cells with a
    +     value are written (default true; [] also means the default). Must be a
    +     scalar logical, 0 or 1. A cell with only blanks counts as empty in both
    +     modes. Empty grRules cells never overwrite. An empty or NaN lb, ub or
    +     rev cell always keeps the existing value. For the charge and the
    +     confidence score, an empty or NaN cell is treated alike: when
    +     overwriteWithEmpty is true and at least one other row of the column has
    +     a value, it becomes 0; a column in which no row has a value is not
    +     written, and the existing values stay. The same holds for a MIRIAM
    +     column: a column in which no row of an existing entity has a value
    +     does not clear the annotations.
    +
    + Returns
    + -------
    + newModel : struct
    +     Curated RAVEN model structure.
    +
    + Examples
    + --------
    +     newModel = curateModelFromTables(model, metsInfo, genesInfo, ...
    +                     rxnsCoeffs, rxnsInfo, metPrefix, rxnPrefix);
    +
    + Notes
    + -----
    + In all three info tables, any column whose header is not one of the
    + listed core fields is treated as a MIRIAM annotation namespace and
    + stored on the matching entity.
    +
    + Cell values: the charge, confidence score, lb, ub, rev, rxnIdx and
    + coefficient cells are read as plain decimal numbers (digits with an
    + optional sign, decimal point and exponent), Inf or NaN in any case. Only
    + the lb, ub and rev cells also accept true and false in any case (read as
    + 1 and 0). Any other text, including text with a comma, hexadecimal numbers
    + and expressions, raises RAVEN:badInput; no expression is evaluated. A
    + charge must be a finite whole number (or empty or NaN). A rev must be 0, 1,
    + true or false (or empty or NaN). The rev column sets the reversibility of
    + existing reactions only, independently of lb and ub, which are written as
    + given; for new reactions the reversibility is derived from lb and ub.
    +
    + Gene rules: a grRules cell must be a well-formed gene rule (genes joined by
    + the words and or or in any case, or by &, &&, | or ||, with balanced
    + parentheses); a rule with an operator that lacks an operand, empty
    + parentheses or two genes without an operator between them raises
    + RAVEN:badInput before the model is changed. A valid rule is stored in
    + RAVEN's standard format. Whether the genes exist is checked when the rule
    + is applied.
    +
    + Genes: a gene id that the genesInfo table lists more than once raises
    + RAVEN:badInput, whether the model has the gene or not.
    +
    + Bounds: a lower bound above the upper bound is written as given. solveLP
    + rejects such a model when it is run ("Invalid set of bounds for
    + reaction(s): ...").
    +
    + Reaction tables: a row of the rxnsCoeffs table without a rxnIdx, rxnNames,
    + metNames and comps value is ignored; a row with any of them needs a
    + rxnIdx and a coefficient that is a finite number other than 0. A row of
    + the rxnsInfo table without any value is ignored; a row with a value needs
    + a rxnIdx. A rxnIdx is a whole number of at least 1; the indices (and
    + names) of the two reaction tables must match, with indices compared by
    + their numeric value (1, 1.0 and 1e0 are the same index). Otherwise
    + RAVEN:badInput is raised.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function newModel=curateModelFromTables(model,metsInfo,varargin)
    +0002 % curateModelFromTables  Curate or add mets, rxns and genes from tables.
    +0003 %
    +0004 % Curates existing metabolites, reactions and genes and adds new ones from
    +0005 % tabular data files.
    +0006 %
    +0007 % If the *.tsv files contain metabolites, reactions or genes that are
    +0008 % already present in the model, the information in the model is
    +0009 % overwritten. This includes empty annotations in the *.tsv files, unless
    +0010 % 'overwriteWithEmpty' is false. Metabolites are matched by
    +0011 % metaboliteName[comp]; reactions by the stoichiometry of their reactants
    +0012 % and products; genes by their gene name. Entities that are not present in
    +0013 % the model are added.
    +0014 %
    +0015 % Columns are identified by their header (case-insensitive, and spaces around
    +0016 % a header are ignored, also inside its quotes), so their order
    +0017 % does not matter and optional columns can be left out. Every field that a
    +0018 % table supplies is written to new and existing entities alike; a field the
    +0019 % model does not have yet is created, with its default value for the other
    +0020 % entities, unless all supplied values for it are empty. An empty grRules
    +0021 % cell leaves the rule of an existing reaction unchanged, and so does an
    +0022 % empty lb, ub or rev cell: an empty bound means that no bound is given.
    +0023 % A new reaction without a lb gets a lower bound of -Inf, and one without an
    +0024 % ub gets model.annotation.defaultUB (Inf if the model has none).
    +0025 %
    +0026 % Parameters
    +0027 % ----------
    +0028 % model : struct
    +0029 %     RAVEN model structure to be curated.
    +0030 % metsInfo : char
    +0031 %     Path to a *.tsv file with metabolite information, or 'none' to skip
    +0032 %     metabolite curation. Columns: metNames, comps (both required),
    +0033 %     formula, charge, inchi, metNotes, and any number of MIRIAM-namespace
    +0034 %     columns. Singular forms and the RAVEN field names (metFormulas,
    +0035 %     metCharges, inchis) are accepted as headers too.
    +0036 %
    +0037 % Name-Value Arguments
    +0038 % --------------------
    +0039 % genesInfo : char
    +0040 %     Path to a *.tsv file with gene information, or 'none'. Columns:
    +0041 %     genes (required), geneShortNames (or geneShortName), and MIRIAM.
    +0042 % rxnsCoeffs : char
    +0043 %     Path to a *.tsv file with reaction stoichiometric coefficients, or
    +0044 %     'none'. Columns (all required): rxnIdx (or index), rxnNames,
    +0045 %     metNames, comps, coefficient. One row per (reaction, metabolite)
    +0046 %     pair.
    +0047 % rxnsInfo : char
    +0048 %     Path to a *.tsv file with reaction information, or 'none'. Columns:
    +0049 %     rxnIdx (or index), rxnNames (both required), grRules, lb, ub, rev,
    +0050 %     subSystems, eccodes, rxnNotes, rxnReferences, rxnConfidenceScores,
    +0051 %     and MIRIAM. An index appears in at most one row of this table,
    +0052 %     otherwise RAVEN:badInput is raised (the rxnsCoeffs file has one row per
    +0053 %     metabolite of the reaction and repeats it). The two reaction tables
    +0054 %     are matched on index and name, and reactions or indices that appear
    +0055 %     in only one of them raise RAVEN:badInput.
    +0056 % metPrefix : char
    +0057 %     Prefix used to create new metabolite ids (for example 's_' for
    +0058 %     yeast-GEM, 'M_' for the BiGG default) (default 'M_').
    +0059 % rxnPrefix : char
    +0060 %     Prefix used to create new reaction ids (default 'R_').
    +0061 % overwriteWithEmpty : logical
    +0062 %     if true, an empty cell in a row of an existing metabolite, gene or
    +0063 %     reaction empties the corresponding field of the model. If false, an
    +0064 %     empty cell leaves the existing value unchanged and only the cells with a
    +0065 %     value are written (default true; [] also means the default). Must be a
    +0066 %     scalar logical, 0 or 1. A cell with only blanks counts as empty in both
    +0067 %     modes. Empty grRules cells never overwrite. An empty or NaN lb, ub or
    +0068 %     rev cell always keeps the existing value. For the charge and the
    +0069 %     confidence score, an empty or NaN cell is treated alike: when
    +0070 %     overwriteWithEmpty is true and at least one other row of the column has
    +0071 %     a value, it becomes 0; a column in which no row has a value is not
    +0072 %     written, and the existing values stay. The same holds for a MIRIAM
    +0073 %     column: a column in which no row of an existing entity has a value
    +0074 %     does not clear the annotations.
    +0075 %
    +0076 % Returns
    +0077 % -------
    +0078 % newModel : struct
    +0079 %     Curated RAVEN model structure.
    +0080 %
    +0081 % Examples
    +0082 % --------
    +0083 %     newModel = curateModelFromTables(model, metsInfo, genesInfo, ...
    +0084 %                     rxnsCoeffs, rxnsInfo, metPrefix, rxnPrefix);
    +0085 %
    +0086 % Notes
    +0087 % -----
    +0088 % In all three info tables, any column whose header is not one of the
    +0089 % listed core fields is treated as a MIRIAM annotation namespace and
    +0090 % stored on the matching entity.
    +0091 %
    +0092 % Cell values: the charge, confidence score, lb, ub, rev, rxnIdx and
    +0093 % coefficient cells are read as plain decimal numbers (digits with an
    +0094 % optional sign, decimal point and exponent), Inf or NaN in any case. Only
    +0095 % the lb, ub and rev cells also accept true and false in any case (read as
    +0096 % 1 and 0). Any other text, including text with a comma, hexadecimal numbers
    +0097 % and expressions, raises RAVEN:badInput; no expression is evaluated. A
    +0098 % charge must be a finite whole number (or empty or NaN). A rev must be 0, 1,
    +0099 % true or false (or empty or NaN). The rev column sets the reversibility of
    +0100 % existing reactions only, independently of lb and ub, which are written as
    +0101 % given; for new reactions the reversibility is derived from lb and ub.
    +0102 %
    +0103 % Gene rules: a grRules cell must be a well-formed gene rule (genes joined by
    +0104 % the words and or or in any case, or by &, &&, | or ||, with balanced
    +0105 % parentheses); a rule with an operator that lacks an operand, empty
    +0106 % parentheses or two genes without an operator between them raises
    +0107 % RAVEN:badInput before the model is changed. A valid rule is stored in
    +0108 % RAVEN's standard format. Whether the genes exist is checked when the rule
    +0109 % is applied.
    +0110 %
    +0111 % Genes: a gene id that the genesInfo table lists more than once raises
    +0112 % RAVEN:badInput, whether the model has the gene or not.
    +0113 %
    +0114 % Bounds: a lower bound above the upper bound is written as given. solveLP
    +0115 % rejects such a model when it is run ("Invalid set of bounds for
    +0116 % reaction(s): ...").
    +0117 %
    +0118 % Reaction tables: a row of the rxnsCoeffs table without a rxnIdx, rxnNames,
    +0119 % metNames and comps value is ignored; a row with any of them needs a
    +0120 % rxnIdx and a coefficient that is a finite number other than 0. A row of
    +0121 % the rxnsInfo table without any value is ignored; a row with a value needs
    +0122 % a rxnIdx. A rxnIdx is a whole number of at least 1; the indices (and
    +0123 % names) of the two reaction tables must match, with indices compared by
    +0124 % their numeric value (1, 1.0 and 1e0 are the same index). Otherwise
    +0125 % RAVEN:badInput is raised.
    +0126 
    +0127 p=parseRAVENargs(varargin, {'genesInfo','none'; 'rxnsCoeffs','none'; 'rxnsInfo','none'; 'metPrefix',[]; 'rxnPrefix',[]; 'overwriteWithEmpty',true});
    +0128 overwriteWithEmpty=p.overwriteWithEmpty;
    +0129 if isempty(overwriteWithEmpty)
    +0130     overwriteWithEmpty=true;
    +0131 end
    +0132 if ~((islogical(overwriteWithEmpty) || isnumeric(overwriteWithEmpty)) && isscalar(overwriteWithEmpty) ...
    +0133         && isreal(overwriteWithEmpty) && ~isnan(overwriteWithEmpty) && ismember(overwriteWithEmpty,[0 1]))
    +0134     error('RAVEN:badInput','overwriteWithEmpty must be a scalar logical or 0/1');
    +0135 end
    +0136 overwriteWithEmpty=logical(overwriteWithEmpty);
    +0137 genesInfo=p.genesInfo;
    +0138 rxnsCoeffs=p.rxnsCoeffs;
    +0139 rxnsInfo=p.rxnsInfo;
    +0140 metPrefix=p.metPrefix;
    +0141 if isempty(metPrefix)
    +0142     metPrefix = 'M_';
    +0143 end
    +0144 rxnPrefix=p.rxnPrefix;
    +0145 if isempty(rxnPrefix)
    +0146     rxnPrefix = 'R_';
    +0147 end
    +0148 if xor(strcmp(rxnsCoeffs,'none'), strcmp(rxnsInfo,'none'))
    +0149     error('RAVEN:badInput','Provide both a ''rxnsInfo'' and a ''rxnsCoeffs'' file')
    +0150 end
    +0151 newModel=model;
    +0152 
    +0153 %% Metabolites
    +0154 if ~strcmp(metsInfo,'none')
    +0155     [header,data] = readCurationTable(metsInfo);
    +0156     [col,miriamCols] = matchColumns(header,metsInfo,{'metNames','comps'},{ ...
    +0157         'metNames',    {'metNames','metName'}; ...
    +0158         'comps',       {'comps','comp'}; ...
    +0159         'metFormulas', {'formula','formulas','metFormula','metFormulas'}; ...
    +0160         'metCharges',  {'charge','charges','metCharge','metCharges'}; ...
    +0161         'inchis',      {'inchi','inchis'}; ...
    +0162         'metNotes',    {'metNotes','metNote'}});
    +0163     metsToAdd.metNames      = data(:,col.metNames);
    +0164     metsToAdd.compartments  = data(:,col.comps);
    +0165     textFields = {'metFormulas','inchis','metNotes'};
    +0166     for f = textFields(isfield(col,textFields))
    +0167         metsToAdd.(f{1}) = data(:,col.(f{1}));
    +0168     end
    +0169     if isfield(col,'metCharges')
    +0170         metsToAdd.metCharges = cellfun(@(x) parseChargeCell(x,'metsInfo'),data(:,col.metCharges),'UniformOutput',false);
    +0171         isNanCharge = cellfun(@(x) isscalar(x) && isnan(x),metsToAdd.metCharges);
    +0172         metsToAdd.metCharges(isNanCharge) = {[]}; % a NaN charge counts as not given
    +0173         emptyEntry = cellfun(@isempty,metsToAdd.metCharges);
    +0174         emptyCharge = emptyEntry;
    +0175         if all(emptyEntry)
    +0176             metsToAdd       = rmfield(metsToAdd,'metCharges');
    +0177         else % If some charges are given, assume 0 for those without charges specified
    +0178             metsToAdd.metCharges(emptyEntry) = {0};
    +0179             metsToAdd.metCharges = cell2mat(metsToAdd.metCharges);
    +0180         end
    +0181     end
    +0182     optFields = intersect({'metFormulas','metCharges','inchis','metNotes'},fieldnames(metsToAdd)','stable');
    +0183 
    +0184     % Check if metabolite already exists (check by metName[comp])
    +0185     existingMets            = [];
    +0186     existingMetsIdx         = [];
    +0187     newMets                 = [];
    +0188     for i=1:numel(metsToAdd.metNames)
    +0189         metIdx              = find(strcmp(newModel.metNames,metsToAdd.metNames{i}));
    +0190         existMet            = strcmp(metsToAdd.compartments{i},newModel.comps(newModel.metComps(metIdx)));
    +0191         if any(existMet)
    +0192             existingMets    = [existingMets, i];
    +0193             existingMetsIdx = [existingMetsIdx, metIdx(existMet)];
    +0194         else
    +0195             newMets    = [newMets, i];
    +0196         end
    +0197     end
    +0198 
    +0199     % Overwrite annotation of existing entries
    +0200     if any(existingMets)
    +0201         for f = optFields
    +0202             skip = [];
    +0203             if ~overwriteWithEmpty
    +0204                 if strcmp(f{1},'metCharges')
    +0205                     skip = emptyCharge(existingMets);
    +0206                 else
    +0207                     skip = emptyCells(metsToAdd.(f{1})(existingMets));
    +0208                 end
    +0209             end
    +0210             newModel = writeExistingField(newModel,f{1},existingMetsIdx,metsToAdd.(f{1})(existingMets),skip);
    +0211         end
    +0212         newModel = extractAndAddMiriam(newModel,header(miriamCols),data(:,miriamCols),existingMets,existingMetsIdx,'met',~overwriteWithEmpty);
    +0213         warning(['The following metabolites are already present in the model, '...
    +0214             'their annotation will be overwritten to match the metsInfo file. '...
    +0215             'If you do not particularly want to curate their annotations, it '...
    +0216             'would be better to remove these metabolites from metsInfo:\n\t\t%s'],...
    +0217             strjoin(strcat(metsToAdd.metNames(existingMets),'[',metsToAdd.compartments(existingMets),']'),'\n\t\t'));
    +0218     end
    +0219 
    +0220     % Continue with new metabolites
    +0221     if numel(newMets)>0
    +0222         metsToAdd.metNames      = metsToAdd.metNames(newMets);
    +0223         metsToAdd.compartments  = metsToAdd.compartments(newMets);
    +0224         for f = optFields
    +0225             metsToAdd.(f{1}) = metsToAdd.(f{1})(newMets);
    +0226             if iscell(metsToAdd.(f{1})) && all(cellfun(@isempty,metsToAdd.(f{1})))
    +0227                 metsToAdd = rmfield(metsToAdd,f{1});
    +0228             end
    +0229         end
    +0230         % Add metabolites
    +0231         newModel        = addMets(newModel,metsToAdd,true,metPrefix);
    +0232         addedIdx        = numel(newModel.mets)-numel(newMets)+1:numel(newModel.mets);
    +0233         newModel        = extractAndAddMiriam(newModel,header(miriamCols),data(:,miriamCols),newMets,addedIdx,'met');
    +0234     end
    +0235 end
    +0236 %% Genes
    +0237 if ~strcmp(genesInfo,'none')
    +0238     [header,data] = readCurationTable(genesInfo);
    +0239     [col,miriamCols] = matchColumns(header,genesInfo,{'genes'},{ ...
    +0240         'genes',          {'genes','gene'}; ...
    +0241         'geneShortNames', {'geneShortNames','geneShortName'}});
    +0242     genesToAdd.genes            = data(:,col.genes);
    +0243     checkGenesListedOnce(genesToAdd.genes);
    +0244     if isfield(col,'geneShortNames')
    +0245         genesToAdd.geneShortNames   = data(:,col.geneShortNames);
    +0246     end
    +0247 
    +0248     [~,notNewGene,existingGene] = intersect(genesToAdd.genes,newModel.genes);
    +0249     if ~isempty(notNewGene)
    +0250         warning(['The following genes are already present in the model, their annotation '...
    +0251             'will be overwritten to match the genesInfo file: \n\t\t%s'],...
    +0252             strjoin(genesToAdd.genes(notNewGene),'\n\t\t'));
    +0253         if isfield(genesToAdd,'geneShortNames')
    +0254             skip = [];
    +0255             if ~overwriteWithEmpty
    +0256                 skip = emptyCells(genesToAdd.geneShortNames(notNewGene));
    +0257             end
    +0258             newModel = writeExistingField(newModel,'geneShortNames',existingGene,genesToAdd.geneShortNames(notNewGene),skip);
    +0259         end
    +0260         newModel = extractAndAddMiriam(newModel,header(miriamCols),data(:,miriamCols),notNewGene,existingGene,'gene',~overwriteWithEmpty);
    +0261     end
    +0262 
    +0263     % Continue with new genes
    +0264     toAdd               = 1:numel(genesToAdd.genes);
    +0265     toAdd(notNewGene)   = [];
    +0266     if ~isempty(toAdd)
    +0267         genesToAdd.genes            = genesToAdd.genes(toAdd);
    +0268         if isfield(genesToAdd,'geneShortNames')
    +0269             genesToAdd.geneShortNames   = genesToAdd.geneShortNames(toAdd);
    +0270             if all(cellfun(@isempty,genesToAdd.geneShortNames))
    +0271                 genesToAdd = rmfield(genesToAdd,'geneShortNames');
    +0272             end
    +0273         end
    +0274         % Add genes
    +0275         newModel = addGenesRaven(newModel,genesToAdd);
    +0276         addedIdx = numel(newModel.genes)-numel(toAdd)+1:numel(newModel.genes);
    +0277         newModel = extractAndAddMiriam(newModel,header(miriamCols),data(:,miriamCols),toAdd,addedIdx,'gene');
    +0278     end
    +0279 end
    +0280 %% Reactions
    +0281 if ~any(strcmp({rxnsCoeffs,rxnsInfo},'none'))
    +0282     % Gather all data, first about reaction stoichiometries...
    +0283     [header,data] = readCurationTable(rxnsCoeffs);
    +0284     col = matchColumns(header,rxnsCoeffs,{'rxnIdx','rxnNames','metNames','comps','coefficient'},{ ...
    +0285         'rxnIdx',      {'rxnIdx','index'}; ...
    +0286         'rxnNames',    {'rxnNames','rxnName'}; ...
    +0287         'metNames',    {'metNames','metName'}; ...
    +0288         'comps',       {'comps','comp'}; ...
    +0289         'coefficient', {'coefficient','coefficients','coeffs','stoichCoeffs'}});
    +0290     % A row without a rxnIdx, rxnNames, metNames and comps value is ignored
    +0291     rowNo = (1:size(data,1))';
    +0292     keyCells = data(:,[col.rxnIdx,col.rxnNames,col.metNames,col.comps]);
    +0293     ignoredRow = all(reshape(emptyCells(keyCells(:)),size(keyCells)),2);
    +0294     data(ignoredRow,:) = [];
    +0295     rowNo(ignoredRow) = [];
    +0296     rxnCheck.coeffsIdx = parseIndexCells(data(:,col.rxnIdx),'rxnIdx','rxnsCoeffs');
    +0297     rxns.rxnNames   = data(:,col.rxnNames);
    +0298     rxns.metNames   = data(:,col.metNames);
    +0299     rxns.comps      = data(:,col.comps);
    +0300     rxns.coeffs     = parseNumericCells(data(:,col.coefficient),'coefficient','rxnsCoeffs');
    +0301     checkCoefficientsGiven(rxnCheck.coeffsIdx,rxns.coeffs,rowNo);
    +0302     rxnCheck.coeffs = indexKeys(rxnCheck.coeffsIdx,rxns.rxnNames);
    +0303 
    +0304     % ... and then additional reaction-specific data.
    +0305     [header,data] = readCurationTable(rxnsInfo);
    +0306     [col,miriamCols] = matchColumns(header,rxnsInfo,{'rxnIdx','rxnNames'},{ ...
    +0307         'rxnIdx',              {'rxnIdx','index'}; ...
    +0308         'rxnNames',            {'rxnNames','rxnName'}; ...
    +0309         'grRules',             {'grRules'}; ...
    +0310         'lb',                  {'lb'}; ...
    +0311         'ub',                  {'ub'}; ...
    +0312         'rev',                 {'rev'}; ...
    +0313         'subSystems',          {'subSystems'}; ...
    +0314         'eccodes',             {'eccodes'}; ...
    +0315         'rxnNotes',            {'rxnNotes'}; ...
    +0316         'rxnReferences',       {'rxnReferences'}; ...
    +0317         'rxnConfidenceScores', {'rxnConfidenceScores'}});
    +0318     % A row without any value is ignored; a row with a value needs a rxnIdx
    +0319     rowNo = (1:size(data,1))';
    +0320     ignoredRow = all(reshape(emptyCells(data(:)),size(data)),2);
    +0321     data(ignoredRow,:) = [];
    +0322     rowNo(ignoredRow) = [];
    +0323     rxnCheck.rxnsIdx = parseIndexCells(data(:,col.rxnIdx),'rxnIdx','rxnsInfo');
    +0324     noIdx = find(isnan(rxnCheck.rxnsIdx),1);
    +0325     if ~isempty(noIdx)
    +0326         error('RAVEN:badInput','Data row %d of the rxnsInfo file has values but its rxnIdx is empty or NaN',rowNo(noIdx));
    +0327     end
    +0328     [uniqueIdx,~,idxGroup] = unique(rxnCheck.rxnsIdx);
    +0329     repeated = find(accumarray(idxGroup,1)>1,1);
    +0330     if ~isempty(repeated)
    +0331         error('RAVEN:badInput','The rxnIdx %s is used by data rows %s of the rxnsInfo file; each reaction needs its own index', ...
    +0332             num2str(uniqueIdx(repeated)),strjoin(arrayfun(@num2str,rowNo(idxGroup==repeated)','UniformOutput',false),', '));
    +0333     end
    +0334     rxnCheck.rxns = indexKeys(rxnCheck.rxnsIdx,data(:,col.rxnNames));
    +0335 
    +0336     notMatching = setxor(rxnCheck.coeffs,rxnCheck.rxns);
    +0337     if ~isempty(notMatching)
    +0338         error('RAVEN:badInput',['The following reactions and/or their indices are not matched '...
    +0339             'between the rxnsInfo and rxnsCoeffs files:\n\t\t%s'],...
    +0340             strjoin(regexprep(notMatching,'^(\d+)\*\*\*(.*)$','index $1 ($2)'),'\n\t\t'))
    +0341     end
    +0342 
    +0343     rxnsToAdd.rxnNames              = data(:,col.rxnNames);
    +0344     for f = intersect({'grRules','subSystems','eccodes','rxnNotes','rxnReferences'},fieldnames(col)','stable')
    +0345         rxnsToAdd.(f{1}) = data(:,col.(f{1}));
    +0346     end
    +0347     if isfield(col,'grRules')
    +0348         rxnsToAdd.grRules = standardGrRules(rxnsToAdd.grRules,rxnsToAdd.rxnNames,rowNo);
    +0349     end
    +0350     for f = intersect({'lb','ub','rev'},fieldnames(col)','stable')
    +0351         rxnsToAdd.(f{1}) = parseNumericCells(data(:,col.(f{1})),f{1},'rxnsInfo');
    +0352     end
    +0353     if isfield(rxnsToAdd,'rev')
    +0354         checkRevValues(rxnsToAdd.rev,data(:,col.rev));
    +0355     end
    +0356     if isfield(col,'rxnConfidenceScores')
    +0357         rxnsToAdd.rxnConfidenceScores   = cellfun(@(x) parseNumberCell(x,'rxnConfidenceScores','rxnsInfo'),data(:,col.rxnConfidenceScores),'UniformOutput',false);
    +0358         isNanScore = cellfun(@(x) isscalar(x) && isnan(x),rxnsToAdd.rxnConfidenceScores);
    +0359         rxnsToAdd.rxnConfidenceScores(isNanScore) = {[]}; % a NaN score counts as not given
    +0360         emptyEntry = cellfun(@isempty,rxnsToAdd.rxnConfidenceScores);
    +0361         emptyScore = emptyEntry;
    +0362         if all(emptyEntry)
    +0363             rxnsToAdd           = rmfield(rxnsToAdd,'rxnConfidenceScores');
    +0364         else % If some rxnConfidenceScores are given, assume 0 for those without rxnConfidenceScores specified
    +0365             rxnsToAdd.rxnConfidenceScores(emptyEntry) = {0};
    +0366             rxnsToAdd.rxnConfidenceScores = cell2mat(rxnsToAdd.rxnConfidenceScores);
    +0367         end
    +0368     end
    +0369     optFields = intersect({'subSystems','eccodes','rxnNotes','rxnReferences','rxnConfidenceScores'},fieldnames(rxnsToAdd)','stable');
    +0370 
    +0371     existingRxn=[];
    +0372     notNewRxn=[];
    +0373     for i=1:numel(rxnCheck.rxnsIdx)
    +0374         rxnRows                     = find(rxnCheck.rxnsIdx(i)==rxnCheck.coeffsIdx);
    +0375         rxnsToAdd.mets{i,1}         = cell(1,1);
    +0376         rxnsToAdd.stoichCoeffs{i,1} = cell(1,1);
    +0377         for j=1:numel(rxnRows)
    +0378             newMetComp = [rxns.metNames{rxnRows(j)}, '[', rxns.comps{rxnRows(j)}, ']'];
    +0379             try
    +0380                 newMetComp = getIndexes(newModel,newMetComp,'metcomps');
    +0381             catch
    +0382                 error('RAVEN:badInput',['Not all metabolites in reaction "%s" are present in the '...
    +0383                     'model or in the provided table with new metabolites.'],rxnsToAdd.rxnNames{i})
    +0384             end
    +0385             rxnsToAdd.mets{i}(j) = newModel.mets(newMetComp);
    +0386             rxnsToAdd.stoichCoeffs{i}{j} = rxns.coeffs(rxnRows(j));
    +0387         end
    +0388         rxnsToAdd.stoichCoeffs{i}=cell2mat(rxnsToAdd.stoichCoeffs{i});
    +0389         %Check if the reaction not already exists, by stoichiometry of its
    +0390         %products and reactants
    +0391         metIdx              = getIndexes(newModel,rxnsToAdd.mets{i},'mets');
    +0392         modelCoeffs         = transpose(newModel.S(metIdx,:));
    +0393         modelCoeffs2        = find(~all(modelCoeffs==0,2));
    +0394         % Only reactions built from exactly these metabolites can be
    +0395         % duplicates. Any model reaction that also involves other
    +0396         % metabolites projects onto the same row of coefficients, but is a
    +0397         % different reaction and must not be overwritten.
    +0398         sameSupport         = full(sum(newModel.S(:,modelCoeffs2)~=0,1))' == numel(metIdx);
    +0399         modelCoeffs2        = modelCoeffs2(sameSupport);
    +0400         modelCoeffs         = modelCoeffs(modelCoeffs2,:);
    +0401         [~, duplicateRxn]   = intersect(modelCoeffs,rxnsToAdd.stoichCoeffs{i},'rows');
    +0402         if ~isempty(duplicateRxn)
    +0403             existingRxn     = [existingRxn,modelCoeffs2(duplicateRxn)];
    +0404             notNewRxn       = [notNewRxn,i];
    +0405         end
    +0406     end
    +0407 
    +0408     % If some reactions already existed, then replace its information
    +0409     if ~isempty(existingRxn)
    +0410         warning(['The following reactions are the same as existing model '...
    +0411             'reactions, their annotation will be overwritten to match the '...
    +0412             'rxnCoeffs file: \n\t\t(Existing reaction ID): New reaction name\n\t\t%s'],...
    +0413             strjoin(strcat('(',newModel.rxns(existingRxn),{'): '},rxnsToAdd.rxnNames(notNewRxn)),'\n\t\t'));
    +0414         nameValues = rxnsToAdd.rxnNames(notNewRxn);
    +0415         nameIdx    = existingRxn;
    +0416         if ~overwriteWithEmpty
    +0417             nameIdx    = nameIdx(~emptyCells(nameValues));
    +0418             nameValues = nameValues(~emptyCells(nameValues));
    +0419         end
    +0420         newModel.rxnNames(nameIdx) = nameValues;
    +0421         for f = intersect({'lb','ub','rev'},fieldnames(rxnsToAdd)','stable')
    +0422             values = rxnsToAdd.(f{1})(notNewRxn);
    +0423             given  = ~isnan(values);
    +0424             newModel.(f{1})(existingRxn(given)) = values(given);
    +0425         end
    +0426         for f = optFields
    +0427             values = rxnsToAdd.(f{1})(notNewRxn);
    +0428             skip = [];
    +0429             if ~overwriteWithEmpty
    +0430                 if strcmp(f{1},'rxnConfidenceScores')
    +0431                     skip = emptyScore(notNewRxn);
    +0432                 else
    +0433                     skip = emptyCells(values);
    +0434                 end
    +0435             end
    +0436             if strcmp(f{1},'subSystems') && isfield(newModel,'subSystems') ...
    +0437                     && any(cellfun(@iscell,newModel.subSystems))
    +0438                 values = cellfun(@(x) {x}, values, 'UniformOutput', false);
    +0439             end
    +0440             newModel = writeExistingField(newModel,f{1},existingRxn,values,skip);
    +0441         end
    +0442         if isfield(rxnsToAdd,'grRules')
    +0443             emptyEntries = emptyCells(rxnsToAdd.grRules(notNewRxn));
    +0444             if ~all(emptyEntries)
    +0445                 newModel    = changeGrRules(newModel,newModel.rxns(existingRxn(~emptyEntries)),rxnsToAdd.grRules(notNewRxn(~emptyEntries)),true);
    +0446             end
    +0447         end
    +0448         newModel    = extractAndAddMiriam(newModel,header(miriamCols),data(:,miriamCols),notNewRxn,existingRxn,'rxn',~overwriteWithEmpty);
    +0449     end
    +0450 
    +0451     % Continue with new reactions
    +0452     toAdd               = 1:numel(rxnsToAdd.rxnNames);
    +0453     toAdd(notNewRxn)    = [];
    +0454     if ~isempty(toAdd)
    +0455         rxnsToAdd.rxnNames      = rxnsToAdd.rxnNames(toAdd);
    +0456         for f = intersect({'lb','ub','rev','grRules'},fieldnames(rxnsToAdd)','stable')
    +0457             rxnsToAdd.(f{1}) = rxnsToAdd.(f{1})(toAdd);
    +0458         end
    +0459         if isfield(rxnsToAdd,'lb')
    +0460             rxnsToAdd.lb(isnan(rxnsToAdd.lb)) = -inf;
    +0461         end
    +0462         if isfield(rxnsToAdd,'ub')
    +0463             defaultUB = inf;
    +0464             if isfield(newModel,'annotation') && isfield(newModel.annotation,'defaultUB')
    +0465                 defaultUB = newModel.annotation.defaultUB;
    +0466             end
    +0467             rxnsToAdd.ub(isnan(rxnsToAdd.ub)) = defaultUB;
    +0468         end
    +0469         for f = optFields
    +0470             rxnsToAdd.(f{1}) = rxnsToAdd.(f{1})(toAdd);
    +0471             if iscell(rxnsToAdd.(f{1})) && all(cellfun(@isempty,rxnsToAdd.(f{1})))
    +0472                 rxnsToAdd = rmfield(rxnsToAdd,f{1});
    +0473             end
    +0474         end
    +0475         rxnsToAdd.mets          = rxnsToAdd.mets(toAdd);
    +0476         rxnsToAdd.stoichCoeffs  = rxnsToAdd.stoichCoeffs(toAdd);
    +0477         rxnsToAdd.rxns          = generateNewIds(model,'rxns',rxnPrefix,numel(rxnsToAdd.rxnNames));
    +0478 
    +0479         newModel        = addRxns(newModel,rxnsToAdd,1,[],false,false);
    +0480         rxnsModelIdx    = numel(newModel.rxns)-numel(toAdd)+1:numel(newModel.rxns);
    +0481         newModel        = extractAndAddMiriam(newModel,header(miriamCols),data(:,miriamCols),toAdd,rxnsModelIdx,'rxn');
    +0482     end
    +0483 end
    +0484 end
    +0485 
    +0486 function [header,data] = readCurationTable(fileName)
    +0487 % Read a tab-delimited curation table into its header row and a cell
    +0488 % array of text with one column per header field.
    +0489 fid = fopen(fileName);
    +0490 if fid==-1
    +0491     error('RAVEN:badInput','Could not open the file "%s"',fileName);
    +0492 end
    +0493 headerLine = fgetl(fid);
    +0494 if ~ischar(headerLine)
    +0495     fclose(fid);
    +0496     error('RAVEN:badInput','The file "%s" is empty',fileName);
    +0497 end
    +0498 headerLine = strrep(headerLine,char(65279),''); % byte order mark
    +0499 header = strtrim(strsplit(headerLine,'\t'));
    +0500 header = strtrim(regexprep(header,'^"(.*)"$','$1'));
    +0501 nCols  = numel(header);
    +0502 raw    = textscan(fid,repmat('%q',1,nCols),'Delimiter','\t');
    +0503 fclose(fid);
    +0504 nRows  = max(cellfun(@numel,raw));
    +0505 data   = repmat({''},nRows,nCols);
    +0506 for i=1:nCols
    +0507     data(1:numel(raw{i}),i) = raw{i};
    +0508 end
    +0509 blank = cellfun(@(x) ischar(x) && isempty(strtrim(x)),data);
    +0510 data(blank) = {''};
    +0511 end
    +0512 
    +0513 function [col,miriamCols] = matchColumns(header,fileName,required,spec)
    +0514 % Locate the core columns of a curation table by header name (case
    +0515 % insensitive). spec has one row per core field: {field, accepted headers}.
    +0516 % Every other column is a MIRIAM namespace.
    +0517 col        = struct();
    +0518 isCore     = false(1,numel(header));
    +0519 for i=1:size(spec,1)
    +0520     match = find(ismember(lower(header),lower(spec{i,2})));
    +0521     if ~isempty(match)
    +0522         col.(spec{i,1}) = match(1);
    +0523         isCore(match)   = true;
    +0524     end
    +0525 end
    +0526 missing = setdiff(required,fieldnames(col)','stable');
    +0527 if ~isempty(missing)
    +0528     error('RAVEN:badInput','The file "%s" has no column with header: %s',fileName,strjoin(missing,', '));
    +0529 end
    +0530 miriamCols = find(~isCore & ~cellfun(@isempty,header));
    +0531 end
    +0532 
    +0533 function model = writeExistingField(model,field,modelIdx,values,skip)
    +0534 % Write the values of an optional field onto existing entities. A field
    +0535 % the model does not have is created from its ravenModelFields default,
    +0536 % unless all supplied values are empty. Entries flagged in the logical
    +0537 % vector skip are not written.
    +0538 if nargin>4 && ~isempty(skip)
    +0539     modelIdx = modelIdx(~skip(:)');
    +0540     values   = values(~skip(:));
    +0541     if isempty(modelIdx)
    +0542         return
    +0543     end
    +0544 end
    +0545 if ~isfield(model,field)
    +0546     if iscell(values) && all(cellfun(@isempty,values))
    +0547         return
    +0548     end
    +0549     reg   = ravenModelFields();
    +0550     entry = reg(strcmp({reg.name},field));
    +0551     n     = numel(model.([entry.type 's']));
    +0552     if iscell(values)
    +0553         model.(field) = repmat({entry.default},n,1);
    +0554     else
    +0555         model.(field) = repmat(entry.default,n,1);
    +0556     end
    +0557 end
    +0558 model.(field)(modelIdx) = values;
    +0559 end
    +0560 
    +0561 function newModel = extractAndAddMiriam(model,miriamName,miriamData,inputIndex,modelIndex,type,skipEmpty)
    +0562 % Replace the MIRIAM annotations of the entities in modelIndex with the
    +0563 % values of the table rows in inputIndex. With skipEmpty, an empty cell
    +0564 % leaves the annotation of that entity unchanged.
    +0565 newModel=model;
    +0566 miriamValues    = miriamData(inputIndex,:);
    +0567 if nargin>6 && skipEmpty
    +0568     for i=1:numel(miriamName)
    +0569         given = ~emptyCells(miriamValues(:,i));
    +0570         if any(given)
    +0571             if ~isfield(newModel,[type 'Miriams'])
    +0572                 newModel.([type 'Miriams'])=cell(numel(newModel.([type 's'])),1);
    +0573             end
    +0574             idx = modelIndex(:);
    +0575             newModel = editMiriam(newModel,type,idx(given),miriamName{i},miriamValues(given,i),'replace');
    +0576         end
    +0577     end
    +0578     return
    +0579 end
    +0580 emptyMiriam     = all(cellfun(@isempty,miriamValues),1);
    +0581 miriamName(emptyMiriam)     = [];
    +0582 miriamValues(:,emptyMiriam) = [];
    +0583 if ~isfield(newModel,[type 'Miriams'])
    +0584     newModel.([type 'Miriams'])=cell(numel(newModel.([type 's'])),1);
    +0585 end
    +0586 for i=1:numel(miriamName)
    +0587     newModel = editMiriam(newModel,type,modelIndex,miriamName{i},miriamValues(:,i),'replace');
    +0588 end
    +0589 end
    +0590 
    +0591 function empty = emptyCells(values)
    +0592 % Logical column vector that is true for the empty entries of a cell array
    +0593 % of text (a cell with only blanks counts as empty) or of a numeric cell.
    +0594 empty = cellfun(@(x) isempty(x) || (ischar(x) && isempty(strtrim(x))),values(:));
    +0595 end
    +0596 
    +0597 function values = parseIndexCells(cells,field,fileLabel)
    +0598 % Reaction indices from table cells: NaN where the cell is empty or says NaN
    +0599 % (the row is ignored), otherwise a finite whole number of at least 1.
    +0600 values = parseNumericCells(cells,field,fileLabel);
    +0601 bad = find(~isnan(values) & (~isfinite(values) | values~=round(values) | values<1),1);
    +0602 if ~isempty(bad)
    +0603     error('RAVEN:badInput','The %s value "%s" in the %s file is not a whole number of at least 1',field,strtrim(cells{bad}),fileLabel);
    +0604 end
    +0605 end
    +0606 
    +0607 function values = parseNumericCells(cells,field,fileLabel)
    +0608 % Numbers from table cells, NaN where the cell is empty or says NaN. A cell
    +0609 % that is not a number (or true/false) raises an error.
    +0610 values = nan(numel(cells),1);
    +0611 for i=1:numel(cells)
    +0612     v = parseNumberCell(cells{i},field,fileLabel);
    +0613     if ~isempty(v)
    +0614         values(i) = v;
    +0615     end
    +0616 end
    +0617 end
    +0618 
    +0619 function v = parseChargeCell(text,fileLabel)
    +0620 % Charge from a table cell: [] for an empty cell, NaN for NaN, otherwise a
    +0621 % finite whole number. A fractional or infinite charge raises an error.
    +0622 v = parseNumberCell(text,'charge',fileLabel);
    +0623 if ~isempty(v) && ~isnan(v) && (~isfinite(v) || v~=round(v))
    +0624     error('RAVEN:badInput','The charge value "%s" in the %s file is not a finite whole number',strtrim(text),fileLabel);
    +0625 end
    +0626 end
    +0627 
    +0628 function checkCoefficientsGiven(idx,coeffs,rowNo)
    +0629 % Every row of the rxnsCoeffs table that is kept has a rxnIdx and a
    +0630 % coefficient that is a finite number other than 0.
    +0631 noIdx = find(isnan(idx),1);
    +0632 if ~isempty(noIdx)
    +0633     error('RAVEN:badInput',['Data row %d of the rxnsCoeffs file has values but its rxnIdx is empty or NaN'],rowNo(noIdx));
    +0634 end
    +0635 missing = find(isnan(coeffs),1);
    +0636 if ~isempty(missing)
    +0637     error('RAVEN:badInput',['The coefficient in data row %d of the rxnsCoeffs file is empty or NaN; ' ...
    +0638         'every row with a reaction, metabolite or compartment needs a coefficient'],rowNo(missing));
    +0639 end
    +0640 bad = find(~isfinite(coeffs) | coeffs==0,1);
    +0641 if ~isempty(bad)
    +0642     error('RAVEN:badInput','The coefficient in data row %d of the rxnsCoeffs file must be a finite non-zero number',rowNo(bad));
    +0643 end
    +0644 end
    +0645 
    +0646 function keys = indexKeys(idx,names)
    +0647 % One key per table row from the numeric reaction index and the name, so that
    +0648 % the indices of two tables are compared by value.
    +0649 keys = cell(numel(idx),1);
    +0650 for i=1:numel(idx)
    +0651     keys{i} = sprintf('%.0f***%s',idx(i),names{i});
    +0652 end
    +0653 end
    +0654 
    +0655 function checkGenesListedOnce(genes)
    +0656 % Raise RAVEN:badInput for a gene id that the genesInfo table lists more than
    +0657 % once, whether the model has the gene or not: two rows for one gene would
    +0658 % say two things about it, and the second row would add a second gene with
    +0659 % the same id.
    +0660 [ids,~,j] = unique(genes);
    +0661 repeated = ids(accumarray(j,1) > 1);
    +0662 if ~isempty(repeated)
    +0663     error('RAVEN:badInput',['The following genes are listed more than once '...
    +0664         'in the genesInfo file:\n\t\t%s'],strjoin(repeated,'\n\t\t'));
    +0665 end
    +0666 end
    +0667 
    +0668 
    +0669 function rules = standardGrRules(rules,rxnNames,rowNo)
    +0670 % Raise RAVEN:badInput for a grRules cell that is not a well-formed gene rule
    +0671 % (unbalanced parentheses, an operator without two operands, empty
    +0672 % parentheses, two genes without an operator between them) and write each
    +0673 % other rule in RAVEN's standard format (" and " / " or ", parentheses only
    +0674 % where needed), so that addRxns and changeGrRules read every spelling that
    +0675 % parseGrRule reads. Empty cells are left as they are.
    +0676 for i = find(~emptyCells(rules))'
    +0677     try
    +0678         rules{i} = grRuleToString(parseGrRule(rules{i}));
    +0679     catch err
    +0680         if strcmp(err.identifier,'RAVEN:badGrRule')
    +0681             error('RAVEN:badInput','The grRules value "%s" of reaction "%s" (data row %d of the rxnsInfo file) is not a valid gene rule: %s',...
    +0682                 rules{i},rxnNames{i},rowNo(i),err.message);
    +0683         end
    +0684         rethrow(err);
    +0685     end
    +0686 end
    +0687 end
    +0688 
    +0689 
    +0690 function checkRevValues(values,cells)
    +0691 % A rev cell is 0, 1, true or false; an empty or NaN cell is not given.
    +0692 bad = find(~isnan(values) & values~=0 & values~=1,1);
    +0693 if ~isempty(bad)
    +0694     error('RAVEN:badInput','The rev value "%s" in the rxnsInfo file is not 0, 1, true or false',strtrim(cells{bad}));
    +0695 end
    +0696 end
    +0697 
    +0698 function v = parseNumberCell(text,field,fileLabel)
    +0699 % Real number from a table cell: [] for an empty cell, 1 or 0 for true or
    +0700 % false in any case (lb, ub and rev cells only), NaN or +/-Inf for those words in any case, otherwise a
    +0701 % plain decimal number (digits with an optional sign, decimal point and
    +0702 % exponent). Any other text raises an error; nothing is evaluated.
    +0703 text = strtrim(text);
    +0704 if isempty(text)
    +0705     v = [];
    +0706     return
    +0707 end
    +0708 word = lower(text);
    +0709 if any(strcmp(word,{'true','false'})) && ~ismember(field,{'lb','ub','rev'})
    +0710     error('RAVEN:badInput','The %s value "%s" in the %s file is not a number',field,text,fileLabel);
    +0711 end
    +0712 if strcmp(word,'true')
    +0713     v = 1;
    +0714 elseif strcmp(word,'false')
    +0715     v = 0;
    +0716 elseif ~isempty(regexp(word,'^[+-]?nan$','once'))
    +0717     v = NaN;
    +0718 elseif ~isempty(regexp(word,'^[+-]?inf$','once'))
    +0719     v = Inf;
    +0720     if word(1)=='-'
    +0721         v = -Inf;
    +0722     end
    +0723 elseif ~isempty(regexp(text,'^[+-]?([0-9]+\.?[0-9]*|\.[0-9]+)([eE][+-]?[0-9]+)?$','once'))
    +0724     v = str2double(text);
    +0725 else
    +0726     error('RAVEN:badInput','The %s value "%s" in the %s file is not a number',field,text,fileLabel);
    +0727 end
    +0728 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/curation/downloadGenomeData.html b/docs/html/curation/downloadGenomeData.html new file mode 100644 index 00000000..4a6aab10 --- /dev/null +++ b/docs/html/curation/downloadGenomeData.html @@ -0,0 +1,211 @@ + + + + Description of downloadGenomeData + + + + + + + + + +
    Home > curation > downloadGenomeData.m
    + + + +

    downloadGenomeData +

    + +

    PURPOSE ^

    +
    downloadGenomeData Download genome annotation files from NCBI.
    + +

    SYNOPSIS ^

    +
    function [gffFile, faaFile] = downloadGenomeData(accession, varargin)
    + +

    DESCRIPTION ^

    +
     downloadGenomeData  Download genome annotation files from NCBI.
    +
    + Downloads the GFF3 annotation and protein FASTA (.faa) files for an NCBI
    + genome assembly accession using the NCBI Datasets v2 API, which returns
    + both files in a single archive. The files are saved as
    + <accession>_genomic.gff and <accession>_protein.faa in outputDir. They
    + are input files for getGeneData.
    +
    + Parameters
    + ----------
    + accession : char
    +     NCBI genome assembly accession, for example 'GCF_000002595.2'. The
    +     prefix is 'GCF_' (RefSeq) or 'GCA_' (GenBank).
    +
    + Name-Value Arguments
    + --------------------
    + outputDir : char
    +     Directory where downloaded files are saved (default: current working
    +     directory).
    + verbose : logical
    +     Print download progress to the command window (default: true).
    +
    + Returns
    + -------
    + gffFile : char
    +     Full path to the downloaded GFF3 annotation file.
    + faaFile : char
    +     Full path to the downloaded protein FASTA file.
    +
    + Examples
    + --------
    +     % Download to current directory
    +     [gff, faa] = downloadGenomeData('GCF_000002595.2');
    +
    +     % Save to a specific directory
    +     [gff, faa] = downloadGenomeData('GCF_000002595.2', 'data/');
    +
    +     % Suppress progress messages
    +     [gff, faa] = downloadGenomeData('GCF_000002595.2', 'data/', false);
    +
    + Notes
    + -----
    + An internet connection is required. When both files are already present
    + in outputDir, no download takes place; deleting them forces a new
    + download. An error is raised when NCBI Datasets does not return both a
    + GFF3 file and a protein FASTA file for the accession.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    • getGeneData getGeneData Build a gene-mapping table from NCBI genome annotation files.
    + + + + +

    SOURCE CODE ^

    +
    0001 function [gffFile, faaFile] = downloadGenomeData(accession, varargin)
    +0002 % downloadGenomeData  Download genome annotation files from NCBI.
    +0003 %
    +0004 % Downloads the GFF3 annotation and protein FASTA (.faa) files for an NCBI
    +0005 % genome assembly accession using the NCBI Datasets v2 API, which returns
    +0006 % both files in a single archive. The files are saved as
    +0007 % <accession>_genomic.gff and <accession>_protein.faa in outputDir. They
    +0008 % are input files for getGeneData.
    +0009 %
    +0010 % Parameters
    +0011 % ----------
    +0012 % accession : char
    +0013 %     NCBI genome assembly accession, for example 'GCF_000002595.2'. The
    +0014 %     prefix is 'GCF_' (RefSeq) or 'GCA_' (GenBank).
    +0015 %
    +0016 % Name-Value Arguments
    +0017 % --------------------
    +0018 % outputDir : char
    +0019 %     Directory where downloaded files are saved (default: current working
    +0020 %     directory).
    +0021 % verbose : logical
    +0022 %     Print download progress to the command window (default: true).
    +0023 %
    +0024 % Returns
    +0025 % -------
    +0026 % gffFile : char
    +0027 %     Full path to the downloaded GFF3 annotation file.
    +0028 % faaFile : char
    +0029 %     Full path to the downloaded protein FASTA file.
    +0030 %
    +0031 % Examples
    +0032 % --------
    +0033 %     % Download to current directory
    +0034 %     [gff, faa] = downloadGenomeData('GCF_000002595.2');
    +0035 %
    +0036 %     % Save to a specific directory
    +0037 %     [gff, faa] = downloadGenomeData('GCF_000002595.2', 'data/');
    +0038 %
    +0039 %     % Suppress progress messages
    +0040 %     [gff, faa] = downloadGenomeData('GCF_000002595.2', 'data/', false);
    +0041 %
    +0042 % Notes
    +0043 % -----
    +0044 % An internet connection is required. When both files are already present
    +0045 % in outputDir, no download takes place; deleting them forces a new
    +0046 % download. An error is raised when NCBI Datasets does not return both a
    +0047 % GFF3 file and a protein FASTA file for the accession.
    +0048 
    +0049     if nargin < 1 || isempty(accession)
    +0050     error('downloadGenomeData:missingInput', ...
    +0051         ['accession is required. ', ...
    +0052          'Provide an NCBI assembly accession, e.g. ''GCF_000002595.2''.']);
    +0053     end
    +0054     accession = char(accession);
    +0055     
    +0056     if ~any(strncmpi(accession, {'GCF_', 'GCA_'}, 4))
    +0057         error('downloadGenomeData:invalidAccession', ...
    +0058         ['Accession must start with ''GCF_'' (RefSeq) or ''GCA_'' (GenBank). ', ...
    +0059          'Received: ''%s''.'], accession);
    +0060     end
    +0061 
    +0062     p = parseRAVENargs(varargin, {'outputDir', pwd; 'verbose', true});
    +0063     downloadDir = char(p.outputDir);
    +0064     verbose = p.verbose;
    +0065 
    +0066     if ~exist(downloadDir, 'dir')
    +0067         mkdir(downloadDir);
    +0068     end
    +0069 
    +0070     gffFile = fullfile(downloadDir, [accession '_genomic.gff']);
    +0071     faaFile = fullfile(downloadDir, [accession '_protein.faa']);
    +0072 
    +0073     % Skip the download if both files are already present
    +0074     if isfile(gffFile) && isfile(faaFile)
    +0075         if verbose; fprintf('Genome data for %s already present, skipping download.\n', accession); end
    +0076         return
    +0077     end
    +0078 
    +0079     % --- NCBI Datasets v2 API: download GFF3 and protein FASTA as one zip ---
    +0080     if verbose; fprintf('Downloading genome data for %s from NCBI Datasets...\n', accession); end
    +0081     url = ['https://api.ncbi.nlm.nih.gov/datasets/v2alpha/genome/accession/', ...
    +0082            accession, '/download?include_annotation_type=GENOME_GFF,PROT_FASTA'];
    +0083     zipFile    = [tempname '.zip'];
    +0084     extractDir = tempname;
    +0085     try
    +0086         websave(zipFile, url);
    +0087     catch ME
    +0088         error('downloadGenomeData:download', 'Download failed for %s: %s', accession, ME.message);
    +0089     end
    +0090     try
    +0091         unzip(zipFile, extractDir);
    +0092     catch ME
    +0093         delete(zipFile);
    +0094         error('downloadGenomeData:extract', 'Extraction failed for %s: %s', accession, ME.message);
    +0095     end
    +0096     delete(zipFile);
    +0097 
    +0098     % The archive stores the files under ncbi_dataset/data/<assembly>/
    +0099     dataParent = fullfile(extractDir, 'ncbi_dataset', 'data');
    +0100     sub = dir(dataParent);
    +0101     sub = sub([sub.isdir] & ~startsWith({sub.name}, '.'));
    +0102     if ~isempty(sub)
    +0103         assemblyDir = fullfile(dataParent, sub(1).name);
    +0104         srcGff = fullfile(assemblyDir, 'genomic.gff');
    +0105         srcFaa = fullfile(assemblyDir, 'protein.faa');
    +0106     else
    +0107         srcGff = '';
    +0108         srcFaa = '';
    +0109     end
    +0110     if ~isfile(srcGff) || ~isfile(srcFaa)
    +0111         rmdir(extractDir, 's');
    +0112         error('downloadGenomeData:noAnnotation', ...
    +0113             ['NCBI Datasets did not return both a GFF3 annotation and a protein ', ...
    +0114              'FASTA for %s. The assembly may lack annotation.'], accession);
    +0115     end
    +0116     movefile(srcGff, gffFile, 'f');
    +0117     movefile(srcFaa, faaFile, 'f');
    +0118     rmdir(extractDir, 's');
    +0119 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/curation/getGeneData.html b/docs/html/curation/getGeneData.html new file mode 100644 index 00000000..277bc9bc --- /dev/null +++ b/docs/html/curation/getGeneData.html @@ -0,0 +1,362 @@ + + + + Description of getGeneData + + + + + + + + + +
    Home > curation > getGeneData.m
    + + + +

    getGeneData +

    + +

    PURPOSE ^

    +
    getGeneData Build a gene-mapping table from NCBI genome annotation files.
    + +

    SYNOPSIS ^

    +
    function geneTable = getGeneData(accession, varargin)
    + +

    DESCRIPTION ^

    +
     getGeneData  Build a gene-mapping table from NCBI genome annotation files.
    +
    + Parses the GFF3 annotation file of an NCBI genome assembly and produces
    + a table mapping locus tags to gene symbols, protein IDs and other
    + identifiers. The table can be passed to renameModelGenes to change gene
    + identifiers in a RAVEN model. If the input is an assembly accession,
    + downloadGenomeData downloads the GFF3 and protein FASTA (.faa) files to
    + downloadDir, unless both are already present there.
    +
    + Parameters
    + ----------
    + accession : char
    +     NCBI genome assembly accession, for example 'GCF_000002595.2', or the
    +     path to a local GFF3 annotation file. An error is raised if the
    +     input is neither an existing file nor an accession of the form
    +     'GCF_' or 'GCA_' followed by digits and an optional version.
    +
    + Name-Value Arguments
    + --------------------
    + outputFile : char
    +     Path of the tab-delimited .tsv file to which the table is written.
    +     No file is written if omitted or empty (default '').
    + downloadDir : char
    +     Directory where genome files are downloaded if not already present.
    +     Only used when the input is an accession (default: current working
    +     directory).
    +
    + Returns
    + -------
    + geneTable : table
    +     MATLAB table with one row per distinct combination of gene and
    +     protein accession (a CDS feature with a protein accession, repeated
    +     CDS rows are merged), with the columns below. A column entry is empty
    +     when the annotation lacks the value.
    +
    +     - locus_tag : locus identifier (for example 'Cre01.g000001')
    +     - old_locus_tag : earlier locus identifier
    +     - GeneID : NCBI Gene ID (for example '5723799')
    +     - gene_name : gene symbol (for example 'rbcL')
    +     - GenBank_protein : protein accession (for example 'XP_001698190.2'),
    +       as in the protein FASTA headers
    +     - UniProt : UniProt accession from the Dbxref attribute of the gene
    +
    + Examples
    + --------
    +     % Fetch and parse gene data for Chlamydomonas reinhardtii
    +     geneTable = getGeneData('GCF_000002595.2');
    +
    +     % Save the mapping table to a specific file
    +     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv');
    +
    +     % Save to a file and download to a specific directory
    +     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv', 'data/');
    +
    + Notes
    + -----
    + When outputFile is provided the table is written as UTF-8 encoded
    + tab-delimited text with a header row, which readtable and
    + renameModelGenes can read. A CDS whose gene has no locus_tag, or whose
    + gene feature is missing, gives a row with an empty locus_tag.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function geneTable = getGeneData(accession, varargin)
    +0002 % getGeneData  Build a gene-mapping table from NCBI genome annotation files.
    +0003 %
    +0004 % Parses the GFF3 annotation file of an NCBI genome assembly and produces
    +0005 % a table mapping locus tags to gene symbols, protein IDs and other
    +0006 % identifiers. The table can be passed to renameModelGenes to change gene
    +0007 % identifiers in a RAVEN model. If the input is an assembly accession,
    +0008 % downloadGenomeData downloads the GFF3 and protein FASTA (.faa) files to
    +0009 % downloadDir, unless both are already present there.
    +0010 %
    +0011 % Parameters
    +0012 % ----------
    +0013 % accession : char
    +0014 %     NCBI genome assembly accession, for example 'GCF_000002595.2', or the
    +0015 %     path to a local GFF3 annotation file. An error is raised if the
    +0016 %     input is neither an existing file nor an accession of the form
    +0017 %     'GCF_' or 'GCA_' followed by digits and an optional version.
    +0018 %
    +0019 % Name-Value Arguments
    +0020 % --------------------
    +0021 % outputFile : char
    +0022 %     Path of the tab-delimited .tsv file to which the table is written.
    +0023 %     No file is written if omitted or empty (default '').
    +0024 % downloadDir : char
    +0025 %     Directory where genome files are downloaded if not already present.
    +0026 %     Only used when the input is an accession (default: current working
    +0027 %     directory).
    +0028 %
    +0029 % Returns
    +0030 % -------
    +0031 % geneTable : table
    +0032 %     MATLAB table with one row per distinct combination of gene and
    +0033 %     protein accession (a CDS feature with a protein accession, repeated
    +0034 %     CDS rows are merged), with the columns below. A column entry is empty
    +0035 %     when the annotation lacks the value.
    +0036 %
    +0037 %     - locus_tag : locus identifier (for example 'Cre01.g000001')
    +0038 %     - old_locus_tag : earlier locus identifier
    +0039 %     - GeneID : NCBI Gene ID (for example '5723799')
    +0040 %     - gene_name : gene symbol (for example 'rbcL')
    +0041 %     - GenBank_protein : protein accession (for example 'XP_001698190.2'),
    +0042 %       as in the protein FASTA headers
    +0043 %     - UniProt : UniProt accession from the Dbxref attribute of the gene
    +0044 %
    +0045 % Examples
    +0046 % --------
    +0047 %     % Fetch and parse gene data for Chlamydomonas reinhardtii
    +0048 %     geneTable = getGeneData('GCF_000002595.2');
    +0049 %
    +0050 %     % Save the mapping table to a specific file
    +0051 %     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv');
    +0052 %
    +0053 %     % Save to a file and download to a specific directory
    +0054 %     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv', 'data/');
    +0055 %
    +0056 % Notes
    +0057 % -----
    +0058 % When outputFile is provided the table is written as UTF-8 encoded
    +0059 % tab-delimited text with a header row, which readtable and
    +0060 % renameModelGenes can read. A CDS whose gene has no locus_tag, or whose
    +0061 % gene feature is missing, gives a row with an empty locus_tag.
    +0062 
    +0063     if nargin < 1 || isempty(accession)
    +0064         error('getGeneData:missingInput', ...
    +0065             'accession is required. Provide an NCBI assembly accession, e.g. ''GCF_000002595.2''.');
    +0066     end
    +0067 
    +0068     p = parseRAVENargs(varargin, {'outputFile', ''; 'downloadDir', pwd});
    +0069     outputFile = char(p.outputFile);
    +0070     downloadDir = char(p.downloadDir);
    +0071 
    +0072     % Resolve input → local GFF3 path. Decide whether input is a file path or an NCBI accession.
    +0073     if ischar(accession) || isstring(accession)
    +0074         inp = char(accession);
    +0075     else
    +0076         error('getGeneData:badInputType', 'Input must be a character vector or string.');
    +0077     end
    +0078     
    +0079     gffPath = '';
    +0080     % If input is an existing file, use it.
    +0081     if isfile(inp)
    +0082         gffPath = inp;
    +0083     else
    +0084         % Check if it looks like an NCBI assembly accession (GCF_... or GCA_...)
    +0085         if ~isempty(regexp(inp, '^GC[FA]_\d+(\.\d+)?$', 'once'))
    +0086             gffPath = downloadGenomeData(inp, downloadDir);
    +0087         else
    +0088             error('getGeneData:badInput', ...
    +0089                 'Input "%s" is neither an existing file nor a recognised NCBI assembly accession.', inp);
    +0090         end
    +0091     end
    +0092 
    +0093     % Parse the GFF3 annotation into a gene-mapping table
    +0094     geneTable = parseGFF(gffPath);
    +0095 
    +0096     % Save result only if an output file was requested
    +0097     if ~isempty(outputFile)
    +0098         writetable(geneTable, outputFile, 'Delimiter', '\t', 'FileType', 'text');
    +0099         fprintf('Gene table saved to: %s  (%d rows)\n', outputFile, height(geneTable));
    +0100     end
    +0101 end
    +0102 
    +0103 %--------------------------------------------------------------------------
    +0104 % Helper functions
    +0105 
    +0106 function T = parseGFF(gffPath)
    +0107 % Parse a GFF3 file extracting gene + CDS pairs. The protein accession is
    +0108 % taken from each CDS protein_id, and the owning gene is resolved through
    +0109 % the Parent chain (CDS → mRNA → gene for eukaryotes, CDS → gene for
    +0110 % prokaryotes), so the GenBank_protein column matches the protein FASTA.
    +0111 %
    +0112 % Returns a table with columns:
    +0113 %   locus_tag | old_locus_tag | GeneID | gene_name | GenBank_protein | UniProt
    +0114 
    +0115     batchSize = 10000;
    +0116     rows  = cell(batchSize, 6);
    +0117     nRows = 0;
    +0118     % Use a containers.Map to hold the last gene record keyed by gene ID,
    +0119     % and an mRNA → gene map to resolve eukaryote CDS parents.
    +0120     geneMap    = containers.Map('KeyType', 'char', 'ValueType', 'any');
    +0121     mrnaToGene = containers.Map('KeyType', 'char', 'ValueType', 'char');
    +0122     emptyGene = struct('locus_tag','','old_locus_tag','','geneID','','name','','uniProt','');
    +0123 
    +0124     fid = fopen(gffPath, 'r');
    +0125     while ~feof(fid)
    +0126         line = fgetl(fid);
    +0127         if ~ischar(line) || isempty(line) || line(1) == '#', continue, end
    +0128         fields = strsplit(line, '\t');
    +0129         if numel(fields) < 9, continue, end
    +0130 
    +0131         featureType = fields{3};
    +0132         attrs       = fields{9};
    +0133 
    +0134         if strcmp(featureType, 'gene')
    +0135             g = parseGeneAttrs(attrs);
    +0136             if ~isempty(g.id)
    +0137                 geneMap(g.id) = g;
    +0138             end
    +0139 
    +0140         elseif strcmp(featureType, 'mRNA')
    +0141             id     = extractAttr(attrs, 'ID');
    +0142             parent = extractAttr(attrs, 'Parent');
    +0143             if ~isempty(id) && ~isempty(parent)
    +0144                 mrnaToGene(id) = parent;
    +0145             end
    +0146 
    +0147         elseif strcmp(featureType, 'CDS')
    +0148             child     = parseCDSAttrs(attrs);
    +0149             proteinID = child.protein_id;
    +0150             if isempty(child.parent) || isempty(proteinID), continue, end
    +0151 
    +0152             % Resolve the owning gene through the Parent chain
    +0153             if isKey(mrnaToGene, child.parent)
    +0154                 geneKey = mrnaToGene(child.parent);
    +0155             else
    +0156                 geneKey = child.parent;  % prokaryote: CDS Parent is the gene
    +0157             end
    +0158             if isKey(geneMap, geneKey)
    +0159                 g = geneMap(geneKey);
    +0160             else
    +0161                 g = emptyGene;
    +0162             end
    +0163 
    +0164             nRows = nRows + 1;
    +0165             if nRows > size(rows, 1)
    +0166                 rows = [rows; cell(batchSize, 6)];
    +0167             end
    +0168             rows(nRows, :) = {g.locus_tag, g.old_locus_tag, ...
    +0169                                g.geneID,    g.name,          proteinID, g.uniProt};
    +0170         end
    +0171     end
    +0172     fclose(fid);
    +0173 
    +0174     rows = rows(1:nRows, :);
    +0175 
    +0176     % Deduplicate rows (CDS repeats per exon).
    +0177     rowKeys = cellfun(@(a,b,c,d,e) strjoin({a,b,c,d,e}, '|'), ...
    +0178         rows(:,1), rows(:,2), rows(:,3), rows(:,4), rows(:,5), ...
    +0179         'UniformOutput', false);
    +0180     [~, ia] = unique(rowKeys, 'stable');
    +0181     rows = rows(ia, :);
    +0182 
    +0183     T = cell2table(rows, 'VariableNames', ...
    +0184         {'locus_tag','old_locus_tag','GeneID','gene_name','GenBank_protein','UniProt'});
    +0185 end
    +0186 
    +0187 % Attibute parsing utilities
    +0188 function value = extractAttr(attrStr, key)
    +0189 % Extract the value of a GFF3 attribute by key.
    +0190 % Handles URL-encoded characters (%2C etc.) for robustness.
    +0191 % Returns '' if key is not present.
    +0192     pattern = sprintf('(?:^|;)%s=([^;]+)', regexptranslate('escape', key));
    +0193     tok = regexp(attrStr, pattern, 'tokens', 'once');
    +0194     if isempty(tok)
    +0195         value = '';
    +0196     else
    +0197         value = gff3Decode(tok{1});
    +0198     end
    +0199 end
    +0200 
    +0201 function out = gff3Decode(str)
    +0202 % Decode GFF3 percent-encoded characters (%XX). Unlike urldecode, '+' is
    +0203 % left untouched, as GFF3 does not use it to encode spaces.
    +0204     out = regexprep(str, '%([0-9A-Fa-f]{2})', '${char(hex2dec($1))}');
    +0205 end
    +0206 
    +0207 function value = extractDbxrefField(dbxref, prefix)
    +0208 % From a comma-separated Dbxref value like
    +0209 %   "Phytozome:Cre16.g651050,GeneID:5723799,GenBank:XM_001698190.2"
    +0210 % extract the part after "prefix:".
    +0211 % Returns '' if not found.
    +0212     if isempty(dbxref)
    +0213         value = '';
    +0214         return
    +0215     end
    +0216     parts   = strsplit(dbxref, ',');
    +0217     pattern = sprintf('^%s:(.+)$', regexptranslate('escape', prefix));
    +0218     value   = '';
    +0219     for i = 1:numel(parts)
    +0220         tok = regexp(strtrim(parts{i}), pattern, 'tokens', 'once');
    +0221         if ~isempty(tok)
    +0222             value = tok{1};
    +0223             return
    +0224         end
    +0225     end
    +0226 end
    +0227 
    +0228 function g = parseGeneAttrs(attrs)
    +0229     g.id            = extractAttr(attrs, 'ID');
    +0230     g.name          = extractAttr(attrs, 'Name');
    +0231     g.locus_tag     = extractAttr(attrs, 'locus_tag');
    +0232     g.old_locus_tag = extractAttr(attrs, 'old_locus_tag');
    +0233 
    +0234     % Dbxref is a comma-separated list like:
    +0235     %   GeneID:5723799,GenBank:XM_001698190.2
    +0236     dbxref          = extractAttr(attrs, 'Dbxref');
    +0237     g.geneID        = extractDbxrefField(dbxref, 'GeneID');
    +0238 
    +0239     % UniProt accession appears as UniProtKB/Swiss-Prot:P12345 or UniProtKB:P12345
    +0240     g.uniProt       = extractDbxrefField(dbxref, 'UniProtKB/Swiss-Prot');
    +0241     if isempty(g.uniProt)
    +0242         g.uniProt   = extractDbxrefField(dbxref, 'UniProtKB');
    +0243     end
    +0244 end
    +0245 
    +0246 function c = parseCDSAttrs(attrs)
    +0247     c.parent     = extractAttr(attrs, 'Parent');
    +0248     c.protein_id = extractAttr(attrs, 'protein_id');
    +0249     if isempty(c.protein_id)
    +0250         % Some files put it in Dbxref as GenBank:WP_...
    +0251         dbxref       = extractAttr(attrs, 'Dbxref');
    +0252         c.protein_id = extractDbxrefField(dbxref, 'GenBank');
    +0253     end
    +0254 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/curation/index.html b/docs/html/curation/index.html similarity index 100% rename from doc/curation/index.html rename to docs/html/curation/index.html diff --git a/docs/html/curation/processProteinFastaFile.html b/docs/html/curation/processProteinFastaFile.html new file mode 100644 index 00000000..16e79f92 --- /dev/null +++ b/docs/html/curation/processProteinFastaFile.html @@ -0,0 +1,245 @@ + + + + Description of processProteinFastaFile + + + + + + + + + +
    Home > curation > processProteinFastaFile.m
    + + + +

    processProteinFastaFile +

    + +

    PURPOSE ^

    +
    processProteinFastaFile Rename protein FASTA headers using a gene mapping table.
    + +

    SYNOPSIS ^

    +
    function processedFaaFile = processProteinFastaFile(faaFile, geneTable, headerCol, varargin)
    + +

    DESCRIPTION ^

    +
     processProteinFastaFile  Rename protein FASTA headers using a gene mapping table.
    +
    + Reads a protein FASTA file (such as one downloaded by downloadGenomeData)
    + and replaces each sequence header with the value from the specified
    + geneTable column. A sequence is matched to a row through the
    + GenBank_protein accession, which is the first token of the original
    + FASTA header. Sequences whose accession is not found in geneTable keep
    + their original header. The numbers of renamed and kept sequences are
    + printed.
    +
    + Parameters
    + ----------
    + faaFile : char
    +     Path to the protein FASTA file (.faa) to process.
    + geneTable : char | table
    +     Either a path to a tab-delimited .tsv file produced by getGeneData,
    +     or a MATLAB table variable. It must contain the columns
    +     'GenBank_protein' and the column named by headerCol.
    + headerCol : char
    +     Name of the geneTable column whose values will replace each FASTA
    +     header (for example 'locus_tag', 'gene_name', 'GenBank_protein').
    +
    + Name-Value Arguments
    + --------------------
    + outputDir : char
    +     Directory where the processed FASTA file is saved. It is created if
    +     missing. The output file name is the original base name with
    +     '_processed' appended before the extension (default: current working
    +     directory).
    +
    + Returns
    + -------
    + processedFaaFile : char
    +     Full path to the written processed FASTA file.
    +
    + Examples
    + --------
    +     % Rename headers using locus_tag
    +     [~, faa]  = downloadGenomeData('GCF_000002595.2');
    +     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv');
    +     outFile   = processProteinFastaFile(faa, geneTable, 'locus_tag');
    +
    +     % Load both inputs from saved files, use gene_name as header
    +     outFile = processProteinFastaFile('GCF_000002595.2_protein.faa', 'chlamy.tsv', 'gene_name');
    +
    +     % Save the processed file to a specific directory
    +     outFile = processProteinFastaFile(faa, 'chlamy.tsv', 'locus_tag', 'results/');
    +
    + Notes
    + -----
    + GenBank_protein must contain accessions equal to the first
    + whitespace-delimited token of each FASTA header (after '>'), for example
    + WP_012345678.1. getGeneData takes the accession from the protein_id
    + attribute of the CDS features of the GFF3 annotation, or from a GenBank
    + entry of the Dbxref attribute when protein_id is empty.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function processedFaaFile = processProteinFastaFile(faaFile, geneTable, headerCol, varargin)
    +0002 % processProteinFastaFile  Rename protein FASTA headers using a gene mapping table.
    +0003 %
    +0004 % Reads a protein FASTA file (such as one downloaded by downloadGenomeData)
    +0005 % and replaces each sequence header with the value from the specified
    +0006 % geneTable column. A sequence is matched to a row through the
    +0007 % GenBank_protein accession, which is the first token of the original
    +0008 % FASTA header. Sequences whose accession is not found in geneTable keep
    +0009 % their original header. The numbers of renamed and kept sequences are
    +0010 % printed.
    +0011 %
    +0012 % Parameters
    +0013 % ----------
    +0014 % faaFile : char
    +0015 %     Path to the protein FASTA file (.faa) to process.
    +0016 % geneTable : char | table
    +0017 %     Either a path to a tab-delimited .tsv file produced by getGeneData,
    +0018 %     or a MATLAB table variable. It must contain the columns
    +0019 %     'GenBank_protein' and the column named by headerCol.
    +0020 % headerCol : char
    +0021 %     Name of the geneTable column whose values will replace each FASTA
    +0022 %     header (for example 'locus_tag', 'gene_name', 'GenBank_protein').
    +0023 %
    +0024 % Name-Value Arguments
    +0025 % --------------------
    +0026 % outputDir : char
    +0027 %     Directory where the processed FASTA file is saved. It is created if
    +0028 %     missing. The output file name is the original base name with
    +0029 %     '_processed' appended before the extension (default: current working
    +0030 %     directory).
    +0031 %
    +0032 % Returns
    +0033 % -------
    +0034 % processedFaaFile : char
    +0035 %     Full path to the written processed FASTA file.
    +0036 %
    +0037 % Examples
    +0038 % --------
    +0039 %     % Rename headers using locus_tag
    +0040 %     [~, faa]  = downloadGenomeData('GCF_000002595.2');
    +0041 %     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv');
    +0042 %     outFile   = processProteinFastaFile(faa, geneTable, 'locus_tag');
    +0043 %
    +0044 %     % Load both inputs from saved files, use gene_name as header
    +0045 %     outFile = processProteinFastaFile('GCF_000002595.2_protein.faa', 'chlamy.tsv', 'gene_name');
    +0046 %
    +0047 %     % Save the processed file to a specific directory
    +0048 %     outFile = processProteinFastaFile(faa, 'chlamy.tsv', 'locus_tag', 'results/');
    +0049 %
    +0050 % Notes
    +0051 % -----
    +0052 % GenBank_protein must contain accessions equal to the first
    +0053 % whitespace-delimited token of each FASTA header (after '>'), for example
    +0054 % WP_012345678.1. getGeneData takes the accession from the protein_id
    +0055 % attribute of the CDS features of the GFF3 annotation, or from a GenBank
    +0056 % entry of the Dbxref attribute when protein_id is empty.
    +0057     
    +0058     if nargin < 1 || isempty(faaFile)
    +0059         error('processProteinFastaFile:missingInput', ...
    +0060             'faaFile is required. Provide a path to a protein FASTA (.faa) file.');
    +0061     end
    +0062     faaFile = char(faaFile);
    +0063     if ~isfile(faaFile)
    +0064         error('processProteinFastaFile:fileNotFound', ...
    +0065             'FASTA file not found: ''%s''.', faaFile);
    +0066     end
    +0067 
    +0068     if nargin < 2 || isempty(geneTable)
    +0069         error('processProteinFastaFile:missingInput', ...
    +0070             'geneTable is required. Provide a table variable or a path to a .tsv file.');
    +0071     end
    +0072 
    +0073     if nargin < 3 || isempty(headerCol)
    +0074         error('processProteinFastaFile:missingInput', ...
    +0075             'headerCol is required. Specify the geneTable column to use as the new FASTA header (e.g. ''locus_tag'').');
    +0076     end
    +0077     headerCol = char(headerCol);
    +0078 
    +0079     p = parseRAVENargs(varargin, {'outputDir', pwd});
    +0080     outputDir = char(p.outputDir);
    +0081 
    +0082     % Load gene table
    +0083     if ischar(geneTable) || isstring(geneTable)
    +0084         tsvPath = char(geneTable);
    +0085         if ~isfile(tsvPath)
    +0086             error('processProteinFastaFile:fileNotFound', ...
    +0087                 'Gene table file not found: ''%s''.', tsvPath);
    +0088         end
    +0089         geneTable = readtable(tsvPath, 'FileType', 'text', 'Delimiter', '\t', 'TextType', 'char');
    +0090     end
    +0091 
    +0092     if ~istable(geneTable)
    +0093         error('processProteinFastaFile:badInput', ...
    +0094             'geneTable must be a MATLAB table or a path to a .tsv file.');
    +0095     end
    +0096 
    +0097     for col = {'GenBank_protein', headerCol}
    +0098         if ~ismember(col{1}, geneTable.Properties.VariableNames)
    +0099             error('processProteinFastaFile:missingColumn', ...
    +0100                 'geneTable is missing required column: ''%s''.', col{1});
    +0101         end
    +0102     end
    +0103 
    +0104     % Build protein-accession → header-value lookup map
    +0105     proteinIDs = geneTable.GenBank_protein;
    +0106     if ~iscell(proteinIDs), proteinIDs = cellstr(proteinIDs); end
    +0107 
    +0108     headerValues = geneTable.(headerCol);
    +0109     if ~iscell(headerValues), headerValues = cellstr(headerValues); end
    +0110 
    +0111     % Keep only rows with a non-empty protein accession
    +0112     validMask  = ~cellfun(@isempty, proteinIDs);
    +0113     proteinMap = containers.Map(proteinIDs(validMask), num2cell(find(validMask)));
    +0114 
    +0115 
    +0116     % Prepare output path
    +0117     if ~exist(outputDir, 'dir')
    +0118         mkdir(outputDir);
    +0119     end
    +0120     [~, baseName, ext] = fileparts(faaFile);
    +0121     processedFaaFile   = fullfile(outputDir, [baseName '_processed' ext]);
    +0122 
    +0123 
    +0124     % Read the FASTA, rename headers, write the result
    +0125     fastaStruct = readFasta(faaFile);
    +0126     nMatched    = 0;
    +0127     for i = 1:numel(fastaStruct)
    +0128         % The protein accession is the first whitespace-delimited token of
    +0129         % the header (readFasta strips the leading '>')
    +0130         tokens     = strsplit(fastaStruct(i).Header);
    +0131         proteinAcc = tokens{1};
    +0132         if isKey(proteinMap, proteinAcc)
    +0133             fastaStruct(i).Header = headerValues{proteinMap(proteinAcc)};
    +0134             nMatched = nMatched + 1;
    +0135         end
    +0136         % Sequences with no match keep their original header
    +0137     end
    +0138     writeFasta(processedFaaFile, fastaStruct);
    +0139 
    +0140     fprintf('Processed FASTA written to: %s\n', processedFaaFile);
    +0141     fprintf('  Total sequences  : %d\n', numel(fastaStruct));
    +0142     fprintf('  Renamed          : %d\n', nMatched);
    +0143     fprintf('  Kept (no match)  : %d\n', numel(fastaStruct) - nMatched);
    +0144 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/curation/renameModelGenes.html b/docs/html/curation/renameModelGenes.html new file mode 100644 index 00000000..d6595a25 --- /dev/null +++ b/docs/html/curation/renameModelGenes.html @@ -0,0 +1,347 @@ + + + + Description of renameModelGenes + + + + + + + + + +
    Home > curation > renameModelGenes.m
    + + + +

    renameModelGenes +

    + +

    PURPOSE ^

    +
    renameModelGenes Replace gene identifiers in a RAVEN model.
    + +

    SYNOPSIS ^

    +
    function model = renameModelGenes(model, geneTable, fromCol, toCol)
    + +

    DESCRIPTION ^

    +
     renameModelGenes  Replace gene identifiers in a RAVEN model.
    +
    + Replaces the identifiers in model.genes and model.grRules that are listed
    + in column fromCol of a mapping table with the identifiers in column
    + toCol. The table is produced by getGeneData or supplied manually. After
    + renaming, model.rxnGeneMat is rebuilt.
    +
    + Parameters
    + ----------
    + model : struct
    +     RAVEN model struct. It must contain model.genes and model.grRules.
    + geneTable : table | char
    +     Gene-mapping table, either
    +       (a) a MATLAB table variable, or
    +       (b) the path to a tab-delimited .tsv file, which is read with
    +           readtable.
    + fromCol : char
    +     Name of the column in geneTable whose values match the identifiers
    +     in model.genes (for example 'locus_tag').
    + toCol : char
    +     Name of the column in geneTable with the new identifiers (for
    +     example 'gene_name').
    +
    + Returns
    + -------
    + model : struct
    +     Model struct with these fields changed:
    +
    +     - model.genes : gene list with the new identifiers.
    +     - model.grRules : gene-reaction rules with the new identifiers.
    +     - model.rxnGeneMat : sparse matrix rebuilt for the new gene list.
    +
    + Examples
    + --------
    +     model     = readYAMLmodel('iCre1355.yml');
    +     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv');
    +     model     = renameModelGenes(model, geneTable, 'locus_tag', 'gene_name');
    +
    +     % Supply a TSV file path directly
    +     model = renameModelGenes(model, 'chlamy.tsv', 'locus_tag', 'gene_name');
    +
    + Notes
    + -----
    + * Genes with no entry in fromCol, or whose toCol value is empty, are left
    +   unchanged, and a warning lists them.
    + * If a fromCol value occurs in several rows, the first row with a
    +   non-empty toCol value is used. Values are trimmed of surrounding
    +   whitespace.
    + * An identifier is replaced only where it is delimited by whitespace,
    +   parentheses or the start or end of the rule, so 'gene1' is not
    +   replaced inside 'gene10'.
    + * Unnecessary outer parentheses are removed from the grRules and the
    +   grRules are standardised with standardizeGrRules before and after
    +   renaming. A warning is issued when standardizeGrRules reports rules
    +   with potential issues.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function model = renameModelGenes(model, geneTable, fromCol, toCol)
    +0002 % renameModelGenes  Replace gene identifiers in a RAVEN model.
    +0003 %
    +0004 % Replaces the identifiers in model.genes and model.grRules that are listed
    +0005 % in column fromCol of a mapping table with the identifiers in column
    +0006 % toCol. The table is produced by getGeneData or supplied manually. After
    +0007 % renaming, model.rxnGeneMat is rebuilt.
    +0008 %
    +0009 % Parameters
    +0010 % ----------
    +0011 % model : struct
    +0012 %     RAVEN model struct. It must contain model.genes and model.grRules.
    +0013 % geneTable : table | char
    +0014 %     Gene-mapping table, either
    +0015 %       (a) a MATLAB table variable, or
    +0016 %       (b) the path to a tab-delimited .tsv file, which is read with
    +0017 %           readtable.
    +0018 % fromCol : char
    +0019 %     Name of the column in geneTable whose values match the identifiers
    +0020 %     in model.genes (for example 'locus_tag').
    +0021 % toCol : char
    +0022 %     Name of the column in geneTable with the new identifiers (for
    +0023 %     example 'gene_name').
    +0024 %
    +0025 % Returns
    +0026 % -------
    +0027 % model : struct
    +0028 %     Model struct with these fields changed:
    +0029 %
    +0030 %     - model.genes : gene list with the new identifiers.
    +0031 %     - model.grRules : gene-reaction rules with the new identifiers.
    +0032 %     - model.rxnGeneMat : sparse matrix rebuilt for the new gene list.
    +0033 %
    +0034 % Examples
    +0035 % --------
    +0036 %     model     = readYAMLmodel('iCre1355.yml');
    +0037 %     geneTable = getGeneData('GCF_000002595.2', 'chlamy.tsv');
    +0038 %     model     = renameModelGenes(model, geneTable, 'locus_tag', 'gene_name');
    +0039 %
    +0040 %     % Supply a TSV file path directly
    +0041 %     model = renameModelGenes(model, 'chlamy.tsv', 'locus_tag', 'gene_name');
    +0042 %
    +0043 % Notes
    +0044 % -----
    +0045 % * Genes with no entry in fromCol, or whose toCol value is empty, are left
    +0046 %   unchanged, and a warning lists them.
    +0047 % * If a fromCol value occurs in several rows, the first row with a
    +0048 %   non-empty toCol value is used. Values are trimmed of surrounding
    +0049 %   whitespace.
    +0050 % * An identifier is replaced only where it is delimited by whitespace,
    +0051 %   parentheses or the start or end of the rule, so 'gene1' is not
    +0052 %   replaced inside 'gene10'.
    +0053 % * Unnecessary outer parentheses are removed from the grRules and the
    +0054 %   grRules are standardised with standardizeGrRules before and after
    +0055 %   renaming. A warning is issued when standardizeGrRules reports rules
    +0056 %   with potential issues.
    +0057 
    +0058     if nargin < 1 || isempty(model)
    +0059         error('renameModelGenes:missingInput', ...
    +0060             'model is required. Provide a RAVEN model struct.');
    +0061     end
    +0062 
    +0063     if nargin < 2 || isempty(geneTable)
    +0064         error('renameModelGenes:missingInput', ...
    +0065             'geneTable is required. Provide a table variable or a path to a .tsv file.');
    +0066     end
    +0067 
    +0068     if nargin < 3 || isempty(fromCol)
    +0069         error('renameModelGenes:missingInput', ...
    +0070             'fromCol is required. Specify the column in geneTable whose values match model.genes.');
    +0071     end
    +0072 
    +0073     if nargin < 4 || isempty(toCol)
    +0074         error('renameModelGenes:missingInput', ...
    +0075             'toCol is required. Specify the column in geneTable with the replacement identifiers.');
    +0076     end
    +0077 
    +0078     fromCol = char(fromCol);
    +0079     toCol   = char(toCol);
    +0080 
    +0081     % Load table if a file path was supplied
    +0082     if ischar(geneTable) || isstring(geneTable)
    +0083         tableFile = char(geneTable);
    +0084         if ~isfile(tableFile)
    +0085             error('renameModelGenes:fileNotFound', ...
    +0086                   'Table file not found: %s', tableFile);
    +0087         end
    +0088         geneTable = readtable(tableFile, 'Delimiter', '\t', 'FileType', 'text', ...
    +0089                               'TextType', 'char');
    +0090         fprintf('Loaded gene table from file: %s  (%d rows)\n', ...
    +0091                 tableFile, height(geneTable));
    +0092     end
    +0093 
    +0094     % Validate column names
    +0095     if ~ismember(fromCol, geneTable.Properties.VariableNames)
    +0096         error('renameModelGenes:badColumn', ...
    +0097               'Column "%s" not found in geneTable.\nAvailable columns: %s', ...
    +0098               fromCol, strjoin(geneTable.Properties.VariableNames, ', '));
    +0099     end
    +0100     if ~ismember(toCol, geneTable.Properties.VariableNames)
    +0101         error('renameModelGenes:badColumn', ...
    +0102               'Column "%s" not found in geneTable.\nAvailable columns: %s', ...
    +0103               toCol, strjoin(geneTable.Properties.VariableNames, ', '));
    +0104     end
    +0105 
    +0106     % Build mapping: oldName → newName
    +0107     % Use only rows where both fromCol and toCol are non-empty. If a fromCol value maps to
    +0108     % multiple toCol values (isoforms), take the first non-empty one — gene names are
    +0109     % per-gene, not per-isoform.
    +0110 
    +0111     fromVals = geneTable.(fromCol);
    +0112     toVals   = geneTable.(toCol);
    +0113 
    +0114     % Convert to char cell if needed
    +0115     if ~iscell(fromVals), fromVals = cellstr(fromVals); end
    +0116     if ~iscell(toVals),   toVals   = cellstr(toVals);   end
    +0117 
    +0118     % Build map (first occurrence wins for duplicates). Only genes with a
    +0119     % non-empty toCol value are mapped; the rest are left unchanged.
    +0120     nameMap = containers.Map('KeyType', 'char', 'ValueType', 'char');
    +0121     for i = 1:numel(fromVals)
    +0122         k = strtrim(fromVals{i});
    +0123         v = strtrim(toVals{i});
    +0124         if ~isempty(k) && ~isempty(v) && ~isKey(nameMap, k)
    +0125             nameMap(k) = v;
    +0126         end
    +0127     end
    +0128 
    +0129     % Standardize grRules before renaming to ensure consistent formatting for accurate replacement.
    +0130     fprintf('Removing unnecessary outer parentheses ...\n');
    +0131     model.grRules = removeUnnecessaryParentheses(model.grRules);
    +0132     
    +0133     fprintf('Standardizing grRules ...\n');
    +0134     [model.grRules, ~, indexes2check] = standardizeGrRules(model, true);
    +0135     if ~isempty(indexes2check)
    +0136         warning('renameModelGenes:potentialErrors', ...
    +0137             '%d grRules have potential issues. Review them before proceeding.', ...
    +0138             numel(indexes2check));
    +0139     end
    +0140 
    +0141     % Rename model.genes; genes without a mapping are left unchanged
    +0142     nGenes   = numel(model.genes);
    +0143     notFound = {};
    +0144     for i = 1:nGenes
    +0145         oldName = model.genes{i};
    +0146         if isKey(nameMap, oldName)
    +0147             model.genes{i} = nameMap(oldName);
    +0148         else
    +0149             notFound{end+1} = oldName; %#ok<AGROW>
    +0150         end
    +0151     end
    +0152 
    +0153     % Report genes that had no mapping and were left unchanged
    +0154     if ~isempty(notFound)
    +0155         warning('renameModelGenes:noMapping', ...
    +0156             '%d gene(s) in the model had no mapping in column "%s" and were left unchanged:\n  %s', ...
    +0157             numel(notFound), fromCol, strjoin(unique(notFound), ', '));
    +0158     end
    +0159 
    +0160     % Rename genes inside grRules Sort keys longest-first to avoid partial substitutions
    +0161     % (e.g. replace 'gene10' before 'gene1').
    +0162     allKeys    = keys(nameMap);
    +0163     allVals    = values(nameMap);
    +0164     [~, order] = sort(cellfun(@numel, allKeys), 'descend');
    +0165     allKeys    = allKeys(order);
    +0166     allVals    = allVals(order);
    +0167     
    +0168     nRxns = numel(model.grRules);
    +0169     for i = 1:nRxns
    +0170         rule = model.grRules{i};
    +0171         if isempty(rule)
    +0172             continue
    +0173         end
    +0174         for j = 1:numel(allKeys)
    +0175             oldG = allKeys{j};
    +0176             newG = allVals{j};
    +0177             % Use word-boundary replacement: only match oldG when surrounded
    +0178             % by non-word characters (spaces, parentheses, start/end of string)
    +0179             % to avoid replacing 'gene1' inside 'gene10'.
    +0180             rule = regexprep(rule, ['(?<![^\s()])' regexptranslate('escape', oldG) ...
    +0181                                     '(?![^\s()])'], newG);
    +0182         end
    +0183         model.grRules{i} = rule;
    +0184     end
    +0185 
    +0186     % Rebuild rxnGeneMat to match the updated gene list
    +0187     fprintf('Rebuilding rxnGeneMat ...\n');
    +0188     [model.grRules, model.rxnGeneMat, ~] = standardizeGrRules(model, true);
    +0189     
    +0190     fprintf('Done. %d genes renamed, %d left unchanged.\n', ...
    +0191             nGenes - numel(notFound), numel(notFound));
    +0192 end
    +0193 
    +0194 %--------------------------------------------------------------------------
    +0195 %  Helper functions
    +0196 
    +0197 function grRules = removeUnnecessaryParentheses(grRules)
    +0198 % Remove unnecessary outer parentheses from grRules.
    +0199 % e.g., '(A and (B or C))' → 'A and (B or C)'
    +0200 %       '(A or B)' → 'A or B'
    +0201 % but preserves '(A and B) or C' (outer parens are needed here)
    +0202 %
    +0203 % Checks if the opening parenthesis at position 1 closes before the end
    +0204 % of the string. If yes, outer parens are necessary. If no, they can be
    +0205 % removed recursively.
    +0206 
    +0207     nRxns = numel(grRules);
    +0208     for i = 1:nRxns
    +0209         rule = grRules{i};
    +0210         if isempty(rule)
    +0211             continue
    +0212         end
    +0213     
    +0214         % Recursively remove outer parentheses while they are unnecessary
    +0215         while length(rule) >= 2 && rule(1) == '(' && rule(end) == ')'
    +0216             % Count brackets to see when the opening paren closes
    +0217             parenCount = 0;
    +0218             closedEarly = false;
    +0219     
    +0220             for j = 1:length(rule)
    +0221                 if rule(j) == '('
    +0222                     parenCount = parenCount + 1;
    +0223                 elseif rule(j) == ')'
    +0224                     parenCount = parenCount - 1;
    +0225                     % If it reaches 0 before the end, outer parens are necessary
    +0226                     if parenCount == 0 && j < length(rule)
    +0227                         closedEarly = true;
    +0228                         break
    +0229                     end
    +0230                 end
    +0231             end
    +0232     
    +0233             if closedEarly
    +0234                 % Opening paren closes early → outer parens are needed
    +0235                 break
    +0236             else
    +0237                 % Opening paren only closes at the end → they're unnecessary
    +0238                 rule = rule(2:end-1);
    +0239             end
    +0240         end
    +0241     
    +0242         grRules{i} = rule;
    +0243     end
    +0244 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/demoicon.gif b/docs/html/demoicon.gif similarity index 100% rename from doc/demoicon.gif rename to docs/html/demoicon.gif diff --git a/doc/down.png b/docs/html/down.png similarity index 100% rename from doc/down.png rename to docs/html/down.png diff --git a/doc/doxysearch.php b/docs/html/doxysearch.php similarity index 100% rename from doc/doxysearch.php rename to docs/html/doxysearch.php diff --git a/doc/fortran.png b/docs/html/fortran.png similarity index 100% rename from doc/fortran.png rename to docs/html/fortran.png diff --git a/doc/gapfilling/canExchange.html b/docs/html/gapfilling/canExchange.html similarity index 58% rename from doc/gapfilling/canExchange.html rename to docs/html/gapfilling/canExchange.html index c739cc7c..95d70536 100644 --- a/doc/gapfilling/canExchange.html +++ b/docs/html/gapfilling/canExchange.html @@ -30,12 +30,14 @@

    DESCRIPTION ^
     canExchange  Check which metabolites a model can produce or consume.
     
      Adds an exchange reaction for each of the specified metabolites and
    - checks which of them can carry a flux. This is the unified replacement
    - for canProduce ('produce') and canConsume ('consume').
    + checks which of them can carry an absolute flux above 1e-5. The direction
    + 'produce' corresponds to the deprecated function canProduce and 'consume'
    + to canConsume.
     
    - This is a less advanced but faster version of checkProduction, which
    - additionally reports which metabolites must be connected in order to make
    - the remaining ones producible.
    + canExchange only tests whether each added exchange reaction can carry flux.
    + checkProduction also reports which metabolites must be connected in order
    + to make the remaining ones producible, which requires further problems to be
    + solved.
     
      Parameters
      ----------
    @@ -74,7 +76,7 @@ 

    CROSS-REFERENCE INFORMATION ^
 <li><a href=canExchange canExchange Check which metabolites a model can produce or consume. This function is called by:
      -
    • canExchange canExchange Check which metabolites a model can produce or consume.
    • gapReport gapReport Perform a gap analysis and summarize the results.
    +
  • canExchange canExchange Check which metabolites a model can produce or consume.
  • gapReport gapReport Perform a gap analysis and summarise the results.
  • @@ -84,66 +86,68 @@

    SOURCE CODE ^% canExchange Check which metabolites a model can produce or consume. 0003 % 0004 % Adds an exchange reaction for each of the specified metabolites and -0005 % checks which of them can carry a flux. This is the unified replacement -0006 % for canProduce ('produce') and canConsume ('consume'). -0007 % -0008 % This is a less advanced but faster version of checkProduction, which -0009 % additionally reports which metabolites must be connected in order to make -0010 % the remaining ones producible. -0011 % -0012 % Parameters -0013 % ---------- -0014 % model : struct -0015 % a model structure. -0016 % direction : char -0017 % 'produce' to check for net production of each metabolite, or -0018 % 'consume' to check for net consumption. -0019 % -0020 % Name-Value Arguments -0021 % -------------------- -0022 % mets : cell or logical or double -0023 % either a cell array of metabolite IDs, a logical vector with the same -0024 % number of elements as metabolites in the model, or a vector of -0025 % indexes to check for (default model.mets). -0026 % -0027 % Returns -0028 % ------- -0029 % canExchange : logical -0030 % vector with true if the corresponding metabolite could be produced -0031 % (direction 'produce') or consumed (direction 'consume'). -0032 % -0033 % Examples -0034 % -------- -0035 % produced = canExchange(model, 'produce'); -0036 % consumed = canExchange(model, 'consume', mets); -0037 % -0038 % See also -0039 % -------- -0040 % checkProduction, findLeakMetabolite -0041 -0042 direction=char(direction); -0043 if ~any(strcmp(direction,{'produce','consume'})) -0044 error('RAVEN:badInput','direction must be ''produce'' or ''consume'''); -0045 end -0046 -0047 p=parseRAVENargs(varargin, {'mets',[]}); -0048 mets=p.mets; -0049 -0050 if isempty(mets) -0051 mets=model.mets; -0052 elseif ~islogical(mets) && ~isnumeric(mets) -0053 mets=convertCharArray(mets); -0054 end -0055 -0056 if strcmp(direction,'produce') -0057 exchangeDir='out'; -0058 else -0059 exchangeDir='in'; -0060 end -0061 -0062 [model, rxns]=addExchangeRxns(model,exchangeDir,mets); -0063 canExchange=haveFlux(model,10^-5,rxns); -0064 end

    +0005 % checks which of them can carry an absolute flux above 1e-5. The direction +0006 % 'produce' corresponds to the deprecated function canProduce and 'consume' +0007 % to canConsume. +0008 % +0009 % canExchange only tests whether each added exchange reaction can carry flux. +0010 % checkProduction also reports which metabolites must be connected in order +0011 % to make the remaining ones producible, which requires further problems to be +0012 % solved. +0013 % +0014 % Parameters +0015 % ---------- +0016 % model : struct +0017 % a model structure. +0018 % direction : char +0019 % 'produce' to check for net production of each metabolite, or +0020 % 'consume' to check for net consumption. +0021 % +0022 % Name-Value Arguments +0023 % -------------------- +0024 % mets : cell or logical or double +0025 % either a cell array of metabolite IDs, a logical vector with the same +0026 % number of elements as metabolites in the model, or a vector of +0027 % indexes to check for (default model.mets). +0028 % +0029 % Returns +0030 % ------- +0031 % canExchange : logical +0032 % vector with true if the corresponding metabolite could be produced +0033 % (direction 'produce') or consumed (direction 'consume'). +0034 % +0035 % Examples +0036 % -------- +0037 % produced = canExchange(model, 'produce'); +0038 % consumed = canExchange(model, 'consume', mets); +0039 % +0040 % See also +0041 % -------- +0042 % checkProduction, findLeakMetabolite +0043 +0044 direction=char(direction); +0045 if ~any(strcmp(direction,{'produce','consume'})) +0046 error('RAVEN:badInput','direction must be ''produce'' or ''consume'''); +0047 end +0048 +0049 p=parseRAVENargs(varargin, {'mets',[]}); +0050 mets=p.mets; +0051 +0052 if isempty(mets) +0053 mets=model.mets; +0054 elseif ~islogical(mets) && ~isnumeric(mets) +0055 mets=convertCharArray(mets); +0056 end +0057 +0058 if strcmp(direction,'produce') +0059 exchangeDir='out'; +0060 else +0061 exchangeDir='in'; +0062 end +0063 +0064 [model, rxns]=addExchangeRxns(model,exchangeDir,mets); +0065 canExchange=haveFlux(model,10^-5,rxns); +0066 end
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/gapfilling/checkProduction.html b/docs/html/gapfilling/checkProduction.html new file mode 100644 index 00000000..8f1b46ec --- /dev/null +++ b/docs/html/gapfilling/checkProduction.html @@ -0,0 +1,283 @@ + + + + Description of checkProduction + + + + + + + + + +
    Home > gapfilling > checkProduction.m
    + + + +

    checkProduction +

    + +

    PURPOSE ^

    +
    checkProduction Check which metabolites can be produced from a model.
    + +

    SYNOPSIS ^

    +
    function [notProduced, notProducedNames, neededForProductionMat,minToConnect,model]=checkProduction(model,varargin)
    + +

    DESCRIPTION ^

    +
     checkProduction  Check which metabolites can be produced from a model.
    +
    + Checks which metabolites can be produced from a model under the current
    + constraints.
    +
    + The function identifies which metabolites must be connected in order to
    + have a fully connected network. It first identifies which metabolites
    + can have a net production in the network. It then calculates which other
    + metabolites must be able to have net production in order to have
    + production of all metabolites in the network. If a network contains the
    + equations A[external]->B, C->D, and D->E, production of C connects the
    + metabolites D and E.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + checkNeededForProduction : logical
    +     true to calculate, for each metabolite that could not be produced,
    +     which of the other such metabolites can be produced when an uptake
    +     reaction for it is added (default false).
    + excretionFromCompartments : cell
    +     cell array with compartment ids from which metabolites can be excreted
    +     (default model.comps).
    + printDetails : logical
    +     true to print the metabolites that could not be produced to the command
    +     window (default true).
    +
    + Returns
    + -------
    + notProduced : double
    +     indices of metabolites that could not be produced, counted among the
    +     metabolites in the compartments of excretionFromCompartments, in model
    +     order.
    + notProducedNames : cell
    +     cell array with names and compartments for metabolites that could not
    +     be produced.
    + neededForProductionMat : logical
    +     matrix where n x m is true if metabolite n allows for production of
    +     metabolite m. The rows and columns follow the order of notProduced.
    +     It is empty if checkNeededForProduction is false.
    + minToConnect : cell
    +     metabolites that need to be connected in order to be able to produce
    +     all other metabolites, each with the number of metabolites it
    +     connects. The metabolites are selected iteratively, each time the one
    +     that connects the most remaining metabolites, so the number of
    +     metabolites need not be the global minimum. It is empty if
    +     checkNeededForProduction is false.
    + model : struct
    +     model structure with an excretion reaction added for each metabolite
    +     in the compartments of excretionFromCompartments. All added reactions
    +     are excretion reactions, also when checkNeededForProduction is true.
    +
    + Examples
    + --------
    +     [notProduced, notProducedNames, neededForProductionMat, minToConnect, model] = ...
    +         checkProduction(model, checkNeededForProduction, excretionFromCompartments, printDetails);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    • gapReport gapReport Perform a gap analysis and summarise the results.
    + + + + +

    SOURCE CODE ^

    +
    0001 function [notProduced, notProducedNames, neededForProductionMat,minToConnect,model]=checkProduction(model,varargin)
    +0002 % checkProduction  Check which metabolites can be produced from a model.
    +0003 %
    +0004 % Checks which metabolites can be produced from a model under the current
    +0005 % constraints.
    +0006 %
    +0007 % The function identifies which metabolites must be connected in order to
    +0008 % have a fully connected network. It first identifies which metabolites
    +0009 % can have a net production in the network. It then calculates which other
    +0010 % metabolites must be able to have net production in order to have
    +0011 % production of all metabolites in the network. If a network contains the
    +0012 % equations A[external]->B, C->D, and D->E, production of C connects the
    +0013 % metabolites D and E.
    +0014 %
    +0015 % Parameters
    +0016 % ----------
    +0017 % model : struct
    +0018 %     a model structure.
    +0019 %
    +0020 % Name-Value Arguments
    +0021 % --------------------
    +0022 % checkNeededForProduction : logical
    +0023 %     true to calculate, for each metabolite that could not be produced,
    +0024 %     which of the other such metabolites can be produced when an uptake
    +0025 %     reaction for it is added (default false).
    +0026 % excretionFromCompartments : cell
    +0027 %     cell array with compartment ids from which metabolites can be excreted
    +0028 %     (default model.comps).
    +0029 % printDetails : logical
    +0030 %     true to print the metabolites that could not be produced to the command
    +0031 %     window (default true).
    +0032 %
    +0033 % Returns
    +0034 % -------
    +0035 % notProduced : double
    +0036 %     indices of metabolites that could not be produced, counted among the
    +0037 %     metabolites in the compartments of excretionFromCompartments, in model
    +0038 %     order.
    +0039 % notProducedNames : cell
    +0040 %     cell array with names and compartments for metabolites that could not
    +0041 %     be produced.
    +0042 % neededForProductionMat : logical
    +0043 %     matrix where n x m is true if metabolite n allows for production of
    +0044 %     metabolite m. The rows and columns follow the order of notProduced.
    +0045 %     It is empty if checkNeededForProduction is false.
    +0046 % minToConnect : cell
    +0047 %     metabolites that need to be connected in order to be able to produce
    +0048 %     all other metabolites, each with the number of metabolites it
    +0049 %     connects. The metabolites are selected iteratively, each time the one
    +0050 %     that connects the most remaining metabolites, so the number of
    +0051 %     metabolites need not be the global minimum. It is empty if
    +0052 %     checkNeededForProduction is false.
    +0053 % model : struct
    +0054 %     model structure with an excretion reaction added for each metabolite
    +0055 %     in the compartments of excretionFromCompartments. All added reactions
    +0056 %     are excretion reactions, also when checkNeededForProduction is true.
    +0057 %
    +0058 % Examples
    +0059 % --------
    +0060 %     [notProduced, notProducedNames, neededForProductionMat, minToConnect, model] = ...
    +0061 %         checkProduction(model, checkNeededForProduction, excretionFromCompartments, printDetails);
    +0062 
    +0063 p=parseRAVENargs(varargin, {'checkNeededForProduction',false; ...
    +0064     'excretionFromCompartments',[]; ...
    +0065     'printDetails',true});
    +0066 checkNeededForProduction=p.checkNeededForProduction;
    +0067 excretionFromCompartments=p.excretionFromCompartments;
    +0068 printDetails=p.printDetails;
    +0069 
    +0070 if isempty(excretionFromCompartments)
    +0071     excretionFromCompartments=model.comps;
    +0072 else
    +0073     excretionFromCompartments=convertCharArray(excretionFromCompartments);
    +0074 end
    +0075 
    +0076 %Add an exchange reaction for each metabolite in the allowed compartments
    +0077 %and see if it can carry a flux
    +0078 allowedMetIds=ismember(model.comps(model.metComps),excretionFromCompartments);
    +0079 allowedMetIndexes=find(allowedMetIds);
    +0080 [model, addedRxns]=addExchangeRxns(model,'out',allowedMetIndexes);
    +0081 
    +0082 canProduce=haveFlux(model,10^-5,addedRxns);
    +0083 
    +0084 notProduced=find(~canProduce);
    +0085 minToConnect={};
    +0086 if checkNeededForProduction==true
    +0087     %For each of the metabolites that couldn't be produced allow uptake and
    +0088     %check which of the other metabolites that couldn't be produced that
    +0089     %can be produced
    +0090     neededForProductionMat=false(numel(notProduced));
    +0091     for i=1:numel(notProduced)
    +0092         %Add uptake for this metabolite
    +0093         if i>1
    +0094             %Reset last iteration
    +0095             model.S(:,numel(model.rxns)-numel(addedRxns)+notProduced(i-1))=model.S(:,numel(model.rxns)-numel(addedRxns)+notProduced(i-1))*-1;
    +0096         end
    +0097         %Change the production reaction to an uptake reaction
    +0098         model.S(:,numel(model.rxns)-numel(addedRxns)+notProduced(i))=model.S(:,numel(model.rxns)-numel(addedRxns)+notProduced(i))*-1;
    +0099         
    +0100         %Test which of the metabolites that couldn't be produced that can
    +0101         %be produced now
    +0102         neededForProductionMat(i,:)=haveFlux(model,10^-5,addedRxns(notProduced));
    +0103     end
    +0104     if ~isempty(notProduced)
    +0105         %Reset the last iteration, so that the returned model has excretion
    +0106         %reactions only
    +0107         lastCol=numel(model.rxns)-numel(addedRxns)+notProduced(end);
    +0108         model.S(:,lastCol)=model.S(:,lastCol)*-1;
    +0109     end
    +0110     %Calculate the smallest number of metabolites that must be connected to
    +0111     %make everything connected and return their names
    +0112     
    +0113     %The algorithm is relatively straight forward. It finds the metabolite
    +0114     %that connects the most unconnected metabolites (iteratively), adds it
    +0115     %and removes the now connected metabolites until all are connected.
    +0116     %This is not guaranteed to find the global minimum
    +0117     neededForProdTemp=neededForProductionMat;
    +0118     while 1==1
    +0119         %Get all metabolites a metabolite makes connected
    +0120         totalConnected=false(size(neededForProdTemp));
    +0121         for i=1:numel(notProduced)
    +0122             totalConnected(i,:)=neededForProdTemp(i,:);
    +0123             
    +0124             lastIter=0;
    +0125             while 1==1
    +0126                 [~, a]=find(neededForProdTemp(totalConnected(i,:),:));
    +0127                 totalConnected(i,a)=true;
    +0128                 if numel(a)==lastIter
    +0129                     break; %No more connections were possible
    +0130                 else
    +0131                     lastIter=numel(a);
    +0132                 end
    +0133             end
    +0134         end
    +0135         [connections, mostConnected]=max(sum(totalConnected,2));
    +0136         
    +0137         if connections>0
    +0138             %Add the most connected metabolite to the list and remove all
    +0139             %metabolites that it's connected to
    +0140             metID=allowedMetIndexes(notProduced(mostConnected));
    +0141             entry=[model.metNames{metID},'[',model.comps{model.metComps(metID)},'] (connects ' num2str(connections) ' metabolites)'];
    +0142             minToConnect=[minToConnect;entry];
    +0143             neededForProdTemp(totalConnected(mostConnected,:),:)=false;
    +0144             neededForProdTemp(:,totalConnected(mostConnected,:))=false;
    +0145         else
    +0146             break;
    +0147         end
    +0148     end
    +0149 else
    +0150     neededForProductionMat=[];
    +0151 end
    +0152 
    +0153 notProducedNames=strcat(model.metNames(allowedMetIndexes(notProduced)),'[',model.comps(model.metComps(allowedMetIndexes(notProduced))),']');
    +0154 
    +0155 if printDetails==true
    +0156     fprintf('The following metabolites could not be produced:\n');
    +0157     [notProducedNamesTemp,perm]=sort(notProducedNames);
    +0158     
    +0159     if checkNeededForProduction==true
    +0160         neededForProdTemp=neededForProductionMat(:,perm);
    +0161         neededForProdTemp=neededForProdTemp(perm,:);
    +0162         fprintf('\tIf the production of a metabolite is dependent on some other metabolites then those are printed under the name\n\n');
    +0163     end
    +0164     for i=1:numel(notProducedNamesTemp)
    +0165         fprintf([notProducedNamesTemp{i} '\n']);
    +0166         neededForProdTemp(i,i)=false; %Not neat to do this here. Prevent printing itself
    +0167         if checkNeededForProduction==true
    +0168             enablesProduction=find(neededForProdTemp(:,i));
    +0169             if any(enablesProduction)
    +0170                 for j=1:numel(enablesProduction)
    +0171                     fprintf(['\t' notProducedNamesTemp{enablesProduction(j)} '\n']);
    +0172                 end
    +0173             end
    +0174         end
    +0175     end
    +0176 end
    +0177 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/gapfilling/fillGaps.html b/docs/html/gapfilling/fillGaps.html new file mode 100644 index 00000000..1cc3b3b4 --- /dev/null +++ b/docs/html/gapfilling/fillGaps.html @@ -0,0 +1,481 @@ + + + + Description of fillGaps + + + + + + + + + +
    Home > gapfilling > fillGaps.m
    + + + +

    fillGaps +

    + +

    PURPOSE ^

    +
    fillGaps Use template model(s) to fill gaps in a model.
    + +

    SYNOPSIS ^

    +
    function [newConnected, cannotConnect, addedRxns, newModel, exitFlag]=fillGaps(model,models,varargin)
    + +

    DESCRIPTION ^

    +
     fillGaps  Use template model(s) to fill gaps in a model.
    +
    + The model is merged with the reference model(s). If useModelConstraints
    + is false, the reactions of the model that cannot carry flux in the model
    + are tested for flux in the merged model. The non-reversible reactions that
    + carry flux in the merged model are the newConnected reactions. The other
    + reactions are returned as cannotConnect: the reactions that cannot carry
    + flux in the merged model and the reversible reactions that can, which are
    + left out of the MILP target set. A MILP then minimises the number of
    + active reactions from the reference models that are required for flux in
    + the reactions that can be connected (newConnected). This requires that
    + the input model has exchange reactions for the nutrients that its
    + metabolism needs. If useModelConstraints is true, the flux test and the
    + split into newConnected and cannotConnect are not run, and the MILP
    + includes as few reactions as possible from the reference models to
    + satisfy the model constraints.
    +
    + A general gap-filling uses useModelConstraints=false. A targeted
    + gap-filling sets constraints in the model structure and uses
    + useModelConstraints=true. For example, a model with a biomass equation
    + whose lower bound is set above 0 gets the smallest set of reactions from
    + the reference models that allows it to produce biomass.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure that may contain gaps to be filled.
    + models : cell or struct
    +     a cell array of reference models or a model structure. Reactions
    +     from these models are used to fill the gaps.
    +
    + Name-Value Arguments
    + --------------------
    + allowNetProduction : logical
    +     true to allow net production of all metabolites. A reaction can be
    +     unable to carry flux because one of the reactants is unavailable or
    +     because one of the products cannot be further processed. If true,
    +     only the first cause is considered (default false).
    + useModelConstraints : logical
    +     true to use the constraints specified in the model structure. If
    +     false, reactions are included from the template model(s) so that as
    +     many reactions as possible in model can carry flux (default false).
    + supressWarnings : logical
    +     true to suppress the warning and the note about missing exchange
    +     reactions (default false).
    + rxnScores : double or cell
    +     scores for each of the reactions in the reference model(s). If more
    +     than one model is supplied, rxnScores is a cell array of vectors. The
    +     solver maximises the sum of the scores of the included reactions.
    +     Used by the 'reference' algorithm (default -1 for all reactions).
    + params : struct
    +     solver parameters passed to getMinNrFluxes (algorithm 'reference') or
    +     to gapFillMILP (algorithm 'gapfillMILP') (default empty struct).
    + algorithm : char
    +     gap-filling algorithm to use (default 'reference'):
    +
    +     - 'reference'   : MILP-based connectivity maximisation
    +     - 'fastLP'      : L1-norm LP relaxation via FASTCORE (gapFillFastLP)
    +     - 'swiftLP'     : SWIFTCORE single-LP variant (gapFillFastLP)
    +     - 'gapfillMILP' : growth-floor MILP adding database reactions
    +                       (gapFillMILP). Uses only models{1} as the database.
    +     - 'topological' : BFS metabolite producibility pre-screening
    +                       (gapFillTopological); no model modification.
    + epsilon : double
    +     minimum flux threshold used by fastLP and swiftLP (default 1e-4).
    + minGrowth : double
    +     minimum growth rate for gapfillMILP (default [], which is 10% of the
    +     maximum growth rate found by FBA on the merged model).
    + verbose : logical
    +     true to print progress messages for the algorithms other than
    +     'reference' (default true).
    +
    + Returns
    + -------
    + newConnected : cell
    +     cell array with the non-reversible reactions of model that cannot
    +     carry flux in model but can in the merged model. Empty if
    +     useModelConstraints is true or if the algorithm is not 'reference'.
    + cannotConnect : cell
    +     cell array with the other reactions of model that cannot carry flux
    +     in model. For the algorithms 'fastLP' and 'swiftLP' it holds the
    +     reactions that remain blocked after merging (from gapFillFastLP), and
    +     for 'topological' the ids of the metabolites that cannot be reached.
    +     Empty if useModelConstraints is true or for 'gapfillMILP'.
    + addedRxns : cell
    +     cell array with the reactions added from "models".
    + newModel : struct
    +     the model with reactions added to fill gaps.
    + exitFlag : double
    +     exit status:
    +
    +     - 1 : optimal solution found
    +     - -1 : no solution found, either because the problem is infeasible or
    +       because the solver reached its time limit before finding one
    +     - -2 : a solution was found but is not proven optimal, because the
    +       solver stopped on its time limit
    +
    + Examples
    + --------
    +     [newConnected, cannotConnect, addedRxns, newModel, exitFlag]=...
    +         fillGaps(model,models,allowNetProduction,useModelConstraints,...
    +         supressWarnings,rxnScores,params);
    +
    + Notes
    + -----
    + The 'reference' and 'gapfillMILP' algorithms solve a MILP with the
    + solver set by setRavenSolver. RAVEN's bundled GLPK solves it for small
    + models. The RAVEN:glpkMILP warning issued with GLPK states that GLPK works
    + for small problems and that Gurobi is much faster and more reliable for
    + larger ones (setRavenSolver('gurobi')).
    +
    + See also
    + --------
    + ftINITFillGaps : the variant of fillGaps for ftINIT, used when the
    +     reference model already contains the reactions of the model. No merge
    +     per task is needed in that case.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • gapFillFastLP gapFillFastLP LP-based gap-filling (fastGapFill or swiftGapFill).
    • gapFillMILP gapFillMILP Objective-based gap-filling via a global growth-floor MILP.
    • gapFillTopological gapFillTopological BFS metabolite-producibility pre-screening.
    +This function is called by: +
      +
    • fitTasks fitTasks Fill gaps in a model so it can perform a list of tasks.
    • gapReport gapReport Perform a gap analysis and summarise the results.
    + + + + +

    SOURCE CODE ^

    +
    0001 function [newConnected, cannotConnect, addedRxns, newModel, exitFlag]=fillGaps(model,models,varargin)
    +0002 % fillGaps  Use template model(s) to fill gaps in a model.
    +0003 %
    +0004 % The model is merged with the reference model(s). If useModelConstraints
    +0005 % is false, the reactions of the model that cannot carry flux in the model
    +0006 % are tested for flux in the merged model. The non-reversible reactions that
    +0007 % carry flux in the merged model are the newConnected reactions. The other
    +0008 % reactions are returned as cannotConnect: the reactions that cannot carry
    +0009 % flux in the merged model and the reversible reactions that can, which are
    +0010 % left out of the MILP target set. A MILP then minimises the number of
    +0011 % active reactions from the reference models that are required for flux in
    +0012 % the reactions that can be connected (newConnected). This requires that
    +0013 % the input model has exchange reactions for the nutrients that its
    +0014 % metabolism needs. If useModelConstraints is true, the flux test and the
    +0015 % split into newConnected and cannotConnect are not run, and the MILP
    +0016 % includes as few reactions as possible from the reference models to
    +0017 % satisfy the model constraints.
    +0018 %
    +0019 % A general gap-filling uses useModelConstraints=false. A targeted
    +0020 % gap-filling sets constraints in the model structure and uses
    +0021 % useModelConstraints=true. For example, a model with a biomass equation
    +0022 % whose lower bound is set above 0 gets the smallest set of reactions from
    +0023 % the reference models that allows it to produce biomass.
    +0024 %
    +0025 % Parameters
    +0026 % ----------
    +0027 % model : struct
    +0028 %     a model structure that may contain gaps to be filled.
    +0029 % models : cell or struct
    +0030 %     a cell array of reference models or a model structure. Reactions
    +0031 %     from these models are used to fill the gaps.
    +0032 %
    +0033 % Name-Value Arguments
    +0034 % --------------------
    +0035 % allowNetProduction : logical
    +0036 %     true to allow net production of all metabolites. A reaction can be
    +0037 %     unable to carry flux because one of the reactants is unavailable or
    +0038 %     because one of the products cannot be further processed. If true,
    +0039 %     only the first cause is considered (default false).
    +0040 % useModelConstraints : logical
    +0041 %     true to use the constraints specified in the model structure. If
    +0042 %     false, reactions are included from the template model(s) so that as
    +0043 %     many reactions as possible in model can carry flux (default false).
    +0044 % supressWarnings : logical
    +0045 %     true to suppress the warning and the note about missing exchange
    +0046 %     reactions (default false).
    +0047 % rxnScores : double or cell
    +0048 %     scores for each of the reactions in the reference model(s). If more
    +0049 %     than one model is supplied, rxnScores is a cell array of vectors. The
    +0050 %     solver maximises the sum of the scores of the included reactions.
    +0051 %     Used by the 'reference' algorithm (default -1 for all reactions).
    +0052 % params : struct
    +0053 %     solver parameters passed to getMinNrFluxes (algorithm 'reference') or
    +0054 %     to gapFillMILP (algorithm 'gapfillMILP') (default empty struct).
    +0055 % algorithm : char
    +0056 %     gap-filling algorithm to use (default 'reference'):
    +0057 %
    +0058 %     - 'reference'   : MILP-based connectivity maximisation
    +0059 %     - 'fastLP'      : L1-norm LP relaxation via FASTCORE (gapFillFastLP)
    +0060 %     - 'swiftLP'     : SWIFTCORE single-LP variant (gapFillFastLP)
    +0061 %     - 'gapfillMILP' : growth-floor MILP adding database reactions
    +0062 %                       (gapFillMILP). Uses only models{1} as the database.
    +0063 %     - 'topological' : BFS metabolite producibility pre-screening
    +0064 %                       (gapFillTopological); no model modification.
    +0065 % epsilon : double
    +0066 %     minimum flux threshold used by fastLP and swiftLP (default 1e-4).
    +0067 % minGrowth : double
    +0068 %     minimum growth rate for gapfillMILP (default [], which is 10% of the
    +0069 %     maximum growth rate found by FBA on the merged model).
    +0070 % verbose : logical
    +0071 %     true to print progress messages for the algorithms other than
    +0072 %     'reference' (default true).
    +0073 %
    +0074 % Returns
    +0075 % -------
    +0076 % newConnected : cell
    +0077 %     cell array with the non-reversible reactions of model that cannot
    +0078 %     carry flux in model but can in the merged model. Empty if
    +0079 %     useModelConstraints is true or if the algorithm is not 'reference'.
    +0080 % cannotConnect : cell
    +0081 %     cell array with the other reactions of model that cannot carry flux
    +0082 %     in model. For the algorithms 'fastLP' and 'swiftLP' it holds the
    +0083 %     reactions that remain blocked after merging (from gapFillFastLP), and
    +0084 %     for 'topological' the ids of the metabolites that cannot be reached.
    +0085 %     Empty if useModelConstraints is true or for 'gapfillMILP'.
    +0086 % addedRxns : cell
    +0087 %     cell array with the reactions added from "models".
    +0088 % newModel : struct
    +0089 %     the model with reactions added to fill gaps.
    +0090 % exitFlag : double
    +0091 %     exit status:
    +0092 %
    +0093 %     - 1 : optimal solution found
    +0094 %     - -1 : no solution found, either because the problem is infeasible or
    +0095 %       because the solver reached its time limit before finding one
    +0096 %     - -2 : a solution was found but is not proven optimal, because the
    +0097 %       solver stopped on its time limit
    +0098 %
    +0099 % Examples
    +0100 % --------
    +0101 %     [newConnected, cannotConnect, addedRxns, newModel, exitFlag]=...
    +0102 %         fillGaps(model,models,allowNetProduction,useModelConstraints,...
    +0103 %         supressWarnings,rxnScores,params);
    +0104 %
    +0105 % Notes
    +0106 % -----
    +0107 % The 'reference' and 'gapfillMILP' algorithms solve a MILP with the
    +0108 % solver set by setRavenSolver. RAVEN's bundled GLPK solves it for small
    +0109 % models. The RAVEN:glpkMILP warning issued with GLPK states that GLPK works
    +0110 % for small problems and that Gurobi is much faster and more reliable for
    +0111 % larger ones (setRavenSolver('gurobi')).
    +0112 %
    +0113 % See also
    +0114 % --------
    +0115 % ftINITFillGaps : the variant of fillGaps for ftINIT, used when the
    +0116 %     reference model already contains the reactions of the model. No merge
    +0117 %     per task is needed in that case.
    +0118 
    +0119 %If the user only supplied a single template model
    +0120 if ~iscell(models)
    +0121     models={models};
    +0122 end
    +0123 
    +0124 p=parseRAVENargs(varargin, {'allowNetProduction',false; ...
    +0125     'useModelConstraints',false; ...
    +0126     'supressWarnings',false; ...
    +0127     'rxnScores',[]; ...
    +0128     'params',[]; ...
    +0129     'algorithm','reference'; ...
    +0130     'epsilon',1e-4; ...
    +0131     'minGrowth',[]; ...
    +0132     'verbose',true});
    +0133 allowNetProduction=p.allowNetProduction;
    +0134 useModelConstraints=p.useModelConstraints;
    +0135 supressWarnings=p.supressWarnings;
    +0136 rxnScores=p.rxnScores;
    +0137 params=p.params;
    +0138 
    +0139 % ---- Dispatch to alternative gap-filling algorithms ----
    +0140 switch lower(p.algorithm)
    +0141     case {'fastlp', 'swiftlp'}
    +0142         if numel(models) < 1
    +0143             error('RAVEN:badInput', 'fillGaps: fastLP/swiftLP requires at least one reference model.');
    +0144         end
    +0145         variant = strrep(lower(p.algorithm), 'lp', '');
    +0146         [addedRxns, newModel, cannotConnect] = gapFillFastLP(model, models{1}, ...
    +0147             'epsilon', p.epsilon, 'verbose', p.verbose, 'variant', variant);
    +0148         newConnected = {};
    +0149         exitFlag = 1;
    +0150         return;
    +0151     case 'gapfillmilp'
    +0152         if numel(models) < 1
    +0153             error('RAVEN:badInput', 'fillGaps: gapfillMILP requires at least one reference model.');
    +0154         end
    +0155         [addedRxns, newModel, exitFlag] = gapFillMILP(model, models{1}, ...
    +0156             'minGrowth', p.minGrowth, 'verbose', p.verbose, 'params', params);
    +0157         newConnected = {};
    +0158         cannotConnect = {};
    +0159         return;
    +0160     case 'topological'
    +0161         if numel(models) < 1
    +0162             error('RAVEN:badInput', 'fillGaps: topological requires at least one reference model.');
    +0163         end
    +0164         result = gapFillTopological(model, models{1}, 'verbose', p.verbose);
    +0165         addedRxns    = {};
    +0166         newModel     = model;
    +0167         cannotConnect = result.blockedMets;
    +0168         newConnected = {};
    +0169         exitFlag = 1;
    +0170         return;
    +0171 end
    +0172 % Falls through to the 'reference' algorithm for all other values.
    +0173 
    +0174 if isempty(rxnScores)
    +0175     rxnScores=cell(numel(models),1);
    +0176     for i=1:numel(models)
    +0177         rxnScores{i}=ones(numel(models{i}.rxns),1)*-1;
    +0178     end
    +0179 end
    +0180 if isempty(params)
    +0181     params=struct();
    +0182 end
    +0183 
    +0184 if ~iscell(rxnScores)
    +0185     rxnScores={rxnScores};
    +0186 end
    +0187 
    +0188 models=models(:);
    +0189 rxnScores=rxnScores(:);
    +0190 
    +0191 %Check if the original model has an unconstrained field. If so, give a
    +0192 %warning
    +0193 if supressWarnings==false
    +0194     if isfield(model,'unconstrained')
    +0195         EM='This algorithm is meant to function on a model with exchange reactions for uptake and excretion of metabolites. The current model still has the "unconstrained" field';
    +0196         warning('RAVEN:warning', '%s', EM);
    +0197     else
    +0198         if isempty(getExchangeRxns(model,'both'))
    +0199             fprintf('NOTE: This algorithm is meant to function on a model with exchange reactions for uptake and excretion of metabolites. The current model does not seem to contain any such reactions.\n');
    +0200         end
    +0201     end
    +0202 end
    +0203 
    +0204 %Simplify the template models to remove constrained rxns. At the same time,
    +0205 %check that the id of the template models is not the same as the model. That
    +0206 %would cause an error further down
    +0207 for i=1:numel(models)
    +0208     models{i}.rxnScores=rxnScores{i};
    +0209     models{i}=simplifyModel(models{i},false,false,true);
    +0210     if strcmpi(models{i}.id,model.id)
    +0211         EM='The reference model(s) cannot have the same id as the model';
    +0212         error('RAVEN:badInput', '%s', EM);
    +0213     end
    +0214 end
    +0215 
    +0216 %This is a rather ugly solution to the issue that it is a bit tricky to keep
    +0217 %track of which scores belong to which reactions. This requires that
    +0218 %removeReactions and mergeModels are modified to check for the new field.
    +0219 model.rxnScores=zeros(numel(model.rxns),1);
    +0220 
    +0221 %First merge all models into one big one
    +0222 allModels=mergeModels([{model};models],'metNames',true);
    +0223 
    +0224 %Add that net production is ok
    +0225 if allowNetProduction==true
    +0226     %A second column in model.b means that the b field is lower and upper
    +0227     %bound on the RHS.
    +0228     model.b=[model.b(:,1) inf(numel(model.mets),1)];
    +0229     allModels.b=[allModels.b(:,1) inf(numel(allModels.mets),1)];
    +0230 end
    +0231 
    +0232 if useModelConstraints==true
    +0233     newConnected={};
    +0234     cannotConnect={};
    +0235     
    +0236     %Check that the input model is not solveable without any input
    +0237     sol=solveLP(model);
    +0238     if ~isempty(sol.f)
    +0239         addedRxns={};
    +0240         newModel=model;
    +0241         exitFlag=1;
    +0242         return;
    +0243     end
    +0244     
    +0245     %Then check that the merged model is solveable
    +0246     sol=solveLP(allModels);
    +0247     if isempty(sol.f)
    +0248         EM='There are no reactions in the template model(s) that can make the model constraints satisfied';
    +0249         error('RAVEN:badInput', '%s', EM);
    +0250     end
    +0251     
    +0252     %Remove dead ends for speed reasons. This has to be done here and
    +0253     %duplicate below because there is otherwise a risk that a reaction
    +0254     %which is constrained to something relevant is removed
    +0255     allModels=simplifyModel(allModels,false,false,false,true,false,false,false,[],true);
    +0256     allModels.c(:)=0;
    +0257 else
    +0258     %Remove dead ends for speed reasons
    +0259     allModels=simplifyModel(allModels,false,false,false,true,false,false,false,[],true);
    +0260     allModels.c(:)=0;
    +0261     
    +0262     %If model constraints should not be used, then determine which reactions
    +0263     %to force to have flux
    +0264     
    +0265     %Get the reactions that can carry flux in the original model
    +0266     originalFlux=haveFlux(model,1);
    +0267     
    +0268     %For the ones that cannot carry flux, see if they can do so in the
    +0269     %merged model. Reactions from model keep their id unchanged in
    +0270     %allModels (mergeModels renames colliding ids in the other input
    +0271     %models, never in model itself), so intersecting with allModels.rxns
    +0272     %both identifies them and drops any that simplifyModel has since
    +0273     %removed as dead ends. The previous strcmp(allModels.rxnFrom,model.id)
    +0274     %failed whenever model already carried a rxnFrom field of its own, as
    +0275     %getModelFromHomology output does, tagging each reaction with its
    +0276     %template rather than model.id, so mergeModels leaves it untouched and
    +0277     %it never matches model.id here.
    +0278     toCheck=intersect(model.rxns(~originalFlux),allModels.rxns);
    +0279     
    +0280     %Get the ones that still cannot carry flux. Guard against an empty
    +0281     %toCheck, since haveFlux interprets an empty reaction list as "all
    +0282     %reactions" rather than "none"
    +0283     if isempty(toCheck)
    +0284         K=toCheck;
    +0285     else
    +0286         I=haveFlux(allModels,1,toCheck);
    +0287         %Get the reactions that cannot carry flux in the original model, but can
    +0288         %in the merged one
    +0289         K=toCheck(I);
    +0290     end
    +0291     
    +0292     %This is a temporary thing to only look at the non-reversible rxns.
    +0293     %This is because all reversible rxns can have a flux in the
    +0294     %irreversible model format that is used by getMinNrFluxes
    +0295     [~, I]=ismember(K,model.rxns);
    +0296     K(model.rev(I)~=0)=[];
    +0297     
    +0298     %Constrain all reactions in the original model to have a flux
    +0299     allModels.lb(ismember(allModels.rxns,K))=0.1;
    +0300     
    +0301     newConnected=K;
    +0302     cannotConnect=setdiff(model.rxns(~originalFlux),newConnected);
    +0303 end
    +0304 
    +0305 %Then minimize for the number of fluxes used. The fixed rxns (model's own)
    +0306 %do not need to participate; see the comment above on why this is
    +0307 %identified by rxn ID rather than allModels.rxnFrom.
    +0308 templateRxns=find(~ismember(allModels.rxns,model.rxns));
    +0309 [~, J, exitFlag]=getMinNrFluxes(allModels,templateRxns,params,allModels.rxnScores(templateRxns));
    +0310 
    +0311 %Remove everything except for the added ones
    +0312 I=true(numel(allModels.rxns),1);
    +0313 I(templateRxns(J))=false;
    +0314 addedModel=removeReactions(allModels,I,true,true,true);
    +0315 
    +0316 newModel=mergeModels({model;addedModel},'metNames',true);
    +0317 addedRxns=setdiff(newModel.rxns,model.rxns);
    +0318 newModel=rmfield(newModel,'rxnScores');
    +0319 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/gapfilling/findLeakMetabolite.html b/docs/html/gapfilling/findLeakMetabolite.html similarity index 78% rename from doc/gapfilling/findLeakMetabolite.html rename to docs/html/gapfilling/findLeakMetabolite.html index 1a61626c..bd52aa65 100644 --- a/doc/gapfilling/findLeakMetabolite.html +++ b/docs/html/gapfilling/findLeakMetabolite.html @@ -30,10 +30,10 @@

    DESCRIPTION ^
     findLeakMetabolite  Find a metabolite that can be freely made or consumed.
     
      Tests whether the model can produce (excrete) or consume (take up) any
    - metabolite without balanced stoichiometric constraints — a sign that the
    - model contains a stoichiometric leak, futile cycle, or unconstrained
    - exchange. This is the unified replacement for makeSomething ('produce')
    - and consumeSomething ('consume').
    + metabolite without balanced stoichiometric constraints. A positive result
    + indicates that the model contains a stoichiometric leak, a futile cycle or
    + an unconstrained exchange. The direction 'produce' corresponds to the
    + deprecated function makeSomething and 'consume' to consumeSomething.
     
      Parameters
      ----------
    @@ -50,33 +50,33 @@ 

    DESCRIPTION ^DESCRIPTION ^canExchange

    @@ -111,10 +111,10 @@

    SOURCE CODE ^% findLeakMetabolite Find a metabolite that can be freely made or consumed. 0003 % 0004 % Tests whether the model can produce (excrete) or consume (take up) any -0005 % metabolite without balanced stoichiometric constraints — a sign that the -0006 % model contains a stoichiometric leak, futile cycle, or unconstrained -0007 % exchange. This is the unified replacement for makeSomething ('produce') -0008 % and consumeSomething ('consume'). +0005 % metabolite without balanced stoichiometric constraints. A positive result +0006 % indicates that the model contains a stoichiometric leak, a futile cycle or +0007 % an unconstrained exchange. The direction 'produce' corresponds to the +0008 % deprecated function makeSomething and 'consume' to consumeSomething. 0009 % 0010 % Parameters 0011 % ---------- @@ -131,33 +131,33 @@

    SOURCE CODE ^% number of elements as metabolites in the model, or a vector of indexes 0023 % for metabolites to exclude from this analysis (default []). 0024 % isNames : logical -0025 % true if the supplied mets represent metabolite names (as opposed to -0026 % IDs). This is a way to delete metabolites in several compartments at -0027 % once without knowing the exact IDs. This only works if ignoreMets is a +0025 % true if the supplied mets are metabolite names and false if they are +0026 % IDs. Names exclude the metabolites of all compartments that carry the +0027 % name, so the exact IDs are not needed. Only works if ignoreMets is a 0028 % cell array (default false). 0029 % minNrFluxes : logical -0030 % solves the MILP problem of minimizing the number of fluxes instead of -0031 % the sum. Slower, but can be used if the sum gives too many fluxes -0032 % (default false). +0030 % true to solve the MILP problem of minimising the number of fluxes. If +0031 % false, the sum of fluxes is minimised. The MILP is slower and applies +0032 % when the sum gives too many fluxes (default false). 0033 % allowExcretion : logical -0034 % allow for excretion of all metabolites. Only used when direction is +0034 % true to allow excretion of all metabolites. Only used when direction is 0035 % 'produce' (default true). 0036 % params : struct -0037 % *obsolete option*. -0038 % ignoreIntBounds : logical -0039 % true if internal bounds (including reversibility) should be ignored. -0040 % Exchange reactions are not affected. This can be used to find -0041 % unbalanced solutions which are not possible using the default -0042 % constraints (default false). +0037 % solver parameters, passed to solveLP when minNrFluxes is true (default +0038 % a structure with relGap 0.8). +0039 % ignoreIntBounds : logical +0040 % true to ignore internal bounds (including reversibility). Exchange +0041 % reactions are not affected. Finds unbalanced solutions that the +0042 % default constraints do not allow (default false). 0043 % 0044 % Returns 0045 % ------- 0046 % solution : double 0047 % flux vector for the solution. 0048 % metabolite : double -0049 % the index of the metabolite(s) which was produced or consumed. If -0050 % possible only one metabolite is reported, but there are situations -0051 % where metabolites can only be exchanged in pairs (or more). +0049 % the index of the metabolite(s) produced or consumed. A single +0050 % metabolite is reported when one suffices; some metabolites can only be +0051 % exchanged in pairs or larger groups. 0052 % 0053 % Examples 0054 % -------- @@ -166,12 +166,12 @@

    SOURCE CODE ^% 0058 % Notes 0059 % ----- -0060 % Works by forcing at least 1 unit of "any metabolite" to be produced or -0061 % consumed, then minimising for the sum of fluxes. If more than one -0062 % metabolite is produced/consumed, it tries to find a single metabolite -0063 % that satisfies the constraint on its own. +0060 % The function forces a flux of at least 1 through a pooled exchange of all +0061 % metabolites, then minimises the sum of fluxes. If more than one metabolite +0062 % is produced or consumed, it searches for a single metabolite that +0063 % satisfies the constraint on its own. 0064 % -0065 % See Also +0065 % See also 0066 % -------- 0067 % canExchange 0068 diff --git a/doc/gapfilling/fitTasks.html b/docs/html/gapfilling/fitTasks.html similarity index 66% rename from doc/gapfilling/fitTasks.html rename to docs/html/gapfilling/fitTasks.html index 13408704..0aab1552 100644 --- a/doc/gapfilling/fitTasks.html +++ b/docs/html/gapfilling/fitTasks.html @@ -31,9 +31,9 @@

    DESCRIPTION ^DESCRIPTION ^DESCRIPTION ^DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    @@ -116,9 +116,9 @@

    SOURCE CODE ^% 0004 % Fills gaps in a model by including reactions from a reference model, so 0005 % that the resulting model can perform all the tasks in a task list. The -0006 % gap-filling is done in a task-by-task manner, rather than solving for -0007 % all tasks at once. This means that the order of the tasks could -0008 % influence the result. +0006 % tasks are gap-filled one at a time in list order, and the reactions added +0007 % for one task are kept for the following tasks. The order of the tasks can +0008 % therefore influence the result. 0009 % 0010 % Parameters 0011 % ---------- @@ -128,7 +128,7 @@

    SOURCE CODE ^% reference model from which to include reactions. 0016 % inputFile : char, optional 0017 % a task list in Excel format. See the function parseTaskList for -0018 % details (optional if taskStructure is supplied). +0018 % details. Optional if taskStructure is supplied. 0019 % 0020 % Name-Value Arguments 0021 % -------------------- @@ -136,30 +136,30 @@

    SOURCE CODE ^% true if the results of the test should be displayed (default true). 0024 % rxnScores : double 0025 % scores for each of the reactions in the reference model. Only -0026 % negative scores are allowed. The solver will try to maximize the sum -0027 % of the scores for the included reactions (default is -1 for all -0028 % reactions). -0029 % taskStructure : struct -0030 % structure with the tasks, as from parseTaskList. If supplied then -0031 % inputFile is ignored. -0032 % params : struct -0033 % solver parameters, forwarded to the gap-filling back-end and from there -0034 % to optimizeProb for each per-task MILP (default []). -0035 % gapFillMode : char -0036 % which gap-filling back-end to use (default 'merge'): -0037 % -0038 % - 'merge' : refModel holds only the candidate reactions, and fillGaps -0039 % merges it with the model for every task. -0040 % - 'preMerged' : refModel already contains the model's own reactions, -0041 % so no merge is needed per task. The task constraints are written -0042 % into refModel as well, and ftINITFillGaps does the gap-filling. -0043 % verbose : logical -0044 % if true, the MILP progression will be shown. Only read when -0045 % gapFillMode is 'preMerged' (default false). -0046 % resolveTies : logical -0047 % if true, pin the gap-fill MILP's degenerate optimum to a canonical answer -0048 % instead of relying on the solver Seed alone. Only read when gapFillMode is -0049 % 'preMerged' -- see ftINITFillGaps' resolveTies (default false). +0026 % negative scores are allowed. The solver maximises the sum of the +0027 % scores of the included reactions (default is -1 for all reactions). +0028 % taskStructure : struct +0029 % structure with the tasks, as returned by parseTaskList. If supplied, +0030 % inputFile is ignored. +0031 % params : struct +0032 % solver parameters, passed to the gap-filling function and from there to +0033 % optimizeProb for each per-task MILP (default []). +0034 % gapFillMode : char +0035 % which gap-filling function to use (default 'merge'): +0036 % +0037 % - 'merge' : refModel holds only the candidate reactions, and fillGaps +0038 % merges it with the model for every task. +0039 % - 'preMerged' : refModel already contains the model's own reactions, +0040 % so no merge is needed per task. The task constraints are written +0041 % into refModel as well, and ftINITFillGaps does the gap-filling. +0042 % verbose : logical +0043 % true to show the MILP progression. Only read when gapFillMode is +0044 % 'preMerged' (default false). +0045 % resolveTies : logical +0046 % true to fix the degenerate optimum of the gap-fill MILP to a canonical +0047 % answer, which otherwise depends on the solver Seed. Only read when +0048 % gapFillMode is 'preMerged'. See resolveTies of ftINITFillGaps +0049 % (default false). 0050 % 0051 % Returns 0052 % ------- @@ -173,13 +173,13 @@

    SOURCE CODE ^% failedTasks : logical 0061 % Nx1, true for each task that could not be gap-filled, either because 0062 % no feasible solution exists using the reference model or because the -0063 % attempt threw. Such tasks add no reactions, so they are otherwise -0064 % indistinguishable from tasks that already worked. +0063 % attempt raised an error. Such tasks add no reactions, and the same +0064 % holds for tasks that are feasible without gap-filling. 0065 % 0066 % Examples 0067 % -------- -0068 % [outModel, addedRxns]=fitTasks(model,refModel,inputFile,printOutput,... -0069 % rxnScores,taskStructure,params); +0068 % [outModel, addedRxns, failedTasks]=fitTasks(model,refModel,inputFile,... +0069 % 'printOutput',false,'rxnScores',rxnScores,'params',params); 0070 0071 p=parseRAVENargs(varargin, {'printOutput',true; ... 0072 'rxnScores',[]; ... @@ -375,185 +375,188 @@

    SOURCE CODE ^end 0263 %Add changed bounds 0264 if ~isempty(taskStructure(i).changed) -0265 tModel=setParam(tModel,'lb',taskStructure(i).changed,taskStructure(i).LBrxn); -0266 tModel=setParam(tModel,'ub',taskStructure(i).changed,taskStructure(i).UBrxn); -0267 if preMerged -0268 tRefModel=setParam(tRefModel,'lb',taskStructure(i).changed,taskStructure(i).LBrxn); -0269 tRefModel=setParam(tRefModel,'ub',taskStructure(i).changed,taskStructure(i).UBrxn); -0270 end -0271 end -0272 -0273 %Solve and print. Display a warning if the problem is not solveable -0274 sol=solveLP(tModel); -0275 if isempty(sol.x) -0276 %Only do gap-filling if it cannot be solved -0277 failed=false; -0278 try -0279 if preMerged -0280 [newRxns, newModel, exitFlag]=ftINITFillGaps(tModel,model,tRefModel,false,supressWarnings,tRxnScores,params,verbose,resolveTies); -0281 else -0282 [~, ~, newRxns, newModel, exitFlag]=fillGaps(tModel,refModel,false,true,supressWarnings,rxnScores,params); -0283 end -0284 if exitFlag==-1 -0285 %No set of reactions from the reference model makes the -0286 %task feasible. Without this branch the task falls -0287 %through and reports "Added 0 reaction(s)", which is the -0288 %same thing an already-feasible task reports. -0289 EM=[taskLabel ' could not be gap-filled: no feasible solution exists using the reference model']; -0290 warning('RAVEN:warning', '%s', EM); -0291 failed=true; -0292 elseif exitFlag==-2 -0293 EM=[taskLabel ' was aborted before reaching optimality. Consider increasing params.TimeLimit']; -0294 warning('RAVEN:warning', '%s', EM); -0295 end -0296 catch e -0297 EM=[taskLabel ' could not be performed for any set of reactions: ' e.message]; -0298 warning('RAVEN:warning', '%s', EM); -0299 failed=true; -0300 end -0301 if failed -0302 failedTasks(i)=true; +0265 %NaN bounds mean "leave unchanged" +0266 lbSet=~isnan(taskStructure(i).LBrxn); +0267 ubSet=~isnan(taskStructure(i).UBrxn); +0268 tModel=setParam(tModel,'lb',taskStructure(i).changed(lbSet),taskStructure(i).LBrxn(lbSet)); +0269 tModel=setParam(tModel,'ub',taskStructure(i).changed(ubSet),taskStructure(i).UBrxn(ubSet)); +0270 if preMerged +0271 tRefModel=setParam(tRefModel,'lb',taskStructure(i).changed(lbSet),taskStructure(i).LBrxn(lbSet)); +0272 tRefModel=setParam(tRefModel,'ub',taskStructure(i).changed(ubSet),taskStructure(i).UBrxn(ubSet)); +0273 end +0274 end +0275 +0276 %Solve and print. Display a warning if the problem is not solveable +0277 sol=solveLP(tModel); +0278 if isempty(sol.x) +0279 %Only do gap-filling if it cannot be solved +0280 failed=false; +0281 try +0282 if preMerged +0283 [newRxns, newModel, exitFlag]=ftINITFillGaps(tModel,model,tRefModel,false,supressWarnings,tRxnScores,params,verbose,resolveTies); +0284 else +0285 [~, ~, newRxns, newModel, exitFlag]=fillGaps(tModel,refModel,false,true,supressWarnings,rxnScores,params); +0286 end +0287 if exitFlag==-1 +0288 %No set of reactions from the reference model makes the +0289 %task feasible. Without this branch the task falls +0290 %through and reports "Added 0 reaction(s)", which is the +0291 %same thing an already-feasible task reports. +0292 EM=[taskLabel ' could not be gap-filled: no feasible solution exists using the reference model']; +0293 warning('RAVEN:warning', '%s', EM); +0294 failed=true; +0295 elseif exitFlag==-2 +0296 EM=[taskLabel ' was aborted before reaching optimality. Consider increasing params.TimeLimit']; +0297 warning('RAVEN:warning', '%s', EM); +0298 end +0299 catch e +0300 EM=[taskLabel ' could not be performed for any set of reactions: ' e.message]; +0301 warning('RAVEN:warning', '%s', EM); +0302 failed=true; 0303 end -0304 if failed==false -0305 if ~isempty(newRxns) -0306 nAdded=nAdded+numel(newRxns); -0307 -0308 if preMerged -0309 %The reference model already contained the model, so -0310 %newModel is the model plus the added reactions -0311 model=newModel; -0312 else -0313 %Add the reactions to the base model. It is not correct -0314 %to use newModel directly, as it may contain -0315 %reactions/constraints that are specific to this task -0316 model=mergeModels({model,removeReactions(newModel,setdiff(newModel.rxns,newRxns),true,true)},'metNames',true); -0317 end -0318 -0319 %Keep track of the added reactions -0320 addedRxns(ismember(refModel.rxns,newRxns),i)=true; -0321 end -0322 if printOutput==true -0323 %Task id/description are arbitrary text and may -0324 %contain "%"; print as literal data rather than as an -0325 %fprintf format string. -0326 fprintf('%s\n', ['[' taskStructure(i).id '] ' taskStructure(i).description ': Added ' num2str(numel(newRxns)) ' reaction(s), ' num2str(nAdded) ' reactions added in total']); -0327 end -0328 end -0329 else -0330 if printOutput==true -0331 fprintf('%s\n', ['[' taskStructure(i).id '] ' taskStructure(i).description ': Added 0 reaction(s), ' num2str(nAdded) ' reactions added in total']); -0332 end -0333 end -0334 supressWarnings=true; -0335 -0336 %Print the output if chosen -0337 if taskStructure(i).printFluxes && printOutput -0338 if ~isempty(sol.x) -0339 sol=solveLP(tModel,1); -0340 printFluxes(tModel,sol.x,false,10^-5,[],'%rxnID (%eqn):%flux\n'); -0341 fprintf('\n'); -0342 else -0343 %If the problem was not solveable then the gap-filled model -0344 %should be used -0345 if failed==false -0346 sol=solveLP(newModel,1); -0347 printFluxes(newModel,sol.x,false,10^-5,[],'%rxnID (%eqn):%flux\n'); -0348 fprintf('\n'); -0349 end -0350 end -0351 end -0352 -0353 tModel=model; -0354 %Since new mets are added by merging the new reactions and not only -0355 %from the task sheet -0356 modelMets=upper(strcat(model.metNames,'[',model.comps(model.metComps),']')); -0357 else -0358 EM=['"[' taskStructure(i).id '] ' taskStructure(i).description '" is set as SHOULD FAIL. Such tasks cannot be modelled using this approach and the task is therefore ignored']; -0359 warning('RAVEN:warning', '%s', EM); -0360 end -0361 end -0362 if size(model.b,2)>1 -0363 model.b(:,2) = []; % resume field b +0304 if failed +0305 failedTasks(i)=true; +0306 end +0307 if failed==false +0308 if ~isempty(newRxns) +0309 nAdded=nAdded+numel(newRxns); +0310 +0311 if preMerged +0312 %The reference model already contained the model, so +0313 %newModel is the model plus the added reactions +0314 model=newModel; +0315 else +0316 %Add the reactions to the base model. It is not correct +0317 %to use newModel directly, as it may contain +0318 %reactions/constraints that are specific to this task +0319 model=mergeModels({model,removeReactions(newModel,setdiff(newModel.rxns,newRxns),true,true)},'metNames',true); +0320 end +0321 +0322 %Keep track of the added reactions +0323 addedRxns(ismember(refModel.rxns,newRxns),i)=true; +0324 end +0325 if printOutput==true +0326 %Task id/description are arbitrary text and may +0327 %contain "%"; print as literal data rather than as an +0328 %fprintf format string. +0329 fprintf('%s\n', ['[' taskStructure(i).id '] ' taskStructure(i).description ': Added ' num2str(numel(newRxns)) ' reaction(s), ' num2str(nAdded) ' reactions added in total']); +0330 end +0331 end +0332 else +0333 if printOutput==true +0334 fprintf('%s\n', ['[' taskStructure(i).id '] ' taskStructure(i).description ': Added 0 reaction(s), ' num2str(nAdded) ' reactions added in total']); +0335 end +0336 end +0337 supressWarnings=true; +0338 +0339 %Print the output if chosen +0340 if taskStructure(i).printFluxes && printOutput +0341 if ~isempty(sol.x) +0342 sol=solveLP(tModel,1); +0343 printFluxes(tModel,sol.x,false,10^-5,[],'%rxnID (%eqn):%flux\n'); +0344 fprintf('\n'); +0345 else +0346 %If the problem was not solveable then the gap-filled model +0347 %should be used +0348 if failed==false +0349 sol=solveLP(newModel,1); +0350 printFluxes(newModel,sol.x,false,10^-5,[],'%rxnID (%eqn):%flux\n'); +0351 fprintf('\n'); +0352 end +0353 end +0354 end +0355 +0356 tModel=model; +0357 %Since new mets are added by merging the new reactions and not only +0358 %from the task sheet +0359 modelMets=upper(strcat(model.metNames,'[',model.comps(model.metComps),']')); +0360 else +0361 EM=['"[' taskStructure(i).id '] ' taskStructure(i).description '" is set as SHOULD FAIL. Such tasks cannot be modelled using this approach and the task is therefore ignored']; +0362 warning('RAVEN:warning', '%s', EM); +0363 end 0364 end -0365 outModel=model; -0366 end -0367 -0368 function m=applyTaskInputBounds(m,I,J,K,L,task,taskLabel) -0369 %Write a task's input constraints into the b field of one model. I and J -0370 %are the ismember result of the task's input metabolites against that -0371 %model's metabolite names; K and L flag ALLMETS and ALLMETSIN entries. -0372 -0373 %If all metabolites should be added. Use the first match of ALLMETS, there -0374 %should only be one, but still.. -0375 if any(K) -0376 m.b(:,1)=task.UBin(find(K,1))*-1; -0377 end -0378 %If metabolites in a specific compartment should be used -0379 if any(L) -0380 L=find(L); -0381 for j=1:numel(L) -0382 %The compartment defined -0383 compartment=upper(task.inputs{L(j)}(11:end-1)); -0384 %Check if it exists in the model -0385 C=find(ismember(upper(m.comps),compartment)); -0386 if any(C) -0387 %Match to metabolites -0388 m.b(m.metComps==C,1)=task.UBin(L(j))*-1; -0389 else -0390 EM=['The compartment defined for ALLMETSIN in ' taskLabel ' does not exist']; -0391 error('RAVEN:badInput', '%s', EM); -0392 end -0393 end -0394 end -0395 %Then add the normal constraints -0396 if any(J(I)) -0397 m.b(J(I),1)=task.UBin(I)*-1; -0398 m.b(J(I),2)=task.LBin(I)*-1; -0399 end -0400 end -0401 -0402 function m=applyTaskOutputBounds(m,I,J,K,L,task,taskLabel) -0403 %Write a task's output constraints into the b field of one model, as -0404 %applyTaskInputBounds does for the inputs. -0405 -0406 %If all metabolites should be added. Use the first match of ALLMETS, there -0407 %should only be one, but still.. -0408 if any(K) -0409 m.b(:,2)=task.UBout(find(K,1)); -0410 end -0411 %If metabolites in a specific compartment should be used -0412 if any(L) -0413 L=find(L); -0414 for j=1:numel(L) -0415 %The compartment defined -0416 compartment=upper(task.outputs{L(j)}(11:end-1)); -0417 %Check if it exists in the model -0418 C=find(ismember(upper(m.comps),compartment)); -0419 if any(C) -0420 %Match to metabolites -0421 m.b(m.metComps==C,2)=task.UBout(L(j)); -0422 else -0423 EM=['The compartment defined for ALLMETSIN in ' taskLabel ' does not exist']; -0424 error('RAVEN:badInput', '%s', EM); -0425 end -0426 end -0427 end -0428 %Then add the normal constraints -0429 if any(J(I)) -0430 %Verify that IN and OUT bounds are consistent. Cannot require that a -0431 %metabolite is simultaneously input AND output at some nonzero flux. -0432 J = J(I); -0433 I = find(I); % otherwise indexing becomes confusing -0434 nonzero_LBin = m.b(J,2) < 0; -0435 nonzero_LBout = task.LBout(I) > 0; -0436 if any(nonzero_LBin & nonzero_LBout) -0437 EM=['The IN LB and OUT LB in ' taskLabel ' cannot be nonzero for the same metabolite']; -0438 error('RAVEN:badInput', '%s', EM); -0439 end -0440 m.b(J(nonzero_LBout),1)=task.LBout(I(nonzero_LBout)); -0441 m.b(J,2)=task.UBout(I); -0442 end -0443 end +0365 if size(model.b,2)>1 +0366 model.b(:,2) = []; % resume field b +0367 end +0368 outModel=model; +0369 end +0370 +0371 function m=applyTaskInputBounds(m,I,J,K,L,task,taskLabel) +0372 %Write a task's input constraints into the b field of one model. I and J +0373 %are the ismember result of the task's input metabolites against that +0374 %model's metabolite names; K and L flag ALLMETS and ALLMETSIN entries. +0375 +0376 %If all metabolites should be added. Use the first match of ALLMETS, there +0377 %should only be one, but still.. +0378 if any(K) +0379 m.b(:,1)=task.UBin(find(K,1))*-1; +0380 end +0381 %If metabolites in a specific compartment should be used +0382 if any(L) +0383 L=find(L); +0384 for j=1:numel(L) +0385 %The compartment defined +0386 compartment=upper(task.inputs{L(j)}(11:end-1)); +0387 %Check if it exists in the model +0388 C=find(ismember(upper(m.comps),compartment)); +0389 if any(C) +0390 %Match to metabolites +0391 m.b(m.metComps==C,1)=task.UBin(L(j))*-1; +0392 else +0393 EM=['The compartment defined for ALLMETSIN in ' taskLabel ' does not exist']; +0394 error('RAVEN:badInput', '%s', EM); +0395 end +0396 end +0397 end +0398 %Then add the normal constraints +0399 if any(J(I)) +0400 m.b(J(I),1)=task.UBin(I)*-1; +0401 m.b(J(I),2)=task.LBin(I)*-1; +0402 end +0403 end +0404 +0405 function m=applyTaskOutputBounds(m,I,J,K,L,task,taskLabel) +0406 %Write a task's output constraints into the b field of one model, as +0407 %applyTaskInputBounds does for the inputs. +0408 +0409 %If all metabolites should be added. Use the first match of ALLMETS, there +0410 %should only be one, but still.. +0411 if any(K) +0412 m.b(:,2)=task.UBout(find(K,1)); +0413 end +0414 %If metabolites in a specific compartment should be used +0415 if any(L) +0416 L=find(L); +0417 for j=1:numel(L) +0418 %The compartment defined +0419 compartment=upper(task.outputs{L(j)}(11:end-1)); +0420 %Check if it exists in the model +0421 C=find(ismember(upper(m.comps),compartment)); +0422 if any(C) +0423 %Match to metabolites +0424 m.b(m.metComps==C,2)=task.UBout(L(j)); +0425 else +0426 EM=['The compartment defined for ALLMETSIN in ' taskLabel ' does not exist']; +0427 error('RAVEN:badInput', '%s', EM); +0428 end +0429 end +0430 end +0431 %Then add the normal constraints +0432 if any(J(I)) +0433 %Verify that IN and OUT bounds are consistent. Cannot require that a +0434 %metabolite is simultaneously input AND output at some nonzero flux. +0435 J = J(I); +0436 I = find(I); % otherwise indexing becomes confusing +0437 nonzero_LBin = m.b(J,2) < 0; +0438 nonzero_LBout = task.LBout(I) > 0; +0439 if any(nonzero_LBin & nonzero_LBout) +0440 EM=['The IN LB and OUT LB in ' taskLabel ' cannot be nonzero for the same metabolite']; +0441 error('RAVEN:badInput', '%s', EM); +0442 end +0443 m.b(J(nonzero_LBout),1)=task.LBout(I(nonzero_LBout)); +0444 m.b(J,2)=task.UBout(I); +0445 end +0446 end
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/gapfilling/ftINITFillGaps.html b/docs/html/gapfilling/ftINITFillGaps.html new file mode 100644 index 00000000..fab58607 --- /dev/null +++ b/docs/html/gapfilling/ftINITFillGaps.html @@ -0,0 +1,223 @@ + + + + Description of ftINITFillGaps + + + + + + + + + +
    Home > gapfilling > ftINITFillGaps.m
    + + + +

    ftINITFillGaps +

    + +

    PURPOSE ^

    +
    ftINITFillGaps
    + +

    SYNOPSIS ^

    +
    function [addedRxns, newModel, exitFlag]=ftINITFillGaps(tModel, origModel, tRefModel,allowNetProduction,supressWarnings,rxnScores,params,verbose,resolveTies)
    + +

    DESCRIPTION ^

    +
     ftINITFillGaps
    +   Variant of fillGaps adapted to speed up the generation of ftINIT models.
    +
    +   tModel              model that contains the task-specific rxns.
    +   origModel           model without task-specific rxns.         
    +   tRefModel           reference tModel: the full tModel, containing all rxns
    +                       used for gap-filling of tModel + the task-specific rxns
    +   allowNetProduction  true if net production of all metabolites is
    +                       allowed. A reaction can be unable to carry flux because one of
    +                       the reactants is unavailable or because one of the
    +                       products cannot be processed further. If this
    +                       parameter is true, only the first cause of blocked
    +                       flux is considered (optional, default false)
    +   supressWarnings     false if warnings should be displayed (optional, default
    +                       false)
    +   rxnScores           scores for each of the reactions in the
    +                       reference tModel. The solver maximises the sum of
    +                       the scores of the included reactions (optional,
    +                       default -1 for all reactions)
    +   params              solver parameters, passed to getMinNrFluxes (optional)
    +   verbose             true to show the MILP progression.
    +   resolveTies         true to fix the degenerate optimum of the gap-fill MILP
    +                       to a canonical answer, which otherwise depends on the
    +                       solver Seed. See resolveTies of getMinNrFluxes
    +                       (optional, default false)
    +
    +   addedRxns           the rxns added
    +   newModel            the tModel with reactions added to fill gaps
    +   exitFlag            1: optimal solution found
    +                      -1: no solution found, either because the problem is
    +                          infeasible or because the solver reached its time
    +                          limit before finding one
    +                      -2: a solution was found but is not proven optimal,
    +                          because the solver stopped before reaching
    +                          optimality
    +
    +   This method works on the reference model, which already contains the
    +   tModel reactions. Reactions constrained to zero flux are removed
    +   (simplifyModel(tRefModel,false,false,true), deleteZeroInterval). It then
    +   solves the MILP problem of including as few additional reactions as
    +   possible from the reference model so that the constraints already set
    +   on tModel (for example a required biomass flux, or exchange bounds) can
    +   be satisfied.
    +
    +   fillGaps merges the reference model with the model on every call, so it
    +   does not need a pre-merged reference model.
    +
    +   See also: fillGaps
    +
    + Usage: [addedRxns, newModel, exitFlag]=...
    +           ftINITFillGaps(tModel,origModel,tRefModel,allowNetProduction,...
    +           supressWarnings,rxnScores,params,verbose,resolveTies)
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    • fitTasks fitTasks Fill gaps in a model so it can perform a list of tasks.
    + + + + +

    SOURCE CODE ^

    +
    0001 function [addedRxns, newModel, exitFlag]=ftINITFillGaps(tModel, origModel, tRefModel,allowNetProduction,supressWarnings,rxnScores,params,verbose,resolveTies)
    +0002 % ftINITFillGaps
    +0003 %   Variant of fillGaps adapted to speed up the generation of ftINIT models.
    +0004 %
    +0005 %   tModel              model that contains the task-specific rxns.
    +0006 %   origModel           model without task-specific rxns.
    +0007 %   tRefModel           reference tModel: the full tModel, containing all rxns
    +0008 %                       used for gap-filling of tModel + the task-specific rxns
    +0009 %   allowNetProduction  true if net production of all metabolites is
    +0010 %                       allowed. A reaction can be unable to carry flux because one of
    +0011 %                       the reactants is unavailable or because one of the
    +0012 %                       products cannot be processed further. If this
    +0013 %                       parameter is true, only the first cause of blocked
    +0014 %                       flux is considered (optional, default false)
    +0015 %   supressWarnings     false if warnings should be displayed (optional, default
    +0016 %                       false)
    +0017 %   rxnScores           scores for each of the reactions in the
    +0018 %                       reference tModel. The solver maximises the sum of
    +0019 %                       the scores of the included reactions (optional,
    +0020 %                       default -1 for all reactions)
    +0021 %   params              solver parameters, passed to getMinNrFluxes (optional)
    +0022 %   verbose             true to show the MILP progression.
    +0023 %   resolveTies         true to fix the degenerate optimum of the gap-fill MILP
    +0024 %                       to a canonical answer, which otherwise depends on the
    +0025 %                       solver Seed. See resolveTies of getMinNrFluxes
    +0026 %                       (optional, default false)
    +0027 %
    +0028 %   addedRxns           the rxns added
    +0029 %   newModel            the tModel with reactions added to fill gaps
    +0030 %   exitFlag            1: optimal solution found
    +0031 %                      -1: no solution found, either because the problem is
    +0032 %                          infeasible or because the solver reached its time
    +0033 %                          limit before finding one
    +0034 %                      -2: a solution was found but is not proven optimal,
    +0035 %                          because the solver stopped before reaching
    +0036 %                          optimality
    +0037 %
    +0038 %   This method works on the reference model, which already contains the
    +0039 %   tModel reactions. Reactions constrained to zero flux are removed
    +0040 %   (simplifyModel(tRefModel,false,false,true), deleteZeroInterval). It then
    +0041 %   solves the MILP problem of including as few additional reactions as
    +0042 %   possible from the reference model so that the constraints already set
    +0043 %   on tModel (for example a required biomass flux, or exchange bounds) can
    +0044 %   be satisfied.
    +0045 %
    +0046 %   fillGaps merges the reference model with the model on every call, so it
    +0047 %   does not need a pre-merged reference model.
    +0048 %
    +0049 %   See also: fillGaps
    +0050 %
    +0051 % Usage: [addedRxns, newModel, exitFlag]=...
    +0052 %           ftINITFillGaps(tModel,origModel,tRefModel,allowNetProduction,...
    +0053 %           supressWarnings,rxnScores,params,verbose,resolveTies)
    +0054 
    +0055 if nargin<9 || isempty(resolveTies)
    +0056     resolveTies=false;
    +0057 end
    +0058 if isempty(rxnScores)
    +0059     rxnScores=ones(numel(tRefModel.rxns),1)*-1;
    +0060 end
    +0061 
    +0062 %Simplify the template models to remove constrained rxns. At the same time,
    +0063 %check that the id of the template models isn't the same as the tModel. That
    +0064 %would cause an error further down
    +0065 tRefModel.rxnScores=rxnScores;
    +0066 tRefModel=simplifyModel(tRefModel,false,false,true);
    +0067 
    +0068 %This is a rather ugly solution to the issue that it's a bit tricky to keep
    +0069 %track of which scores belong to which reactions. This requires that
    +0070 %removeReactions and mergeModels are modified to check for the new field.
    +0071 tModel.rxnScores=zeros(numel(tModel.rxns),1);
    +0072 
    +0073 %First merge all models into one big one
    +0074 fullModel = tRefModel;%mergeModels([{tModel};models],'metNames',true);
    +0075 
    +0076 %Add that net production is ok
    +0077 if allowNetProduction==true
    +0078     %A second column in tModel.b means that the b field is lower and upper
    +0079     %bound on the RHS.
    +0080     tModel.b=[tModel.b(:,1) inf(numel(tModel.mets),1)];
    +0081     fullModel.b=[fullModel.b(:,1) inf(numel(fullModel.mets),1)];
    +0082 end
    +0083 
    +0084 
    +0085 fullModel.c(:)=0;
    +0086 
    +0087 %Then minimize for the number of fluxes used. The fixed rxns doesn't need
    +0088 %to participate
    +0089 templateRxns = find(~ismember(fullModel.rxns, tModel.rxns)); %Check if this is slow, in that case keep track of this in fitTasksOpt and send it in
    +0090 
    +0091 if isempty(templateRxns)
    +0092     %The reference model holds no reaction that the model does not already
    +0093     %have, so no set of additions can satisfy the constraints. This has to
    +0094     %be caught first: an empty toMinimize means "all reactions" to
    +0095     %getMinNrFluxes, and its solution would then be indexed against an
    +0096     %empty templateRxns.
    +0097     addedRxns = {};
    +0098     newModel = origModel;
    +0099     exitFlag = -1;
    +0100     return;
    +0101 end
    +0102 
    +0103 %The reversible formulation uses one binary per reaction rather than one
    +0104 %per irreversible reaction, which is what makes this fast enough to run
    +0105 %once per task on a genome-scale reference model
    +0106 [~, J, exitFlag]=getMinNrFluxes(fullModel,templateRxns,params,fullModel.rxnScores(templateRxns),'formulation','reversible','verbose',verbose,'resolveTies',resolveTies);%only the scores from the template rxns are used, so the others doesn't matter
    +0107 
    +0108 %Remove everything except for the added ones
    +0109 addedRxns = fullModel.rxns(templateRxns(J));
    +0110 if isempty(addedRxns)
    +0111     %No reactions were selected, either because the MILP found no solution
    +0112     %or because none were needed. Handing an empty set to addRxns below
    +0113     %errors out, which would reach the caller as a thrown exception rather
    +0114     %than as the exitFlag it keys on.
    +0115     addedRxns = {};
    +0116     newModel = origModel;
    +0117     return;
    +0118 end
    +0119 rxnsToAdd.rxns = addedRxns;
    +0120 rxnsToAdd.equations = constructEquations(fullModel, addedRxns);
    +0121 rxnsToAdd.ub = fullModel.ub(templateRxns(J));
    +0122 rxnsToAdd.lb = fullModel.lb(templateRxns(J));
    +0123 newModel = addRxns(origModel,rxnsToAdd, 3, [], true); %we ignore gene rules etc here, they are not needed - we regenerate the model in the end by subtracting rxns from the full model.
    +0124 
    +0125 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/gapfilling/gapFillFastCore.html b/docs/html/gapfilling/gapFillFastCore.html new file mode 100644 index 00000000..e7f0aa16 --- /dev/null +++ b/docs/html/gapfilling/gapFillFastCore.html @@ -0,0 +1,203 @@ + + + + Description of gapFillFastCore + + + + + + + + + +
    Home > gapfilling > gapFillFastCore.m
    + + + +

    gapFillFastCore +

    + +

    PURPOSE ^

    +
    gapFillFastCore L1-norm LP subroutine for fastGapFill.
    + +

    SYNOPSIS ^

    +
    function activeRxns = gapFillFastCore(model, coreRxns, epsilon)
    + +

    DESCRIPTION ^

    +
     gapFillFastCore  L1-norm LP subroutine for fastGapFill.
    +
    + Finds the minimum-L1-flux extension of the model that activates all
    + reactions in the core set. Any reaction with a flux of at least
    + epsilon / 2 in the solution is "consistent" with the core. gapFillFastLP
    + calls this function.
    +
    + Based on FASTCORE (Vlassis et al. 2014, PLoS Comput Biol 10:e1003424).
    +
    + Parameters
    + ----------
    + model : struct
    +     a RAVEN model structure.
    + coreRxns : cell or logical or double
    +     core reaction set: cell array of reaction IDs, a logical vector, or a
    +     vector of reaction indices into model.rxns. All core reactions are
    +     forced to carry flux >= epsilon in the LP solution.
    + epsilon : double
    +     minimum flux threshold for a reaction to be considered active.
    +
    + Returns
    + -------
    + activeRxns : logical
    +     logical vector (length numel(model.rxns)) where true indicates that the
    +     reaction carries flux in the L1-minimal solution. All elements are
    +     false if the LP is infeasible (the core reactions cannot all carry flux
    +     simultaneously).
    +
    + See also
    + --------
    + gapFillSwiftCore, gapFillFastLP
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    • gapFillFastLP gapFillFastLP LP-based gap-filling (fastGapFill or swiftGapFill).
    + + + + +

    SOURCE CODE ^

    +
    0001 function activeRxns = gapFillFastCore(model, coreRxns, epsilon)
    +0002 % gapFillFastCore  L1-norm LP subroutine for fastGapFill.
    +0003 %
    +0004 % Finds the minimum-L1-flux extension of the model that activates all
    +0005 % reactions in the core set. Any reaction with a flux of at least
    +0006 % epsilon / 2 in the solution is "consistent" with the core. gapFillFastLP
    +0007 % calls this function.
    +0008 %
    +0009 % Based on FASTCORE (Vlassis et al. 2014, PLoS Comput Biol 10:e1003424).
    +0010 %
    +0011 % Parameters
    +0012 % ----------
    +0013 % model : struct
    +0014 %     a RAVEN model structure.
    +0015 % coreRxns : cell or logical or double
    +0016 %     core reaction set: cell array of reaction IDs, a logical vector, or a
    +0017 %     vector of reaction indices into model.rxns. All core reactions are
    +0018 %     forced to carry flux >= epsilon in the LP solution.
    +0019 % epsilon : double
    +0020 %     minimum flux threshold for a reaction to be considered active.
    +0021 %
    +0022 % Returns
    +0023 % -------
    +0024 % activeRxns : logical
    +0025 %     logical vector (length numel(model.rxns)) where true indicates that the
    +0026 %     reaction carries flux in the L1-minimal solution. All elements are
    +0027 %     false if the LP is infeasible (the core reactions cannot all carry flux
    +0028 %     simultaneously).
    +0029 %
    +0030 % See also
    +0031 % --------
    +0032 % gapFillSwiftCore, gapFillFastLP
    +0033 
    +0034 if nargin < 3 || isempty(epsilon)
    +0035     epsilon = 1e-4;
    +0036 end
    +0037 
    +0038 % ---- Resolve core reaction indices into original model ----
    +0039 % coreOrigIdx: numeric indices into model.rxns
    +0040 if iscell(coreRxns)
    +0041     coreOrigIdx = find(getIndexes(model, coreRxns, 'rxns', true));
    +0042 elseif islogical(coreRxns)
    +0043     coreOrigIdx = find(coreRxns);
    +0044 else
    +0045     coreOrigIdx = coreRxns(:);
    +0046 end
    +0047 
    +0048 % ---- Convert to irreversible form ----
    +0049 % All fluxes become non-negative → L1 norm = sum of fluxes.
    +0050 [irrevModel, matchRev, ~, irrev2rev] = convertToIrrev(model);
    +0051 nIrrev = numel(irrevModel.rxns);
    +0052 nMets  = numel(irrevModel.mets);
    +0053 
    +0054 % Map core reactions to their irreversible equivalents. convertToIrrev
    +0055 % keeps the forward copy at the reaction's original index, so coreOrigIdx
    +0056 % already are that copy's indices.
    +0057 coreIdxIrrev = false(nIrrev, 1);
    +0058 coreIdxIrrev(coreOrigIdx) = true;
    +0059 
    +0060 % A reversible core reaction also has a reverse copy elsewhere in
    +0061 % irrevModel, with identical but oppositely-signed stoichiometry. Forcing
    +0062 % both copies to >= epsilon would let them cancel out in S_irrev*v=0
    +0063 % (v_fwd=v_bwd=epsilon nets to zero for every metabolite the reaction
    +0064 % touches), satisfying the "core must carry flux" requirement with a
    +0065 % self-contained loop that needs no support from the rest of the network,
    +0066 % regardless of whether the reaction has any real connectivity. Instead,
    +0067 % require flux on one copy at a time: try forward first, and if that is
    +0068 % infeasible, retry with reverse forced instead, so a reaction whose only
    +0069 % genuine support is in the reverse direction is still recognised.
    +0070 isRevCore   = matchRev(coreOrigIdx) > 0;
    +0071 fwdCoreCopy = coreOrigIdx(isRevCore);          % forward copy of reversible core rxns
    +0072 revCoreCopy = matchRev(coreOrigIdx(isRevCore)); % their reverse copy
    +0073 irrCoreCopy = coreOrigIdx(~isRevCore);          % irreversible core rxns: single copy
    +0074 
    +0075 c = ones(nIrrev, 1);
    +0076 c(coreIdxIrrev) = 0;   % core reactions are not in the objective
    +0077 
    +0078 prob.A      = irrevModel.S;
    +0079 prob.a      = prob.A;
    +0080 prob.b      = zeros(nMets, 1);
    +0081 prob.csense = repmat('E', 1, nMets);
    +0082 prob.c      = c;
    +0083 prob.osense = 1;          % minimise
    +0084 prob.vartype = repmat('C', 1, nIrrev);
    +0085 
    +0086 % ---- Formulate LP ----
    +0087 % Minimize sum of non-core fluxes (all fluxes >= 0 in irreversible form,
    +0088 % so this is the L1 norm over non-core reactions).
    +0089 %
    +0090 %   min   sum_{j not in C} v_j
    +0091 %   s.t.  S_irrev * v = 0
    +0092 %         v_j >= epsilon   for j in C
    +0093 %         lb <= v <= ub
    +0094 %
    +0095 forceCopy   = {fwdCoreCopy, revCoreCopy};
    +0096 disableCopy = {revCoreCopy, fwdCoreCopy};
    +0097 nPasses     = 1 + ~isempty(fwdCoreCopy);   % a second pass only differs when a reversible core rxn exists
    +0098 for pass = 1:nPasses
    +0099     lb = irrevModel.lb;
    +0100     lb(irrCoreCopy)         = max(lb(irrCoreCopy), epsilon);
    +0101     lb(forceCopy{pass})     = max(lb(forceCopy{pass}), epsilon);
    +0102     ub = irrevModel.ub;
    +0103     ub(disableCopy{pass})   = 0;
    +0104     prob.lb = lb;
    +0105     prob.ub = ub;
    +0106 
    +0107     sol = optimizeProb(prob, [], false);
    +0108     if checkSolution(sol)
    +0109         break;
    +0110     end
    +0111 end
    +0112 
    +0113 if ~checkSolution(sol)
    +0114     activeRxns = false(numel(model.rxns), 1);
    +0115     return;
    +0116 end
    +0117 
    +0118 % ---- Map solution back to original model ----
    +0119 irrevActive = sol.full(1:nIrrev) >= epsilon / 2;
    +0120 activeRxns  = false(numel(model.rxns), 1);
    +0121 for k = 1:nIrrev
    +0122     if irrevActive(k)
    +0123         activeRxns(irrev2rev(k)) = true;
    +0124     end
    +0125 end
    +0126 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/gapfilling/gapFillFastLP.html b/docs/html/gapfilling/gapFillFastLP.html new file mode 100644 index 00000000..61c38098 --- /dev/null +++ b/docs/html/gapfilling/gapFillFastLP.html @@ -0,0 +1,263 @@ + + + + Description of gapFillFastLP + + + + + + + + + +
    Home > gapfilling > gapFillFastLP.m
    + + + +

    gapFillFastLP +

    + +

    PURPOSE ^

    +
    gapFillFastLP LP-based gap-filling (fastGapFill or swiftGapFill).
    + +

    SYNOPSIS ^

    +
    function [addedRxns, newModel, cannotConnect] = gapFillFastLP(model, universalModel, varargin)
    + +

    DESCRIPTION ^

    +
     gapFillFastLP  LP-based gap-filling (fastGapFill or swiftGapFill).
    +
    + Merges the draft model with a universal reaction database, then for each
    + blocked draft reaction calls an LP subroutine (FASTCORE or SWIFTCORE) to
    + find the minimum set of database reactions that makes the blocked reaction
    + carry flux. The union of all activated database reactions across all blocked
    + reactions is returned as addedRxns.
    +
    + Based on fastGapFill (Thiele et al. 2014, PLoS Comput Biol 10:e1003515)
    + and swiftGapFill (Tefagh & Boyd 2020, BMC Bioinformatics 21:23).
    +
    + Parameters
    + ----------
    + model : struct
    +     draft RAVEN model to be gap-filled.
    + universalModel : struct
    +     universal reaction database (RAVEN model), for example built with
    +     getKEGGModelForOrganism with broad scope.
    +
    + Name-Value Arguments
    + --------------------
    + epsilon : double
    +     minimum flux threshold for a reaction to be considered active
    +     (default 1e-4).
    + variant : char
    +     the LP subroutine (default 'fast'):
    +
    +     - 'fast' : use the FASTCORE L1-norm LP (gapFillFastCore).
    +     - 'swift' : use the SWIFTCORE single LP (gapFillSwiftCore), which
    +       maximises the sum of the non-core fluxes. The LP is degenerate, so
    +       results can vary between runs and solvers.
    + verbose : logical
    +     true to print progress messages (default true).
    +
    + Returns
    + -------
    + addedRxns : cell
    +     reaction IDs from universalModel that should be added to rescue
    +     blocked reactions.
    + newModel : struct
    +     copy of model with addedRxns incorporated.
    + cannotConnect : cell
    +     IDs of blocked draft reactions that remain blocked after merging with
    +     the full universalModel (the database cannot rescue them).
    +
    + See also
    + --------
    + gapFillFastCore, gapFillSwiftCore, gapFillMILP, fillGaps
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    • fillGaps fillGaps Use template model(s) to fill gaps in a model.
    + + + + +

    SOURCE CODE ^

    +
    0001 function [addedRxns, newModel, cannotConnect] = gapFillFastLP(model, universalModel, varargin)
    +0002 % gapFillFastLP  LP-based gap-filling (fastGapFill or swiftGapFill).
    +0003 %
    +0004 % Merges the draft model with a universal reaction database, then for each
    +0005 % blocked draft reaction calls an LP subroutine (FASTCORE or SWIFTCORE) to
    +0006 % find the minimum set of database reactions that makes the blocked reaction
    +0007 % carry flux. The union of all activated database reactions across all blocked
    +0008 % reactions is returned as addedRxns.
    +0009 %
    +0010 % Based on fastGapFill (Thiele et al. 2014, PLoS Comput Biol 10:e1003515)
    +0011 % and swiftGapFill (Tefagh & Boyd 2020, BMC Bioinformatics 21:23).
    +0012 %
    +0013 % Parameters
    +0014 % ----------
    +0015 % model : struct
    +0016 %     draft RAVEN model to be gap-filled.
    +0017 % universalModel : struct
    +0018 %     universal reaction database (RAVEN model), for example built with
    +0019 %     getKEGGModelForOrganism with broad scope.
    +0020 %
    +0021 % Name-Value Arguments
    +0022 % --------------------
    +0023 % epsilon : double
    +0024 %     minimum flux threshold for a reaction to be considered active
    +0025 %     (default 1e-4).
    +0026 % variant : char
    +0027 %     the LP subroutine (default 'fast'):
    +0028 %
    +0029 %     - 'fast' : use the FASTCORE L1-norm LP (gapFillFastCore).
    +0030 %     - 'swift' : use the SWIFTCORE single LP (gapFillSwiftCore), which
    +0031 %       maximises the sum of the non-core fluxes. The LP is degenerate, so
    +0032 %       results can vary between runs and solvers.
    +0033 % verbose : logical
    +0034 %     true to print progress messages (default true).
    +0035 %
    +0036 % Returns
    +0037 % -------
    +0038 % addedRxns : cell
    +0039 %     reaction IDs from universalModel that should be added to rescue
    +0040 %     blocked reactions.
    +0041 % newModel : struct
    +0042 %     copy of model with addedRxns incorporated.
    +0043 % cannotConnect : cell
    +0044 %     IDs of blocked draft reactions that remain blocked after merging with
    +0045 %     the full universalModel (the database cannot rescue them).
    +0046 %
    +0047 % See also
    +0048 % --------
    +0049 % gapFillFastCore, gapFillSwiftCore, gapFillMILP, fillGaps
    +0050 
    +0051 % ---- Parse optional arguments ----
    +0052 p = inputParser();
    +0053 addParameter(p, 'epsilon', 1e-4,   @isnumeric);
    +0054 addParameter(p, 'variant', 'fast', @ischar);
    +0055 addParameter(p, 'verbose', true,   @islogical);
    +0056 parse(p, varargin{:});
    +0057 epsilon = p.Results.epsilon;
    +0058 variant = lower(p.Results.variant);
    +0059 verbose = p.Results.verbose;
    +0060 
    +0061 % ---- Initialise outputs ----
    +0062 addedRxns    = {};
    +0063 cannotConnect = {};
    +0064 newModel     = model;
    +0065 
    +0066 % ---- Identify blocked reactions in the draft model ----
    +0067 blocked = find(~haveFlux(model, epsilon));
    +0068 if isempty(blocked)
    +0069     if verbose
    +0070         fprintf('gapFillFastLP: all reactions carry flux; nothing to repair.\n');
    +0071     end
    +0072     return;
    +0073 end
    +0074 nBlocked = numel(blocked);
    +0075 if verbose
    +0076     fprintf('gapFillFastLP: %d blocked reaction(s) found in draft model.\n', nBlocked);
    +0077 end
    +0078 
    +0079 % ---- Merge draft + universal database ----
    +0080 mergedModel = mergeModels({model; universalModel});
    +0081 
    +0082 % For each draft reaction, find its position in the merged model.
    +0083 [inMerged, draftPosInMerged] = ismember(model.rxns, mergedModel.rxns);
    +0084 if any(~inMerged)
    +0085     warning('gapFillFastLP: %d draft reaction(s) not found in merged model — skipped.', sum(~inMerged));
    +0086 end
    +0087 
    +0088 % ---- Determine which blocked reactions can carry flux in the merged model ----
    +0089 mergedHasFlux = haveFlux(mergedModel, epsilon);
    +0090 draftCanCarry = mergedHasFlux(draftPosInMerged);   % indexed by draft position
    +0091 
    +0092 % Blocked reactions that still cannot carry flux even with the full DB.
    +0093 blockedCanCarry = draftCanCarry(blocked);
    +0094 cannotConnectIdx = blocked(~blockedCanCarry);
    +0095 cannotConnect    = model.rxns(cannotConnectIdx);
    +0096 rescuable        = blocked(blockedCanCarry);
    +0097 
    +0098 if verbose && ~isempty(cannotConnect)
    +0099     fprintf('gapFillFastLP: %d reaction(s) cannot be rescued by the universal database.\n', ...
    +0100         numel(cannotConnect));
    +0101 end
    +0102 
    +0103 if isempty(rescuable)
    +0104     if verbose
    +0105         fprintf('gapFillFastLP: no rescuable blocked reactions; nothing to add.\n');
    +0106     end
    +0107     return;
    +0108 end
    +0109 if verbose
    +0110     fprintf('gapFillFastLP: %d/%d blocked reaction(s) are rescuable; running %s...\n', ...
    +0111         numel(rescuable), nBlocked, variant);
    +0112 end
    +0113 
    +0114 % ---- For each rescuable blocked reaction, run the LP subroutine ----
    +0115 % Accumulate which merged-model reactions are activated.
    +0116 isUniversal   = ~ismember(mergedModel.rxns, model.rxns);
    +0117 candidateSet  = false(numel(mergedModel.rxns), 1);
    +0118 
    +0119 nSkipped = 0;
    +0120 for i = 1:numel(rescuable)
    +0121     coreIdxInMerged = draftPosInMerged(rescuable(i));
    +0122     if coreIdxInMerged == 0
    +0123         nSkipped = nSkipped + 1;
    +0124         continue;
    +0125     end
    +0126 
    +0127     if strcmp(variant, 'swift')
    +0128         active = gapFillSwiftCore(mergedModel, coreIdxInMerged, epsilon);
    +0129     else
    +0130         active = gapFillFastCore(mergedModel, coreIdxInMerged, epsilon);
    +0131     end
    +0132 
    +0133     if any(active)
    +0134         candidateSet = candidateSet | active;
    +0135     else
    +0136         nSkipped = nSkipped + 1;
    +0137         if verbose
    +0138             fprintf('gapFillFastLP: LP infeasible for %s; skipping.\n', model.rxns{rescuable(i)});
    +0139         end
    +0140     end
    +0141 end
    +0142 
    +0143 if verbose && nSkipped > 0
    +0144     fprintf('gapFillFastLP: %d reaction(s) skipped (infeasible LP).\n', nSkipped);
    +0145 end
    +0146 
    +0147 % ---- Extract universal reactions that appear in any solution ----
    +0148 addedMask = candidateSet & isUniversal;
    +0149 addedRxns = mergedModel.rxns(addedMask);
    +0150 
    +0151 if isempty(addedRxns)
    +0152     if verbose
    +0153         fprintf('gapFillFastLP: no universal reactions needed (all gaps already resolved).\n');
    +0154     end
    +0155     return;
    +0156 end
    +0157 
    +0158 % ---- Build new model: draft + selected universal reactions ----
    +0159 toRemove = find(isUniversal & ~addedMask);
    +0160 if ~isempty(toRemove)
    +0161     newModel = removeReactions(mergedModel, mergedModel.rxns(toRemove));
    +0162 else
    +0163     newModel = mergedModel;
    +0164 end
    +0165 
    +0166 if verbose
    +0167     fprintf('gapFillFastLP: added %d reaction(s) from universal database.\n', numel(addedRxns));
    +0168 end
    +0169 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/gapfilling/gapFillMILP.html b/docs/html/gapfilling/gapFillMILP.html new file mode 100644 index 00000000..33903605 --- /dev/null +++ b/docs/html/gapfilling/gapFillMILP.html @@ -0,0 +1,366 @@ + + + + Description of gapFillMILP + + + + + + + + + +
    Home > gapfilling > gapFillMILP.m
    + + + +

    gapFillMILP +

    + +

    PURPOSE ^

    +
    gapFillMILP Objective-based gap-filling via a global growth-floor MILP.
    + +

    SYNOPSIS ^

    +
    function [addedRxns, newModel, exitFlag] = gapFillMILP(model, universalModel, varargin)
    + +

    DESCRIPTION ^

    +
     gapFillMILP  Objective-based gap-filling via a global growth-floor MILP.
    +
    + Finds the smallest set of reactions from a universal database that makes
    + the model's objective (biomass) >= minGrowth, following the database
    + repair of Kumar et al. 2007 (BMC Bioinformatics 8:212,
    + doi:10.1186/1471-2105-8-212). Reactions already in the draft model keep
    + their bounds, including a wrong reaction direction.
    +
    + The universal model contains all candidate reactions. Exchange and
    + transport reactions are candidates only if the universal model includes
    + them.
    +
    + The function solves a single-level MILP that minimises the number of added
    + reactions subject to the objective floor. Compared with gapFillFastLP, the
    + MILP takes more computation time.
    +
    + Parameters
    + ----------
    + model : struct
    +     draft RAVEN model to be gap-filled.
    + universalModel : struct
    +     universal reaction database (RAVEN model).
    +
    + Name-Value Arguments
    + --------------------
    + minGrowth : double
    +     minimum objective value to achieve. If empty, FBA on the merged model
    +     gives the maximum objective value and 10% of it is the floor
    +     (default []).
    + bigM : double
    +     Big-M constant for the coupling constraints of universal reactions
    +     with an infinite bound. A finite bound is used as it is (default 1000).
    + params : struct
    +     solver parameter struct passed to optimizeProb. Fields such as
    +     params.TimeLimit and params.intTol tune the MILP. If empty, intTol is
    +     1e-9 and TimeLimit is 600 (default []).
    + verbose : logical
    +     true to print progress messages (default true).
    +
    + Returns
    + -------
    + addedRxns : cell
    +     reaction IDs from universalModel that are selected by the MILP.
    + newModel : struct
    +     model with addedRxns incorporated.
    + exitFlag : double
    +     1 = the MILP returned a solution, -1 = no gap-filling result: the FBA
    +     or MILP is infeasible, the objective of the merged model is not
    +     positive, or the solver stops without a solution.
    +
    + Notes
    + -----
    + The MILP is solved with the solver set by setRavenSolver. RAVEN's bundled
    + GLPK solves it for small models. The RAVEN:glpkMILP warning issued with
    + GLPK states that GLPK works for small problems and that Gurobi is much
    + faster and more reliable for larger ones (setRavenSolver('gurobi')).
    +
    + See also
    + --------
    + gapFillFastLP, gapFillTopological, fillGaps
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    • fillGaps fillGaps Use template model(s) to fill gaps in a model.
    + + + + +

    SOURCE CODE ^

    +
    0001 function [addedRxns, newModel, exitFlag] = gapFillMILP(model, universalModel, varargin)
    +0002 % gapFillMILP  Objective-based gap-filling via a global growth-floor MILP.
    +0003 %
    +0004 % Finds the smallest set of reactions from a universal database that makes
    +0005 % the model's objective (biomass) >= minGrowth, following the database
    +0006 % repair of Kumar et al. 2007 (BMC Bioinformatics 8:212,
    +0007 % doi:10.1186/1471-2105-8-212). Reactions already in the draft model keep
    +0008 % their bounds, including a wrong reaction direction.
    +0009 %
    +0010 % The universal model contains all candidate reactions. Exchange and
    +0011 % transport reactions are candidates only if the universal model includes
    +0012 % them.
    +0013 %
    +0014 % The function solves a single-level MILP that minimises the number of added
    +0015 % reactions subject to the objective floor. Compared with gapFillFastLP, the
    +0016 % MILP takes more computation time.
    +0017 %
    +0018 % Parameters
    +0019 % ----------
    +0020 % model : struct
    +0021 %     draft RAVEN model to be gap-filled.
    +0022 % universalModel : struct
    +0023 %     universal reaction database (RAVEN model).
    +0024 %
    +0025 % Name-Value Arguments
    +0026 % --------------------
    +0027 % minGrowth : double
    +0028 %     minimum objective value to achieve. If empty, FBA on the merged model
    +0029 %     gives the maximum objective value and 10% of it is the floor
    +0030 %     (default []).
    +0031 % bigM : double
    +0032 %     Big-M constant for the coupling constraints of universal reactions
    +0033 %     with an infinite bound. A finite bound is used as it is (default 1000).
    +0034 % params : struct
    +0035 %     solver parameter struct passed to optimizeProb. Fields such as
    +0036 %     params.TimeLimit and params.intTol tune the MILP. If empty, intTol is
    +0037 %     1e-9 and TimeLimit is 600 (default []).
    +0038 % verbose : logical
    +0039 %     true to print progress messages (default true).
    +0040 %
    +0041 % Returns
    +0042 % -------
    +0043 % addedRxns : cell
    +0044 %     reaction IDs from universalModel that are selected by the MILP.
    +0045 % newModel : struct
    +0046 %     model with addedRxns incorporated.
    +0047 % exitFlag : double
    +0048 %     1 = the MILP returned a solution, -1 = no gap-filling result: the FBA
    +0049 %     or MILP is infeasible, the objective of the merged model is not
    +0050 %     positive, or the solver stops without a solution.
    +0051 %
    +0052 % Notes
    +0053 % -----
    +0054 % The MILP is solved with the solver set by setRavenSolver. RAVEN's bundled
    +0055 % GLPK solves it for small models. The RAVEN:glpkMILP warning issued with
    +0056 % GLPK states that GLPK works for small problems and that Gurobi is much
    +0057 % faster and more reliable for larger ones (setRavenSolver('gurobi')).
    +0058 %
    +0059 % See also
    +0060 % --------
    +0061 % gapFillFastLP, gapFillTopological, fillGaps
    +0062 
    +0063 % ---- Parse optional arguments ----
    +0064 p = inputParser();
    +0065 addParameter(p, 'minGrowth', [],  @(x) isempty(x) || isnumeric(x));
    +0066 addParameter(p, 'bigM',      1000,      @isnumeric);
    +0067 addParameter(p, 'params',    [],        @(x) isempty(x) || isstruct(x));
    +0068 addParameter(p, 'verbose',   true,      @islogical);
    +0069 parse(p, varargin{:});
    +0070 minGrowth = p.Results.minGrowth;
    +0071 M         = p.Results.bigM;
    +0072 params    = p.Results.params;
    +0073 verbose   = p.Results.verbose;
    +0074 
    +0075 % ---- Initialise outputs ----
    +0076 addedRxns = {};
    +0077 newModel  = model;
    +0078 exitFlag  = -1;
    +0079 
    +0080 % Check that the model has an objective
    +0081 if all(model.c == 0)
    +0082     error('gapFillMILP: model has no objective function. Set model.c.');
    +0083 end
    +0084 
    +0085 % ---- Merge draft + universal database ----
    +0086 mergedModel = mergeModels({model; universalModel});
    +0087 nMergedMets = numel(mergedModel.mets);
    +0088 nMergedRxns = numel(mergedModel.rxns);
    +0089 
    +0090 % Identify draft reactions in the merged model
    +0091 isDraft = ismember(mergedModel.rxns, model.rxns);
    +0092 isUniv  = ~isDraft;
    +0093 
    +0094 % Universal reactions that can carry flux (non-trivially blocked)
    +0095 canCarry = (mergedModel.ub > 0 | mergedModel.lb < 0);
    +0096 isUniv   = isUniv & canCarry;
    +0097 univIdx  = find(isUniv);     % positions in merged model
    +0098 nUniv    = numel(univIdx);
    +0099 
    +0100 if verbose
    +0101     fprintf('gapFillMILP: merged model has %d mets, %d rxns (%d draft, %d universal).\n', ...
    +0102         nMergedMets, nMergedRxns, sum(isDraft), nUniv);
    +0103     fprintf('gapFillMILP: %d database candidates.\n', nUniv);
    +0104 end
    +0105 
    +0106 % ---- Determine minGrowth ----
    +0107 if isempty(minGrowth)
    +0108     % Run FBA on merged model (unconstrained) to get max growth.
    +0109     probFBA.A      = mergedModel.S;
    +0110     probFBA.a      = probFBA.A;
    +0111     probFBA.b      = zeros(nMergedMets, 1);
    +0112     probFBA.csense = repmat('E', 1, nMergedMets);
    +0113     probFBA.c      = -mergedModel.c;   % negate: osense=1 minimises
    +0114     probFBA.osense = 1;
    +0115     probFBA.lb     = mergedModel.lb;
    +0116     probFBA.ub     = mergedModel.ub;
    +0117     probFBA.vartype = repmat('C', 1, nMergedRxns);
    +0118     solFBA = optimizeProb(probFBA, params, false);
    +0119     if ~checkSolution(solFBA)
    +0120         if verbose
    +0121             fprintf('gapFillMILP: FBA on merged model is infeasible — cannot gap-fill.\n');
    +0122         end
    +0123         return;
    +0124     end
    +0125     maxGrowth = mergedModel.c' * solFBA.full(1:nMergedRxns);
    +0126     if maxGrowth <= 0
    +0127         if verbose
    +0128             fprintf('gapFillMILP: merged model objective is non-positive (%.4g). Cannot gap-fill.\n', ...
    +0129                 maxGrowth);
    +0130         end
    +0131         return;
    +0132     end
    +0133     minGrowth = 0.1 * maxGrowth;
    +0134     if verbose
    +0135         fprintf('gapFillMILP: setting minGrowth = %.4g (10%% of max %.4g).\n', minGrowth, maxGrowth);
    +0136     end
    +0137 end
    +0138 
    +0139 % ---- Build MILP ----
    +0140 % Variable ordering:
    +0141 %   cols 1 : nMergedRxns          → v (all fluxes, continuous)
    +0142 %   cols nMergedRxns+1 : end      → y_db (binary, universal reactions)
    +0143 nVar = nMergedRxns + nUniv;
    +0144 
    +0145 % --- Variable bounds ---
    +0146 lb = mergedModel.lb;
    +0147 ub = mergedModel.ub;
    +0148 
    +0149 % Universal reactions: allow them to be forced to 0 (extend lb toward 0)
    +0150 lb(univIdx) = min(mergedModel.lb(univIdx), 0);
    +0151 
    +0152 % Binary variable bounds
    +0153 lb = [lb; zeros(nUniv, 1)];
    +0154 ub = [ub; ones(nUniv, 1)];
    +0155 
    +0156 % --- Objective: number of added universal reactions ---
    +0157 c = zeros(nVar, 1);
    +0158 c(nMergedRxns + 1 : end) = 1;
    +0159 
    +0160 % --- Constraint blocks ---
    +0161 % (1) Steady state: S * v = 0   [nMergedMets rows, 'E']
    +0162 A_SS = [mergedModel.S, sparse(nMergedMets, nUniv)];
    +0163 
    +0164 % (2) Growth floor: c' * v >= minGrowth   [1 row, 'G']
    +0165 A_growth = [sparse(mergedModel.c'), sparse(1, nUniv)];
    +0166 
    +0167 % (3) Universal upper coupling: v_k - ub_k * y_db_k <= 0   [nUniv rows, 'L']
    +0168 %     (forces v_k <= 0 when y_db_k = 0)
    +0169 % Coupling coefficients are the reaction bounds. An infinite bound is replaced
    +0170 % by the Big-M constant: an unbounded universal reaction (ub = Inf, RAVEN's own
    +0171 % default when no upper-bound annotation is set) would otherwise put an Inf
    +0172 % coefficient into the constraint matrix, and Inf*0 (y_db_k = 0) is NaN, not 0.
    +0173 yDbCols = nMergedRxns + (1:nUniv);
    +0174 uniUb = mergedModel.ub(univIdx);
    +0175 uniUb(isinf(uniUb)) = M;
    +0176 uniLb = mergedModel.lb(univIdx);
    +0177 uniLb(isinf(uniLb)) = -M;
    +0178 A_uniUp = sparse(...
    +0179     [1:nUniv, 1:nUniv], ...
    +0180     [univIdx(:)', yDbCols], ...
    +0181     [ones(1, nUniv), -uniUb'], ...
    +0182     nUniv, nVar);
    +0183 
    +0184 % (4) Universal lower coupling: v_k - lb_k * y_db_k >= 0   [nUniv rows, 'G']
    +0185 %     (forces v_k >= 0 when y_db_k = 0; together with (3): v_k = 0 when y_db_k = 0)
    +0186 A_uniLo = sparse(...
    +0187     [1:nUniv, 1:nUniv], ...
    +0188     [univIdx(:)', yDbCols], ...
    +0189     [ones(1, nUniv), -uniLb'], ...
    +0190     nUniv, nVar);
    +0191 
    +0192 % --- Assemble ---
    +0193 prob.A = [A_SS; A_growth; A_uniUp; A_uniLo];
    +0194 prob.a = prob.A;
    +0195 prob.b = [zeros(nMergedMets, 1); minGrowth; zeros(2 * nUniv, 1)];
    +0196 prob.csense = [repmat('E', 1, nMergedMets), 'G', ...
    +0197                repmat('L', 1, nUniv), ...
    +0198                repmat('G', 1, nUniv)];
    +0199 prob.c      = c;
    +0200 prob.osense = 1;   % minimise the number of added reactions
    +0201 prob.lb     = lb;
    +0202 prob.ub     = ub;
    +0203 prob.vartype = repmat('C', 1, nVar);
    +0204 prob.vartype(nMergedRxns + 1 : end) = 'B';   % y_db is binary
    +0205 
    +0206 if isempty(params)
    +0207     params.intTol   = 1e-9;
    +0208     params.TimeLimit = 600;
    +0209 end
    +0210 
    +0211 % ---- Solve ----
    +0212 if verbose
    +0213     fprintf('gapFillMILP: solving MILP (%d vars, %d binary, %d constraints)...\n', ...
    +0214         nVar, nUniv, size(prob.A, 1));
    +0215 end
    +0216 sol = optimizeProb(prob, params, verbose);
    +0217 
    +0218 if ~checkSolution(sol)
    +0219     if verbose
    +0220         fprintf('gapFillMILP: MILP infeasible or timed out.\n');
    +0221     end
    +0222     exitFlag = -1;
    +0223     return;
    +0224 end
    +0225 
    +0226 % ---- Extract solution ----
    +0227 binThreshold = 1e-3;
    +0228 yDb = sol.full(nMergedRxns + 1 : end);
    +0229 dbSelected = univIdx(yDb > binThreshold);
    +0230 
    +0231 % Map back to original IDs. dbSelected is already restricted to universal
    +0232 % reactions via univIdx/isDraft above (an id-membership test against
    +0233 % model.rxns, not a name it could have been renamed away from), so no
    +0234 % further filtering against universalModel.rxns is needed here -- doing so
    +0235 % would silently drop a universal reaction that mergeModels renamed
    +0236 % because its id collided with one already in the draft model.
    +0237 addedRxns = mergedModel.rxns(dbSelected);
    +0238 
    +0239 if verbose
    +0240     fprintf('gapFillMILP: added %d universal reaction(s).\n', numel(addedRxns));
    +0241 end
    +0242 
    +0243 % ---- Build new model ----
    +0244 newModel = model;
    +0245 
    +0246 % Add universal reactions. Extracted from mergedModel by the dbSelected
    +0247 % indices directly, not by looking addedRxns' names back up in
    +0248 % universalModel: that lookup fails whenever mergeModels renamed one of
    +0249 % them because its id collided with one already in the draft model, in
    +0250 % which case setdiff would keep the (unrenamed) original in universalModel
    +0251 % marked for removal, silently dropping the very reaction just selected.
    +0252 if ~isempty(dbSelected)
    +0253     toRemove = true(nMergedRxns, 1);
    +0254     toRemove(dbSelected) = false;
    +0255     trimmedUniv = removeReactions(mergedModel, toRemove);
    +0256     newModel = mergeModels({newModel; trimmedUniv});
    +0257 end
    +0258 
    +0259 exitFlag = 1;
    +0260 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/gapfilling/gapFillSwiftCore.html b/docs/html/gapfilling/gapFillSwiftCore.html similarity index 80% rename from doc/gapfilling/gapFillSwiftCore.html rename to docs/html/gapfilling/gapFillSwiftCore.html index c429f0d8..d103b518 100644 --- a/doc/gapfilling/gapFillSwiftCore.html +++ b/docs/html/gapfilling/gapFillSwiftCore.html @@ -31,14 +31,13 @@

    DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 </ul>
 This function is called by:
 <ul style= -
  • gapFillFastLP gapFillFastLP LP-based gap-filling (fastGapFill / swiftGapFill).
  • +
  • gapFillFastLP gapFillFastLP LP-based gap-filling (fastGapFill or swiftGapFill).
  • @@ -81,33 +81,33 @@

    SOURCE CODE ^% 0004 % Single-LP alternative to gapFillFastCore. Maximises the sum of non-core 0005 % fluxes while enforcing that every core reaction carries at least epsilon -0006 % flux. Any reaction with non-zero flux in the solution is "consistent" -0007 % with the core. +0006 % flux. Any reaction with a flux of at least epsilon / 2 in the solution is +0007 % "consistent" with the core. 0008 % -0009 % Because the maximisation objective makes the LP degenerate (many optima -0010 % exist that differ only in which non-core reactions carry flux), the -0011 % returned active set can vary between LP solvers and between runs with -0012 % slightly different numerical conditions. This is the expected behaviour -0013 % of SWIFTCORE (Tefagh & Boyd 2020). -0014 % -0015 % Based on SWIFTCORE (Tefagh & Boyd 2020, BMC Bioinformatics 21:23). -0016 % -0017 % Parameters -0018 % ---------- -0019 % model : struct -0020 % a RAVEN model structure. -0021 % coreRxns : cell or logical or double -0022 % core reaction set: cell array of reaction IDs, a logical vector, or a -0023 % vector of reaction indices into model.rxns. All core reactions are -0024 % forced to carry flux >= epsilon in the LP solution. -0025 % epsilon : double -0026 % minimum flux threshold for a reaction to be considered active. -0027 % -0028 % Returns -0029 % ------- -0030 % activeRxns : logical -0031 % logical vector (length numel(model.rxns)) where true indicates the -0032 % reaction carries flux in the solution. Empty on infeasibility. +0009 % The maximisation objective makes the LP degenerate: many optima differ only +0010 % in which non-core reactions carry flux. The returned active set can +0011 % therefore vary between LP solvers and between runs with slightly different +0012 % numerical conditions. +0013 % +0014 % Based on SWIFTCORE (Tefagh & Boyd 2020, BMC Bioinformatics 21:23). +0015 % +0016 % Parameters +0017 % ---------- +0018 % model : struct +0019 % a RAVEN model structure. +0020 % coreRxns : cell or logical or double +0021 % core reaction set: cell array of reaction IDs, a logical vector, or a +0022 % vector of reaction indices into model.rxns. All core reactions are +0023 % forced to carry flux >= epsilon in the LP solution. +0024 % epsilon : double +0025 % minimum flux threshold for a reaction to be considered active. +0026 % +0027 % Returns +0028 % ------- +0029 % activeRxns : logical +0030 % logical vector (length numel(model.rxns)) where true indicates that the +0031 % reaction carries flux in the solution. All elements are false if the LP +0032 % is infeasible. 0033 % 0034 % See also 0035 % -------- diff --git a/docs/html/gapfilling/gapFillTopological.html b/docs/html/gapfilling/gapFillTopological.html new file mode 100644 index 00000000..7d9f1963 --- /dev/null +++ b/docs/html/gapfilling/gapFillTopological.html @@ -0,0 +1,381 @@ + + + + Description of gapFillTopological + + + + + + + + + +
    Home > gapfilling > gapFillTopological.m
    + + + +

    gapFillTopological +

    + +

    PURPOSE ^

    +
    gapFillTopological BFS metabolite-producibility pre-screening.
    + +

    SYNOPSIS ^

    +
    function result = gapFillTopological(model, universalModel, varargin)
    + +

    DESCRIPTION ^

    +
     gapFillTopological  BFS metabolite-producibility pre-screening.
    +
    + Identifies which metabolites in the draft model cannot be produced from a
    + given set of seed metabolites (medium), using a graph-based fixed-point
    + "scope" computation that needs no solver. For each unreachable metabolite,
    + finds candidate reactions in the universal database that produce it
    + directly.
    +
    + The step prunes the universal database before a gap-filling solve. It is
    + inspired by the topological analysis used in Meneco (Prigent et al. 2017,
    + PLoS Comput Biol).
    +
    + Algorithm
    + ---------
    + Starting from seed metabolites (available in the medium), iteratively
    + mark reactions as "fireable" when all their substrates are reachable,
    + then mark their products as newly reachable. Continue until no new
    + metabolite is reached (fixed point). Reversible reactions (lb < 0) are
    + also considered in their reverse direction.
    +
    + Parameters
    + ----------
    + model : struct
    +     draft RAVEN model.
    + universalModel : struct
    +     universal reaction database (RAVEN model).
    +
    + Name-Value Arguments
    + --------------------
    + seeds : cell
    +     metabolite IDs available from the medium. If empty, the seeds are the
    +     metabolites of every exchange reaction whose bounds let it supply the
    +     metabolite, that is getExchangeRxns 'uptake' plus 'reverse'
    +     (default []).
    + targets : cell
    +     metabolite IDs that should be produced. If empty, the targets are every
    +     metabolite of the objective (biomass) reaction, substrates and
    +     products; the first one when there are several objective reactions.
    +     Without targets and without an objective reaction the function raises
    +     RAVEN:badInput (default []).
    + verbose : logical
    +     true to print a summary of the results (default true).
    +
    + Returns
    + -------
    + result : struct
    +     .reachableMets: logical vector (length nMets) marking producible mets
    +     .blockedMets: cell array of metabolite IDs that cannot be reached
    +     .candidateRxns: n-by-1 cell array of universal rxn ID lists, one cell
    +         per blocked metabolite (same order as blockedMets)
    +     .pruningFraction: fraction of universal reactions pruned (not
    +         candidates for any blocked metabolite)
    +
    + See also
    + --------
    + gapFillFastLP, gapFillMILP, fillGaps
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    • fillGaps fillGaps Use template model(s) to fill gaps in a model.
    + + + + +

    SOURCE CODE ^

    +
    0001 function result = gapFillTopological(model, universalModel, varargin)
    +0002 % gapFillTopological  BFS metabolite-producibility pre-screening.
    +0003 %
    +0004 % Identifies which metabolites in the draft model cannot be produced from a
    +0005 % given set of seed metabolites (medium), using a graph-based fixed-point
    +0006 % "scope" computation that needs no solver. For each unreachable metabolite,
    +0007 % finds candidate reactions in the universal database that produce it
    +0008 % directly.
    +0009 %
    +0010 % The step prunes the universal database before a gap-filling solve. It is
    +0011 % inspired by the topological analysis used in Meneco (Prigent et al. 2017,
    +0012 % PLoS Comput Biol).
    +0013 %
    +0014 % Algorithm
    +0015 % ---------
    +0016 % Starting from seed metabolites (available in the medium), iteratively
    +0017 % mark reactions as "fireable" when all their substrates are reachable,
    +0018 % then mark their products as newly reachable. Continue until no new
    +0019 % metabolite is reached (fixed point). Reversible reactions (lb < 0) are
    +0020 % also considered in their reverse direction.
    +0021 %
    +0022 % Parameters
    +0023 % ----------
    +0024 % model : struct
    +0025 %     draft RAVEN model.
    +0026 % universalModel : struct
    +0027 %     universal reaction database (RAVEN model).
    +0028 %
    +0029 % Name-Value Arguments
    +0030 % --------------------
    +0031 % seeds : cell
    +0032 %     metabolite IDs available from the medium. If empty, the seeds are the
    +0033 %     metabolites of every exchange reaction whose bounds let it supply the
    +0034 %     metabolite, that is getExchangeRxns 'uptake' plus 'reverse'
    +0035 %     (default []).
    +0036 % targets : cell
    +0037 %     metabolite IDs that should be produced. If empty, the targets are every
    +0038 %     metabolite of the objective (biomass) reaction, substrates and
    +0039 %     products; the first one when there are several objective reactions.
    +0040 %     Without targets and without an objective reaction the function raises
    +0041 %     RAVEN:badInput (default []).
    +0042 % verbose : logical
    +0043 %     true to print a summary of the results (default true).
    +0044 %
    +0045 % Returns
    +0046 % -------
    +0047 % result : struct
    +0048 %     .reachableMets: logical vector (length nMets) marking producible mets
    +0049 %     .blockedMets: cell array of metabolite IDs that cannot be reached
    +0050 %     .candidateRxns: n-by-1 cell array of universal rxn ID lists, one cell
    +0051 %         per blocked metabolite (same order as blockedMets)
    +0052 %     .pruningFraction: fraction of universal reactions pruned (not
    +0053 %         candidates for any blocked metabolite)
    +0054 %
    +0055 % See also
    +0056 % --------
    +0057 % gapFillFastLP, gapFillMILP, fillGaps
    +0058 
    +0059 % ---- Parse optional arguments ----
    +0060 p = inputParser();
    +0061 addParameter(p, 'seeds',   [], @(x) isempty(x) || iscell(x));
    +0062 addParameter(p, 'targets', [], @(x) isempty(x) || iscell(x));
    +0063 addParameter(p, 'verbose', true, @islogical);
    +0064 parse(p, varargin{:});
    +0065 seeds   = p.Results.seeds;
    +0066 targets = p.Results.targets;
    +0067 verbose = p.Results.verbose;
    +0068 
    +0069 nMets = numel(model.mets);
    +0070 nRxns = numel(model.rxns);
    +0071 
    +0072 % ---- Identify seed metabolites ----
    +0073 if isempty(seeds)
    +0074     % Default: metabolites that an exchange reaction can supply. Ask
    +0075     % getExchangeRxns rather than testing lb < 0 here: RAVEN writes an uptake
    +0076     % exchange either as "=> met" (addExchangeRxns 'in', so lb = 0 and a
    +0077     % positive flux supplies the metabolite) or as "met =>" with lb < 0. Only
    +0078     % the second has lb < 0, so a hand-rolled lb < 0 test silently misses
    +0079     % every 'in' exchange.
    +0080     %
    +0081     % 'uptake' alone is not enough either: it means the bounds allow *only*
    +0082     % uptake, so a reversible exchange ("met <=>") is classified 'reverse'
    +0083     % even though it supplies the metabolite just as well. The union of the
    +0084     % two is exactly "forward flux produces it, or reverse flux does", which
    +0085     % is what analyse_topology's lower_bound < 0 test means under cobra's
    +0086     % convention, where every exchange is written "met <=>".
    +0087     [~, onlyUptakeIdx] = getExchangeRxns(model, 'uptake');
    +0088     [~, reversibleIdx] = getExchangeRxns(model, 'reverse');
    +0089     uptakeIdx = union(onlyUptakeIdx, reversibleIdx);
    +0090     if isempty(uptakeIdx)
    +0091         warning(['gapFillTopological: no uptake exchange reactions found. ' ...
    +0092             'Provide seeds manually via the ''seeds'' option.']);
    +0093         seedMetIdx = [];
    +0094     else
    +0095         [seedRows, ~] = find(model.S(:, uptakeIdx) ~= 0);
    +0096         seedMetIdx = unique(seedRows);
    +0097     end
    +0098 else
    +0099     [~, seedMetIdx] = ismember(seeds, model.mets);
    +0100     seedMetIdx = seedMetIdx(seedMetIdx > 0);
    +0101 end
    +0102 
    +0103 % ---- Identify target metabolites ----
    +0104 if isempty(targets)
    +0105     % Default: every metabolite of the objective (biomass) reaction
    +0106     objRxns = find(model.c ~= 0);
    +0107     if isempty(objRxns)
    +0108         error('RAVEN:badInput', ['gapFillTopological: no objective function defined. ' ...
    +0109             'Provide targets manually via the ''targets'' option.']);
    +0110     end
    +0111     biomassIdx = objRxns(1);
    +0112     targetMetIdx = find(model.S(:, biomassIdx) ~= 0);
    +0113 else
    +0114     [~, targetMetIdx] = ismember(targets, model.mets);
    +0115     targetMetIdx = targetMetIdx(targetMetIdx > 0);
    +0116 end
    +0117 
    +0118 % ---- Build adjacency lists ----
    +0119 % subOf{m}  = reaction indices where metabolite m is a substrate (S < 0)
    +0120 % prodOf{m} = reaction indices where metabolite m is a product  (S > 0)
    +0121 % rxnSubs{j} = substrate metabolite indices of reaction j
    +0122 % rxnProds{j} = product metabolite indices of reaction j
    +0123 %
    +0124 % Use find() on the sparse S directly to avoid materialising a dense logical
    +0125 % matrix: extract all nonzeros then partition by sign.
    +0126 [allRows, allCols, allVals] = find(model.S);
    +0127 subMask    = allVals < 0;
    +0128 proMask    = allVals > 0;
    +0129 subMetRows = allRows(subMask);
    +0130 subRxnCols = allCols(subMask);
    +0131 proMetRows = allRows(proMask);
    +0132 proRxnCols = allCols(proMask);
    +0133 
    +0134 subOf  = cell(nMets, 1);
    +0135 prodOf = cell(nMets, 1);
    +0136 for k = 1:numel(subMetRows)
    +0137     subOf{subMetRows(k)}(end+1) = subRxnCols(k);
    +0138 end
    +0139 for k = 1:numel(proMetRows)
    +0140     prodOf{proMetRows(k)}(end+1) = proRxnCols(k);
    +0141 end
    +0142 
    +0143 rxnSubs  = cell(nRxns, 1);
    +0144 rxnProds = cell(nRxns, 1);
    +0145 for k = 1:numel(subMetRows)
    +0146     rxnSubs{subRxnCols(k)}(end+1) = subMetRows(k);
    +0147 end
    +0148 for k = 1:numel(proMetRows)
    +0149     rxnProds{proRxnCols(k)}(end+1) = proMetRows(k);
    +0150 end
    +0151 
    +0152 isRev = model.lb < 0;
    +0153 
    +0154 % ---- BFS scope computation ----
    +0155 % Uses a countdown approach: a reaction fires when all its substrates are
    +0156 % reachable. Each time a metabolite becomes reachable, decrement the
    +0157 % substrate count of all reactions it participates in.
    +0158 subCountFwd = cellfun(@numel, rxnSubs);   % remaining unreachable substrates (fwd)
    +0159 subCountRev = cellfun(@numel, rxnProds);  % remaining unreachable products  (rev)
    +0160 
    +0161 reachable = false(nMets, 1);
    +0162 firedFwd  = false(nRxns, 1);
    +0163 firedRev  = false(nRxns, 1);
    +0164 
    +0165 % Initialize queue with seed metabolites
    +0166 queue  = seedMetIdx(:)';
    +0167 qHead  = 1;
    +0168 reachable(seedMetIdx) = true;
    +0169 
    +0170 % Reactions with no substrates have nothing to wait for and fire straight
    +0171 % away. They are in no subOf list, so the countdown below never reaches them
    +0172 % and their products would otherwise stay unreachable forever. This is where
    +0173 % "=> met" uptake exchanges enter the scope.
    +0174 for j = find(subCountFwd == 0)'
    +0175     firedFwd(j) = true;
    +0176     for p = rxnProds{j}
    +0177         if ~reachable(p)
    +0178             reachable(p) = true;
    +0179             queue(end+1) = p; %#ok<AGROW>
    +0180         end
    +0181     end
    +0182 end
    +0183 
    +0184 while qHead <= numel(queue)
    +0185     m = queue(qHead);
    +0186     qHead = qHead + 1;
    +0187 
    +0188     % --- Forward direction ---
    +0189     % m is a substrate of rxn j (S(m,j) < 0)
    +0190     for j = subOf{m}
    +0191         subCountFwd(j) = subCountFwd(j) - 1;
    +0192         if subCountFwd(j) == 0 && ~firedFwd(j)
    +0193             firedFwd(j) = true;
    +0194             for p = rxnProds{j}
    +0195                 if ~reachable(p)
    +0196                     reachable(p) = true;
    +0197                     queue(end+1) = p;
    +0198                 end
    +0199             end
    +0200         end
    +0201     end
    +0202 
    +0203     % --- Reverse direction (reversible reactions only) ---
    +0204     % m is a product of rxn j (S(m,j) > 0), which in reverse means m is a
    +0205     % substrate; if all products are reachable, the reaction can fire in
    +0206     % reverse and produce the substrates.
    +0207     for j = prodOf{m}
    +0208         if isRev(j)
    +0209             subCountRev(j) = subCountRev(j) - 1;
    +0210             if subCountRev(j) == 0 && ~firedRev(j)
    +0211                 firedRev(j) = true;
    +0212                 for s = rxnSubs{j}
    +0213                     if ~reachable(s)
    +0214                         reachable(s) = true;
    +0215                         queue(end+1) = s;
    +0216                     end
    +0217                 end
    +0218             end
    +0219         end
    +0220     end
    +0221 end
    +0222 
    +0223 % ---- Report blocked target metabolites ----
    +0224 targetReachable = reachable(targetMetIdx);
    +0225 blockedMetIdx   = targetMetIdx(~targetReachable);
    +0226 blockedMets     = model.mets(blockedMetIdx);
    +0227 
    +0228 if verbose
    +0229     fprintf('gapFillTopological: %d/%d target metabolites are reachable.\n', ...
    +0230         sum(targetReachable), numel(targetMetIdx));
    +0231     if ~isempty(blockedMets)
    +0232         fprintf('gapFillTopological: %d blocked target metabolite(s).\n', numel(blockedMets));
    +0233     end
    +0234 end
    +0235 
    +0236 % ---- Find candidate universal reactions for each blocked metabolite ----
    +0237 candidateRxns  = cell(numel(blockedMets), 1);
    +0238 allCandidates  = {};
    +0239 
    +0240 for k = 1:numel(blockedMetIdx)
    +0241     m     = blockedMetIdx(k);
    +0242     metId = model.mets{m};
    +0243 
    +0244     % Find this metabolite in the universal model
    +0245     univMetIdx = find(strcmp(universalModel.mets, metId));
    +0246     if isempty(univMetIdx)
    +0247         candidateRxns{k} = {};
    +0248         continue;
    +0249     end
    +0250     univMetIdx = univMetIdx(1);   % take first match; IDs should be unique
    +0251 
    +0252     univSRow = full(universalModel.S(univMetIdx, :));
    +0253 
    +0254     % Forward production: rxns with S(m,j) > 0
    +0255     fwdProdMask = univSRow > 0;
    +0256     % Reverse production: rxns with S(m,j) < 0 that can carry negative flux
    +0257     revProdMask = univSRow < 0 & (universalModel.lb' < 0);
    +0258 
    +0259     candidateMask         = fwdProdMask | revProdMask;
    +0260     candidateRxns{k}      = universalModel.rxns(candidateMask);
    +0261     allCandidates         = [allCandidates; candidateRxns{k}];
    +0262 end
    +0263 
    +0264 % ---- Pruning fraction ----
    +0265 uniqueCandidates   = unique(allCandidates);
    +0266 pruningFraction    = 1 - numel(uniqueCandidates) / numel(universalModel.rxns);
    +0267 
    +0268 if verbose
    +0269     fprintf('gapFillTopological: %d/%d universal reactions pruned (%.0f%%).\n', ...
    +0270         numel(universalModel.rxns) - numel(uniqueCandidates), ...
    +0271         numel(universalModel.rxns), pruningFraction * 100);
    +0272 end
    +0273 
    +0274 % ---- Pack output ----
    +0275 result.reachableMets     = reachable;
    +0276 result.blockedMets       = blockedMets;
    +0277 result.candidateRxns     = candidateRxns;
    +0278 result.pruningFraction   = pruningFraction;
    +0279 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/gapfilling/gapReport.html b/docs/html/gapfilling/gapReport.html similarity index 50% rename from doc/gapfilling/gapReport.html rename to docs/html/gapfilling/gapReport.html index e41dec3e..a58ce0e9 100644 --- a/doc/gapfilling/gapReport.html +++ b/docs/html/gapfilling/gapReport.html @@ -4,7 +4,7 @@ Description of gapReport - + @@ -21,22 +21,22 @@

    gapReport

    PURPOSE ^

    -
    gapReport Perform a gap analysis and summarize the results.
    +
    gapReport Perform a gap analysis and summarise the results.

    SYNOPSIS ^

    function [noFluxRxns, noFluxRxnsRelaxed, subGraphs, notProducedMets, minToConnect,neededForProductionMat, canProduceWithoutInput, canConsumeWithoutOutput,connectedFromTemplates, addedFromTemplates]=gapReport(model, varargin)

    DESCRIPTION ^

    -
     gapReport  Perform a gap analysis and summarize the results.
    +
     gapReport  Perform a gap analysis and summarise the results.
     
    - Runs a series of connectivity checks on the model - which reactions are
    + Runs a series of connectivity checks on the model: which reactions are
      blocked, how many isolated sub-networks exist, which metabolites cannot
      be produced (and the minimal set of metabolites that would need to be
    - connected to fix that), and, for models with exchange reactions, which
    - metabolites can be produced/consumed without any input/output. If
    - templateModels is supplied, it also attempts to gap-fill using those
    - models. A formatted report is printed to the command window; the same
    - results are returned as outputs for further use.
    + connected to fix that), and which metabolites can be produced without any
    + input or consumed without any output, with every boundary reaction closed.
    + If templateModels is supplied, it also attempts to gap-fill using those
    + models. A formatted report is printed to the command window, and the same
    + results are returned as outputs.
     
      Parameters
      ----------
    @@ -46,8 +46,8 @@ 

    DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    @@ -96,22 +109,24 @@

    CROSS-REFERENCE INFORMATION ^
 </ul>
 <!-- crossreference -->
 
-
+<h2><a name=SUBFUNCTIONS ^

    +

    SOURCE CODE ^

    0001 function [noFluxRxns, noFluxRxnsRelaxed, subGraphs, notProducedMets, minToConnect,...
     0002     neededForProductionMat, canProduceWithoutInput, canConsumeWithoutOutput, ...
     0003     connectedFromTemplates, addedFromTemplates]=gapReport(model, varargin)
    -0004 % gapReport  Perform a gap analysis and summarize the results.
    +0004 % gapReport  Perform a gap analysis and summarise the results.
     0005 %
    -0006 % Runs a series of connectivity checks on the model - which reactions are
    +0006 % Runs a series of connectivity checks on the model: which reactions are
     0007 % blocked, how many isolated sub-networks exist, which metabolites cannot
     0008 % be produced (and the minimal set of metabolites that would need to be
    -0009 % connected to fix that), and, for models with exchange reactions, which
    -0010 % metabolites can be produced/consumed without any input/output. If
    -0011 % templateModels is supplied, it also attempts to gap-fill using those
    -0012 % models. A formatted report is printed to the command window; the same
    -0013 % results are returned as outputs for further use.
    +0009 % connected to fix that), and which metabolites can be produced without any
    +0010 % input or consumed without any output, with every boundary reaction closed.
    +0011 % If templateModels is supplied, it also attempts to gap-fill using those
    +0012 % models. A formatted report is printed to the command window, and the same
    +0013 % results are returned as outputs.
     0014 %
     0015 % Parameters
     0016 % ----------
    @@ -121,8 +136,8 @@ 

    SOURCE CODE ^% Name-Value Arguments 0021 % -------------------- 0022 % templateModels : cell -0023 % a cell array of template models to use for gap filling. If omitted, -0024 % the automated gap-filling section is skipped. +0023 % a cell array of template models used for gap filling. If omitted, the +0024 % automated gap-filling section is skipped. 0025 % 0026 % Returns 0027 % ------- @@ -144,96 +159,127 @@

    SOURCE CODE ^% matrix where n x m is true if metabolite n allows for production of 0044 % metabolite m. 0045 % canProduceWithoutInput : cell -0046 % metabolites that could be produced even when there is no input to -0047 % the model. +0046 % metabolites that could be produced with every boundary reaction +0047 % closed. 0048 % canConsumeWithoutOutput : cell -0049 % metabolites that could be consumed even when there is no output from -0050 % the model. +0049 % metabolites that could be consumed with every boundary reaction +0050 % closed. 0051 % connectedFromTemplates : cell 0052 % the reactions that could be connected using the template models. 0053 % addedFromTemplates : struct 0054 % the reactions that were added from the template models and which 0055 % model they were added from. 0056 % -0057 % Examples -0058 % -------- -0059 % [noFluxRxns, noFluxRxnsRelaxed, subGraphs, notProducedMets, ... -0060 % minToConnect, neededForProductionMat, connectedFromTemplates, ... -0061 % addedFromTemplates] = gapReport(model, templateModels); -0062 -0063 p=parseRAVENargs(varargin, {'templateModels',[]}); -0064 templateModels=p.templateModels; -0065 -0066 if isempty(templateModels) -0067 templateModels=[]; -0068 connectedFromTemplates=[]; -0069 addedFromTemplates=[]; -0070 end -0071 -0072 %Escapes model text before it is spliced into an fprintf template, since -0073 %a literal "%" in e.g. a model id or metabolite name would otherwise be -0074 %misread as a format directive and swallow the remainder of the line. -0075 esc=@(s) strrep(s,'%','%%'); -0076 -0077 fprintf(['Gap analysis for ' esc(model.id) ' - ' esc(model.name) '\n\n']); -0078 if isfield(model,'unconstrained') -0079 calculateINOUT=true; -0080 closedModel=model; -0081 model=simplifyModel(model); -0082 else -0083 canConsumeWithoutOutput={}; -0084 canProduceWithoutInput={}; -0085 calculateINOUT=false; -0086 end -0087 -0088 model2=model; -0089 model2.b=[model2.b inf(numel(model2.mets),1)]; -0090 I=haveFlux(model); -0091 noFluxRxns=model.rxns(~I); -0092 J=haveFlux(model2); -0093 noFluxRxnsRelaxed=model2.rxns(~J); -0094 bModel=removeReactions(model,~I,true,true); -0095 cModel=removeReactions(model2,~J,true,true); -0096 fprintf('***Overview\n'); -0097 fprintf([num2str(numel(model.rxns)-sum(I)) ' out of ' num2str(numel(model.rxns))... -0098 ' reactions cannot carry flux (' num2str(numel(model.rxns)-sum(J)) ' if net production of all metabolites is allowed)\n']); -0099 fprintf([num2str(numel(model.mets)-numel(bModel.mets)) ' out of ' num2str(numel(model.mets))... -0100 ' metabolites are unreachable (' num2str(numel(model.mets)-numel(cModel.mets)) ' if net production of all metabolites is allowed)\n']); -0101 -0102 fprintf('\n***Isolated subnetworks\n'); -0103 subGraphs=getAllSubGraphs(model); -0104 fprintf(['A total of ' num2str(size(subGraphs,2)) ' isolated sub-networks are present in the model\n']); -0105 for i=1:size(subGraphs,2) -0106 fprintf(['\t' num2str(i) '. ' num2str(sum(subGraphs(:,i))) ' metabolites\n']); -0107 end -0108 -0109 fprintf('\n***Metabolite connectivity\n'); -0110 [notProducedMets, ~, neededForProductionMat,minToConnect]=checkProduction(model,true,model.comps,false); -0111 fprintf(['To enable net production of all metabolites, a total of ' num2str(numel(minToConnect)) ' metabolites must be connected\n']); -0112 fprintf('Top 10 metabolites to connect:\n'); -0113 for i=1:min(10,numel(minToConnect)) -0114 fprintf(['\t' num2str(i) '. ' esc(minToConnect{i}) '\n']); -0115 end -0116 -0117 if calculateINOUT==true -0118 fprintf('\n***Mass balancing\n'); -0119 produced=canExchange(closedModel,'produce'); -0120 canProduceWithoutInput=closedModel.mets(produced); -0121 consumed=canExchange(closedModel,'consume'); -0122 canConsumeWithoutOutput=closedModel.mets(consumed); -0123 fprintf([num2str(numel(canConsumeWithoutOutput)) ' metabolites could be consumed without any outputs\n' num2str(numel(canProduceWithoutInput)) ' metabolites could be produced without any inputs\n']); -0124 end -0125 -0126 if ~isempty(templateModels) -0127 fprintf('\n***Automated gap-filling\n'); -0128 [connectedFromTemplates, ~, addedFromTemplates]=fillGaps(model,templateModels); -0129 t=esc(templateModels{1}.id); -0130 for i=2:numel(templateModels) -0131 t=[t ', ' esc(templateModels{i}.id)]; -0132 end -0133 fprintf([num2str(numel(connectedFromTemplates)) ' unconnected reactions can be connected by including ' num2str(numel(addedFromTemplates)) ' reactions from\n' t '\n']); -0134 end -0135 end

    +0057 % Notes +0058 % ----- +0059 % The boundary reactions are those getExchangeRxns returns: exchanges, +0060 % sinks and demands. For a model that keeps its boundary metabolites (an +0061 % unconstrained field), those metabolites are deleted for the connectivity +0062 % checks and kept, balanced, for the closed model. +0063 % +0064 % The model must be feasible under its own bounds and with its boundary +0065 % closed. Otherwise gapReport stops with a RAVEN:infeasibleModel error that +0066 % names the reactions with a forced flux. Such reactions, for example ATP +0067 % maintenance, need bounds that allow zero flux. +0068 % +0069 % Examples +0070 % -------- +0071 % [noFluxRxns, noFluxRxnsRelaxed, subGraphs, notProducedMets, ... +0072 % minToConnect, neededForProductionMat, canProduceWithoutInput, ... +0073 % canConsumeWithoutOutput, connectedFromTemplates, addedFromTemplates] = ... +0074 % gapReport(model, 'templateModels', templateModels); +0075 +0076 p=parseRAVENargs(varargin, {'templateModels',[]}); +0077 templateModels=p.templateModels; +0078 +0079 if isempty(templateModels) +0080 templateModels=[]; +0081 connectedFromTemplates=[]; +0082 addedFromTemplates=[]; +0083 end +0084 +0085 %Escapes model text before it is spliced into an fprintf template, since +0086 %a literal "%" in e.g. a model id or metabolite name would otherwise be +0087 %misread as a format directive and swallow the remainder of the line. +0088 esc=@(s) strrep(s,'%','%%'); +0089 +0090 fprintf(['Gap analysis for ' esc(model.id) ' - ' esc(model.name) '\n\n']); +0091 if isfield(model,'unconstrained') +0092 closedModel=model; +0093 model=simplifyModel(model); +0094 else +0095 closedModel=model; +0096 [~,boundary]=getExchangeRxns(closedModel); +0097 closedModel.lb(boundary)=0; +0098 closedModel.ub(boundary)=0; +0099 end +0100 +0101 requireFeasible(model,'under its own bounds'); +0102 requireFeasible(closedModel,'once its boundary reactions are closed'); +0103 +0104 model2=model; +0105 model2.b=[model2.b inf(numel(model2.mets),1)]; +0106 I=haveFlux(model); +0107 noFluxRxns=model.rxns(~I); +0108 J=haveFlux(model2); +0109 noFluxRxnsRelaxed=model2.rxns(~J); +0110 bModel=removeReactions(model,~I,true,true); +0111 cModel=removeReactions(model2,~J,true,true); +0112 fprintf('***Overview\n'); +0113 fprintf([num2str(numel(model.rxns)-sum(I)) ' out of ' num2str(numel(model.rxns))... +0114 ' reactions cannot carry flux (' num2str(numel(model.rxns)-sum(J)) ' if net production of all metabolites is allowed)\n']); +0115 fprintf([num2str(numel(model.mets)-numel(bModel.mets)) ' out of ' num2str(numel(model.mets))... +0116 ' metabolites are unreachable (' num2str(numel(model.mets)-numel(cModel.mets)) ' if net production of all metabolites is allowed)\n']); +0117 +0118 fprintf('\n***Isolated subnetworks\n'); +0119 subGraphs=getAllSubGraphs(model); +0120 fprintf(['A total of ' num2str(size(subGraphs,2)) ' isolated sub-networks are present in the model\n']); +0121 for i=1:size(subGraphs,2) +0122 fprintf(['\t' num2str(i) '. ' num2str(sum(subGraphs(:,i))) ' metabolites\n']); +0123 end +0124 +0125 fprintf('\n***Metabolite connectivity\n'); +0126 [notProducedMets, ~, neededForProductionMat,minToConnect]=checkProduction(model,true,model.comps,false); +0127 fprintf(['To enable net production of all metabolites, a total of ' num2str(numel(minToConnect)) ' metabolites must be connected\n']); +0128 fprintf('Top 10 metabolites to connect:\n'); +0129 for i=1:min(10,numel(minToConnect)) +0130 fprintf(['\t' num2str(i) '. ' esc(minToConnect{i}) '\n']); +0131 end +0132 +0133 fprintf('\n***Mass balancing\n'); +0134 produced=canExchange(closedModel,'produce'); +0135 canProduceWithoutInput=closedModel.mets(produced); +0136 consumed=canExchange(closedModel,'consume'); +0137 canConsumeWithoutOutput=closedModel.mets(consumed); +0138 fprintf([num2str(numel(canConsumeWithoutOutput)) ' metabolites could be consumed without any outputs\n' num2str(numel(canProduceWithoutInput)) ' metabolites could be produced without any inputs\n']); +0139 +0140 if ~isempty(templateModels) +0141 fprintf('\n***Automated gap-filling\n'); +0142 [connectedFromTemplates, ~, addedFromTemplates]=fillGaps(model,templateModels); +0143 t=esc(templateModels{1}.id); +0144 for i=2:numel(templateModels) +0145 t=[t ', ' esc(templateModels{i}.id)]; +0146 end +0147 fprintf([num2str(numel(connectedFromTemplates)) ' unconnected reactions can be connected by including ' num2str(numel(addedFromTemplates)) ' reactions from\n' t '\n']); +0148 end +0149 end +0150 +0151 function requireFeasible(model,condition) +0152 %Errors, naming the forced-flux reactions, if the model is infeasible +0153 model.c(:)=0; +0154 sol=solveLP(model); +0155 if sol.stat~=-1 +0156 return +0157 end +0158 forced=model.rxns(model.lb>0 | model.ub<0); +0159 hint=''; +0160 if ~isempty(forced) +0161 hint=[' Reactions with a forced flux: ' strjoin(forced,', ') '.']; +0162 end +0163 error('RAVEN:infeasibleModel',['The model is infeasible ' condition ... +0164 ', so gapReport cannot analyse it.' strrep(hint,'%','%%') ... +0165 ' Set the bounds of ATP maintenance and any other forced flux to zero first.']); +0166 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/gapfilling/index.html b/docs/html/gapfilling/index.html similarity index 79% rename from doc/gapfilling/index.html rename to docs/html/gapfilling/index.html index c553c80e..ac538767 100644 --- a/doc/gapfilling/index.html +++ b/docs/html/gapfilling/index.html @@ -19,7 +19,7 @@

    Index for gapfilling

    Matlab files in this directory:

    -
     canExchangecanExchange Check which metabolites a model can produce or consume.
     checkProductioncheckProduction Check which metabolites can be produced from a model.
     fillGapsfillGaps Use template model(s) to fill gaps in a model.
     findLeakMetabolitefindLeakMetabolite Find a metabolite that can be freely made or consumed.
     fitTasksfitTasks Fill gaps in a model so it can perform a list of tasks.
     ftINITFillGapsftINITFillGaps
     gapFillFastCoregapFillFastCore L1-norm LP subroutine for fastGapFill.
     gapFillFastLPgapFillFastLP LP-based gap-filling (fastGapFill / swiftGapFill).
     gapFillMILPgapFillMILP Objective-based gap-filling via a global growth-floor MILP.
     gapFillSwiftCoregapFillSwiftCore SWIFTCORE LP subroutine for swiftGapFill.
     gapFillTopologicalgapFillTopological BFS metabolite-producibility pre-screening.
     gapReportgapReport Perform a gap analysis and summarize the results.
    + canExchangecanExchange Check which metabolites a model can produce or consume.  checkProductioncheckProduction Check which metabolites can be produced from a model.  fillGapsfillGaps Use template model(s) to fill gaps in a model.  findLeakMetabolitefindLeakMetabolite Find a metabolite that can be freely made or consumed.  fitTasksfitTasks Fill gaps in a model so it can perform a list of tasks.  ftINITFillGapsftINITFillGaps  gapFillFastCoregapFillFastCore L1-norm LP subroutine for fastGapFill.  gapFillFastLPgapFillFastLP LP-based gap-filling (fastGapFill or swiftGapFill).  gapFillMILPgapFillMILP Objective-based gap-filling via a global growth-floor MILP.  gapFillSwiftCoregapFillSwiftCore SWIFTCORE LP subroutine for swiftGapFill.  gapFillTopologicalgapFillTopological BFS metabolite-producibility pre-screening.  gapReportgapReport Perform a gap analysis and summarise the results. diff --git a/doc/hp.png b/docs/html/hp.png similarity index 100% rename from doc/hp.png rename to docs/html/hp.png diff --git a/docs/html/index.html b/docs/html/index.html new file mode 100644 index 00000000..270669f8 --- /dev/null +++ b/docs/html/index.html @@ -0,0 +1,72 @@ + + + + Matlab Index + + + + + + + + + +

    Matlab Index

    +

    Matlab Directories

    + +

    Matlab Files found in these Directories

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FSEOF exportModel grRuleToDNF removeMets
    INITStepDesc exportToExcelFormat grRuleToString removeRavenFromPath
    addExchangeRxns extractMiriam groupRxnScores removeReactions
    addGenesRaven fetchToolboxAsset guessComposition renameModelGenes
    addIdentifierPrefix fillGaps haveFlux replaceMets
    addMets findDuplicateRxns hitsFromSuppOrth reporterMetabolites
    addRavenToUserPath findGeneDeletions importModel rescaleModelForINIT
    addRxns findLeakMetabolite isDnfGrRule reverseRxns
    addRxnsGenesMets findPotentialErrors keggDataVersion runDynamicFBA
    addTransport findRAVENroot legacyMethodNotice runINIT
    analyzeSampling fitParameters loadDeltaGCSV runPhenotypePhasePlane
    applyCondition fitTasks makeFakeBlastStructure runProductionEnvelope
    assignCompartments ftINIT mergeCompartments runRAVENtests
    assignSBOterms ftINITFillGaps mergeLinear runRobustnessAnalysis
    buildEquation ftINITInternalAlg mergeModels runSimpleOptKnock
    buildGlobalGPR gapFillFastCore modelSummary runSuppOrth
    buildGlobalKEGGModel gapFillFastLP optimizeProb sampleACHR
    canExchange gapFillMILP parallelWorkersRAVEN sampleCHRR
    changeGrRules gapFillSwiftCore parseFormulas sampleChebyshevCenter
    changeRxns gapFillTopological parseGrRule sampleMaxVolEllipse
    checkFileExistence gapReport parseHPA sampleWarmupPoints
    checkFunctionUniqueness generateNewIds parseHPArna saveDeltaGCSV
    checkModelStruct getAllRxnsFromGenes parseRAVENargs scaleBiomassFraction
    checkProduction getAllSubGraphs parseRxnEqu scaleBiomassPseudoreaction
    checkSolution getAllowedBounds parseScores scoreModel
    checkTasks getBiomassFractions parseTaskList setExchangeBounds
    cleanSheet getBlast parseYAML setGAM
    closeModel getDiamond permuteModel setParam
    compareFluxes getElementalBalance predictLocalization setRavenProgress
    compareMultipleModels getEssentialRxns prepINITModel setRavenSolver
    constructEquations getExchangeRxns printFluxes simplifyModel
    constructS getFluxZ printModelStats solveLP
    contractModel getGeneData printOrange sortIdentifiers
    convertCharArray getGenesFromGrRules processProteinFastaFile sortModel
    convertToIrrev getINITModel progressReport splitProbForConditioning
    copyToComps getINITSteps randomSampling standardizeGrRules
    curateModelFromTables getIndexes ravenCobraWrapper standardizeModelFieldOrder
    defaultCompartmentMap getKEGGModelForOrganism ravenList traceFluxPath
    deleteUnusedGenes getMinNrFluxes ravenModelFields updateDocumentation
    deprecationWarning getMinimalMedium readFasta walkFluxes
    diffModels getModelFromHomology readKEGGTable writeExcel
    downloadGenomeData getModelFromKEGG readYAMLmodel writeFasta
    downloadRavenBinaries getPhylDist removeBadRxns writeYAMLmodel
    editMiriam getRxnsInComp removeGenes
    expandModel getTransportRxns removeIdentifierPrefix
    exportForGit getUniProtScores removeLowScoreGenes
    + + +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/installation/addRavenToUserPath.html b/docs/html/installation/addRavenToUserPath.html similarity index 56% rename from doc/installation/addRavenToUserPath.html rename to docs/html/installation/addRavenToUserPath.html index c7d1e4e0..59175464 100644 --- a/doc/installation/addRavenToUserPath.html +++ b/docs/html/installation/addRavenToUserPath.html @@ -28,16 +28,17 @@

    SYNOPSIS ^DESCRIPTION ^

     This function writes a startup.m file in the userpath, adding RAVEN (and
    - all subdirectories) to the path each time Matlab is started.
    - This function is useful if the user has no rights to save paths to the
    + all subdirectories except those with .git or doc in their path) to the
    + path each time MATLAB is started.
    + The function applies when the user has no rights to save paths to the
      pathdef.m file in the matlabroot, for instance due to a lack of admin
    - rights. As the startup.m file in the userpath automatically runs with
    - each Matlab start, the paths are automatically loaded.
    + rights. The startup.m file in the userpath runs at each MATLAB start and
    + loads the paths.
     
      Name-Value Arguments
      --------------------
    -   overwrite       logical, whether startup.m in the userpath should
    -                   overwritten (otherwise the RAVEN paths are appended)
    +   overwrite       logical, true if startup.m in the userpath is
    +                   overwritten and false if the RAVEN paths are appended
                        (default true)
    @@ -55,47 +56,48 @@

    CROSS-REFERENCE INFORMATION ^
 <h2><a name=SOURCE CODE ^

    0001 function addRavenToUserPath(varargin)
     0002 % This function writes a startup.m file in the userpath, adding RAVEN (and
    -0003 % all subdirectories) to the path each time Matlab is started.
    -0004 % This function is useful if the user has no rights to save paths to the
    -0005 % pathdef.m file in the matlabroot, for instance due to a lack of admin
    -0006 % rights. As the startup.m file in the userpath automatically runs with
    -0007 % each Matlab start, the paths are automatically loaded.
    -0008 %
    -0009 % Name-Value Arguments
    -0010 % --------------------
    -0011 %   overwrite       logical, whether startup.m in the userpath should
    -0012 %                   overwritten (otherwise the RAVEN paths are appended)
    -0013 %                   (default true)
    -0014 
    -0015 p=parseRAVENargs(varargin, {'overwrite',true});
    -0016 overwrite=p.overwrite;
    -0017 
    -0018 % Get current RAVEN directory
    -0019 ravenDir=findRAVENroot();
    -0020 
    -0021 % Lists all subdirectories
    -0022 subpath=regexp(genpath(ravenDir),pathsep,'split');
    -0023 % Remove .git and doc folders
    -0024 pathsToKeep=cellfun(@(x) isempty(strfind(x,'.git')),subpath) & cellfun(@(x) isempty(strfind(x,'doc')),subpath);
    -0025 % Only keep useful paths
    -0026 subpath = subpath(pathsToKeep);
    -0027 subpath = subpath(1:end-1); % Remove last entry, is empty field
    -0028 
    -0029 % Write startup.m file
    -0030 if overwrite
    -0031     fid=fopen(fullfile(userpath,'startup.m'),'w');
    -0032     fprintf(fid,'%sn','%%% RAVEN path');
    -0033 else
    -0034     fid=fopen(fullfile(userpath,'startup.m'),'a');
    -0035     fprintf(fid,'n%sn','%%% RAVEN path');
    -0036 end
    -0037 fprintf(fid,'%sn',strcat('addpath(''',subpath{1},''',...'));
    -0038 for i=2(length(subpath)-1)
    -0039     fprintf(fid,'t%sn',strcat('''',subpath{i},''',...'));
    -0040 end
    -0041 fprintf(fid,'t%s',strcat('''',subpath{length(subpath)},''');'));
    -0042 fclose(fid);
    -0043 end
    +0003 % all subdirectories except those with .git or doc in their path) to the +0004 % path each time MATLAB is started. +0005 % The function applies when the user has no rights to save paths to the +0006 % pathdef.m file in the matlabroot, for instance due to a lack of admin +0007 % rights. The startup.m file in the userpath runs at each MATLAB start and +0008 % loads the paths. +0009 % +0010 % Name-Value Arguments +0011 % -------------------- +0012 % overwrite logical, true if startup.m in the userpath is +0013 % overwritten and false if the RAVEN paths are appended +0014 % (default true) +0015 +0016 p=parseRAVENargs(varargin, {'overwrite',true}); +0017 overwrite=p.overwrite; +0018 +0019 % Get current RAVEN directory +0020 ravenDir=findRAVENroot(); +0021 +0022 % Lists all subdirectories +0023 subpath=regexp(genpath(ravenDir),pathsep,'split'); +0024 % Remove .git and doc folders +0025 pathsToKeep=cellfun(@(x) isempty(strfind(x,'.git')),subpath) & cellfun(@(x) isempty(strfind(x,'doc')),subpath); +0026 % Only keep useful paths +0027 subpath = subpath(pathsToKeep); +0028 subpath = subpath(1:end-1); % Remove last entry, is empty field +0029 +0030 % Write startup.m file +0031 if overwrite +0032 fid=fopen(fullfile(userpath,'startup.m'),'w'); +0033 fprintf(fid,'%sn','%%% RAVEN path'); +0034 else +0035 fid=fopen(fullfile(userpath,'startup.m'),'a'); +0036 fprintf(fid,'n%sn','%%% RAVEN path'); +0037 end +0038 fprintf(fid,'%sn',strcat('addpath(''',subpath{1},''',...')); +0039 for i=2(length(subpath)-1) +0040 fprintf(fid,'t%sn',strcat('''',subpath{i},''',...')); +0041 end +0042 fprintf(fid,'t%s',strcat('''',subpath{length(subpath)},''');')); +0043 fclose(fid); +0044 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/installation/checkFunctionUniqueness.html b/docs/html/installation/checkFunctionUniqueness.html new file mode 100644 index 00000000..0bce9b09 --- /dev/null +++ b/docs/html/installation/checkFunctionUniqueness.html @@ -0,0 +1,170 @@ + + + + Description of checkFunctionUniqueness + + + + + + + + + +
    Home > installation > checkFunctionUniqueness.m
    + + + +

    checkFunctionUniqueness +

    + +

    PURPOSE ^

    +
    checkFunctionUniqueness
    + +

    SYNOPSIS ^

    +
    function status = checkFunctionUniqueness(varargin)
    + +

    DESCRIPTION ^

    +
     checkFunctionUniqueness
    +   Checks whether RAVEN toolbox functions are unique across other
    +   toolboxes and user-created functions on the MATLAB path.
    +
    + Name-Value Arguments
    + --------------------
    +   altDirs     char with alternative directories with functions, separated
    +               by the path separator of the platform (';' on Windows, ':'
    +               otherwise). Specified only when the function checks
    +               directories other than the RAVEN toolbox.
    +
    + Output:
    +   status      true if all functions are unique, otherwise false. Without
    +               an output argument the function prints Pass or Fail.
    +
    + Usage: checkFunctionUniqueness()
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function status = checkFunctionUniqueness(varargin)
    +0002 % checkFunctionUniqueness
    +0003 %   Checks whether RAVEN toolbox functions are unique across other
    +0004 %   toolboxes and user-created functions on the MATLAB path.
    +0005 %
    +0006 % Name-Value Arguments
    +0007 % --------------------
    +0008 %   altDirs     char with alternative directories with functions, separated
    +0009 %               by the path separator of the platform (';' on Windows, ':'
    +0010 %               otherwise). Specified only when the function checks
    +0011 %               directories other than the RAVEN toolbox.
    +0012 %
    +0013 % Output:
    +0014 %   status      true if all functions are unique, otherwise false. Without
    +0015 %               an output argument the function prints Pass or Fail.
    +0016 %
    +0017 % Usage: checkFunctionUniqueness()
    +0018 
    +0019 p=parseRAVENargs(varargin, {'altDirs',[]});
    +0020 altDirs=p.altDirs;
    +0021 
    +0022 if isempty(altDirs)
    +0023     %Get the RAVEN path
    +0024     ravenDir=findRAVENroot();
    +0025     
    +0026     %Now getting all RAVEN functions recursively;
    +0027     temp_res1=dir([ravenDir '/*/*.m']);
    +0028     temp_res2=dir([ravenDir '/*/*/*.m']);
    +0029     
    +0030     ravenFunctions={temp_res1.name,temp_res2.name}';
    +0031 else
    +0032     if ispc
    +0033         splitChar = ';';
    +0034     else
    +0035         splitChar = ':';
    +0036     end
    +0037     altDirs = strsplit(altDirs,splitChar)';
    +0038     ravenFunctions={};
    +0039     for i=1:numel(altDirs)
    +0040         temp_res=dir(fullfile(char(altDirs(i)),'*.m'));
    +0041         ravenFunctions = [ravenFunctions, temp_res.name];
    +0042     end
    +0043     ravenFunctions=ravenFunctions';
    +0044     ravenDir=altDirs;
    +0045 end
    +0046 
    +0047 %startup.m is not a normal function, any startup.m in the path should run
    +0048 %during startup, so duplicate use of this name is fine
    +0049 ravenFunctions=ravenFunctions(~ismember(ravenFunctions,'startup.m'));
    +0050 
    +0051 %Getting all the paths added to Matlab
    +0052 if ispc
    +0053     matlabPaths=regexp(path, ';', 'split')';
    +0054 elseif isunix
    +0055     matlabPaths=regexp(path, ':', 'split')';
    +0056 end
    +0057 
    +0058 overlapPath={};
    +0059 overlapFunctions={};
    +0060 multiRaven=false;
    +0061 multiFunction=false;
    +0062 
    +0063 for i=1:numel(matlabPaths)
    +0064     if ~startsWith(matlabPaths{i},ravenDir)
    +0065         temp_res=dir([matlabPaths{i} '/*.m']);
    +0066         if ~isempty(temp_res)
    +0067             pathFunctions={temp_res.name}';
    +0068         else
    +0069             pathFunctions='';
    +0070         end
    +0071         if ~isempty(pathFunctions) && ~any(ismember('Contents.m',pathFunctions))
    +0072             if any(ismember(ravenFunctions,pathFunctions))
    +0073                 if sum(ismember(ravenFunctions,pathFunctions))>(numel(ravenFunctions)/4)
    +0074                     multiRaven=true;
    +0075                 else
    +0076                     multiFunction=true;
    +0077                     overlapPath = [overlapPath, matlabPaths(i)];
    +0078                     overlapFunctions = [overlapFunctions, {ravenFunctions(ismember(ravenFunctions,pathFunctions))}];
    +0079                 end
    +0080             end
    +0081         end
    +0082     end
    +0083 end
    +0084 
    +0085 if multiRaven==true || multiFunction == true
    +0086         if nargout > 0
    +0087             status = false;
    +0088         else
    +0089             fprintf('Fail\n')
    +0090         end
    +0091     if multiRaven==true && isempty(altDirs)
    +0092         error(['Multiple RAVEN versions detected in MATLAB path. Remove all ',...
    +0093                'RAVEN directories from the MATLAB path with removeRavenFromPath(), ',...
    +0094                'or manually remove them. Afterwards, re-run checkRaven']);
    +0095     elseif multiFunction == true
    +0096         for i=1:numel(overlapPath)
    +0097             fprintf(['   Duplicate functions in ',regexprep(overlapPath{i},'(\\)','\\$1'),'\n']);
    +0098             fprintf(['     ' strjoin(overlapFunctions{i},'\n     ') '\n']);
    +0099         end
    +0100     fprintf('   Resolve conflicting functions to avoid unexpected behaviour\n');
    +0101     end
    +0102 else
    +0103     if nargout > 0
    +0104         status = true;
    +0105     else
    +0106         fprintf('Pass\n')
    +0107     end
    +0108 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/installation/downloadRavenBinaries.html b/docs/html/installation/downloadRavenBinaries.html new file mode 100644 index 00000000..4c60eb54 --- /dev/null +++ b/docs/html/installation/downloadRavenBinaries.html @@ -0,0 +1,180 @@ + + + + Description of downloadRavenBinaries + + + + + + + + + +
    Home > installation > downloadRavenBinaries.m
    + + + +

    downloadRavenBinaries +

    + +

    PURPOSE ^

    +
    downloadRavenBinaries
    + +

    SYNOPSIS ^

    +
    function downloadRavenBinaries(tools)
    + +

    DESCRIPTION ^

    +
     downloadRavenBinaries
    +   Download RAVEN's external command-line binaries on demand from the
    +   toolbox-assets repository into the src/external directory of RAVEN. Only
    +   the binary matching the current platform is fetched, and only when it is
    +   missing.
    +
    +   Input:
    +   tools   cell array, subset of {'blast+','diamond','hmmer'} to
    +           download. Defaults to all of them. Tools whose target executable is
    +           already present are skipped.
    +
    +   The download requires an internet connection. An offline installation
    +   uses the offline bundle (RAVEN-<tag>-full.zip) attached to each RAVEN
    +   release, which contains the binaries of all platforms in src/external.
    +
    +   The binaries are hosted as release assets in
    +   https://github.com/CompMetEng/toolbox-assets.
    +
    +   Usage: downloadRavenBinaries({'blast+','diamond'})
    +
    + getBlast, getDiamond and getKEGGModelForOrganism call this function
    + when a binary is missing. checkRaven calls it for all three tools when its
    + checkBinaries input is true.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function downloadRavenBinaries(tools)
    +0002 % downloadRavenBinaries
    +0003 %   Download RAVEN's external command-line binaries on demand from the
    +0004 %   toolbox-assets repository into the src/external directory of RAVEN. Only
    +0005 %   the binary matching the current platform is fetched, and only when it is
    +0006 %   missing.
    +0007 %
    +0008 %   Input:
    +0009 %   tools   cell array, subset of {'blast+','diamond','hmmer'} to
    +0010 %           download. Defaults to all of them. Tools whose target executable is
    +0011 %           already present are skipped.
    +0012 %
    +0013 %   The download requires an internet connection. An offline installation
    +0014 %   uses the offline bundle (RAVEN-<tag>-full.zip) attached to each RAVEN
    +0015 %   release, which contains the binaries of all platforms in src/external.
    +0016 %
    +0017 %   The binaries are hosted as release assets in
    +0018 %   https://github.com/CompMetEng/toolbox-assets.
    +0019 %
    +0020 %   Usage: downloadRavenBinaries({'blast+','diamond'})
    +0021 %
    +0022 % getBlast, getDiamond and getKEGGModelForOrganism call this function
    +0023 % when a binary is missing. checkRaven calls it for all three tools when its
    +0024 % checkBinaries input is true.
    +0025 
    +0026 if nargin < 1 || isempty(tools)
    +0027     tools = {'blast+','diamond','hmmer'};
    +0028 elseif ischar(tools)
    +0029     tools = {tools};
    +0030 end
    +0031 
    +0032 ravenDir = findRAVENroot();
    +0033 
    +0034 % toolbox-assets platform key for the per-platform binary ZIPs.
    +0035 if ispc
    +0036     plat = 'windows-x86_64';
    +0037 elseif ismac
    +0038     plat = 'macos-arm64';
    +0039 else
    +0040     plat = 'linux-x86_64';
    +0041 end
    +0042 
    +0043 for i = 1:numel(tools)
    +0044     tool = tools{i};
    +0045     switch tool
    +0046         case 'blast+'
    +0047             tag = 'blast-2.17.0';
    +0048             asset = ['blast-2.17.0-' plat '.zip'];
    +0049             destDir = fullfile(ravenDir,'src','external','blast+');
    +0050             sentinel = fullfile(destDir,'blastp');           % bare name on every OS (.exe on Windows)
    +0051             execs = {'blastp','makeblastdb'};
    +0052         case 'diamond'
    +0053             tag = 'diamond-2.1.17';
    +0054             asset = ['diamond-2.1.17-' plat '.zip'];
    +0055             destDir = fullfile(ravenDir,'src','external','diamond');
    +0056             sentinel = fullfile(destDir,'diamond');
    +0057             execs = {'diamond'};
    +0058         case 'hmmer'
    +0059             if ispc
    +0060                 tag = 'hmmer-3.3.2';                          % native Windows build (Cygwin); reads HMMER3/f
    +0061                 asset = ['hmmer-3.3.2-' plat '.zip'];
    +0062             else
    +0063                 tag = 'hmmer-3.4.0';
    +0064                 asset = ['hmmer-3.4.0-' plat '.zip'];
    +0065             end
    +0066             destDir = fullfile(ravenDir,'src','external','hmmer');
    +0067             sentinel = fullfile(destDir,'hmmsearch');
    +0068             execs = {'hmmsearch'};
    +0069         otherwise
    +0070             error('downloadRavenBinaries: unknown tool ''%s''.',tool);
    +0071     end
    +0072 
    +0073     % macOS uses a .mac suffix and Windows a .exe suffix (Linux: bare name).
    +0074     if ispc
    +0075         sentinel = [sentinel '.exe']; %#ok<AGROW>
    +0076     elseif ismac
    +0077         sentinel = [sentinel '.mac']; %#ok<AGROW>
    +0078     end
    +0079     if exist(sentinel,'file')
    +0080         continue;   % already provisioned
    +0081     end
    +0082 
    +0083     zipPath = fetchToolboxAsset(fullfile(ravenDir,'src','external'),tag,asset);
    +0084     unzip(zipPath,destDir);
    +0085     delete(zipPath);
    +0086 
    +0087     % macOS uses the .mac suffix throughout RAVEN, but the toolbox-assets macOS ZIP
    +0088     % ships bare names, so rename the extracted executables to match.
    +0089     if ismac
    +0090         for k = 1:numel(execs)
    +0091             bare = fullfile(destDir,execs{k});
    +0092             if isfile(bare)
    +0093                 movefile(bare, fullfile(destDir,[execs{k} '.mac']));
    +0094             end
    +0095         end
    +0096     end
    +0097 
    +0098     % MATLAB's unzip does not preserve Unix execute permissions; restore them.
    +0099     if isunix
    +0100         if ismac; sfx = '.mac'; else; sfx = ''; end
    +0101         for k = 1:numel(execs)
    +0102             system(['chmod +x "' fullfile(destDir,[execs{k} sfx]) '"']);
    +0103         end
    +0104         % Downloaded binaries are quarantined by macOS Gatekeeper and will not
    +0105         % execute until the com.apple.quarantine attribute is cleared.
    +0106         if ismac
    +0107             system(['xattr -dr com.apple.quarantine "' destDir '"']);
    +0108         end
    +0109     end
    +0110 end
    +0111 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/installation/fetchToolboxAsset.html b/docs/html/installation/fetchToolboxAsset.html new file mode 100644 index 00000000..ce1da222 --- /dev/null +++ b/docs/html/installation/fetchToolboxAsset.html @@ -0,0 +1,207 @@ + + + + Description of fetchToolboxAsset + + + + + + + + + +
    Home > installation > fetchToolboxAsset.m
    + + + +

    fetchToolboxAsset +

    + +

    PURPOSE ^

    +
    fetchToolboxAsset Download (if needed) a toolbox-assets release asset.
    + +

    SYNOPSIS ^

    +
    function localPath=fetchToolboxAsset(destDir,releaseTag,assetName)
    + +

    DESCRIPTION ^

    +
     fetchToolboxAsset  Download (if needed) a toolbox-assets release asset.
    +
    + Downloads assetName from the given release tag of
    + https://github.com/CompMetEng/toolbox-assets into destDir
    + and returns its local path. An already-downloaded copy is reused without a
    + new download or checksum test. The function applies to every toolbox-assets
    + release family (KEGG artefacts, HMM libraries, binaries), and the caller
    + supplies the release tag and the asset name.
    +
    + A download is checked against the SHA-256 checksum that the
    + toolbox-assets manifest (release manifest-v3) lists for it. A file whose
    + checksum differs is deleted and an error is raised. An asset that the
    + manifest does not list, or a manifest that cannot be read, gives a
    + warning and the file is kept unchecked.
    +
    + Parameters
    + ----------
    + destDir : char
    +     directory to download into (created if it does not exist yet).
    + releaseTag : char
    +     the toolbox-assets release tag, for example 'kegg118'.
    + assetName : char
    +     the release asset file name, for example 'kegg118_core.tar.gz'.
    +
    + Returns
    + -------
    + localPath : char
    +     path to the downloaded file.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: + + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function localPath=fetchToolboxAsset(destDir,releaseTag,assetName)
    +0002 % fetchToolboxAsset  Download (if needed) a toolbox-assets release asset.
    +0003 %
    +0004 % Downloads assetName from the given release tag of
    +0005 % https://github.com/CompMetEng/toolbox-assets into destDir
    +0006 % and returns its local path. An already-downloaded copy is reused without a
    +0007 % new download or checksum test. The function applies to every toolbox-assets
    +0008 % release family (KEGG artefacts, HMM libraries, binaries), and the caller
    +0009 % supplies the release tag and the asset name.
    +0010 %
    +0011 % A download is checked against the SHA-256 checksum that the
    +0012 % toolbox-assets manifest (release manifest-v3) lists for it. A file whose
    +0013 % checksum differs is deleted and an error is raised. An asset that the
    +0014 % manifest does not list, or a manifest that cannot be read, gives a
    +0015 % warning and the file is kept unchecked.
    +0016 %
    +0017 % Parameters
    +0018 % ----------
    +0019 % destDir : char
    +0020 %     directory to download into (created if it does not exist yet).
    +0021 % releaseTag : char
    +0022 %     the toolbox-assets release tag, for example 'kegg118'.
    +0023 % assetName : char
    +0024 %     the release asset file name, for example 'kegg118_core.tar.gz'.
    +0025 %
    +0026 % Returns
    +0027 % -------
    +0028 % localPath : char
    +0029 %     path to the downloaded file.
    +0030 
    +0031 if ~isfolder(destDir)
    +0032     mkdir(destDir);
    +0033 end
    +0034 localPath=fullfile(destDir,assetName);
    +0035 if isfile(localPath)
    +0036     return;
    +0037 end
    +0038 url=['https://github.com/CompMetEng/toolbox-assets/releases/download/' releaseTag '/' assetName];
    +0039 partPath=[localPath '.part'];
    +0040 fprintf(['Downloading ' assetName '... ']);
    +0041 try
    +0042     websave(partPath,url);
    +0043 catch ME
    +0044     if isfile(partPath)
    +0045         delete(partPath);
    +0046     end
    +0047     if strcmp(ME.identifier,'MATLAB:webservices:HTTP404StatusCodeError')
    +0048         error('Failed to download %s, the server returned a 404 error, try again later. If the problem persists please report it on the RAVEN GitHub Issues page: https://github.com/CompMetEng/RAVEN/issues',assetName)
    +0049     else
    +0050         rethrow(ME);
    +0051     end
    +0052 end
    +0053 expected=manifestChecksum(url);
    +0054 if isempty(expected)
    +0055     warning('RAVEN:fetchToolboxAsset:unverified',['The toolbox-assets manifest gives no ' ...
    +0056         'checksum for %s; the download is not verified.'],assetName);
    +0057 else
    +0058     actual=sha256File(partPath);
    +0059     if ~strcmpi(actual,expected)
    +0060         delete(partPath);
    +0061         error('RAVEN:fetchToolboxAsset:checksum',['The download of %s is not the published ' ...
    +0062             'file (SHA-256 %s, expected %s) and was deleted. Try again; if it persists, ' ...
    +0063             'report it on the RAVEN GitHub Issues page: https://github.com/CompMetEng/RAVEN/issues'], ...
    +0064             assetName,actual,expected)
    +0065     end
    +0066 end
    +0067 movefile(partPath,localPath);
    +0068 fprintf('COMPLETE\n');
    +0069 end
    +0070 
    +0071 function sha=manifestChecksum(url)
    +0072 % The sha256 the toolbox-assets manifest lists for an asset URL, or '' when
    +0073 % it lists none or cannot be read. The manifest is read once per session.
    +0074 persistent manifest
    +0075 sha='';
    +0076 if isempty(manifest)
    +0077     try
    +0078         manifest=webread('https://github.com/CompMetEng/toolbox-assets/releases/download/manifest-v3/manifest.json', ...
    +0079             weboptions('ContentType','json','Timeout',30));
    +0080     catch ME
    +0081         warning('RAVEN:fetchToolboxAsset:manifest','Cannot read the toolbox-assets manifest: %s',ME.message);
    +0082         return
    +0083     end
    +0084 end
    +0085 sha=findUrl(manifest,url);
    +0086 end
    +0087 
    +0088 function sha=findUrl(node,url)
    +0089 % Depth-first search for a struct with this url and a sha256 field. The
    +0090 % manifest's file names are not valid MATLAB field names, so the search
    +0091 % goes by value.
    +0092 sha='';
    +0093 if isstruct(node)
    +0094     for k=1:numel(node)
    +0095         if isfield(node(k),'url') && isfield(node(k),'sha256') && strcmp(node(k).url,url)
    +0096             sha=node(k).sha256;
    +0097             return
    +0098         end
    +0099         f=fieldnames(node(k));
    +0100         for i=1:numel(f)
    +0101             sha=findUrl(node(k).(f{i}),url);
    +0102             if ~isempty(sha)
    +0103                 return
    +0104             end
    +0105         end
    +0106     end
    +0107 elseif iscell(node)
    +0108     for i=1:numel(node)
    +0109         sha=findUrl(node{i},url);
    +0110         if ~isempty(sha)
    +0111             return
    +0112         end
    +0113     end
    +0114 end
    +0115 end
    +0116 
    +0117 function sha=sha256File(path)
    +0118 % SHA-256 of a file as lower-case hex, read in chunks.
    +0119 md=java.security.MessageDigest.getInstance('SHA-256');
    +0120 fid=fopen(path,'r');
    +0121 cleanup=onCleanup(@() fclose(fid));
    +0122 while true
    +0123     chunk=fread(fid,2^24,'*uint8');
    +0124     if isempty(chunk)
    +0125         break
    +0126     end
    +0127     md.update(typecast(chunk,'int8'));
    +0128 end
    +0129 digest=typecast(md.digest(),'uint8');
    +0130 sha=lower(reshape(dec2hex(digest,2)',1,[]));
    +0131 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/installation/findRAVENroot.html b/docs/html/installation/findRAVENroot.html new file mode 100644 index 00000000..554cf959 --- /dev/null +++ b/docs/html/installation/findRAVENroot.html @@ -0,0 +1,114 @@ + + + + Description of findRAVENroot + + + + + + + + + +
    Home > installation > findRAVENroot.m
    + + + +

    findRAVENroot +

    + +

    PURPOSE ^

    +
    findRAVENroot
    + +

    SYNOPSIS ^

    +
    function [ravenPath, prevDir] = findRAVENroot()
    + +

    DESCRIPTION ^

    +
     findRAVENroot
    +   Finds the root of the RAVEN directory by searching for the path to
    +   version.txt. Can also record the current directory, for functions that use
    +   the ravenPath to navigate to a precise folder and return to the previous
    +   directory afterwards. See, for example, optimizeProb calling glpk.
    +
    +   The RAVEN.ravenPath preference is used if it points to a directory with a
    +   version.txt file. Otherwise the search starts at the location of this
    +   function file and moves up the directory tree.
    +
    +   The resolved path is cached for the rest of the MATLAB session, so the
    +   preference is read once although the function is called on every solver
    +   invocation. `clear findRAVENroot` drops the cached path after a change of
    +   the preference (checkRaven does this) without a restart of MATLAB.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: + + + + + +

    SOURCE CODE ^

    +
    0001 function [ravenPath, prevDir] = findRAVENroot()
    +0002 % findRAVENroot
    +0003 %   Finds the root of the RAVEN directory by searching for the path to
    +0004 %   version.txt. Can also record the current directory, for functions that use
    +0005 %   the ravenPath to navigate to a precise folder and return to the previous
    +0006 %   directory afterwards. See, for example, optimizeProb calling glpk.
    +0007 %
    +0008 %   The RAVEN.ravenPath preference is used if it points to a directory with a
    +0009 %   version.txt file. Otherwise the search starts at the location of this
    +0010 %   function file and moves up the directory tree.
    +0011 %
    +0012 %   The resolved path is cached for the rest of the MATLAB session, so the
    +0013 %   preference is read once although the function is called on every solver
    +0014 %   invocation. `clear findRAVENroot` drops the cached path after a change of
    +0015 %   the preference (checkRaven does this) without a restart of MATLAB.
    +0016 
    +0017 persistent cachedPath
    +0018 prevDir = pwd();
    +0019 if ~isempty(cachedPath)
    +0020     ravenPath = cachedPath;
    +0021     return;
    +0022 end
    +0023 
    +0024 % A stored preference is only trusted if it still points at a real RAVEN
    +0025 % install; otherwise fall through to walking up from the currently
    +0026 % executing copy of this file. Without this check, a stale preference
    +0027 % left over from a different RAVEN checkout on the same machine silently
    +0028 % resolves to that other copy's data, not the one actually running.
    +0029 ravenPath = '';
    +0030 if ispref('RAVEN','ravenPath')
    +0031     prefPath = getpref('RAVEN','ravenPath');
    +0032     if isfile(fullfile(prefPath,'version.txt'))
    +0033         ravenPath = prefPath;
    +0034     end
    +0035 end
    +0036 if isempty(ravenPath)
    +0037     ST=dbstack('-completenames');
    +0038     ravenPath = ST(strcmp({ST.name},'findRAVENroot')).file;
    +0039     rootFound = 0;
    +0040     while rootFound == 0
    +0041         isRoot = isfile(fullfile(ravenPath,'version.txt'));
    +0042         if isRoot
    +0043             rootFound = 1;
    +0044         else
    +0045             ravenPathOld = ravenPath;
    +0046             ravenPath = fileparts(ravenPath);
    +0047             if strcmp(ravenPathOld,ravenPath)
    +0048                 error('Cannot find the RAVEN root directory. Make sure you have not removed the version.txt file from your RAVEN installation.')
    +0049             end
    +0050         end
    +0051     end
    +0052 end
    +0053 cachedPath = ravenPath;
    +0054 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/installation/index.html b/docs/html/installation/index.html similarity index 73% rename from doc/installation/index.html rename to docs/html/installation/index.html index c1c42ff6..1d1275fa 100644 --- a/doc/installation/index.html +++ b/docs/html/installation/index.html @@ -19,7 +19,7 @@

    Index for installation

    Matlab files in this directory:

    -
     addRavenToUserPathThis function writes a startup.m file in the userpath, adding RAVEN (and
     checkFunctionUniquenesscheckFunctionUniqueness
     downloadRavenBinariesdownloadRavenBinaries
     fetchRavenDataAssetfetchRavenDataAsset Download (if needed) a raven-data release asset.
     findRAVENrootfindRAVENroot
     removeRavenFromPathremoveRavenFromPath
     updateDocumentationupdateDocumentation
    + addRavenToUserPathThis function writes a startup.m file in the userpath, adding RAVEN (and  checkFunctionUniquenesscheckFunctionUniqueness  downloadRavenBinariesdownloadRavenBinaries  fetchToolboxAssetfetchToolboxAsset Download (if needed) a toolbox-assets release asset.  findRAVENrootfindRAVENroot  removeRavenFromPathremoveRavenFromPath  updateDocumentationupdateDocumentation diff --git a/doc/installation/removeRavenFromPath.html b/docs/html/installation/removeRavenFromPath.html similarity index 59% rename from doc/installation/removeRavenFromPath.html rename to docs/html/installation/removeRavenFromPath.html index 5ba2cd21..d42642f0 100644 --- a/doc/installation/removeRavenFromPath.html +++ b/docs/html/installation/removeRavenFromPath.html @@ -29,10 +29,9 @@

    SYNOPSIS ^DESCRIPTION ^

     removeRavenFromPath
        This function removes all RAVEN directories and subdirectories from the
    -   MATLAB path. This function only removes RAVEN from the MATLAB path, it
    -   does not delete the RAVEN folder itself. If RAVEN was installed as
    -   MATLAB Add-On, the user is prompted to instead uninstall RAVEN via the
    -   Add-On Manager.
    +   MATLAB path and saves the path. The RAVEN folder itself is kept. If RAVEN
    +   is installed as MATLAB Add-On, the function raises an error that
    +   advises uninstalling RAVEN via the Add-On Manager.
     
      Usage: removeRavenFromPath()
    @@ -52,46 +51,45 @@

    SOURCE CODE ^
    0001 function removeRavenFromPath()
     0002 % removeRavenFromPath
     0003 %   This function removes all RAVEN directories and subdirectories from the
    -0004 %   MATLAB path. This function only removes RAVEN from the MATLAB path, it
    -0005 %   does not delete the RAVEN folder itself. If RAVEN was installed as
    -0006 %   MATLAB Add-On, the user is prompted to instead uninstall RAVEN via the
    -0007 %   Add-On Manager.
    -0008 %
    -0009 % Usage: removeRavenFromPath()
    -0010 
    -0011 % Check for installation as Add-On
    -0012 addList = matlab.addons.installedAddons;
    -0013 if any(strcmp(addList.Name,'RAVEN Toolbox'))
    -0014     error(['RAVEN is installed as MATLAB Add-On. You should uninstall RAVEN via '...
    -0015         'the Add-On Manager instead. Once uninstalled, you may attempt to run '...
    -0016         'removeRavenFromPath again to remove any potential remaining RAVEN installations.'])
    -0017 end
    -0018 
    -0019 % Get current paths
    -0020 currPath  = transpose(strsplit(path(),{';',':'}));
    -0021 ravenPath = false(numel(currPath),1);
    -0022 for i=1:numel(currPath)
    -0023     dirCont = ls(currPath{i});
    -0024     dirCont = cellstr(dirCont(3:end,:));
    -0025     if any(contains(dirCont,{'ravenCobraWrapper.m','makeFakeBlastStructure.m','setRavenSolver.m'})) % A few (hopefully) unique RAVEN functions
    -0026         ravenPath(i)=true;
    -0027     end
    -0028 end
    -0029 
    -0030 ravenPath = unique(regexprep(currPath(ravenPath),'(\\|\/)((reconstruction)|(conversion)|(solver))',''));
    -0031 addOnDir = contains(ravenPath,'MATLAB Add-Ons');
    -0032 if any(addOnDir)
    -0033     warning(['RAVEN is installed as MATLAB Add-On at the following directory, but MATLAB '...
    -0034         'somehow does not recognize this installation. You are advised to manually delete '...
    -0035         'the directory to remove any RAVEN remnants: ' ravenPath{addOnDir}]);
    -0036 end
    -0037 ravenSubDirs = currPath(startsWith(currPath,ravenPath));
    -0038 % Remove from path
    -0039 if ~isempty(ravenSubDirs)
    -0040     rmpath(ravenSubDirs{:});
    -0041     savepath;
    -0042 end
    -0043 end
    +0004 % MATLAB path and saves the path. The RAVEN folder itself is kept. If RAVEN +0005 % is installed as MATLAB Add-On, the function raises an error that +0006 % advises uninstalling RAVEN via the Add-On Manager. +0007 % +0008 % Usage: removeRavenFromPath() +0009 +0010 % Check for installation as Add-On +0011 addList = matlab.addons.installedAddons; +0012 if any(strcmp(addList.Name,'RAVEN Toolbox')) +0013 error(['RAVEN is installed as MATLAB Add-On. You should uninstall RAVEN via '... +0014 'the Add-On Manager instead. Once uninstalled, you may attempt to run '... +0015 'removeRavenFromPath again to remove any potential remaining RAVEN installations.']) +0016 end +0017 +0018 % Get current paths +0019 currPath = transpose(strsplit(path(),{';',':'})); +0020 ravenPath = false(numel(currPath),1); +0021 for i=1:numel(currPath) +0022 dirCont = ls(currPath{i}); +0023 dirCont = cellstr(dirCont(3:end,:)); +0024 if any(contains(dirCont,{'ravenCobraWrapper.m','makeFakeBlastStructure.m','setRavenSolver.m'})) % A few (hopefully) unique RAVEN functions +0025 ravenPath(i)=true; +0026 end +0027 end +0028 +0029 ravenPath = unique(regexprep(currPath(ravenPath),'(\\|\/)((reconstruction)|(conversion)|(solver))','')); +0030 addOnDir = contains(ravenPath,'MATLAB Add-Ons'); +0031 if any(addOnDir) +0032 warning(['RAVEN is installed as MATLAB Add-On at the following directory, but MATLAB '... +0033 'somehow does not recognize this installation. You are advised to manually delete '... +0034 'the directory to remove any RAVEN remnants: ' ravenPath{addOnDir}]); +0035 end +0036 ravenSubDirs = currPath(startsWith(currPath,ravenPath)); +0037 % Remove from path +0038 if ~isempty(ravenSubDirs) +0039 rmpath(ravenSubDirs{:}); +0040 savepath; +0041 end +0042 end
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/installation/updateDocumentation.html b/docs/html/installation/updateDocumentation.html similarity index 67% rename from doc/installation/updateDocumentation.html rename to docs/html/installation/updateDocumentation.html index fafe7404..242f22a8 100644 --- a/doc/installation/updateDocumentation.html +++ b/docs/html/installation/updateDocumentation.html @@ -28,13 +28,13 @@

    SYNOPSIS ^DESCRIPTION ^

     updateDocumentation
    -    Regenerates the HTML documentation in RAVEN's doc directory from the
    -    function help texts. The doc directory is deleted and rebuilt, so
    -    anything hand-edited there is lost.
    +    Regenerates the HTML documentation in the docs/html directory of RAVEN from
    +    the function help texts. The docs/html directory is deleted and rebuilt, so
    +    manual edits there are lost.
     
    -    The Documentation workflow runs this on every push to a release branch
    -    and commits the result, so doc stays in step with the sources without
    -    anyone running it by hand. Run it locally only to preview.
    +    The Documentation workflow runs this function on every push to the develop
    +    or main branch that changes a .m file or the workflow file, and commits the
    +    result. Running the function locally previews the documentation.
     
         Usage: updateDocumentation()
    @@ -53,48 +53,48 @@

    CROSS-REFERENCE INFORMATION ^
 <h2><a name=SOURCE CODE ^

    0001 function updateDocumentation()
     0002 % updateDocumentation
    -0003 %    Regenerates the HTML documentation in RAVEN's doc directory from the
    -0004 %    function help texts. The doc directory is deleted and rebuilt, so
    -0005 %    anything hand-edited there is lost.
    +0003 %    Regenerates the HTML documentation in the docs/html directory of RAVEN from
    +0004 %    the function help texts. The docs/html directory is deleted and rebuilt, so
    +0005 %    manual edits there are lost.
     0006 %
    -0007 %    The Documentation workflow runs this on every push to a release branch
    -0008 %    and commits the result, so doc stays in step with the sources without
    -0009 %    anyone running it by hand. Run it locally only to preview.
    +0007 %    The Documentation workflow runs this function on every push to the develop
    +0008 %    or main branch that changes a .m file or the workflow file, and commits the
    +0009 %    result. Running the function locally previews the documentation.
     0010 %
     0011 %    Usage: updateDocumentation()
     0012 
     0013 %Get the RAVEN path
     0014 ravenDir=findRAVENroot();
    -0015 %Make sure that RAVEN-provided m2html is used
    -0016 path(fullfile(ravenDir,'software','m2html'),path);
    -0017 %Get a non-redundant list of RAVEN subdirectories containing MATLAB
    -0018 %functions. Absolute paths are not compatible with M2HTML, so convert them
    -0019 %to the relative paths instead.
    -0020 ravenDirs=dir(fullfile(ravenDir,'**/*.m'));
    -0021 ravenDirs=unique({ravenDirs.folder})';
    -0022 
    -0023 %Get rid of MATLAB functions from external software
    -0024 ravenDirs(startsWith(ravenDirs,strcat(ravenDir,filesep,'software')))=[];
    -0025 ravenDirs(startsWith(ravenDirs,strcat(ravenDir,filesep,'legacy',filesep,'software')))=[];
    +0015 srcDir=fullfile(ravenDir,'src');
    +0016 %Make sure that RAVEN-provided m2html is used
    +0017 path(fullfile(ravenDir,'src','external','m2html'),path);
    +0018 %Get a non-redundant list of RAVEN subdirectories containing MATLAB
    +0019 %functions. Absolute paths are not compatible with M2HTML, so convert them
    +0020 %to the relative paths instead.
    +0021 ravenDirs=dir(fullfile(srcDir,'**/*.m'));
    +0022 ravenDirs=unique({ravenDirs.folder})';
    +0023 
    +0024 %Get rid of MATLAB functions from external software
    +0025 ravenDirs(startsWith(ravenDirs,strcat(srcDir,filesep,'external')))=[];
     0026 
     0027 %Skip deprecated wrappers: they are on the path so existing scripts keep
     0028 %working, but documenting them would advertise functions that are on their
     0029 %way out. Each one names its replacement when called.
    -0030 ravenDirs(startsWith(ravenDirs,strcat(ravenDir,filesep,'deprecated')))=[];
    +0030 ravenDirs(startsWith(ravenDirs,strcat(srcDir,filesep,'deprecated')))=[];
     0031 
     0032 %Remove keggModel.mat if it exists
    -0033 if exist(fullfile(ravenDir,'reconstruction','kegg','keggModel.mat'), 'file') == 2
    -0034     delete(fullfile(ravenDir,'reconstruction','kegg','keggModel.mat'));
    +0033 if exist(fullfile(ravenDir,'src','reconstruction','kegg','keggModel.mat'), 'file') == 2
    +0034     delete(fullfile(ravenDir,'src','reconstruction','kegg','keggModel.mat'));
     0035 end
     0036 
    -0037 %Remove the existing "doc" directory from RAVEN
    -0038 docDir=fullfile(ravenDir,'doc');
    +0037 %Remove the existing "docs/html" directory from RAVEN
    +0038 docDir=fullfile(ravenDir,'docs','html');
     0039 if isfolder(docDir)
     0040     rmdir(docDir,'s');
     0041 end
     0042 
     0043 %Make relative path
    -0044 relStart = numel(ravenDir)+2;
    +0044 relStart = numel(srcDir)+2;
     0045 for i=1:numel(ravenDirs)
     0046     ravenDirs{i,1} = ravenDirs{i,1}(relStart:end);
     0047 end
    @@ -111,11 +111,11 @@ 

    SOURCE CODE ^end 0059 end 0060 -0061 %Save the current working directory and go to RAVEN root directory +0061 %Save the current working directory and go to RAVEN source directory 0062 originalDir=pwd; -0063 cd(ravenDir); +0063 cd(srcDir); 0064 %Generate HTML documentation files for RAVEN MATLAB functions -0065 m2html('mFiles',ravenDirs,'htmldir','doc'); +0065 m2html('mFiles',ravenDirs,'htmldir',docDir); 0066 %Go back to the original working directory 0067 cd(originalDir); 0068 diff --git a/doc/io/checkFileExistence.html b/docs/html/io/checkFileExistence.html similarity index 84% rename from doc/io/checkFileExistence.html rename to docs/html/io/checkFileExistence.html index 7e40a45f..9e2c83fa 100644 --- a/doc/io/checkFileExistence.html +++ b/docs/html/io/checkFileExistence.html @@ -29,14 +29,14 @@

    SYNOPSIS ^DESCRIPTION ^

     checkFileExistence  Check whether files exist.
     
    - If no full path is given a file should be located in the current folder,
    - which by default is appended to the filename.
    + A file name without a full path refers to a file in the current folder,
    + which is prepended to the file name.
     
      Parameters
      ----------
      files : char or cell
    -     string or cell array of strings with path to file(s) or path or
    -     filename(s).
    +     string or cell array of strings with the path or file name of one or
    +     more files.
     
      Name-Value Arguments
      --------------------
    @@ -48,17 +48,17 @@ 

    DESCRIPTION ^SOURCE CODE ^
    0001 function files=checkFileExistence(files,varargin)
     0002 % checkFileExistence  Check whether files exist.
     0003 %
    -0004 % If no full path is given a file should be located in the current folder,
    -0005 % which by default is appended to the filename.
    +0004 % A file name without a full path refers to a file in the current folder,
    +0005 % which is prepended to the file name.
     0006 %
     0007 % Parameters
     0008 % ----------
     0009 % files : char or cell
    -0010 %     string or cell array of strings with path to file(s) or path or
    -0011 %     filename(s).
    +0010 %     string or cell array of strings with the path or file name of one or
    +0011 %     more files.
     0012 %
     0013 % Name-Value Arguments
     0014 % --------------------
    @@ -99,17 +99,17 @@ 

    SOURCE CODE ^% - 2 : copy file(s) to system default temporary folder and return 0021 % full path 0022 % allowSpace : logical -0023 % whether the 'space' character is allowed in the path (default true). +0023 % true if the space character is allowed in the path (default true). 0024 % checkExist : logical -0025 % whether file existence should really be checked, as this function can -0026 % also be used to return the full path to a new file (default true). -0027 % Can only be set to false if fullOrTemp is set to 1. +0025 % true if file existence is checked. Setting it to false returns the full +0026 % path to a file that does not exist yet (default true). Can only be set +0027 % to false if fullOrTemp is set to 1. 0028 % 0029 % Returns 0030 % ------- 0031 % files : char or cell -0032 % string or cell array of strings with updated paths if fullOrTemp was -0033 % set as 1 or 2, otherwise original paths are returned. +0032 % string or cell array of strings with updated paths if fullOrTemp is 1 +0033 % or 2, otherwise the original paths. 0034 % 0035 % Examples 0036 % -------- diff --git a/docs/html/io/cleanSheet.html b/docs/html/io/cleanSheet.html new file mode 100644 index 00000000..2d5437ac --- /dev/null +++ b/docs/html/io/cleanSheet.html @@ -0,0 +1,193 @@ + + + + Description of cleanSheet + + + + + + + + + +
    Home > io > cleanSheet.m
    + + + +

    cleanSheet +

    + +

    PURPOSE ^

    +
    cleanSheet Clean up an Excel sheet.
    + +

    SYNOPSIS ^

    +
    function [raw,keptRows,keptCols]=cleanSheet(raw,varargin)
    + +

    DESCRIPTION ^

    +
     cleanSheet  Clean up an Excel sheet.
    +
    + Removes empty rows and columns, and performs further checks.
    +
    + Parameters
    + ----------
    + raw : cell
    +     cell array with the data in the sheet.
    +
    + Name-Value Arguments
    + --------------------
    + removeComments : logical
    +     true if commented lines (non-empty first cell in each row) should be
    +     removed (default true).
    + removeOnlyCap : logical
    +     true to remove columns with captions but no other values (default
    +     false).
    + removeNoCap : logical
    +     true to remove columns without captions (default true).
    + removeEmptyRows : logical
    +     true to remove rows with no non-empty cells (default true).
    +
    + Returns
    + -------
    + raw : cell
    +     the cleaned cell array.
    + keptRows : double
    +     indices of the kept rows in the original structure.
    + keptCols : double
    +     indices of the kept columns in the original structure.
    +
    + Examples
    + --------
    +     [raw, keptRows, keptCols] = cleanSheet(raw, removeComments, ...
    +         removeOnlyCap, removeNoCap, removeEmptyRows);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 % cleanSheet  Clean up an Excel sheet.
    +0002 %
    +0003 % Removes empty rows and columns, and performs further checks.
    +0004 %
    +0005 % Parameters
    +0006 % ----------
    +0007 % raw : cell
    +0008 %     cell array with the data in the sheet.
    +0009 %
    +0010 % Name-Value Arguments
    +0011 % --------------------
    +0012 % removeComments : logical
    +0013 %     true if commented lines (non-empty first cell in each row) should be
    +0014 %     removed (default true).
    +0015 % removeOnlyCap : logical
    +0016 %     true to remove columns with captions but no other values (default
    +0017 %     false).
    +0018 % removeNoCap : logical
    +0019 %     true to remove columns without captions (default true).
    +0020 % removeEmptyRows : logical
    +0021 %     true to remove rows with no non-empty cells (default true).
    +0022 %
    +0023 % Returns
    +0024 % -------
    +0025 % raw : cell
    +0026 %     the cleaned cell array.
    +0027 % keptRows : double
    +0028 %     indices of the kept rows in the original structure.
    +0029 % keptCols : double
    +0030 %     indices of the kept columns in the original structure.
    +0031 %
    +0032 % Examples
    +0033 % --------
    +0034 %     [raw, keptRows, keptCols] = cleanSheet(raw, removeComments, ...
    +0035 %         removeOnlyCap, removeNoCap, removeEmptyRows);
    +0036 function [raw,keptRows,keptCols]=cleanSheet(raw,varargin)
    +0037 p=parseRAVENargs(varargin, {'removeComments',true; 'removeOnlyCap',false; 'removeNoCap',true; 'removeEmptyRows',true});
    +0038 removeComments=p.removeComments; removeOnlyCap=p.removeOnlyCap; removeNoCap=p.removeNoCap; removeEmptyRows=p.removeEmptyRows;
    +0039 
    +0040 keptRows=1:size(raw,1);
    +0041 keptRows=keptRows(:);
    +0042 keptCols=1:size(raw,2);
    +0043 keptCols=keptCols(:);
    +0044 
    +0045 %First check that it's a cell array. If a sheet is completely empty, then
    +0046 %raw=NaN
    +0047 if iscell(raw)
    +0048     %Clear cells which contain only white spaces or NaN. This could happen
    +0049     %if you accidentally inserted a space, or if a blank/NaN cell was read
    +0050     %from the source file.
    +0051     whites=cellfun(@wrapperWS,raw);
    +0052     raw(whites)={[]};
    +0053     
    +0054     %Find the rows that are not commented. This corresponds to the first
    +0055     %row and the ones which are empty in the first column
    +0056     if removeComments==true
    +0057         keepers=cellfun(@isempty,raw(:,1));
    +0058         keepers(1)=true;
    +0059         raw=raw(keepers,:);
    +0060         keptRows=keptRows(keepers);
    +0061     end
    +0062     
    +0063     %Remove columns that don't have string headers. If you cut and paste a
    +0064     %lot in the sheet there tends to be columns that are empty
    +0065     if removeNoCap==true
    +0066         I=cellfun(@isstr,raw(1,:));
    +0067         raw=raw(:,I);
    +0068         keptCols=keptCols(I);
    +0069     end
    +0070     
    +0071     %Remove columns which are empty except for header
    +0072     if removeOnlyCap==true
    +0073         I=~all(cellfun(@isempty,raw(2:end,:)),1);
    +0074         raw=raw(:,I);
    +0075         keptCols=keptCols(I);
    +0076     end
    +0077     
    +0078     %Check if there are any rows that are all empty. This could happen if
    +0079     %it reads too far or if the user has inserted them for layout reasons.
    +0080     %Remove any such rows
    +0081     if removeEmptyRows==true
    +0082         I=~all(cellfun(@isempty,raw),2);
    +0083         raw=raw(I,:);
    +0084         keptRows=keptRows(I);
    +0085     end
    +0086 else
    +0087     raw={[]};
    +0088 end
    +0089 
    +0090 %Checks if something is all white spaces or NaN
    +0091     function I=wrapperWS(A)
    +0092         if isnan(A)
    +0093             I=true;
    +0094         else
    +0095             %isstrprop gives an error if boolean
    +0096             if islogical(A)
    +0097                 I=false;
    +0098             else
    +0099                 %I have to make this check, since otherwise it will pick up
    +0100                 %on doubles for which the ASCII representation is a white
    +0101                 %space character
    +0102                 if isnumeric(A)
    +0103                     I=false;
    +0104                 else
    +0105                     I=all(isstrprop(A,'wspace'));
    +0106                 end
    +0107             end
    +0108         end
    +0109     end
    +0110 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/io/exportForGit.html b/docs/html/io/exportForGit.html new file mode 100644 index 00000000..2f46a071 --- /dev/null +++ b/docs/html/io/exportForGit.html @@ -0,0 +1,367 @@ + + + + Description of exportForGit + + + + + + + + + +
    Home > io > exportForGit.m
    + + + +

    exportForGit +

    + +

    PURPOSE ^

    +
    exportForGit Export a model for a Git-maintained model repository.
    + +

    SYNOPSIS ^

    +
    function out=exportForGit(model,varargin)
    + +

    DESCRIPTION ^

    +
     exportForGit  Export a model for a Git-maintained model repository.
    +
    + Generates a directory structure and populates it with model files, ready
    + to be committed to a Git or GitHub repository of a model. Writes the
    + model as SBML L3V1 FBCv2 (XML) and YAML, COBRA text, MATLAB MAT-file and
    + Microsoft Excel formats.
    +
    + The flags mainBranchFlag, subDirs, COBRAtext and neverPrefixIDs accept a
    + scalar logical, the value 0 or 1, or an empty value for the default. Text
    + and other types raise RAVEN:badInput.
    +
    + Parameters
    + ----------
    + model : struct
    +     model structure in RAVEN format that should be exported.
    +
    + Name-Value Arguments
    + --------------------
    + prefix : char
    +     prefix for all filenames (default 'model').
    + path : char
    +     path where the directory structure should be generated and populated
    +     with all files (default current working directory). The folder, with
    +     any missing parent folders, is created if it does not exist.
    + formats : cell
    +     cell array of strings specifying in what file formats the model
    +     should be exported (default all formats as {'mat', 'txt', 'xlsx',
    +     'xml', 'yml'}).
    + mainBranchFlag : logical
    +     true to raise an error if RAVEN (and GECKO for enzyme-constrained
    +     models) is not on the main or master branch (default false).
    + subDirs : logical
    +     true to write the model files of each file format in their own
    +     subdirectory, with 'model' as parent directory, following the
    +     standard-GEM repository format. If false, all files are stored in the
    +     same folder (default true).
    + COBRAtext : logical
    +     true to write the txt file in COBRA Toolbox format, which uses
    +     metabolite IDs. If false, the file uses metabolite names and
    +     compartments (default false).
    + neverPrefixIDs : logical
    +     true if prefixes are never added to identifiers, even if they start
    +     with digits. This can result in invalid SBML files (default false).
    +
    + Examples
    + --------
    +     exportForGit(model, prefix, path, formats, mainBranchFlag, subDirs, ...
    +         COBRAtext, neverPrefixIDs);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • exportModel exportModel Export a constraint-based model to an SBML file (L3V1 FBCv2).
    • exportToExcelFormat exportToExcelFormat Export a model to the Microsoft Excel model format.
    • writeYAMLmodel writeYAMLmodel Write a model to a YAML file in the cobra-format !!omap structure.
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function out=exportForGit(model,varargin)
    +0002 % exportForGit  Export a model for a Git-maintained model repository.
    +0003 %
    +0004 % Generates a directory structure and populates it with model files, ready
    +0005 % to be committed to a Git or GitHub repository of a model. Writes the
    +0006 % model as SBML L3V1 FBCv2 (XML) and YAML, COBRA text, MATLAB MAT-file and
    +0007 % Microsoft Excel formats.
    +0008 %
    +0009 % The flags mainBranchFlag, subDirs, COBRAtext and neverPrefixIDs accept a
    +0010 % scalar logical, the value 0 or 1, or an empty value for the default. Text
    +0011 % and other types raise RAVEN:badInput.
    +0012 %
    +0013 % Parameters
    +0014 % ----------
    +0015 % model : struct
    +0016 %     model structure in RAVEN format that should be exported.
    +0017 %
    +0018 % Name-Value Arguments
    +0019 % --------------------
    +0020 % prefix : char
    +0021 %     prefix for all filenames (default 'model').
    +0022 % path : char
    +0023 %     path where the directory structure should be generated and populated
    +0024 %     with all files (default current working directory). The folder, with
    +0025 %     any missing parent folders, is created if it does not exist.
    +0026 % formats : cell
    +0027 %     cell array of strings specifying in what file formats the model
    +0028 %     should be exported (default all formats as {'mat', 'txt', 'xlsx',
    +0029 %     'xml', 'yml'}).
    +0030 % mainBranchFlag : logical
    +0031 %     true to raise an error if RAVEN (and GECKO for enzyme-constrained
    +0032 %     models) is not on the main or master branch (default false).
    +0033 % subDirs : logical
    +0034 %     true to write the model files of each file format in their own
    +0035 %     subdirectory, with 'model' as parent directory, following the
    +0036 %     standard-GEM repository format. If false, all files are stored in the
    +0037 %     same folder (default true).
    +0038 % COBRAtext : logical
    +0039 %     true to write the txt file in COBRA Toolbox format, which uses
    +0040 %     metabolite IDs. If false, the file uses metabolite names and
    +0041 %     compartments (default false).
    +0042 % neverPrefixIDs : logical
    +0043 %     true if prefixes are never added to identifiers, even if they start
    +0044 %     with digits. This can result in invalid SBML files (default false).
    +0045 %
    +0046 % Examples
    +0047 % --------
    +0048 %     exportForGit(model, prefix, path, formats, mainBranchFlag, subDirs, ...
    +0049 %         COBRAtext, neverPrefixIDs);
    +0050 p=parseRAVENargs(varargin, {'prefix',[]; 'path',[]; 'formats',[]; 'mainBranchFlag',[]; 'subDirs',[]; 'COBRAtext',[]; 'neverPrefixIDs',false});
    +0051 prefix=p.prefix; path=p.path; formats=p.formats; mainBranchFlag=p.mainBranchFlag; subDirs=p.subDirs; COBRAtext=p.COBRAtext; neverPrefixIDs=p.neverPrefixIDs;
    +0052 COBRAtext=checkFlag(COBRAtext,'COBRAtext',false);
    +0053 subDirs=checkFlag(subDirs,'subDirs',true);
    +0054 mainBranchFlag=checkFlag(mainBranchFlag,'mainBranchFlag',false);
    +0055 neverPrefixIDs=checkFlag(neverPrefixIDs,'neverPrefixIDs',false);
    +0056 if isempty(formats)
    +0057     formats={'mat', 'txt', 'xlsx', 'xml', 'yml'};
    +0058 else
    +0059     formats=convertCharArray(formats);
    +0060 end
    +0061 if any(~ismember(formats, {'mat', 'txt', 'xlsx', 'xml', 'yml'}))
    +0062     EM='Unknown file format defined. Only mat, txt, xlsx, xml and yml are allowed file formats.';
    +0063     error(EM)
    +0064 end
    +0065 if isempty(path)
    +0066     path='.';
    +0067 else
    +0068     path=char(path);
    +0069 end
    +0070 if isempty(prefix)
    +0071     prefix='model';
    +0072 else
    +0073     prefix=char(prefix);
    +0074 end
    +0075 
    +0076 %Sort reactions, metabolites and genes alphabetically
    +0077 model=sortIdentifiers(model);
    +0078 
    +0079 %Get versions or commits of toolboxes. GECKO is only relevant for
    +0080 %enzyme-constrained models (those with an "ec" field).
    +0081 RAVENver = getToolboxVersion('RAVEN','ravenCobraWrapper.m',mainBranchFlag);
    +0082 if isfield(model,'ec')
    +0083     GECKOver = getToolboxVersion('GECKO','GECKOInstaller.m',mainBranchFlag);
    +0084 else
    +0085     GECKOver = [];
    +0086 end
    +0087 %Retrieve libSBML version:
    +0088 [ravenDir,prevDir]=findRAVENroot();
    +0089 try % 5.17.0 and newer
    +0090     libSBMLver=OutputSBML_RAVEN;
    +0091     libSBMLver=libSBMLver.libSBML_version_string;
    +0092 catch % before 5.17.0
    +0093     fid = fopen('tempModelForLibSBMLversion.xml','w+');
    +0094     fclose(fid);
    +0095     evalc('[~,~,libSBMLver]=TranslateSBML_RAVEN(''tempModelForLibSBMLversion.xml'',0,0)');
    +0096     libSBMLver=libSBMLver.libSBML_version_string;
    +0097     delete('tempModelForLibSBMLversion.xml');
    +0098 end
    +0099 
    +0100 % Make the folders, no warnings if they already exist
    +0101 if subDirs
    +0102     path=fullfile(path,'model');
    +0103     filePath=strcat(path,filesep,{'txt','yml','mat','xlsx','xml'});
    +0104     [~,~,~]=mkdir(path);
    +0105     for i = 1:length(formats)
    +0106         [~,~,~]=mkdir(fullfile(path,formats{i}));
    +0107     end
    +0108 else
    +0109     [~,~,~]=mkdir(path);
    +0110     filePath=cell(1,5); filePath(:)={path};
    +0111 end
    +0112 
    +0113 
    +0114 % Write TXT format
    +0115 if ismember('txt', formats)
    +0116     fid=fopen(fullfile(filePath{1},strcat(prefix,'.txt')),'w');
    +0117     if COBRAtext==true
    +0118         eqns=constructEquations(model,model.rxns,false,false,false);
    +0119         eqns=strrep(eqns,' => ','  -> ');
    +0120         eqns=strrep(eqns,' <=> ','  <=> ');
    +0121         eqns=regexprep(eqns,'> $','>');
    +0122         grRules=regexprep(model.grRules,'\((?!\()','( ');
    +0123         grRules=regexprep(grRules,'(?<!\))\)',' )');
    +0124     else
    +0125         eqns=constructEquations(model,model.rxns);
    +0126         grRules=model.grRules;
    +0127     end
    +0128     fprintf(fid, 'Rxn id\tFormula\tGene-reaction association\tLB\tUB\tObjective\n');
    +0129     for i = 1:numel(model.rxns)
    +0130         fprintf(fid, '%s\t', model.rxns{i});
    +0131         fprintf(fid, '%s\t', strtrim(eqns{i}));
    +0132         fprintf(fid, '%s\t', grRules{i});
    +0133         fprintf(fid, '%s\t%s\t%s\n', shortestNumber(model.lb(i)), shortestNumber(model.ub(i)), shortestNumber(model.c(i)));
    +0134     end
    +0135     fclose(fid);
    +0136 end
    +0137 
    +0138 % Write YML format
    +0139 if ismember('yml', formats)
    +0140     writeYAMLmodel(model,fullfile(filePath{2},strcat(prefix,'.yml')));
    +0141 end
    +0142 
    +0143 % Write MAT format
    +0144 if ismember('mat', formats)
    +0145     save(fullfile(filePath{3},strcat(prefix,'.mat')),'model');
    +0146 end
    +0147 
    +0148 % Write XLSX format
    +0149 if ismember('xlsx', formats)
    +0150     exportToExcelFormat(model,fullfile(filePath{4},strcat(prefix,'.xlsx')));
    +0151 end
    +0152 
    +0153 % Write XML format
    +0154 if ismember('xml', formats)
    +0155         exportModel(model,fullfile(filePath{5},strcat(prefix,'.xml')),neverPrefixIDs);
    +0156 end
    +0157 
    +0158 %Save file with versions:
    +0159 fid = fopen(fullfile(path,'dependencies.txt'),'wt');
    +0160 fprintf(fid,['MATLAB\t' version '\n']);
    +0161 fprintf(fid,['libSBML\t' libSBMLver '\n']);
    +0162 fprintf(fid,['RAVEN_toolbox\t' RAVENver '\n']);
    +0163 if ~isempty(GECKOver)
    +0164     fprintf(fid,['GECKO_toolbox\t' GECKOver '\n']);
    +0165 end
    +0166 if isfield(model,'modelVersion')
    +0167     fields = fieldnames(model.modelVersion);
    +0168     for i = 1:length(fields)
    +0169         value = model.modelVersion.(fields{i});
    +0170         fprintf(fid,[fields{i} '\t' num2str(value) '\n']);
    +0171     end
    +0172 end
    +0173 fclose(fid);
    +0174 end
    +0175 
    +0176 function s = shortestNumber(x)
    +0177 % shortestNumber  The shortest text that reads back as exactly x, so a bound
    +0178 % is never rounded on the way out (1/3 is 0.3333333333333333, 1000 is 1000).
    +0179 if isinf(x)
    +0180     if x > 0
    +0181         s = 'Inf';
    +0182     else
    +0183         s = '-Inf';
    +0184     end
    +0185     return
    +0186 elseif isnan(x)
    +0187     s = 'NaN';
    +0188     return
    +0189 end
    +0190 if x == floor(x) && abs(x) < 1e15
    +0191     s = sprintf('%d', x);
    +0192     return
    +0193 end
    +0194 for digits = 1:17
    +0195     s = sprintf(['%.' num2str(digits) 'g'], x);
    +0196     if str2double(s) == x
    +0197         break
    +0198     end
    +0199 end
    +0200 end
    +0201 
    +0202 function version = getToolboxVersion(toolbox,fileID,mainBranchFlag)
    +0203 % getToolboxVersion  Return the version of RAVEN or GECKO.
    +0204 %
    +0205 % Returns the version of the toolbox as stated in its version.txt, or
    +0206 % 'unknown' if that file cannot be found.
    +0207 %
    +0208 % toolbox        name of the toolbox (e.g. 'RAVEN' or 'GECKO').
    +0209 % fileID         a file only found in that toolbox, used to locate its root
    +0210 %                (e.g. 'ravenCobraWrapper.m' or 'GECKOInstaller.m').
    +0211 % mainBranchFlag if true, error if the toolbox is not on the main branch.
    +0212 currentPath = pwd;
    +0213 version     = '';
    +0214 
    +0215 %Try to find root of toolbox:
    +0216 try
    +0217     toolboxPath = which(fileID);                %full file path
    +0218     slashPos    = getSlashPos(toolboxPath);
    +0219     toolboxPath = toolboxPath(1:slashPos(end)); %folder path
    +0220     %Go up until the root is found:
    +0221     D = dir(toolboxPath);
    +0222     while ~ismember({'.git'},{D.name})
    +0223         slashPos    = getSlashPos(toolboxPath);
    +0224         toolboxPath = toolboxPath(1:slashPos(end-1));
    +0225         D           = dir(toolboxPath);
    +0226     end
    +0227     cd(toolboxPath);
    +0228 catch
    +0229     disp([toolbox ' toolbox cannot be found'])
    +0230     version = 'unknown';
    +0231 end
    +0232 %Check if in main:
    +0233 if mainBranchFlag
    +0234     [~,currentBranch] = system('git rev-parse --abbrev-ref HEAD');
    +0235     currentBranch = strtrim(currentBranch);
    +0236     if ~any([strcmp(currentBranch, "main"), strcmp(currentBranch, "master")])
    +0237         cd(currentPath);
    +0238         error('RAVEN:notMainBranch',['ERROR: ' toolbox ' not in main (or master) branch. Check-out this branch of ' toolbox ' before submitting model for Git.'])
    +0239     end
    +0240 end
    +0241 %Read the version from the toolbox version.txt; report 'unknown' otherwise
    +0242 if isempty(version)
    +0243     fid = fopen([toolboxPath 'version.txt'],'r');
    +0244     if fid ~= -1
    +0245         version = fscanf(fid,'%s');
    +0246         fclose(fid);
    +0247     end
    +0248     if isempty(version)
    +0249         version = 'unknown';
    +0250     end
    +0251 end
    +0252 cd(currentPath);
    +0253 end
    +0254 
    +0255 function slashPos = getSlashPos(path)
    +0256 slashPos = strfind(path,'\');       %Windows
    +0257 if isempty(slashPos)
    +0258     slashPos = strfind(path,'/');   %MAC/Linux
    +0259 end
    +0260 end
    +0261 
    +0262 function flag = checkFlag(flag, name, default)
    +0263 %An empty value gives the default; otherwise a scalar logical or 0/1 is required
    +0264 if isempty(flag)
    +0265     flag = default;
    +0266 elseif (islogical(flag) || isnumeric(flag)) && isscalar(flag) && isreal(flag) && (flag == 0 || flag == 1)
    +0267     flag = logical(flag);
    +0268 else
    +0269     error('RAVEN:badInput', '%s must be a scalar logical or 0/1.', name);
    +0270 end
    +0271 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/io/exportModel.html b/docs/html/io/exportModel.html similarity index 98% rename from doc/io/exportModel.html rename to docs/html/io/exportModel.html index 322fd827..79f63fd4 100644 --- a/doc/io/exportModel.html +++ b/docs/html/io/exportModel.html @@ -37,18 +37,18 @@

    DESCRIPTION ^SOURCE CODE ^% Name-Value Arguments 0010 % -------------------- 0011 % fileName : char -0012 % filename to export the model to. A dialog window will open if no file -0013 % name is specified. +0012 % file name to export the model to. A dialog window opens if no file name +0013 % is specified. 0014 % neverPrefix : logical 0015 % true if prefixes are never added to identifiers, even if they start -0016 % with e.g. digits. This might result in invalid SBML files (default -0017 % false). -0018 % supressWarnings : logical -0019 % true if warnings should be suppressed. This might result in invalid -0020 % SBML files, as no checks are performed (default false). +0016 % with digits. This can result in invalid SBML files (default false). +0017 % supressWarnings : logical +0018 % true if warnings should be suppressed. The check of the model +0019 % structure (checkModelStruct) is skipped, which can result in invalid +0020 % SBML files (default false). 0021 % sortIds : logical -0022 % whether metabolites, reactions and genes should be sorted -0023 % alphabetically by their identifiers (default false). +0022 % true to sort metabolites, reactions and genes alphabetically by their +0023 % identifiers (default false). 0024 % 0025 % Examples 0026 % -------- diff --git a/doc/io/exportToExcelFormat.html b/docs/html/io/exportToExcelFormat.html similarity index 98% rename from doc/io/exportToExcelFormat.html rename to docs/html/io/exportToExcelFormat.html index 47f89e1a..c034f1d2 100644 --- a/doc/io/exportToExcelFormat.html +++ b/docs/html/io/exportToExcelFormat.html @@ -37,11 +37,11 @@

    DESCRIPTION ^SOURCE CODE ^% Name-Value Arguments 0010 % -------------------- 0011 % fileName : char -0012 % file name of the Excel file. Only xlsx format is supported. A dialog -0013 % window will open if fileName is empty. +0012 % file name of the Excel file. Only the xlsx format is supported. A +0013 % dialog window opens if fileName is empty. 0014 % sortIds : logical -0015 % whether metabolites, reactions and genes should be sorted -0016 % alphabetically by their identifiers (default false). +0015 % true to sort metabolites, reactions and genes alphabetically by their +0016 % identifiers (default false). 0017 % 0018 % Examples 0019 % -------- diff --git a/docs/html/io/importModel.html b/docs/html/io/importModel.html new file mode 100644 index 00000000..5c744fbd --- /dev/null +++ b/docs/html/io/importModel.html @@ -0,0 +1,1093 @@ + + + + Description of importModel + + + + + + + + + +
    Home > io > importModel.m
    + + + +

    importModel +

    + +

    PURPOSE ^

    +
    importModel Import a constraint-based model from an SBML file.
    + +

    SYNOPSIS ^

    +
    function model=importModel(varargin)
    + +

    DESCRIPTION ^

    +
     importModel  Import a constraint-based model from an SBML file.
    +
    + Name-Value Arguments
    + --------------------
    + fileName : char
    +     a SBML file to import. A dialog window opens if no file name is
    +     specified.
    + removeExcMets : logical
    +     true to remove exchange metabolites from the imported model (default
    +     true).
    + removePrefix : logical
    +     true to remove identifier prefixes when loading the model: G_ for
    +     genes, R_ for reactions, M_ for metabolites, and C_ for compartments.
    +     The prefix of a type is removed only if all identifiers of that type
    +     contain it (default true).
    + supressWarnings : logical
    +     true to suppress warnings about the model structure (default false).
    +
    + Returns
    + -------
    + model : struct
    +     imported model structure with fields:
    +
    +     - id : model ID
    +     - name : name of model contents
    +     - annotation : additional information about model
    +     - rxns : reaction ids
    +     - mets : metabolite ids
    +     - S : stoichiometric matrix
    +     - lb : lower bounds
    +     - ub : upper bounds
    +     - rev : reversibility vector
    +     - c : objective coefficients
    +     - b : equality constraints for the metabolite equations
    +     - comps : compartment ids
    +     - compNames : compartment names
    +     - compOutside : the id (as in comps) for the compartment surrounding
    +       each of the compartments
    +     - compMiriams : structure with MIRIAM information about the
    +       compartments
    +     - rxnNames : reaction description
    +     - rxnComps : compartments for reactions
    +     - grRules : reaction to gene rules in text form
    +     - rxnGeneMat : reaction-to-gene mapping in sparse matrix form
    +     - subSystems : subsystem name for each reaction
    +     - eccodes : EC-codes for the reactions
    +     - rxnMiriams : structure with MIRIAM information about the reactions
    +     - rxnNotes : reaction notes
    +     - rxnReferences : reaction references
    +     - rxnConfidenceScores : reaction confidence scores
    +     - rxnDeltaG : reaction Gibbs free energy, from the `deltaG` notes
    +       field (the convention that exportModel writes and raven-toolbox
    +       reads and writes; omitted if no reaction has one)
    +     - genes : list of all genes
    +     - geneComps : compartments for genes
    +     - geneMiriams : structure with MIRIAM information about the genes
    +     - geneShortNames : gene alternative names (for example ERG10)
    +     - proteins : protein associated to each gene
    +     - metNames : metabolite description
    +     - metComps : compartments for metabolites
    +     - inchis : InChI-codes for metabolites
    +     - metFormulas : metabolite chemical formula
    +     - metMiriams : structure with MIRIAM information about the metabolites
    +     - metCharges : metabolite charge
    +     - metDeltaG : metabolite Gibbs free energy, from the `deltaG` notes
    +       field (the convention that exportModel writes and raven-toolbox
    +       reads and writes; omitted if no metabolite has one)
    +     - unconstrained : true if the metabolite is an exchange metabolite
    +
    + Examples
    + --------
    +     model = importModel(fileName, removeExcMets, removePrefix, supressWarnings);
    +
    + Notes
    + -----
    + checkModelStruct checks the consistency of the imported model and raises
    + warnings about problems in the model structure. supressWarnings skips this
    + check.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function model=importModel(varargin)
    +0002 % importModel  Import a constraint-based model from an SBML file.
    +0003 %
    +0004 % Name-Value Arguments
    +0005 % --------------------
    +0006 % fileName : char
    +0007 %     a SBML file to import. A dialog window opens if no file name is
    +0008 %     specified.
    +0009 % removeExcMets : logical
    +0010 %     true to remove exchange metabolites from the imported model (default
    +0011 %     true).
    +0012 % removePrefix : logical
    +0013 %     true to remove identifier prefixes when loading the model: G_ for
    +0014 %     genes, R_ for reactions, M_ for metabolites, and C_ for compartments.
    +0015 %     The prefix of a type is removed only if all identifiers of that type
    +0016 %     contain it (default true).
    +0017 % supressWarnings : logical
    +0018 %     true to suppress warnings about the model structure (default false).
    +0019 %
    +0020 % Returns
    +0021 % -------
    +0022 % model : struct
    +0023 %     imported model structure with fields:
    +0024 %
    +0025 %     - id : model ID
    +0026 %     - name : name of model contents
    +0027 %     - annotation : additional information about model
    +0028 %     - rxns : reaction ids
    +0029 %     - mets : metabolite ids
    +0030 %     - S : stoichiometric matrix
    +0031 %     - lb : lower bounds
    +0032 %     - ub : upper bounds
    +0033 %     - rev : reversibility vector
    +0034 %     - c : objective coefficients
    +0035 %     - b : equality constraints for the metabolite equations
    +0036 %     - comps : compartment ids
    +0037 %     - compNames : compartment names
    +0038 %     - compOutside : the id (as in comps) for the compartment surrounding
    +0039 %       each of the compartments
    +0040 %     - compMiriams : structure with MIRIAM information about the
    +0041 %       compartments
    +0042 %     - rxnNames : reaction description
    +0043 %     - rxnComps : compartments for reactions
    +0044 %     - grRules : reaction to gene rules in text form
    +0045 %     - rxnGeneMat : reaction-to-gene mapping in sparse matrix form
    +0046 %     - subSystems : subsystem name for each reaction
    +0047 %     - eccodes : EC-codes for the reactions
    +0048 %     - rxnMiriams : structure with MIRIAM information about the reactions
    +0049 %     - rxnNotes : reaction notes
    +0050 %     - rxnReferences : reaction references
    +0051 %     - rxnConfidenceScores : reaction confidence scores
    +0052 %     - rxnDeltaG : reaction Gibbs free energy, from the `deltaG` notes
    +0053 %       field (the convention that exportModel writes and raven-toolbox
    +0054 %       reads and writes; omitted if no reaction has one)
    +0055 %     - genes : list of all genes
    +0056 %     - geneComps : compartments for genes
    +0057 %     - geneMiriams : structure with MIRIAM information about the genes
    +0058 %     - geneShortNames : gene alternative names (for example ERG10)
    +0059 %     - proteins : protein associated to each gene
    +0060 %     - metNames : metabolite description
    +0061 %     - metComps : compartments for metabolites
    +0062 %     - inchis : InChI-codes for metabolites
    +0063 %     - metFormulas : metabolite chemical formula
    +0064 %     - metMiriams : structure with MIRIAM information about the metabolites
    +0065 %     - metCharges : metabolite charge
    +0066 %     - metDeltaG : metabolite Gibbs free energy, from the `deltaG` notes
    +0067 %       field (the convention that exportModel writes and raven-toolbox
    +0068 %       reads and writes; omitted if no metabolite has one)
    +0069 %     - unconstrained : true if the metabolite is an exchange metabolite
    +0070 %
    +0071 % Examples
    +0072 % --------
    +0073 %     model = importModel(fileName, removeExcMets, removePrefix, supressWarnings);
    +0074 %
    +0075 % Notes
    +0076 % -----
    +0077 % checkModelStruct checks the consistency of the imported model and raises
    +0078 % warnings about problems in the model structure. supressWarnings skips this
    +0079 % check.
    +0080 
    +0081 p=parseRAVENargs(varargin, {'fileName',[]; 'removeExcMets',[]; 'removePrefix',[]; 'supressWarnings',false});
    +0082 fileName=p.fileName; removeExcMets=p.removeExcMets; removePrefix=p.removePrefix; supressWarnings=p.supressWarnings;
    +0083 if isempty(fileName)
    +0084     [fileName, pathName] = uigetfile({'*.xml;*.sbml'}, 'Please select the model file');
    +0085     if fileName == 0
    +0086         error('You should select a model file')
    +0087     else
    +0088         fileName = fullfile(pathName,fileName);
    +0089     end
    +0090 end
    +0091 fileName=char(fileName);
    +0092 if isempty(removeExcMets)
    +0093     removeExcMets=true;
    +0094 end
    +0095 
    +0096 if isempty(removePrefix)
    +0097     removePrefix=true;
    +0098 end
    +0099 
    +0100 fileName=checkFileExistence(fileName,1);
    +0101 % If path contains non-ASCII characters, copy file to tempdir first, as
    +0102 % libSBML is known to have problems with this on Windows:
    +0103 % https://sbml.org/software/libsbml/libsbml-docs/known-pitfalls/#matlab-on-windows-has-issues-with-unicode-filenames
    +0104 if ispc && any(double(fileName)>128)
    +0105     [~,originalFile,ext] = fileparts(fileName);
    +0106     tempFile = fullfile(tempdir,[originalFile ext]);
    +0107     copyfile(fileName,tempFile);
    +0108     fileName = tempFile;
    +0109 end
    +0110 
    +0111 %Set a canonical order of the model fields
    +0112 model=[];
    +0113 model.id=[];
    +0114 model.name=[];
    +0115 model.annotation=[];
    +0116 model.rxns={};
    +0117 model.mets={};
    +0118 model.S=[];
    +0119 model.lb=[];
    +0120 model.ub=[];
    +0121 model.rev=[];
    +0122 model.c=[];
    +0123 model.b=[];
    +0124 model.comps={};
    +0125 model.compNames={};
    +0126 model.compOutside={};
    +0127 model.compMiriams={};
    +0128 model.rxnNames={};
    +0129 model.rxnComps=[];
    +0130 model.grRules={};
    +0131 model.rxnGeneMat=[];
    +0132 model.subSystems={};
    +0133 model.eccodes={};
    +0134 model.rxnMiriams={};
    +0135 model.rxnNotes={};
    +0136 model.rxnReferences={};
    +0137 model.rxnConfidenceScores=[];
    +0138 model.genes={};
    +0139 model.geneComps=[];
    +0140 model.geneMiriams={};
    +0141 model.geneShortNames={};
    +0142 model.proteins={};
    +0143 model.metNames={};
    +0144 model.metComps=[];
    +0145 model.inchis={};
    +0146 model.metFormulas={};
    +0147 model.metMiriams={};
    +0148 model.metCharges=[];
    +0149 model.unconstrained=[];
    +0150 
    +0151 %Load the model using libSBML
    +0152 [modelSBML,errorMsg] = TranslateSBML_RAVEN(fileName,0,0,[1 1]);
    +0153 if exist('tempFile','var')
    +0154     delete(tempFile)
    +0155 end
    +0156 
    +0157 if isempty(modelSBML)
    +0158     EM=['There is a problem with the SBML file. Try using the SBML Validator at http://sbml.org/Facilities/Validator.\nlibSBML reports: ', errorMsg.message];
    +0159     error('RAVEN:badInput', '%s', EM);
    +0160 end
    +0161 
    +0162 %RAVEN only supports SBML Level 3 Version 1 with the FBC version 2 package.
    +0163 if ~(isfield(modelSBML,'SBML_level') && isequal(modelSBML.SBML_level,3) && ...
    +0164         isfield(modelSBML,'SBML_version') && isequal(modelSBML.SBML_version,1) && ...
    +0165         isfield(modelSBML,'fbc_version') && isequal(modelSBML.fbc_version,2))
    +0166     EM=['importModel only supports SBML Level 3 Version 1 with the FBC '...
    +0167         'version 2 package. Convert the model to this format first (for '...
    +0168         'example with another tool), or use an older RAVEN version to '...
    +0169         'import it.'];
    +0170     error('RAVEN:badInput', '%s', EM);
    +0171 end
    +0172 
    +0173 %Retrieve compartment names and IDs
    +0174 compartmentNames=cell(numel(modelSBML.compartment),1);
    +0175 compartmentIDs=cell(numel(modelSBML.compartment),1);
    +0176 compartmentOutside=cell(numel(modelSBML.compartment),1);
    +0177 compartmentMiriams=cell(numel(modelSBML.compartment),1);
    +0178 
    +0179 if isfield(modelSBML.compartment,'sboTerm') && isscalar(unique([modelSBML.compartment.sboTerm]))
    +0180     %If all the SBO terms are identical, do not add them to compMiriams
    +0181     modelSBML.compartment = rmfield(modelSBML.compartment,'sboTerm');
    +0182 end
    +0183 
    +0184 for i=1:numel(modelSBML.compartment)
    +0185     compartmentNames{i}=modelSBML.compartment(i).name;
    +0186     compartmentIDs{i}=modelSBML.compartment(i).id;
    +0187     if isfield(modelSBML.compartment(i),'outside')
    +0188         if ~isempty(modelSBML.compartment(i).outside)
    +0189             compartmentOutside{i}=modelSBML.compartment(i).outside;
    +0190         else
    +0191             compartmentOutside{i}='';
    +0192         end
    +0193     else
    +0194         compartmentOutside{i}=[];
    +0195     end
    +0196 
    +0197     if isfield(modelSBML.compartment(i),'annotation')
    +0198         compartmentMiriams{i}=parseMiriam(modelSBML.compartment(i).annotation);
    +0199     else
    +0200         compartmentMiriams{i}=[];
    +0201     end
    +0202 
    +0203     if isfield(modelSBML.compartment(i),'sboTerm') && ~(modelSBML.compartment(i).sboTerm==-1)
    +0204         compartmentMiriams{i} = addSBOtoMiriam(compartmentMiriams{i},modelSBML.compartment(i).sboTerm);
    +0205     end
    +0206 end
    +0207 
    +0208 %If there are no compartment names then use compartment id as name
    +0209 if all(cellfun(@isempty,compartmentNames))
    +0210     compartmentNames=compartmentIDs;
    +0211 end
    +0212 
    +0213 %Retrieve info on metabolites
    +0214 metaboliteNames={};
    +0215 metaboliteIDs={};
    +0216 metaboliteCompartments={};
    +0217 metaboliteUnconstrained=[];
    +0218 metaboliteFormula={};
    +0219 metaboliteInChI={};
    +0220 metaboliteMiriams={};
    +0221 metaboliteCharges=[];
    +0222 metaboliteDeltaG=[];
    +0223 
    +0224 %Gene information is collected from fbc_geneProduct further below
    +0225 geneMiriams={};
    +0226 geneShortNames={};
    +0227 proteins={};
    +0228 
    +0229 %In FBC v2 all species are metabolites; genes are stored as fbc_geneProduct
    +0230 %and are handled separately below.
    +0231 metSBOs = [];
    +0232 %Regex of compartment names, later to be used to remove from metabolite
    +0233 %names if present as suffix.
    +0234 escapedCompNames = regexptranslate('escape',{modelSBML.compartment.name});
    +0235 regexCompNames = ['\s?\[((' strjoin(escapedCompNames,')|(') '))\]$'];
    +0236 for i=1:numel(modelSBML.species)
    +0237     metaboliteNames{numel(metaboliteNames)+1,1}=modelSBML.species(i).name;
    +0238     metaboliteIDs{numel(metaboliteIDs)+1,1}=modelSBML.species(i).id;
    +0239     metaboliteCompartments{numel(metaboliteCompartments)+1,1}=modelSBML.species(i).compartment;
    +0240     metaboliteUnconstrained(numel(metaboliteUnconstrained)+1,1)=modelSBML.species(i).boundaryCondition;
    +0241 
    +0242     %For each metabolite retrieve the formula and the InChI code if
    +0243     %available First add the InChI code and the formula from the
    +0244     %InChI. This allows for overwriting the formula by setting the
    +0245     %actual formula field
    +0246     if ~isempty(modelSBML.species(i).annotation)
    +0247         %Get the formula if available
    +0248         startString='>InChI=';
    +0249         endString='</in:inchi>';
    +0250         formStart=strfind(modelSBML.species(i).annotation,startString);
    +0251         if isempty(formStart)
    +0252             startString='InChI=';
    +0253             endString='"/>';
    +0254         end
    +0255         formStart=strfind(modelSBML.species(i).annotation,startString);
    +0256         if ~isempty(formStart)
    +0257             formEnd=strfind(modelSBML.species(i).annotation,endString);
    +0258             formEndIndex=find(formEnd>formStart, 1 );
    +0259             formula=modelSBML.species(i).annotation(formStart+numel(startString):formEnd(formEndIndex)-1);
    +0260             metaboliteInChI{numel(metaboliteInChI)+1,1}=formula;
    +0261 
    +0262             %The composition is most often present between the
    +0263             %first and second "/" in the model. In some simple
    +0264             %molecules, such as salts, there is no second "/". The
    +0265             %formula is then assumed to be to the end of the string
    +0266             compositionIndexes=strfind(formula,'/');
    +0267             if numel(compositionIndexes)>1
    +0268                 metaboliteFormula{numel(metaboliteFormula)+1,1}=...
    +0269                     formula(compositionIndexes(1)+1:compositionIndexes(2)-1);
    +0270             else
    +0271                 if isscalar(compositionIndexes)
    +0272                     %Probably a simple molecule which can have only
    +0273                     %one conformation
    +0274                     metaboliteFormula{numel(metaboliteFormula)+1,1}=...
    +0275                         formula(compositionIndexes(1)+1:numel(formula));
    +0276                 else
    +0277                     metaboliteFormula{numel(metaboliteFormula)+1,1}='';
    +0278                 end
    +0279             end
    +0280         elseif isfield(modelSBML.species(i),'fbc_chemicalFormula')
    +0281             metaboliteInChI{numel(metaboliteInChI)+1,1}='';
    +0282             if ~isempty(modelSBML.species(i).fbc_chemicalFormula)
    +0283                 %Cannot extract InChi from formula, so remains
    +0284                 %empty
    +0285                 metaboliteFormula{numel(metaboliteFormula)+1,1}=modelSBML.species(i).fbc_chemicalFormula;
    +0286             else
    +0287                 metaboliteFormula{numel(metaboliteFormula)+1,1}='';
    +0288             end
    +0289         else
    +0290             metaboliteInChI{numel(metaboliteInChI)+1,1}='';
    +0291             metaboliteFormula{numel(metaboliteFormula)+1,1}='';
    +0292         end
    +0293 
    +0294         %Get Miriam info
    +0295         metMiriam=parseMiriam(modelSBML.species(i).annotation);
    +0296         metaboliteMiriams{numel(metaboliteMiriams)+1,1}=metMiriam;
    +0297     else
    +0298         metaboliteInChI{numel(metaboliteInChI)+1,1}='';
    +0299         metaboliteFormula{numel(metaboliteFormula)+1,1}='';
    +0300         metaboliteMiriams{numel(metaboliteMiriams)+1,1}=[];
    +0301     end
    +0302     %Get SBO term. One entry per species, always, so metSBOs stays aligned
    +0303     %with metaboliteNames below even when some species have no SBO term;
    +0304     %-1 (SBML's own "unset" value) marks those.
    +0305     if isfield(modelSBML.species(i),'sboTerm') && ~(modelSBML.species(i).sboTerm==-1)
    +0306         metSBOs(end+1,1) = modelSBML.species(i).sboTerm;
    +0307     else
    +0308         metSBOs(end+1,1) = -1;
    +0309     end
    +0310 
    +0311     %Remove trailing [compartment] from metabolite name if present
    +0312     metaboliteNames{end,1}=regexprep(metaboliteNames{end,1},regexCompNames,'');
    +0313     if isfield(modelSBML.species(i),'fbc_charge')
    +0314         if ~isempty(modelSBML.species(i).fbc_charge) && modelSBML.species(i).isSetfbc_charge
    +0315             metaboliteCharges(numel(metaboliteCharges)+1,1)=double(modelSBML.species(i).fbc_charge);
    +0316         else
    +0317             metaboliteCharges(numel(metaboliteCharges)+1,1)=NaN;
    +0318         end
    +0319     else
    +0320         metaboliteCharges(numel(metaboliteCharges)+1,1)=NaN;
    +0321     end
    +0322     %deltaG: same <p>deltaG: VALUE</p> notes convention exportModel writes
    +0323     %and raven-toolbox reads/writes -- see RAVEN#724.
    +0324     %str2double('')==NaN, matching metaboliteDeltaG's own NaN default.
    +0325     if isfield(modelSBML.species(i),'notes')
    +0326         metaboliteDeltaG(numel(metaboliteDeltaG)+1,1)=str2double(parseNote(modelSBML.species(i).notes,'deltaG'));
    +0327     else
    +0328         metaboliteDeltaG(numel(metaboliteDeltaG)+1,1)=NaN;
    +0329     end
    +0330     %Additional information from FBC format Chemical formula
    +0331     if isfield(modelSBML.species(i),'fbc_chemicalFormula')
    +0332         if ~isempty(modelSBML.species(i).fbc_chemicalFormula)
    +0333             metaboliteFormula{numel(metaboliteFormula),1}=modelSBML.species(i).fbc_chemicalFormula;
    +0334         end
    +0335     end
    +0336 end
    +0337 
    +0338 %Add SBO terms to metabolite miriam fields
    +0339 if numel(unique(metSBOs)) > 1
    +0340     for i = 1:numel(metaboliteNames)
    +0341         if metSBOs(i) ~= -1
    +0342             metaboliteMiriams{i} = addSBOtoMiriam(metaboliteMiriams{i},metSBOs(i));
    +0343         end
    +0344     end
    +0345 end
    +0346 
    +0347 %Retrieve info on reactions
    +0348 reactionNames=cell(numel(modelSBML.reaction),1);
    +0349 reactionIDs=cell(numel(modelSBML.reaction),1);
    +0350 subsystems=cell(numel(modelSBML.reaction),1);
    +0351 eccodes=cell(numel(modelSBML.reaction),1);
    +0352 eccodes(:,:)=cellstr('');
    +0353 rxnconfidencescores=NaN(numel(modelSBML.reaction),1);
    +0354 rxndeltag=NaN(numel(modelSBML.reaction),1);
    +0355 rxnreferences=cell(numel(modelSBML.reaction),1);
    +0356 rxnreferences(:,:)=cellstr('');
    +0357 rxnnotes=cell(numel(modelSBML.reaction),1);
    +0358 rxnnotes(:,:)=cellstr('');
    +0359 grRules=cell(numel(modelSBML.reaction),1);
    +0360 grRules(:,:)=cellstr('');
    +0361 rxnComps=zeros(numel(modelSBML.reaction),1);
    +0362 rxnMiriams=cell(numel(modelSBML.reaction),1);
    +0363 reactionReversibility=zeros(numel(modelSBML.reaction),1);
    +0364 reactionUB=zeros(numel(modelSBML.reaction),1);
    +0365 reactionLB=zeros(numel(modelSBML.reaction),1);
    +0366 reactionObjective=zeros(numel(modelSBML.reaction),1);
    +0367 
    +0368 %Construct the stoichiometric matrix while the reaction info is read.
    +0369 %Accumulated as (row,col,value) triples and assembled into a sparse
    +0370 %matrix in one pass at the end (sparse() sums duplicate (row,col)
    +0371 %triples, matching the +'d accumulation below), rather than allocating a
    +0372 %dense (mets x rxns) matrix, which for a genome-scale model can be
    +0373 %hundreds of MB even though S is typically >95% sparse.
    +0374 nStoichEntries=sum(arrayfun(@(r) numel(r.reactant)+numel(r.product),modelSBML.reaction));
    +0375 sRow=zeros(nStoichEntries,1);
    +0376 sCol=zeros(nStoichEntries,1);
    +0377 sVal=zeros(nStoichEntries,1);
    +0378 sN=0;
    +0379 
    +0380 counter=0;
    +0381 %If FBC, then bounds have parameter ids defined for the whole model
    +0382 if isfield(modelSBML,'parameter')
    +0383     parameter.name=cell(numel(modelSBML.parameter),1);
    +0384     parameter.name={modelSBML.parameter(:).id}';
    +0385     parameter.value={modelSBML.parameter(:).value}';
    +0386 end
    +0387 
    +0388 if isfield(modelSBML.reaction,'sboTerm') && isscalar(unique([modelSBML.reaction.sboTerm]))
    +0389     %If all the SBO terms are identical, do not add them to rxnMiriams
    +0390     modelSBML.reaction = rmfield(modelSBML.reaction,'sboTerm');
    +0391 end
    +0392 
    +0393 for i=1:numel(modelSBML.reaction)
    +0394 
    +0395     counter=counter+1;
    +0396 
    +0397     reactionNames{counter}=modelSBML.reaction(i).name;
    +0398 
    +0399     reactionIDs{counter}=modelSBML.reaction(i).id;
    +0400     reactionReversibility(counter)=modelSBML.reaction(i).reversible;
    +0401 
    +0402     %If model is FBC, first get parameter of bound and then replace it with
    +0403     %the correct value. Probably faster with replace(), but this was only
    +0404     %introduced in Matlab R2016b
    +0405     if isfield(modelSBML.reaction(i),'fbc_lowerFluxBound')
    +0406         lb=modelSBML.reaction(i).fbc_lowerFluxBound;
    +0407         ub=modelSBML.reaction(i).fbc_upperFluxBound;
    +0408         [~,fluxBoundIdx] = ismember({lb,ub},parameter.name);
    +0409         reactionLB(counter) = parameter.value{fluxBoundIdx(1)};
    +0410         reactionUB(counter) = parameter.value{fluxBoundIdx(2)};
    +0411     else
    +0412         if reactionReversibility(counter)==true
    +0413             reactionLB(counter)=-inf;
    +0414         else
    +0415             reactionLB(counter)=0;
    +0416         end
    +0417         reactionUB(counter)=inf;
    +0418         reactionObjective(counter)=0;
    +0419     end
    +0420 
    +0421     %Find the associated gene if available. In FBC v2 gene association data
    +0422     %is stored in the fbc_geneProductAssociation field.
    +0423     if isfield(modelSBML.reaction(i),'fbc_geneProductAssociation')
    +0424         if ~isempty(modelSBML.reaction(i).fbc_geneProductAssociation) && ~isempty(modelSBML.reaction(i).fbc_geneProductAssociation.fbc_association)
    +0425             grRules{counter}=modelSBML.reaction(i).fbc_geneProductAssociation.fbc_association.fbc_association;
    +0426         end
    +0427     end
    +0428 
    +0429     %Add reaction compartment
    +0430     rxnComp='';
    +0431     if isfield(modelSBML.reaction(i),'compartment')
    +0432         if ~isempty(modelSBML.reaction(i).compartment)
    +0433             rxnComp=modelSBML.reaction(i).compartment;
    +0434         end
    +0435     end
    +0436     if ~isempty(rxnComp)
    +0437         %Find it in the compartment list
    +0438         [~, J]=ismember(rxnComp,compartmentIDs);
    +0439         rxnComps(counter)=J;
    +0440     end
    +0441 
    +0442 
    +0443     miriamStruct=parseMiriam(modelSBML.reaction(i).annotation);
    +0444     rxnMiriams{counter}=miriamStruct;
    +0445     if isfield(modelSBML.reaction(i),'notes')
    +0446         %parseNote joins several SUBSYSTEM notes on a reaction with ';',
    +0447         %so split on it, or a reaction with more than one subsystem ends
    +0448         %up with a single subsystem literally named "a;b"
    +0449         subsystems{counter,1}=strtrim(strsplit(parseNote(modelSBML.reaction(i).notes,'SUBSYSTEM'),';'));
    +0450         subsystems{counter,1}(cellfun('isempty',subsystems{counter,1})) = [];
    +0451         if strfind(modelSBML.reaction(i).notes,'Confidence Level')
    +0452             confScore = parseNote(modelSBML.reaction(i).notes,'Confidence Level');
    +0453             if isempty(confScore)
    +0454                 confScore = 0;
    +0455             end
    +0456             rxnconfidencescores(counter)=str2double(confScore);
    +0457         end
    +0458         rxnreferences{counter,1}=parseNote(modelSBML.reaction(i).notes,'AUTHORS');
    +0459         rxnnotes{counter,1}=parseNote(modelSBML.reaction(i).notes,'NOTES');
    +0460         %deltaG: same <p>deltaG: VALUE</p> notes convention exportModel
    +0461         %writes and raven-toolbox reads/writes -- see RAVEN#724.
    +0462         %str2double('')==NaN, matching rxndeltag's own NaN default.
    +0463         rxndeltag(counter)=str2double(parseNote(modelSBML.reaction(i).notes,'deltaG'));
    +0464     end
    +0465 
    +0466     %Get SBO terms
    +0467     if isfield(modelSBML.reaction(i),'sboTerm') && ~(modelSBML.reaction(i).sboTerm==-1)
    +0468         rxnMiriams{counter} = addSBOtoMiriam(rxnMiriams{counter}, modelSBML.reaction(i).sboTerm);
    +0469     end
    +0470 
    +0471     %Get ec-codes
    +0472     eccode='';
    +0473     if ~isempty(modelSBML.reaction(i).annotation)
    +0474         if strfind(modelSBML.reaction(i).annotation,'http://identifiers.org/ec-code')
    +0475             eccode=parseAnnotation(modelSBML.reaction(i).annotation,'http://identifiers.org/','/','ec-code');
    +0476         elseif strfind(modelSBML.reaction(i).annotation,'https://identifiers.org/ec-code')
    +0477             eccode=parseAnnotation(modelSBML.reaction(i).annotation,'https://identifiers.org/','/','ec-code');
    +0478         end
    +0479     end
    +0480     eccodes{counter}=eccode;
    +0481 
    +0482     %Add all reactants
    +0483     for j=1:numel(modelSBML.reaction(i).reactant)
    +0484         %Get the index of the metabolite in metaboliteIDs. External
    +0485         %metabolites will be removed at a later stage
    +0486         metIndex=find(strcmp(modelSBML.reaction(i).reactant(j).species,metaboliteIDs),1);
    +0487         if isempty(metIndex)
    +0488             EM=['Could not find metabolite ' modelSBML.reaction(i).reactant(j).species ' in reaction ' reactionIDs{counter}];
    +0489             error('RAVEN:badInput', '%s', EM);
    +0490         end
    +0491         sN=sN+1;
    +0492         sRow(sN)=metIndex;
    +0493         sCol(sN)=counter;
    +0494         sVal(sN)=modelSBML.reaction(i).reactant(j).stoichiometry*-1;
    +0495     end
    +0496 
    +0497     %Add all products
    +0498     for j=1:numel(modelSBML.reaction(i).product)
    +0499         %Get the index of the metabolite in metaboliteIDs.
    +0500         metIndex=find(strcmp(modelSBML.reaction(i).product(j).species,metaboliteIDs),1);
    +0501         if isempty(metIndex)
    +0502             EM=['Could not find metabolite ' modelSBML.reaction(i).product(j).species ' in reaction ' reactionIDs{counter}];
    +0503             error('RAVEN:badInput', '%s', EM);
    +0504         end
    +0505         sN=sN+1;
    +0506         sRow(sN)=metIndex;
    +0507         sCol(sN)=counter;
    +0508         sVal(sN)=modelSBML.reaction(i).product(j).stoichiometry;
    +0509     end
    +0510 end
    +0511 
    +0512 %if FBC, objective function is separately defined. Multiple objective
    +0513 %functions can be defined, one is set as active
    +0514 if isfield(modelSBML, 'fbc_activeObjective')
    +0515     obj=modelSBML.fbc_activeObjective;
    +0516     for i=1:numel(modelSBML.fbc_objective)
    +0517         if strcmp(obj,modelSBML.fbc_objective(i).fbc_id)
    +0518             if ~isempty(modelSBML.fbc_objective(i).fbc_fluxObjective)
    +0519                 %fbc_fluxObjective is a struct array when the objective is
    +0520                 %a combination of more than one reaction; indexing it
    +0521                 %directly would silently keep only the first
    +0522                 fluxObjective=modelSBML.fbc_objective(i).fbc_fluxObjective;
    +0523                 for k=1:numel(fluxObjective)
    +0524                     idx=ismember(reactionIDs,fluxObjective(k).fbc_reaction);
    +0525                     reactionObjective(idx)=fluxObjective(k).fbc_coefficient;
    +0526                 end
    +0527             end
    +0528         end
    +0529     end
    +0530 end
    +0531 
    +0532 %subSystems can be stored as groups instead of in annotations
    +0533 if isfield(modelSBML,'groups_group')
    +0534     for i=1:numel(modelSBML.groups_group)
    +0535         groupreactions={modelSBML.groups_group(i).groups_member(:).groups_idRef};
    +0536         [~, idx] = ismember(groupreactions, reactionIDs);
    +0537         if any(idx)
    +0538             for j=1:numel(idx)
    +0539                 if isempty(subsystems{idx(j)}) % First subsystem
    +0540                     subsystems{idx(j)} = {modelSBML.groups_group(i).groups_name};
    +0541                 else % Consecutive subsystems: concatenate
    +0542                     subsystems{idx(j)} = horzcat(subsystems{idx(j)}, modelSBML.groups_group(i).groups_name);
    +0543                 end
    +0544             end
    +0545         end
    +0546     end
    +0547 end
    +0548 
    +0549 %Shrink the structures if complex-forming reactions had to be skipped
    +0550 reactionNames=reactionNames(1:counter);
    +0551 reactionIDs=reactionIDs(1:counter);
    +0552 subsystems=subsystems(1:counter);
    +0553 eccodes=eccodes(1:counter);
    +0554 rxnconfidencescores=rxnconfidencescores(1:counter);
    +0555 rxndeltag=rxndeltag(1:counter);
    +0556 rxnreferences=rxnreferences(1:counter);
    +0557 rxnnotes=rxnnotes(1:counter);
    +0558 grRules=grRules(1:counter);
    +0559 rxnMiriams=rxnMiriams(1:counter);
    +0560 reactionReversibility=reactionReversibility(1:counter);
    +0561 reactionUB=reactionUB(1:counter);
    +0562 reactionLB=reactionLB(1:counter);
    +0563 reactionObjective=reactionObjective(1:counter);
    +0564 
    +0565 model.name=modelSBML.name;
    +0566 model.id=modelSBML.id;
    +0567 model.rxns=reactionIDs;
    +0568 model.mets=metaboliteIDs;
    +0569 model.S=sparse(sRow(1:sN),sCol(1:sN),sVal(1:sN),numel(metaboliteIDs),counter);
    +0570 model.lb=reactionLB;
    +0571 model.ub=reactionUB;
    +0572 model.rev=reactionReversibility;
    +0573 model.c=reactionObjective;
    +0574 model.b=zeros(numel(metaboliteIDs),1);
    +0575 model.comps=compartmentIDs;
    +0576 model.compNames=compartmentNames;
    +0577 model.rxnConfidenceScores=rxnconfidencescores;
    +0578 model.rxnReferences=rxnreferences;
    +0579 model.rxnNotes=rxnnotes;
    +0580 model.rxnDeltaG=rxndeltag;
    +0581 
    +0582 %Load annotation if available. If there are several authors, only the first
    +0583 %author credentials are imported
    +0584 if isfield(modelSBML,'annotation')
    +0585     endString='</';
    +0586     I=strfind(modelSBML.annotation,endString);
    +0587     J=strfind(modelSBML.annotation,'<vCard:Family>');
    +0588     if any(J)
    +0589         model.annotation.familyName=modelSBML.annotation(J(1)+14:I(find(I>J(1),1))-1);
    +0590     end
    +0591     J=strfind(modelSBML.annotation,'<vCard:Given>');
    +0592     if any(J)
    +0593         model.annotation.givenName=modelSBML.annotation(J(1)+13:I(find(I>J(1),1))-1);
    +0594     end
    +0595     J=strfind(modelSBML.annotation,'<vCard:EMAIL>');
    +0596     if any(J)
    +0597         model.annotation.email=modelSBML.annotation(J(1)+13:I(find(I>J(1),1))-1);
    +0598     end
    +0599     J=strfind(modelSBML.annotation,'<vCard:Orgname>');
    +0600     if any(J)
    +0601         model.annotation.organization=modelSBML.annotation(J(1)+15:I(find(I>J(1),1))-1);
    +0602     end
    +0603     endString='"/>';
    +0604     I=strfind(modelSBML.annotation,endString);
    +0605     %Finding whether the taxonomy is written in the old or the new way
    +0606     J=strfind(modelSBML.annotation,'"http://identifiers.org/');
    +0607     if any(J)
    +0608         I = I(find(I>J,1))-1;
    +0609         J = J(find(J<I,1))+24;
    +0610         model.annotation.taxonomy=modelSBML.annotation(J:I);
    +0611     else
    +0612         J=strfind(modelSBML.annotation,'"https://identifiers.org/');
    +0613         if any(J)
    +0614             I = I(find(I>J,1))-1;
    +0615             J = J(find(J<I,1))+25;
    +0616             model.annotation.taxonomy=modelSBML.annotation(J:I);
    +0617         end
    +0618     end
    +0619 end
    +0620 if isfield(modelSBML,'notes')
    +0621     startString=strfind(modelSBML.notes,'xhtml">');
    +0622     endString=strfind(modelSBML.notes,'</body>');
    +0623     if any(startString) && any(endString)
    +0624         model.annotation.note=modelSBML.notes(startString(1)+7:endString-1);
    +0625         model.annotation.note=regexprep(model.annotation.note,'<p.*?>|</p.*?>','');
    +0626         model.annotation.note=strtrim(model.annotation.note);
    +0627         if regexp(model.annotation.note,'This file was generated using the exportModel function in RAVEN Toolbox \d\.\d and OutputSBML in libSBML')
    +0628             model.annotation=rmfield(model.annotation,'note'); % Default note added when running exportModel
    +0629         end
    +0630     end
    +0631 end
    +0632 
    +0633 if any(~cellfun(@isempty,compartmentOutside))
    +0634     model.compOutside=compartmentOutside;
    +0635 end
    +0636 
    +0637 model.rxnNames=reactionNames;
    +0638 model.metNames=metaboliteNames;
    +0639 
    +0640 %Match the compartments for metabolites
    +0641 [~, J]=ismember(metaboliteCompartments,model.comps);
    +0642 model.metComps=J;
    +0643 
    +0644 %If any gene associations have been loaded, build the gene list
    +0645 if ~all(cellfun(@isempty,grRules))
    +0646     %If fbc_geneProduct exists, follow the specified gene order, such
    +0647     %that matching geneShortNames in function below will work
    +0648     if isfield(modelSBML,'fbc_geneProduct')
    +0649         genes={modelSBML.fbc_geneProduct.fbc_id};
    +0650 
    +0651         %Get gene Miriams from fbc_geneProduct
    +0652         if isempty(geneMiriams)
    +0653             geneMiriams = cell(numel(genes),1);
    +0654             if isfield(modelSBML.fbc_geneProduct,'sboTerm') && isscalar(unique([modelSBML.fbc_geneProduct.sboTerm]))
    +0655                 %If all the SBO terms are identical, do not add them to geneMiriams
    +0656                 modelSBML.fbc_geneProduct = rmfield(modelSBML.fbc_geneProduct,'sboTerm');
    +0657             end
    +0658             for i = 1:numel(genes)
    +0659                 geneMiriams{i}=parseMiriam(modelSBML.fbc_geneProduct(i).annotation);
    +0660                 if isfield(modelSBML.fbc_geneProduct(i),'sboTerm') && ~(modelSBML.fbc_geneProduct(i).sboTerm==-1)
    +0661                     geneMiriams{i} = addSBOtoMiriam(geneMiriams{i},modelSBML.fbc_geneProduct(i).sboTerm);
    +0662                 end
    +0663             end
    +0664         end
    +0665         proteins={modelSBML.fbc_geneProduct.fbc_name};
    +0666     else
    +0667         genes=getGenesFromGrRules(grRules);
    +0668     end
    +0669     model.genes=genes;
    +0670     model.grRules=grRules;
    +0671     [grRules,rxnGeneMat] = standardizeGrRules(model,true);
    +0672     model.grRules = grRules;
    +0673     model.rxnGeneMat = rxnGeneMat;
    +0674 end
    +0675 
    +0676 if all(cellfun(@isempty,geneShortNames))
    +0677     if isfield(modelSBML,'fbc_geneProduct')
    +0678         %Iterate fbc_geneProduct directly: genes is only assigned above
    +0679         %when grRules is non-empty, but fbc_geneProduct can be present
    +0680         %(even as an empty list) regardless of that.
    +0681         for i=1:numel(modelSBML.fbc_geneProduct)
    +0682             if ~isempty(modelSBML.fbc_geneProduct(i).fbc_label)
    +0683                 geneShortNames{i,1}=modelSBML.fbc_geneProduct(i).fbc_label;
    +0684             elseif ~isempty(modelSBML.fbc_geneProduct(i).fbc_name)
    +0685                 geneShortNames{i,1}=modelSBML.fbc_geneProduct(i).fbc_name;
    +0686             else
    +0687                 geneShortNames{i,1}='';
    +0688             end
    +0689         end
    +0690     end
    +0691 end
    +0692 
    +0693 %If any InChIs have been loaded
    +0694 if any(~cellfun(@isempty,metaboliteInChI))
    +0695     model.inchis=metaboliteInChI;
    +0696 end
    +0697 
    +0698 %If any formulas have been loaded
    +0699 if any(~cellfun(@isempty,metaboliteFormula))
    +0700     model.metFormulas=metaboliteFormula;
    +0701 end
    +0702 
    +0703 %If any charges have been loaded
    +0704 if ~isempty(metaboliteCharges)
    +0705     model.metCharges=metaboliteCharges;
    +0706 end
    +0707 
    +0708 %If any deltaG values have been loaded
    +0709 if ~isempty(metaboliteDeltaG) && ~all(isnan(metaboliteDeltaG))
    +0710     model.metDeltaG=metaboliteDeltaG;
    +0711 end
    +0712 
    +0713 %If any gene short names have been loaded
    +0714 if any(~cellfun(@isempty,geneShortNames))
    +0715     model.geneShortNames=geneShortNames;
    +0716 end
    +0717 
    +0718 %If any Miriam strings for compartments have been loaded
    +0719 if any(~cellfun(@isempty,compartmentMiriams))
    +0720     model.compMiriams=compartmentMiriams;
    +0721 end
    +0722 
    +0723 %If any Miriam strings for metabolites have been loaded
    +0724 if any(~cellfun(@isempty,metaboliteMiriams))
    +0725     model.metMiriams=metaboliteMiriams;
    +0726 end
    +0727 
    +0728 %If any subsystems have been loaded
    +0729 if any(~cellfun(@isempty,subsystems))
    +0730     model.subSystems=subsystems;
    +0731 end
    +0732 if any(rxnComps)
    +0733     if all(rxnComps)
    +0734         model.rxnComps=rxnComps;
    +0735     else
    +0736         if supressWarnings==false
    +0737             EM='The compartments for the following reactions could not be matched. Ignoring reaction compartment information';
    +0738             warning('RAVEN:warning', '%s', ravenList(EM,model.rxns(rxnComps==0)));
    +0739         end
    +0740     end
    +0741 end
    +0742 
    +0743 %If any ec-codes have been loaded
    +0744 if any(~cellfun(@isempty,eccodes))
    +0745     model.eccodes=eccodes;
    +0746 end
    +0747 
    +0748 %If any Miriam strings for reactions have been loaded
    +0749 if any(~cellfun(@isempty,rxnMiriams))
    +0750     model.rxnMiriams=rxnMiriams;
    +0751 end
    +0752 
    +0753 %If any Miriam strings for genes have been loaded
    +0754 if any(~cellfun(@isempty,geneMiriams))
    +0755     model.geneMiriams=geneMiriams;
    +0756 end
    +0757 
    +0758 %If any protein strings have been loaded
    +0759 if any(~cellfun(@isempty,proteins))
    +0760     proteins = reshape(proteins,[],1);
    +0761     model.proteins=proteins;
    +0762 end
    +0763 
    +0764 model.unconstrained=metaboliteUnconstrained;
    +0765 
    +0766 %Convert SBML IDs back into their original strings. Here we are using part
    +0767 %from convertSBMLID, originating from the COBRA Toolbox
    +0768 model.rxns=regexprep(model.rxns,'__([0-9]+)__','${char(str2num($1))}');
    +0769 model.mets=regexprep(model.mets,'__([0-9]+)__','${char(str2num($1))}');
    +0770 model.comps=regexprep(model.comps,'__([0-9]+)__','${char(str2num($1))}');
    +0771 model.grRules=regexprep(model.grRules,'__([0-9]+)__','${char(str2num($1))}');
    +0772 model.genes=regexprep(model.genes,'__([0-9]+)__','${char(str2num($1))}');
    +0773 model.id=regexprep(model.id,'__([0-9]+)__','${char(str2num($1))}');
    +0774 
    +0775 if removePrefix
    +0776     [model, hasChanged]=removeIdentifierPrefix(model);
    +0777     warning('RAVEN:warning', '%s', ravenList(['The following fields have prefixes removed from all entries. '...
    +0778     'If this is undesired, run importModel with removePrefix as false. Example: '...
    +0779     'importModel(''filename.xml'',[],false);'],hasChanged))
    +0780 end
    +0781 
    +0782 %Remove unused fields
    +0783 if isempty(model.annotation)
    +0784     model=rmfield(model,'annotation');
    +0785 end
    +0786 if isempty(model.compOutside)
    +0787     model=rmfield(model,'compOutside');
    +0788 end
    +0789 if isempty(model.compMiriams)
    +0790     model=rmfield(model,'compMiriams');
    +0791 end
    +0792 if isempty(model.rxnComps)
    +0793     model=rmfield(model,'rxnComps');
    +0794 end
    +0795 if isempty(model.grRules)
    +0796     model=rmfield(model,'grRules');
    +0797 end
    +0798 if isempty(model.rxnGeneMat)
    +0799     model=rmfield(model,'rxnGeneMat');
    +0800 end
    +0801 if isempty(model.subSystems)
    +0802     model=rmfield(model,'subSystems');
    +0803 else
    +0804     model.subSystems(cellfun(@isempty,subsystems))={{''}};
    +0805     % If all subSystems have single entries, then unnest them
    +0806     if all(cellfun(@(x) iscell(x) && isscalar(x), model.subSystems))
    +0807         model.subSystems = transpose([model.subSystems{:}]);
    +0808     end
    +0809 end
    +0810 if isempty(model.eccodes)
    +0811     model=rmfield(model,'eccodes');
    +0812 end
    +0813 if isempty(model.rxnMiriams)
    +0814     model=rmfield(model,'rxnMiriams');
    +0815 end
    +0816 if cellfun(@isempty,model.rxnNotes)
    +0817     model=rmfield(model,'rxnNotes');
    +0818 end
    +0819 if cellfun(@isempty,model.rxnReferences)
    +0820     model=rmfield(model,'rxnReferences');
    +0821 end
    +0822 if isempty(model.rxnConfidenceScores) || all(isnan(model.rxnConfidenceScores))
    +0823     model=rmfield(model,'rxnConfidenceScores');
    +0824 end
    +0825 if isempty(model.rxnDeltaG) || all(isnan(model.rxnDeltaG))
    +0826     model=rmfield(model,'rxnDeltaG');
    +0827 end
    +0828 if isempty(model.genes)
    +0829     model=rmfield(model,'genes');
    +0830 elseif isrow(model.genes)
    +0831     model.genes=transpose(model.genes);
    +0832 end
    +0833 if isempty(model.geneComps)
    +0834     model=rmfield(model,'geneComps');
    +0835 end
    +0836 if isempty(model.geneMiriams)
    +0837     model=rmfield(model,'geneMiriams');
    +0838 end
    +0839 if isempty(model.geneShortNames)
    +0840     model=rmfield(model,'geneShortNames');
    +0841 end
    +0842 if isempty(model.proteins)
    +0843     model=rmfield(model,'proteins');
    +0844 end
    +0845 if isempty(model.inchis)
    +0846     model=rmfield(model,'inchis');
    +0847 end
    +0848 if isempty(model.metFormulas)
    +0849     model=rmfield(model,'metFormulas');
    +0850 end
    +0851 if isempty(model.metMiriams)
    +0852     model=rmfield(model,'metMiriams');
    +0853 end
    +0854 %Unlike inchis/metFormulas/metMiriams above, metCharges always gets one
    +0855 %entry per metabolite (NaN where SBML had no fbc_charge), so it is never
    +0856 %literally empty; drop it only when none of those entries are real, not
    +0857 %when they are all genuinely zero (any(zeros)==false would remove a
    +0858 %field of real, all-neutral charges; any(NaN)==true would keep an
    +0859 %all-unset field)
    +0860 if all(isnan(model.metCharges))
    +0861     model=rmfield(model,'metCharges');
    +0862 end
    +0863 
    +0864 %This just removes the grRules if no genes have been loaded
    +0865 if ~isfield(model,'genes') && isfield(model,'grRules')
    +0866     model=rmfield(model,'grRules');
    +0867 end
    +0868 
    +0869 %Print warnings about bad structure
    +0870 if supressWarnings==false
    +0871     checkModelStruct(model,false);
    +0872 end
    +0873 
    +0874 if removeExcMets==true
    +0875     model=simplifyModel(model);
    +0876 end
    +0877 end
    +0878 
    +0879 function fieldContent=parseNote(searchString,fieldName)
    +0880 %The function obtains the particular information from "notes" field, using
    +0881 %fieldName as the dummy string
    +0882 
    +0883 fieldContent='';
    +0884 
    +0885 if strfind(searchString,fieldName)
    +0886     [~,targetString] = regexp(searchString,['<p>' fieldName '.*?</p>'],'tokens','match');
    +0887     targetString=regexprep(targetString,'<p>|</p>','');
    +0888     targetString=regexprep(targetString,[fieldName, ':'],'');
    +0889     for i=1:numel(targetString)
    +0890         fieldContent=[fieldContent ';' strtrim(targetString{1,i})];
    +0891     end
    +0892     fieldContent=regexprep(fieldContent,'^;|;$','');
    +0893 else
    +0894     fieldContent='';
    +0895 end
    +0896 end
    +0897 
    +0898 function fieldContent=parseAnnotation(searchString,startString,midString,fieldName)
    +0899 
    +0900 fieldContent='';
    +0901 
    +0902 %Removing whitespace characters from the ending strings, which may occur in
    +0903 %several cases
    +0904 searchString=regexprep(searchString,'" />','"/>');
    +0905 [~,targetString] = regexp(searchString,['<rdf:li rdf:resource="' startString fieldName midString '.*?"/>'],'tokens','match');
    +0906 targetString=regexprep(targetString,'<rdf:li rdf:resource="|"/>','');
    +0907 targetString=regexprep(targetString,startString,'');
    +0908 targetString=regexprep(targetString,[fieldName midString],'');
    +0909 
    +0910 for i=1:numel(targetString)
    +0911     fieldContent=[fieldContent ';' strtrim(targetString{1,i})];
    +0912 end
    +0913 
    +0914 fieldContent=regexprep(fieldContent,'^;|;$','');
    +0915 end
    +0916 
    +0917 function miriamStruct=parseMiriam(searchString)
    +0918 %Generates miriam structure from annotation field
    +0919 
    +0920 %Finding whether miriams are written in the old or the new way
    +0921 if strfind(searchString,'http://identifiers.org/')
    +0922     startString='http://identifiers.org/';
    +0923 elseif strfind(searchString,'https://identifiers.org/')
    +0924     startString='https://identifiers.org/';
    +0925 else
    +0926     miriamStruct=[];
    +0927     return;
    +0928 end
    +0929 
    +0930 miriamStruct=[];
    +0931 
    +0932 searchString=regexprep(searchString,'" />','"/>');
    +0933 [~,targetString] = regexp(searchString,'<rdf:li rdf:resource=".*?"/>','tokens','match');
    +0934 targetString=regexprep(targetString,'<rdf:li rdf:resource="|"/>','');
    +0935 targetString=regexprep(targetString,startString,'');
    +0936 
    +0937 fwdslash  = contains(targetString,'/');
    +0938 midString = cell(numel(targetString),1);
    +0939 midString(fwdslash) = {'/'};
    +0940 midString(~fwdslash) = {':'};
    +0941 
    +0942 counter=0;
    +0943 for i=1:numel(targetString)
    +0944     if isempty(regexp(targetString{1,i},'inchi|ec-code', 'once'))
    +0945         counter=counter+1;
    +0946         miriamStruct.name{counter,1} = regexprep(targetString{1,i},[midString{i} '.+'],'','once');
    +0947         miriamStruct.value{counter,1} = regexprep(targetString{1,i},[miriamStruct.name{counter,1} midString{i}],'','once');
    +0948         miriamStruct.name{counter,1} = regexprep(miriamStruct.name{counter,1},'^obo\.','');
    +0949     end
    +0950 end
    +0951 end
    +0952 
    +0953 function miriam = addSBOtoMiriam(miriam,sboTerm)
    +0954 %Appends SBO term to miriam structure
    +0955 
    +0956 sboTerm = {['SBO:' sprintf('%07u',sboTerm)]};  % convert to proper format
    +0957 if isempty(miriam)
    +0958     miriam.name = {'sbo'};
    +0959     miriam.value = sboTerm;
    +0960 elseif any(strcmp('sbo',miriam.name))
    +0961     currSbo = strcmp('sbo',miriam.name);
    +0962     miriam.value(currSbo) = sboTerm;
    +0963 else
    +0964     miriam.name(end+1) = {'sbo'};
    +0965     miriam.value(end+1) = sboTerm;
    +0966 end
    +0967 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/io/index.html b/docs/html/io/index.html similarity index 69% rename from doc/io/index.html rename to docs/html/io/index.html index 0f7726ed..480d02b3 100644 --- a/doc/io/index.html +++ b/docs/html/io/index.html @@ -19,7 +19,7 @@

    Index for io

    Matlab files in this directory:

    -
     checkFileExistencecheckFileExistence Check whether files exist.
     cleanSheetcleanSheet Clean up an Excel sheet.
     exportForGitexportForGit Export a model for a Git-maintained model repository.
     exportModelexportModel Export a constraint-based model to an SBML file (L3V1 FBCv2).
     exportToExcelFormatexportToExcelFormat Export a model to the Microsoft Excel model format.
     importModelimportModel Import a constraint-based model from an SBML file.
     parseYAMLparseYAML Read a YAML file into a MATLAB struct/cell tree.
     readFastareadFasta Read sequences from a FASTA file.
     readYAMLmodelreadYAMLmodel Read a model structure from a YAML file.
     writeExcelwriteExcel Write one or more sheets to an .xlsx (Office Open XML) file.
     writeFastawriteFasta Write sequences to a FASTA file.
     writeYAMLmodelwriteYAMLmodel Write a model to a yaml file in the standard cobra-format
    + checkFileExistencecheckFileExistence Check whether files exist.  cleanSheetcleanSheet Clean up an Excel sheet.  exportForGitexportForGit Export a model for a Git-maintained model repository.  exportModelexportModel Export a constraint-based model to an SBML file (L3V1 FBCv2).  exportToExcelFormatexportToExcelFormat Export a model to the Microsoft Excel model format.  importModelimportModel Import a constraint-based model from an SBML file.  parseYAMLparseYAML Read a YAML file into a tree of MATLAB structs and cells.  readFastareadFasta Read sequences from a FASTA file.  readYAMLmodelreadYAMLmodel Read a model structure from a YAML file.  writeExcelwriteExcel Write one or more sheets to an .xlsx (Office Open XML) file.  writeFastawriteFasta Write sequences to a FASTA file.  writeYAMLmodelwriteYAMLmodel Write a model to a YAML file in the cobra-format !!omap structure. diff --git a/docs/html/io/parseYAML.html b/docs/html/io/parseYAML.html new file mode 100644 index 00000000..caa23842 --- /dev/null +++ b/docs/html/io/parseYAML.html @@ -0,0 +1,365 @@ + + + + Description of parseYAML + + + + + + + + + +
    Home > io > parseYAML.m
    + + + +

    parseYAML +

    + +

    PURPOSE ^

    +
    parseYAML Read a YAML file into a tree of MATLAB structs and cells.
    + +

    SYNOPSIS ^

    +
    function out = parseYAML(filename)
    + +

    DESCRIPTION ^

    +
     parseYAML  Read a YAML file into a tree of MATLAB structs and cells.
    +
    + Supports block-style mappings and sequences (indentation-based),
    + flow-style mappings and sequences (`{ key: value }`, `[a, b]`), quoted
    + and unquoted scalars, and comments. Does not support multi-document
    + files, anchors, aliases, tags, or block scalars (`|`, `>`).
    +
    + The function parses YAML configuration and data files with nested
    + mappings, sequences and scalars (for example condition files consumed by
    + applyCondition). readYAMLmodel loads a cobra-format model YAML: it applies
    + the model schema and returns a populated RAVEN model struct.
    +
    + Parameters
    + ----------
    + filename : char
    +     path to the YAML file.
    +
    + Returns
    + -------
    + out : struct or cell or char or double or logical or []
    +     MATLAB representation of the document:
    +
    +     - mapping  -> struct (keys sanitised to valid MATLAB field names)
    +     - sequence -> cell column vector
    +     - quoted or unquoted string -> char
    +     - integer or float -> double
    +     - true, false -> logical
    +     - null / ~ / empty value -> []
    +
    + Examples
    + --------
    +     cfg = parseYAML('data/conditions/anaerobic.yml');
    +
    + See also
    + --------
    + readYAMLmodel
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function out = parseYAML(filename)
    +0002 % parseYAML  Read a YAML file into a tree of MATLAB structs and cells.
    +0003 %
    +0004 % Supports block-style mappings and sequences (indentation-based),
    +0005 % flow-style mappings and sequences (`{ key: value }`, `[a, b]`), quoted
    +0006 % and unquoted scalars, and comments. Does not support multi-document
    +0007 % files, anchors, aliases, tags, or block scalars (`|`, `>`).
    +0008 %
    +0009 % The function parses YAML configuration and data files with nested
    +0010 % mappings, sequences and scalars (for example condition files consumed by
    +0011 % applyCondition). readYAMLmodel loads a cobra-format model YAML: it applies
    +0012 % the model schema and returns a populated RAVEN model struct.
    +0013 %
    +0014 % Parameters
    +0015 % ----------
    +0016 % filename : char
    +0017 %     path to the YAML file.
    +0018 %
    +0019 % Returns
    +0020 % -------
    +0021 % out : struct or cell or char or double or logical or []
    +0022 %     MATLAB representation of the document:
    +0023 %
    +0024 %     - mapping  -> struct (keys sanitised to valid MATLAB field names)
    +0025 %     - sequence -> cell column vector
    +0026 %     - quoted or unquoted string -> char
    +0027 %     - integer or float -> double
    +0028 %     - true, false -> logical
    +0029 %     - null / ~ / empty value -> []
    +0030 %
    +0031 % Examples
    +0032 % --------
    +0033 %     cfg = parseYAML('data/conditions/anaerobic.yml');
    +0034 %
    +0035 % See also
    +0036 % --------
    +0037 % readYAMLmodel
    +0038 
    +0039 if ~isfile(filename)
    +0040     error('parseYAML:fileNotFound', 'File not found: %s', filename);
    +0041 end
    +0042 
    +0043 [lines, indents] = readYAMLLines(filename);
    +0044 if isempty(lines)
    +0045     out = struct();
    +0046     return
    +0047 end
    +0048 [out, ~] = parseNode(lines, 1, indents);
    +0049 end
    +0050 
    +0051 
    +0052 function [lines, indents] = readYAMLLines(filename)
    +0053 % Read filename into non-blank, comment-stripped content lines, paired
    +0054 % with each line's leading-space indentation count. Document markers
    +0055 % (`---`, `...`) are dropped.
    +0056 fid = fopen(filename, 'r');
    +0057 cleanup = onCleanup(@() fclose(fid));
    +0058 
    +0059 lines = {};
    +0060 indents = [];
    +0061 tline = fgetl(fid);
    +0062 while ischar(tline)
    +0063     content = stripComment(tline);
    +0064     trimmed = strtrim(content);
    +0065     if ~isempty(trimmed) && ~strcmp(trimmed, '---') && ~strcmp(trimmed, '...')
    +0066         indents(end+1,1) = numel(content) - numel(regexprep(content, '^ +', '')); %#ok<AGROW>
    +0067         lines{end+1,1} = trimmed; %#ok<AGROW>
    +0068     end
    +0069     tline = fgetl(fid);
    +0070 end
    +0071 end
    +0072 
    +0073 
    +0074 function line = stripComment(line)
    +0075 % Strip a trailing `# comment`, ignoring `#` inside a quoted string.
    +0076 inSingle = false;
    +0077 inDouble = false;
    +0078 for i = 1:numel(line)
    +0079     c = line(i);
    +0080     if c == '''' && ~inDouble
    +0081         inSingle = ~inSingle;
    +0082     elseif c == '"' && ~inSingle
    +0083         inDouble = ~inDouble;
    +0084     elseif c == '#' && ~inSingle && ~inDouble
    +0085         line = line(1:i-1);
    +0086         break
    +0087     end
    +0088 end
    +0089 end
    +0090 
    +0091 
    +0092 function [value, nextIdx] = parseNode(lines, idx, indents)
    +0093 % Dispatch to a block sequence or block mapping, based on whether the
    +0094 % line at idx starts a sequence item (`-`) or a mapping key.
    +0095 if startsWithDash(lines{idx})
    +0096     [value, nextIdx] = parseSequence(lines, idx, indents);
    +0097 else
    +0098     [value, nextIdx] = parseMapping(lines, idx, indents);
    +0099 end
    +0100 end
    +0101 
    +0102 
    +0103 function tf = startsWithDash(s)
    +0104 tf = ~isempty(s) && (strcmp(s, '-') || (numel(s) >= 2 && s(1) == '-' && s(2) == ' '));
    +0105 end
    +0106 
    +0107 
    +0108 function [value, nextIdx] = parseMapping(lines, idx, indents)
    +0109 baseIndent = indents(idx);
    +0110 value = struct();
    +0111 while idx <= numel(lines) && indents(idx) == baseIndent && ~startsWithDash(lines{idx})
    +0112     [key, rest] = splitKeyValue(lines{idx});
    +0113     key = matlabFieldName(key);
    +0114     if isempty(rest)
    +0115         if idx+1 <= numel(lines) && indents(idx+1) > baseIndent
    +0116             [childVal, idx] = parseNode(lines, idx+1, indents);
    +0117         else
    +0118             childVal = [];
    +0119             idx = idx + 1;
    +0120         end
    +0121     else
    +0122         childVal = parseScalarOrFlow(rest);
    +0123         idx = idx + 1;
    +0124     end
    +0125     value.(key) = childVal;
    +0126 end
    +0127 nextIdx = idx;
    +0128 end
    +0129 
    +0130 
    +0131 function [value, nextIdx] = parseSequence(lines, idx, indents)
    +0132 baseIndent = indents(idx);
    +0133 items = {};
    +0134 while idx <= numel(lines) && indents(idx) == baseIndent && startsWithDash(lines{idx})
    +0135     rest = strtrim(lines{idx}(2:end));
    +0136     if isempty(rest)
    +0137         if idx+1 <= numel(lines) && indents(idx+1) > baseIndent
    +0138             [itemVal, idx] = parseNode(lines, idx+1, indents);
    +0139         else
    +0140             itemVal = [];
    +0141             idx = idx + 1;
    +0142         end
    +0143     else
    +0144         itemVal = parseScalarOrFlow(rest);
    +0145         idx = idx + 1;
    +0146     end
    +0147     items{end+1,1} = itemVal; %#ok<AGROW>
    +0148 end
    +0149 value = items;
    +0150 nextIdx = idx;
    +0151 end
    +0152 
    +0153 
    +0154 function [key, rest] = splitKeyValue(line)
    +0155 % Split "key: value" (or "key:") on the first top-level colon.
    +0156 inSingle = false;
    +0157 inDouble = false;
    +0158 colonPos = 0;
    +0159 for i = 1:numel(line)
    +0160     c = line(i);
    +0161     if c == '''' && ~inDouble
    +0162         inSingle = ~inSingle;
    +0163     elseif c == '"' && ~inSingle
    +0164         inDouble = ~inDouble;
    +0165     elseif c == ':' && ~inSingle && ~inDouble
    +0166         colonPos = i;
    +0167         break
    +0168     end
    +0169 end
    +0170 if colonPos == 0
    +0171     error('parseYAML:invalidLine', 'Expected "key: value" or "key:", got: %s', line);
    +0172 end
    +0173 key = strtrim(line(1:colonPos-1));
    +0174 key = strrep(strrep(key, '"', ''), '''', '');
    +0175 rest = strtrim(line(colonPos+1:end));
    +0176 end
    +0177 
    +0178 
    +0179 function value = parseScalarOrFlow(s)
    +0180 s = strtrim(s);
    +0181 if ~isempty(s) && s(1) == '{'
    +0182     value = parseFlowMap(s);
    +0183 elseif ~isempty(s) && s(1) == '['
    +0184     value = parseFlowSeq(s);
    +0185 else
    +0186     value = parseScalar(s);
    +0187 end
    +0188 end
    +0189 
    +0190 
    +0191 function value = parseFlowMap(s)
    +0192 inner = strtrim(s(2:end-1));
    +0193 value = struct();
    +0194 if isempty(inner)
    +0195     return
    +0196 end
    +0197 parts = splitTopLevel(inner, ',');
    +0198 for i = 1:numel(parts)
    +0199     [key, rest] = splitKeyValue(parts{i});
    +0200     value.(matlabFieldName(key)) = parseScalarOrFlow(rest);
    +0201 end
    +0202 end
    +0203 
    +0204 
    +0205 function value = parseFlowSeq(s)
    +0206 inner = strtrim(s(2:end-1));
    +0207 if isempty(inner)
    +0208     value = cell(0,1);
    +0209     return
    +0210 end
    +0211 parts = splitTopLevel(inner, ',');
    +0212 value = cell(numel(parts), 1);
    +0213 for i = 1:numel(parts)
    +0214     value{i} = parseScalarOrFlow(parts{i});
    +0215 end
    +0216 end
    +0217 
    +0218 
    +0219 function parts = splitTopLevel(s, delim)
    +0220 % Split s on delim, ignoring delim inside quotes or nested {}/[].
    +0221 parts = {};
    +0222 depth = 0;
    +0223 inSingle = false;
    +0224 inDouble = false;
    +0225 last = 1;
    +0226 for i = 1:numel(s)
    +0227     c = s(i);
    +0228     if c == '''' && ~inDouble
    +0229         inSingle = ~inSingle;
    +0230     elseif c == '"' && ~inSingle
    +0231         inDouble = ~inDouble;
    +0232     elseif ~inSingle && ~inDouble
    +0233         if c == '{' || c == '['
    +0234             depth = depth + 1;
    +0235         elseif c == '}' || c == ']'
    +0236             depth = depth - 1;
    +0237         elseif c == delim && depth == 0
    +0238             parts{end+1} = s(last:i-1); %#ok<AGROW>
    +0239             last = i + 1;
    +0240         end
    +0241     end
    +0242 end
    +0243 parts{end+1} = s(last:end); %#ok<AGROW>
    +0244 parts = strtrim(parts);
    +0245 end
    +0246 
    +0247 
    +0248 function value = parseScalar(s)
    +0249 if isempty(s) || strcmp(s, '~') || strcmpi(s, 'null')
    +0250     value = [];
    +0251     return
    +0252 end
    +0253 if numel(s) >= 2 && ((s(1) == '"' && s(end) == '"') || (s(1) == '''' && s(end) == ''''))
    +0254     value = s(2:end-1);
    +0255     return
    +0256 end
    +0257 if strcmpi(s, 'true')
    +0258     value = true;
    +0259     return
    +0260 end
    +0261 if strcmpi(s, 'false')
    +0262     value = false;
    +0263     return
    +0264 end
    +0265 if ~isempty(regexp(s, '^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$', 'once'))
    +0266     value = str2double(s);
    +0267     return
    +0268 end
    +0269 value = s;
    +0270 end
    +0271 
    +0272 
    +0273 function name = matlabFieldName(key)
    +0274 % Sanitise a YAML key into a valid MATLAB field name. Replaces non-
    +0275 % alphanumeric characters with underscores; prefixes a digit-starting
    +0276 % key with 'f_'.
    +0277 name = regexprep(key, '[^A-Za-z0-9_]', '_');
    +0278 if isempty(name) || ~isstrprop(name(1), 'alpha')
    +0279     name = ['f_' name];
    +0280 end
    +0281 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/io/readFasta.html b/docs/html/io/readFasta.html similarity index 100% rename from doc/io/readFasta.html rename to docs/html/io/readFasta.html diff --git a/docs/html/io/readYAMLmodel.html b/docs/html/io/readYAMLmodel.html new file mode 100644 index 00000000..1ecf7ac2 --- /dev/null +++ b/docs/html/io/readYAMLmodel.html @@ -0,0 +1,1027 @@ + + + + Description of readYAMLmodel + + + + + + + + + +
    Home > io > readYAMLmodel.m
    + + + +

    readYAMLmodel +

    + +

    PURPOSE ^

    +
    readYAMLmodel Read a model structure from a YAML file.
    + +

    SYNOPSIS ^

    +
    function model=readYAMLmodel(varargin)
    + +

    DESCRIPTION ^

    +
     readYAMLmodel  Read a model structure from a YAML file.
    +
    + Reads a yaml file in the cobra-format model yaml structure that
    + writeYAMLmodel writes.
    +
    + Name-Value Arguments
    + --------------------
    + fileName : char
    +     a model file in yaml file format. A dialog window opens if no file
    +     name is specified.
    + verbose : logical
    +     true to print the progress (default false).
    +
    + Returns
    + -------
    + model : struct
    +     a model structure.
    +
    + Examples
    + --------
    +     model = readYAMLmodel(fileName, verbose);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function model=readYAMLmodel(varargin)
    +0002 % readYAMLmodel  Read a model structure from a YAML file.
    +0003 %
    +0004 % Reads a yaml file in the cobra-format model yaml structure that
    +0005 % writeYAMLmodel writes.
    +0006 %
    +0007 % Name-Value Arguments
    +0008 % --------------------
    +0009 % fileName : char
    +0010 %     a model file in yaml file format. A dialog window opens if no file
    +0011 %     name is specified.
    +0012 % verbose : logical
    +0013 %     true to print the progress (default false).
    +0014 %
    +0015 % Returns
    +0016 % -------
    +0017 % model : struct
    +0018 %     a model structure.
    +0019 %
    +0020 % Examples
    +0021 % --------
    +0022 %     model = readYAMLmodel(fileName, verbose);
    +0023 p=parseRAVENargs(varargin, {'fileName',[]; 'verbose',false});
    +0024 fileName=p.fileName; verbose=p.verbose;
    +0025 if isempty(fileName)
    +0026     [fileName, pathName] = uigetfile({'*.yml;*.yaml'}, 'Please select the model file');
    +0027     if fileName == 0
    +0028         error('You should select a model file')
    +0029     else
    +0030         fileName = fullfile(pathName,fileName);
    +0031     end
    +0032 end
    +0033 
    +0034 if ~isfile(fileName)
    +0035     error('Yaml file %s cannot be found', string(fileName));
    +0036 end
    +0037 
    +0038 if verLessThan('matlab','9.9') %readlines introduced 2020b
    +0039     fid=fopen(fileName);
    +0040     line_raw=cell(1000000,1);
    +0041     i=1;
    +0042     while ~feof(fid)
    +0043         line_raw{i}=fgetl(fid);
    +0044         i=i+1;
    +0045     end
    +0046     line_raw(i:end)=[];
    +0047     line_raw=string(line_raw);
    +0048     fclose(fid);
    +0049 else
    +0050     line_raw=readlines(fileName);
    +0051 end
    +0052 % If entry is broken over multiple lines (a folded scalar --- some YAML
    +0053 % writers wrap a plain or single-quoted scalar that would exceed a
    +0054 % configured width), concatenate the continuation onto the previous line.
    +0055 % Assumes at
    +0056 % least 6 leading spaces to avoid metaData being concatenated.
    +0057 %
    +0058 % A continuation line is recognised by NOT starting with '- ': every real
    +0059 % entry in this format, at every nesting depth (including a nested list
    +0060 % under a key, e.g. an annotation sub-entry indented 6+ spaces), is itself
    +0061 % introduced with a list-item marker (a dash followed by a space, or a dash
    +0062 % ending the line), while a folded scalar's continuation is raw text with no
    +0063 % list-item marker of its own. Such text can itself start with a dash, as in
    +0064 % a KEGG reaction name wrapped before "-epoxidizing)".
    +0065 newLine=regexp(line_raw,'^ {6,}((?!-(\s|$))\S.*)','tokenExtents');
    +0066 brokenLine=find(~cellfun('isempty',newLine));
    +0067 for i=flip(1:numel(brokenLine))
    +0068     extraLine = char(line_raw(brokenLine(i)));
    +0069     extraLine = extraLine(newLine{brokenLine(i)}{1}(1):end);
    +0070     % deblank, not strtrim: only the trailing whitespace is spurious (e.g.
    +0071     % "- inchis: " with nothing after the colon on this line); the leading
    +0072     % indentation must survive untouched for the key-detection regex below.
    +0073     line_raw{brokenLine(i)-1} = strjoin({deblank(line_raw{brokenLine(i)-1}),extraLine},' ');
    +0074 end
    +0075 line_raw(brokenLine)=[];
    +0076 
    +0077 line_key = regexprep(line_raw,'^ *-? ([^:]+)(:)($| .*)','$1');
    +0078 line_key = regexprep(line_key,'(.*!!omap)|(---)|( {4,}.*)','');
    +0079 
    +0080 line_value = regexprep(line_raw, '.*:$','');
    +0081 % Captures everything after "key: " verbatim, quotes and all; any wrapping
    +0082 % quotes are left in place here and removed next by the anchored-pair
    +0083 % strip below, which handles both quote styles.
    +0084 line_value = regexprep(line_value, '[^":]+: (.+)$','$1');
    +0085 % The list-marker strip must run before quote detection below, not after:
    +0086 % a quoted *list item* (e.g. a pubmed id under an annotation block,
    +0087 % `        - "10714900"`) still has its leading "- " attached at this
    +0088 % point, so the anchored ^".*"$/^'.*'$ checks --- which require the quote
    +0089 % to be the very first character --- would never match it, leaving the
    +0090 % quotes on the value uninspected and unescaped. Stripping the marker
    +0091 % first means a quoted list item's value starts directly with the quote
    +0092 % character, the same shape as a quoted `key: "value"` scalar, so the
    +0093 % existing anchored logic below already handles both without further
    +0094 % changes.
    +0095 line_value = regexprep(line_value, '^ {4,}- ','');
    +0096 % Strips wrapping quotes, either style, but only a matching pair anchored
    +0097 % at both ends of the value --- not every quote character in it. Some
    +0098 % writers single-quote anything YAML requires quoting (a value that would
    +0099 % otherwise parse as a date/number/boolean, or one containing a character
    +0100 % with special meaning), where RAVEN's own writer only ever double-quotes,
    +0101 % so both styles must be handled here. Anchoring (rather than a global
    +0102 % strip) also preserves a genuine apostrophe in an unquoted value, e.g.
    +0103 % "yeast's hexokinase reaction" --- only the wrapping pair is a delimiter,
    +0104 % not every quote-like character.
    +0105 wasSingleQuoted = ~cellfun('isempty', regexp(line_value, '^''.*''$', 'once'));
    +0106 wasDoubleQuoted = ~cellfun('isempty', regexp(line_value, '^".*"$', 'once'));
    +0107 line_value = regexprep(line_value, '^(["''])(.*)\1$','$2');
    +0108 % Undo each quote style's own escaping so the reader recovers the exact
    +0109 % original text: single-quoted YAML doubles a literal apostrophe ('' -> ');
    +0110 % double-quoted YAML backslash-escapes a literal backslash and double quote
    +0111 % (\\ -> \, \" -> "). The backslash pass goes through a sentinel character
    +0112 % first so an escaped backslash immediately followed by an escaped quote
    +0113 % (\\\" in the file, a literal \" in the value) round-trips correctly
    +0114 % instead of the second pass misreading the first's output.
    +0115 line_value(wasSingleQuoted) = strrep(line_value(wasSingleQuoted), '''''', '''');
    +0116 line_value(wasDoubleQuoted) = strrep(line_value(wasDoubleQuoted), '\\', char(1));
    +0117 line_value(wasDoubleQuoted) = strrep(line_value(wasDoubleQuoted), '\"', '"');
    +0118 line_value(wasDoubleQuoted) = strrep(line_value(wasDoubleQuoted), char(1), '\');
    +0119 line_value(strcmp(line_value,'''''')) = {''};
    +0120 line_value(strcmp(line_value,line_raw)) = {''};
    +0121 
    +0122 modelFields =   {'id',char();...
    +0123                'name',char();...
    +0124         'description',char();...
    +0125             'version',char();...
    +0126                'date',char();...
    +0127          'annotation',struct();...
    +0128                'rxns',{};...
    +0129            'rxnNames',{};...
    +0130                'mets',{};...
    +0131            'metNames',{};...
    +0132                   'S',sparse([]);...
    +0133                  'lb',{};... %Changed to double in the end.
    +0134                  'ub',{};... %Changed to double in the end.
    +0135                 'rev',{};... %Changed to double in the end.
    +0136                   'c',[];...
    +0137                   'b',cell(0,0);... %Changed to double in the end.
    +0138               'genes',cell(0,0);...
    +0139             'grRules',cell(0,0);...
    +0140          'rxnGeneMat',sparse([]);...
    +0141            'rxnComps',cell(0,0);... %Changed to double in the end.
    +0142          'subSystems',cell(0,0);...
    +0143             'eccodes',cell(0,0);...
    +0144          'rxnMiriams',cell(0,0);...
    +0145           'rxnDeltaG',{};... %Changed to double in the end.
    +0146            'rxnNotes',cell(0,0);...
    +0147       'rxnReferences',cell(0,0);...
    +0148 'rxnConfidenceScores',cell(0,0);...
    +0149            'metComps',cell(0,0);... %Changed to double in the end.
    +0150              'inchis',cell(0,0);...
    +0151           'metSmiles',cell(0,0);...
    +0152         'metFormulas',cell(0,0);...
    +0153          'metMiriams',cell(0,0);...
    +0154           'metDeltaG',{};... %Changed to double in the end.
    +0155          'metCharges',cell(0,0);... %Changed to double in the end.
    +0156            'metNotes',cell(0,0);...
    +0157               'comps',cell(0,0);...
    +0158           'compNames',cell(0,0);...
    +0159         'compOutside',cell(0,0);...
    +0160         'compMiriams',cell(0,0);...
    +0161           'geneComps',cell(0,0);... %Changed to double in the end.
    +0162         'geneMiriams',cell(0,0);...
    +0163      'geneShortNames',cell(0,0);...
    +0164        'proteins',cell(0,0);...
    +0165       'unconstrained',cell(0,0);... %Changed to double in the end.
    +0166             'metFrom',cell(0,0);...
    +0167             'rxnFrom',cell(0,0)};
    +0168 for i=1:size(modelFields,1)
    +0169     model.(modelFields{i,1})=modelFields{i,2};
    +0170 end
    +0171 
    +0172 % If GECKO model — accept both the legacy `geckoLight` (inside metaData)
    +0173 % and the alternate top-level `gecko_light` key.
    +0174 if any(contains(line_key,'geckoLight')) || any(contains(line_key,'gecko_light'))
    +0175     isGECKO=true;
    +0176     ecFields = {'geckoLight', false;...
    +0177                       'rxns', {};...
    +0178                       'kcat', {};...
    +0179                     'source', cell(0,0);...
    +0180                      'notes', cell(0,0);...
    +0181                    'eccodes', cell(0,0);...
    +0182                      'genes', cell(0,0);...
    +0183                    'enzymes', cell(0,0);...
    +0184                         'mw', cell(0,0);...
    +0185                   'sequence', cell(0,0);...
    +0186                      'concs', cell(0,0);...
    +0187                  'rxnEnzMat', []};
    +0188     for i=1:size(ecFields,1)
    +0189         model.ec.(ecFields{i,1})=ecFields{i,2};
    +0190     end
    +0191     ecGecko=cell(25000,2);      ecGeckoNo=1;
    +0192     enzStoich=cell(100000,3);   enzStoichNo=1;
    +0193 else
    +0194     isGECKO=false;
    +0195 end
    +0196 
    +0197 section = 0;
    +0198 readList = '';
    +0199 metMiriams=cell(100000,3);   metMirNo=1;
    +0200 rxnMiriams=cell(100000,3);   rxnMirNo=1;
    +0201 geneMiriams=cell(100000,3);  genMirNo=1;
    +0202 compMiriams=cell(1000,3);    compMirNo=1;
    +0203 subSystems=cell(100000,2);   subSysNo=1;
    +0204 eccodes=cell(100000,2);      ecCodeNo=1;
    +0205 equations=cell(100000,3);    equatiNo=1;
    +0206 
    +0207 for i=1:numel(line_key)
    +0208     tline_raw = line_raw{i};
    +0209     tline_key = line_key{i};
    +0210     tline_value = line_value{i};
    +0211     % import different sections — accept the !!omap-tagged variant as
    +0212     % well, so a file using that tag is recognized too.
    +0213     switch tline_raw
    +0214         case {'- metaData:', '- metaData: !!omap'}
    +0215             section = 1;
    +0216             if verbose
    +0217                 fprintf('\t%d\n', section);
    +0218             end
    +0219             continue % Go to next line
    +0220         case '- metabolites:'
    +0221             section = 2;
    +0222             if verbose
    +0223                 fprintf('\t%d\n', section);
    +0224             end
    +0225             pos=0;
    +0226             continue
    +0227         case '- reactions:'
    +0228             section = 3;
    +0229             if verbose
    +0230                 fprintf('\t%d\n', section);
    +0231             end
    +0232             pos=0;
    +0233             continue
    +0234         case '- genes:'
    +0235             section = 4;
    +0236             if verbose
    +0237                 fprintf('\t%d\n', section);
    +0238             end
    +0239             pos=0;
    +0240             continue
    +0241         case '- genes: []'
    +0242             % Flow-style empty list: a gene-less model. No entries follow,
    +0243             % so there is nothing to switch into --- the next recognised
    +0244             % header is matched fresh on the next iteration regardless of
    +0245             % what `section` is left as.
    +0246             continue
    +0247         case '- compartments: !!omap'
    +0248             section = 5;
    +0249             if verbose
    +0250                 fprintf('\t%d\n', section);
    +0251             end
    +0252             pos=0;
    +0253             continue
    +0254         case '- ec-rxns:'
    +0255             section = 6;
    +0256             if verbose
    +0257                 fprintf('\t%d\n', section);
    +0258             end
    +0259             pos=0;
    +0260             continue
    +0261         case '- ec-enzymes:'
    +0262             section = 7;
    +0263             if verbose
    +0264                 fprintf('\t%d\n', section);
    +0265             end
    +0266             pos=0;
    +0267             continue
    +0268     end
    +0269 
    +0270     % Alternate root-level keys (id, name, version, gecko_light), written
    +0271     % at the top level with no metaData section. RAVEN's own writer
    +0272     % normally puts them inside metaData, but we accept both layouts so a
    +0273     % YAML file using the top-level convention can be ingested directly.
    +0274     if isempty(regexp(tline_raw, '^ {2,}', 'once'))
    +0275         switch tline_key
    +0276             case 'id'
    +0277                 if isempty(model.id), model.id = tline_value; end
    +0278                 continue
    +0279             case 'name'
    +0280                 if isempty(model.name), model.name = tline_value; end
    +0281                 continue
    +0282             case 'version'
    +0283                 if ~isfield(model,'version') || isempty(model.version)
    +0284                     model.version = tline_value;
    +0285                 end
    +0286                 continue
    +0287             case 'gecko_light'
    +0288                 if isGECKO && strcmp(tline_value,'true')
    +0289                     model.ec.geckoLight = true;
    +0290                 end
    +0291                 continue
    +0292         end
    +0293     end
    +0294 
    +0295     % skip over empty keys
    +0296     if isempty(tline_raw) || (isempty(tline_key) && contains(tline_raw,'!!omap'))
    +0297         continue;
    +0298     end
    +0299     
    +0300     % import metaData
    +0301     if section == 1
    +0302         switch tline_key
    +0303             case {'short_name','id'} %short_name used by human-GEM
    +0304                 model.id = tline_value;
    +0305             case 'name'
    +0306                 model.name = tline_value;                
    +0307             case 'full_name' %used by human-GEM
    +0308                 model.description = tline_value;
    +0309             case 'version'
    +0310                 model.version = tline_value;
    +0311             case 'date'
    +0312                 model.date = tline_value;                
    +0313             case 'taxonomy'
    +0314                 model.annotation.taxonomy = tline_value;
    +0315             case {'description','note'} %description used by human-GEM
    +0316                 model.annotation.note = tline_value;
    +0317             case 'github'
    +0318                 model.annotation.sourceUrl = tline_value;
    +0319             case 'sourceUrl'
    +0320                 model.annotation.sourceUrl = tline_value;                
    +0321             case 'givenName'
    +0322                 model.annotation.givenName = tline_value;
    +0323             case 'familyName'
    +0324                 model.annotation.familyName = tline_value;
    +0325             case 'authors'
    +0326                 model.annotation.authorList = tline_value;
    +0327             case 'email'
    +0328                 model.annotation.email = tline_value;
    +0329             case 'organization'
    +0330                 model.annotation.organization = tline_value;
    +0331             case {'geckoLight','gecko_light'}
    +0332                 if strcmp(tline_value,'true')
    +0333                     model.ec.geckoLight = true;
    +0334                 end
    +0335             otherwise
    +0336                 % Caller-specific provenance key with no RAVEN-defined slot
    +0337                 % (e.g. a tool-specific `_version` key): preserve it instead
    +0338                 % of silently dropping it, so a write/read round trip is
    +0339                 % lossless. Mirrors writeYAMLmodel.m's symmetric fallback,
    +0340                 % which emits any model.annotation field not in its own
    +0341                 % fixed list.
    +0342                 if isvarname(tline_key)
    +0343                     model.annotation.(tline_key) = tline_value;
    +0344                 end
    +0345         end; continue
    +0346     end
    +0347 
    +0348     % import metabolites:
    +0349     if section == 2
    +0350         % The lines of a notes map are indented below the entry's own
    +0351         % fields; their keys are not entry fields, whatever their name.
    +0352         if strcmp(readList, 'notes') && ~isempty(regexp(tline_raw, '^ {6,}', 'once'))
    +0353             model = appendNote(model, 'metNotes', tline_raw, tline_key, tline_value, pos);
    +0354             continue
    +0355         end
    +0356         switch tline_key
    +0357             case 'id'
    +0358                 pos = pos + 1;
    +0359                 model = readFieldValue(model, 'mets', tline_value,pos);
    +0360                 readList=''; miriamKey='';
    +0361             case 'name'
    +0362                 model = readFieldValue(model, 'metNames', tline_value, pos);
    +0363                 readList=''; miriamKey='';
    +0364             case 'compartment'
    +0365                 model = readFieldValue(model, 'metComps', tline_value, pos);
    +0366                 readList=''; miriamKey='';
    +0367             case 'formula'
    +0368                 model = readFieldValue(model, 'metFormulas', tline_value, pos);
    +0369                 readList=''; miriamKey='';
    +0370             case 'charge'
    +0371                 model = readFieldValue(model, 'metCharges', tline_value, pos);
    +0372                 readList=''; miriamKey='';
    +0373             case 'notes'
    +0374                 % A notes map (other tools' keys, as cobrapy and
    +0375                 % raven-toolbox write them) is read into the note text.
    +0376                 if isempty(tline_value) || strcmp(tline_value, '!!omap')
    +0377                     readList = 'notes';
    +0378                 else
    +0379                     model = readFieldValue(model, 'metNotes', tline_value, pos);
    +0380                     readList=''; miriamKey='';
    +0381                 end
    +0382             case 'inchis'
    +0383                 model = readFieldValue(model, 'inchis', tline_value, pos);
    +0384                 readList=''; miriamKey='';
    +0385             case 'smiles'
    +0386                 % Top-level (legacy MATLAB) and inside-annotation (current
    +0387                 % writer) layouts both land here. The current writer
    +0388                 % always emits smiles as a block list, even for a single
    +0389                 % value; a state distinct from 'annotation'
    +0390                 % ('smilesInAnnotation') reads that list so that once it
    +0391                 % ends this can resume annotation gathering rather than
    +0392                 % getting stuck treating every later line in the same
    +0393                 % annotation block as another smiles entry. A single-line
    +0394                 % "smiles: value" (legacy, or empty tline_value absent)
    +0395                 % doesn't reset readList — preserves the annotation
    +0396                 % gathering state if SMILES is nested inside the
    +0397                 % annotation block alongside other entries.
    +0398                 if isempty(tline_value)
    +0399                     readList = 'smilesInAnnotation';
    +0400                 else
    +0401                     model = readFieldValue(model, 'metSmiles', tline_value, pos);
    +0402                 end
    +0403             case 'deltaG'
    +0404                 model = readFieldValue(model, 'metDeltaG', tline_value, pos);
    +0405                 readList=''; miriamKey='';
    +0406             case 'metFrom'
    +0407                 model = readFieldValue(model, 'metFrom', tline_value, pos);
    +0408                 readList=''; miriamKey='';
    +0409             case 'annotation'
    +0410                 readList = 'annotation';
    +0411             otherwise
    +0412                 switch readList
    +0413                     case 'annotation'
    +0414                         [metMiriams, miriamKey, metMirNo] = gatherAnnotation(pos,metMiriams,tline_key,tline_value,miriamKey,metMirNo);
    +0415                     case 'smilesInAnnotation'
    +0416                         % A bare list item ("- OC(=O)...") has no colon,
    +0417                         % so tline_key is empty --- still inside the list.
    +0418                         % Anything else is the annotation block's next
    +0419                         % entry, not another smiles value: the list has
    +0420                         % ended (RAVEN keeps only one smiles per
    +0421                         % metabolite, so a second list item, if any,
    +0422                         % simply overwrites the first).
    +0423                         if isempty(tline_key)
    +0424                             model = readFieldValue(model, 'metSmiles', regexprep(tline_value,'^ +- "?(.*)"?$','$1'), pos);
    +0425                         else
    +0426                             readList = 'annotation';
    +0427                             [metMiriams, miriamKey, metMirNo] = gatherAnnotation(pos,metMiriams,tline_key,tline_value,miriamKey,metMirNo);
    +0428                         end
    +0429                     otherwise
    +0430                         error(['Unknown entry in yaml file: ' tline_raw])
    +0431                 end
    +0432         end; continue
    +0433     end
    +0434 
    +0435     % import reactions:
    +0436     if section == 3
    +0437         % The lines of a notes map are indented below the entry's own
    +0438         % fields; their keys are not entry fields, whatever their name.
    +0439         if strcmp(readList, 'notes') && ~isempty(regexp(tline_raw, '^ {6,}', 'once'))
    +0440             model = appendNote(model, 'rxnNotes', tline_raw, tline_key, tline_value, pos);
    +0441             continue
    +0442         end
    +0443         switch tline_key
    +0444             case 'id'
    +0445                 pos = pos + 1;
    +0446                 model = readFieldValue(model, 'rxns', tline_value,pos);
    +0447                 readList=''; miriamKey='';
    +0448             case 'name'
    +0449                 model = readFieldValue(model, 'rxnNames', tline_value, pos);
    +0450                 readList=''; miriamKey='';
    +0451             case 'lower_bound'
    +0452                 model.lb(pos,1) = {tline_value};
    +0453                 readList=''; miriamKey='';
    +0454             case 'upper_bound'
    +0455                 model.ub(pos,1) = {tline_value};
    +0456                 readList=''; miriamKey='';
    +0457             case 'rev'
    +0458                 model.rev(pos,1) = {tline_value};
    +0459                 readList=''; miriamKey='';
    +0460             case 'gene_reaction_rule'
    +0461                 model = readFieldValue(model, 'grRules', tline_value, pos);
    +0462                 readList=''; miriamKey='';
    +0463             case {'notes','rxnNotes'}
    +0464                 % `notes` is the canonical reaction-side key; `rxnNotes`
    +0465                 % is the legacy RAVEN MATLAB key, kept here for
    +0466                 % backward-compatible reads.
    +0467                 if isempty(tline_value) || strcmp(tline_value, '!!omap')
    +0468                     readList = 'notes';
    +0469                 else
    +0470                     model = readFieldValue(model, 'rxnNotes', tline_value, pos);
    +0471                     readList=''; miriamKey='';
    +0472                 end
    +0473             case 'rxnFrom'
    +0474                 model = readFieldValue(model, 'rxnFrom', tline_value, pos);
    +0475                 readList=''; miriamKey='';
    +0476             case 'deltaG'
    +0477                 model = readFieldValue(model, 'rxnDeltaG', tline_value, pos);
    +0478                 readList=''; miriamKey='';                
    +0479             case 'objective_coefficient'
    +0480                 model.c(pos,1) = str2double(tline_value);
    +0481                 readList=''; miriamKey='';
    +0482             case 'references'
    +0483                 model = readFieldValue(model, 'rxnReferences', tline_value, pos);
    +0484                 readList=''; miriamKey='';
    +0485             case 'confidence_score'
    +0486                 model = readFieldValue(model, 'rxnConfidenceScores', tline_value, pos);
    +0487                 readList=''; miriamKey='';
    +0488             case 'eccodes'
    +0489                 if isempty(tline_value)
    +0490                     readList = 'eccodes';
    +0491                 else
    +0492                     eccodes(ecCodeNo,1:2)={pos,tline_value};
    +0493                     ecCodeNo=ecCodeNo+1;
    +0494                 end
    +0495             case 'ec-code'
    +0496                 % EC numbers emitted inside the `annotation` block
    +0497                 % (current writer). Captured into model.eccodes the same
    +0498                 % way metabolite `smiles` is pulled out of the annotation
    +0499                 % block; the legacy top-level `eccodes` key above is still
    +0500                 % accepted for older files. A block list of codes follows
    +0501                 % when the value is empty, read via a state distinct from
    +0502                 % the legacy 'eccodes' above ('eccodesInAnnotation', not
    +0503                 % 'eccodes') specifically so that once the list ends this
    +0504                 % can resume annotation gathering rather than getting
    +0505                 % stuck treating every later line in the same annotation
    +0506                 % block as another EC code.
    +0507                 if isempty(tline_value)
    +0508                     readList = 'eccodesInAnnotation';
    +0509                 else
    +0510                     eccodes(ecCodeNo,1:2)={pos,tline_value};
    +0511                     ecCodeNo=ecCodeNo+1;
    +0512                 end
    +0513             case 'subsystem'
    +0514                 if isempty(tline_value)
    +0515                     readList = 'subsystem';
    +0516                 else
    +0517                     subSystems(subSysNo,1:2)={pos,tline_value};
    +0518                     subSysNo=subSysNo+1;
    +0519                 end
    +0520             case 'metabolites'
    +0521                 readList = 'equation';
    +0522             case 'annotation'
    +0523                 readList = 'annotation';
    +0524                 
    +0525             otherwise
    +0526                 switch readList
    +0527                     case 'eccodes'
    +0528                         eccodes(ecCodeNo,1:2)={pos,regexprep(tline_value,'^ +- "?(.*)"?$','$1')};
    +0529                         ecCodeNo=ecCodeNo+1;
    +0530                     case 'eccodesInAnnotation'
    +0531                         % A bare list item ("- 1.1.2.4") has no colon, so
    +0532                         % tline_key is empty --- still inside the list.
    +0533                         % Anything else is the annotation block's next
    +0534                         % entry (e.g. "kegg.reaction: ..."), not another
    +0535                         % EC code: the list has ended, so resume normal
    +0536                         % annotation gathering for this line and all that
    +0537                         % follow, instead of silently absorbing them here.
    +0538                         if isempty(tline_key)
    +0539                             eccodes(ecCodeNo,1:2)={pos,regexprep(tline_value,'^ +- "?(.*)"?$','$1')};
    +0540                             ecCodeNo=ecCodeNo+1;
    +0541                         else
    +0542                             readList = 'annotation';
    +0543                             [rxnMiriams, miriamKey,rxnMirNo] = gatherAnnotation(pos,rxnMiriams,tline_key,tline_value,miriamKey,rxnMirNo);
    +0544                         end
    +0545                     case 'subsystem'
    +0546                         subSystems(subSysNo,1:2)={pos,regexprep(tline_value,'^ +- "?(.*)"?$','$1')};
    +0547                         subSysNo=subSysNo+1;
    +0548                     case 'annotation'
    +0549                         [rxnMiriams, miriamKey,rxnMirNo] = gatherAnnotation(pos,rxnMiriams,tline_key,tline_value,miriamKey,rxnMirNo);
    +0550                     case 'equation'
    +0551                         coeff = sscanf(tline_value,'%f');
    +0552                         equations(equatiNo,1:3)={pos,tline_key,coeff};
    +0553                         equatiNo=equatiNo+1;
    +0554                     otherwise
    +0555                         error(['Unknown entry in yaml file: ' tline_raw])
    +0556                 end
    +0557         end; continue
    +0558     end
    +0559 
    +0560     % import genes:
    +0561     if section == 4
    +0562         switch tline_key
    +0563             case 'id'
    +0564                 pos = pos + 1;
    +0565                 model = readFieldValue(model, 'genes', tline_value, pos);
    +0566                 readList = '';
    +0567                 miriamKey = '';
    +0568             case 'name'
    +0569                 model = readFieldValue(model, 'geneShortNames', tline_value, pos);
    +0570             case 'protein'
    +0571                 model = readFieldValue(model, 'proteins', tline_value, pos);
    +0572             case 'annotation'
    +0573                 readList = 'annotation';
    +0574             otherwise
    +0575                 switch readList
    +0576                     case 'annotation'
    +0577                         [geneMiriams, miriamKey,genMirNo] = gatherAnnotation(pos,geneMiriams,tline_key,tline_value,miriamKey,genMirNo);
    +0578                     otherwise
    +0579                         error(['Unknown entry in yaml file: ' tline_raw])
    +0580                 end
    +0581         end; continue
    +0582     end
    +0583 
    +0584     % import compartments:
    +0585     if section == 5
    +0586         switch tline_key
    +0587             case 'annotation'
    +0588                 readList = 'annotation';
    +0589             otherwise
    +0590                 % Annotation entries are indented deeper than the
    +0591                 % compartment they belong to, so anything at compartment
    +0592                 % level ends the annotation and starts a new compartment.
    +0593                 if strcmp(readList,'annotation') && ~isempty(regexp(tline_raw,'^ {6,}','once'))
    +0594                     [compMiriams, miriamKey, compMirNo] = gatherAnnotation(pos,compMiriams,tline_key,tline_value,miriamKey,compMirNo);
    +0595                 else
    +0596                     pos = pos + 1;
    +0597                     model.comps(end+1,1) = {tline_key};
    +0598                     model.compNames(end+1,1) = {tline_value};
    +0599                     readList = ''; miriamKey = '';
    +0600                 end
    +0601         end; continue
    +0602     end
    +0603 
    +0604     % import ec reaction info
    +0605     if section == 6
    +0606         switch tline_key
    +0607             case 'id'
    +0608                 pos = pos + 1;
    +0609                 model.ec = readFieldValue(model.ec, 'rxns', tline_value, pos);
    +0610                 readList='';
    +0611             case 'kcat'
    +0612                 model.ec = readFieldValue(model.ec, 'kcat', tline_value, pos);
    +0613                 readList='';
    +0614             case 'source'
    +0615                 model.ec = readFieldValue(model.ec, 'source', tline_value, pos);
    +0616                 readList='';
    +0617             case 'notes'
    +0618                 model.ec = readFieldValue(model.ec, 'notes', tline_value, pos);
    +0619                 readList='';
    +0620             case 'eccodes'
    +0621                 if isempty(tline_value)
    +0622                     readList = 'eccodes';
    +0623                 else
    +0624                     ecGecko(ecGeckoNo,1:2)={pos,tline_value};
    +0625                     ecGeckoNo=ecGeckoNo+1;
    +0626                 end
    +0627             case 'enzymes'
    +0628                 readList = 'enzStoich';
    +0629             otherwise
    +0630                 switch readList
    +0631                     case 'eccodes'
    +0632                         ecGecko(ecGeckoNo,1:2)={pos,regexprep(tline_value,'^ +- "?(.*)"?$','$1')};
    +0633                         ecGeckoNo=ecGeckoNo+1;
    +0634                     case 'enzStoich'
    +0635                         coeff = sscanf(tline_value,'%f');
    +0636                         enzStoich(enzStoichNo,1:3)={pos,tline_key,coeff};
    +0637                         enzStoichNo=enzStoichNo+1;
    +0638                     otherwise
    +0639                         error(['Unknown entry in yaml file: ' tline_raw])
    +0640                 end
    +0641         end; continue
    +0642     end
    +0643 
    +0644     % import ec enzyme info
    +0645     if section == 7
    +0646         switch tline_key
    +0647             case 'genes'
    +0648                 pos = pos + 1;
    +0649                 model.ec = readFieldValue(model.ec, 'genes', tline_value, pos);
    +0650             case 'enzymes'
    +0651                 model.ec = readFieldValue(model.ec, 'enzymes', tline_value, pos);
    +0652             case 'mw'
    +0653                 model.ec = readFieldValue(model.ec, 'mw', tline_value, pos);
    +0654             case 'sequence'
    +0655                 model.ec = readFieldValue(model.ec, 'sequence', tline_value, pos);
    +0656             case 'concs'
    +0657                 model.ec = readFieldValue(model.ec, 'concs', tline_value, pos);
    +0658             otherwise
    +0659                 error(['Unknown entry in yaml file: ' tline_raw])
    +0660         end; continue
    +0661     end
    +0662 end
    +0663 
    +0664 %Parse annotations
    +0665 if ~isempty(metMiriams)
    +0666     locs = cell2mat(metMiriams(:,1));
    +0667     for i=unique(locs)'
    +0668         model.metMiriams{i,1}.name=metMiriams(locs==i,2);
    +0669         model.metMiriams{i,1}.value=metMiriams(locs==i,3);
    +0670     end
    +0671 end
    +0672 if ~isempty(rxnMiriams)
    +0673     locs = cell2mat(rxnMiriams(:,1));
    +0674     for i=unique(locs)'
    +0675         model.rxnMiriams{i,1}.name=rxnMiriams(locs==i,2);
    +0676         model.rxnMiriams{i,1}.value=rxnMiriams(locs==i,3);
    +0677     end
    +0678 end
    +0679 if ~isempty(geneMiriams)
    +0680     locs = cell2mat(geneMiriams(:,1));
    +0681     for i=unique(locs)'
    +0682         model.geneMiriams{i,1}.name=geneMiriams(locs==i,2);
    +0683         model.geneMiriams{i,1}.value=geneMiriams(locs==i,3);
    +0684     end
    +0685 end
    +0686 if ~isempty(compMiriams)
    +0687     locs = cell2mat(compMiriams(:,1));
    +0688     for i=unique(locs)'
    +0689         model.compMiriams{i,1}.name=compMiriams(locs==i,2);
    +0690         model.compMiriams{i,1}.value=compMiriams(locs==i,3);
    +0691     end
    +0692 end
    +0693 
    +0694 %Parse subSystems
    +0695 if ~isempty(subSystems)
    +0696     locs = cell2mat(subSystems(:,1));
    +0697     for i=unique(locs)'
    +0698         model.subSystems{i,1}=subSystems(locs==i,2);
    +0699     end
    +0700 end
    +0701 
    +0702 %Parse ec-codes
    +0703 if ~isempty(eccodes)
    +0704     locs = cell2mat(eccodes(:,1));
    +0705     for i=unique(locs)'
    +0706         eccodesCat=strjoin(eccodes(locs==i,2),';');
    +0707         model.eccodes{i,1}=eccodesCat;
    +0708     end
    +0709     emptyEc=cellfun('isempty',model.eccodes);
    +0710     model.eccodes(emptyEc)={''};
    +0711 end
    +0712 
    +0713 % follow-up data processing
    +0714 if verbose
    +0715     fprintf('\nimporting completed\nfollow-up processing...');
    +0716 end
    +0717 [~, model.metComps] = ismember(model.metComps, model.comps);
    +0718 [~, model.geneComps] = ismember(model.geneComps, model.comps);
    +0719 [~, model.rxnComps] = ismember(model.rxnComps, model.comps);
    +0720 % A metabolite with no explicit compartment is still ismember'd above (it
    +0721 % parses as ''), so it resolves to 0 -- not the "assume first compartment"
    +0722 % default the emptyOrFill call further down intends -- unless it happens
    +0723 % to be the trailing metabolite in the file, the only case emptyOrFill
    +0724 % actually reaches (it only extends a too-short array, and ismember has
    +0725 % already made this one full-length). Catch every 0 here so the default
    +0726 % applies regardless of position, not just at the end of the list.
    +0727 model.metComps(model.metComps==0) = 1;
    +0728 
    +0729 % Fill S-matrix
    +0730 rxnIdx = cellfun('isempty', equations(:,1));
    +0731 equations(rxnIdx,:) = '';
    +0732 rxnIdx = cell2mat(equations(:,1));
    +0733 [~,metIdx] = ismember(equations(:,2),model.mets);
    +0734 coeffs = cell2mat(equations(:,3));
    +0735 model.S=sparse(max(metIdx),max(rxnIdx));
    +0736 linearIndices = sub2ind([max(metIdx), max(rxnIdx)],metIdx,rxnIdx);
    +0737 model.S(linearIndices) = coeffs;
    +0738 
    +0739 % Convert strings to numeric
    +0740 model.metCharges = str2double(model.metCharges);
    +0741 model.lb = str2double(model.lb);
    +0742 model.ub = str2double(model.ub);
    +0743 model.rxnConfidenceScores = str2double(model.rxnConfidenceScores);
    +0744 model.b = zeros(length(model.mets),1);
    +0745 model.metDeltaG = str2double(model.metDeltaG);
    +0746 model.rxnDeltaG = str2double(model.rxnDeltaG);
    +0747 
    +0748 % Fill some other fields
    +0749 model.annotation.defaultLB = min(model.lb);
    +0750 model.annotation.defaultUB = max(model.ub);
    +0751 if numel(model.lb)<numel(model.rxns) %No LB reported = min
    +0752     model.lb(end+1:numel(model.rxns)-numel(model.lb),1) = double(model.annotation.defaultLB);
    +0753 end
    +0754 if numel(model.ub)<numel(model.rxns) %No UB reported = max
    +0755     model.ub(end+1:numel(model.rxns)-numel(model.ub),1) = double(model.annotation.defaultUB);
    +0756 end
    +0757 if ~all(cellfun('isempty',model.rev))
    +0758     model.rev = str2double(model.rev);
    +0759 else
    +0760     model.rev = [];
    +0761 end
    +0762 if numel(model.rev)<numel(model.rxns) %No rev reported, assume from LB and UB
    +0763     model.rev(end+1:numel(model.rxns)-numel(model.rev),1) = double(model.lb<0 & model.ub>0);
    +0764 end
    +0765 
    +0766 % Remove empty fields, otherwise fill to correct length
    +0767 % Reactions
    +0768 for i={'rxnNames','grRules','eccodes','rxnNotes','rxnReferences',...
    +0769        'rxnFrom','subSystems','rxnMiriams'} % Empty strings
    +0770    model = emptyOrFill(model,i{1},{''},'rxns');
    +0771 end
    +0772 for i={'c'} % Zeros
    +0773    model = emptyOrFill(model,i{1},0,'rxns',true);
    +0774 end
    +0775 for i={'rxnConfidenceScores','rxnDeltaG'} % NaNs
    +0776    model = emptyOrFill(model,i{1},NaN,'rxns');
    +0777 end
    +0778 for i={'rxnComps'} % Ones, assume first compartment
    +0779    model = emptyOrFill(model,i{1},1,'rxns');
    +0780 end
    +0781 % Metabolites
    +0782 for i={'metNames','inchis','metFormulas','metMiriams','metFrom','metSmiles','metNotes'} % Empty strings
    +0783    model = emptyOrFill(model,i{1},{''},'mets');
    +0784 end
    +0785 for i={'unconstrained'} % Zeros
    +0786    model = emptyOrFill(model,i{1},0,'mets');
    +0787 end
    +0788 for i={'metCharges','metDeltaG'} % NaNs
    +0789     model = emptyOrFill(model,i{1},NaN,'mets');
    +0790  end
    +0791 for i={'metComps'} % Ones, assume first compartment
    +0792    model = emptyOrFill(model,i{1},1,'mets');
    +0793 end
    +0794 % Genes
    +0795 for i={'geneMiriams','geneShortNames','proteins'} % Empty strings
    +0796    model = emptyOrFill(model,i{1},{''},'genes');
    +0797 end
    +0798 for i={'geneComps'} % Ones, assume first compartment
    +0799    model = emptyOrFill(model,i{1},1,'genes');
    +0800 end
    +0801 % Comps
    +0802 for i={'compNames','compMiriams'} % Empty strings
    +0803    model = emptyOrFill(model,i{1},{''},'comps');
    +0804 end
    +0805 for i={'compOutside'} % First comp
    +0806    model = emptyOrFill(model,i{1},model.comps{1},'comps');
    +0807 end
    +0808 % Single fields are kept, even if empty
    +0809 % for i={'description','name','version','date','annotation'}
    +0810 %     if isempty(model.(i{1}))
    +0811 %         model = rmfield(model,i{1});
    +0812 %     end
    +0813 % end
    +0814 
    +0815 if isfield(model,'subSystems')
    +0816 % A reaction without subsystem holds [] or '' here: give it an empty entry,
    +0817 % so every reaction keeps its place when the entries are flattened
    +0818     model.subSystems(cellfun(@isempty, model.subSystems)) = {{''}};
    +0819 % If all entries are 1x1, then flatten
    +0820     if all(cellfun(@(x) numel(x) <= 1, model.subSystems))
    +0821         model.subSystems = cellfun(@(x) x{1}, model.subSystems, 'UniformOutput', false);
    +0822     end
    +0823 end
    +0824 
    +0825 % Make rxnGeneMat fields and map to the existing model.genes field
    +0826 if isfield(model,'grRules')
    +0827     [genes, rxnGeneMat] = getGenesFromGrRules(model.grRules);
    +0828     model.rxnGeneMat = sparse(numel(model.rxns),numel(model.genes));
    +0829     [~,geneOrder] = ismember(genes,model.genes);
    +0830     if any(geneOrder == 0)
    +0831         error(['The grRules includes the following gene(s), that are not in '...
    +0832             'the list of model genes: ', genes{~geneOrder}])
    +0833     end
    +0834     model.rxnGeneMat(:,geneOrder) = rxnGeneMat;
    +0835 else
    +0836     % Gene-less model: keep rxnGeneMat dimensionally consistent with the
    +0837     % number of reactions and genes, rather than leaving it empty.
    +0838     model.rxnGeneMat = sparse(numel(model.rxns),numel(model.genes));
    +0839 end
    +0840 
    +0841 % Finalize GECKO model
    +0842 if isGECKO
    +0843     % Fill in empty fields and empty entries
    +0844     for i={'kcat','source','notes','eccodes'} % Even keep empty
    +0845         model.ec = emptyOrFill(model.ec,i{1},{''},'rxns',true);
    +0846     end
    +0847     for i={'enzymes','mw','sequence'}
    +0848         model.ec = emptyOrFill(model.ec,i{1},{''},'genes',true);
    +0849     end
    +0850     model.ec = emptyOrFill(model.ec,'concs',{'NaN'},'genes',true);
    +0851     model.ec = emptyOrFill(model.ec,'kcat',{'0'},'genes',true);
    +0852     % Change string to double
    +0853     for i={'kcat','mw','concs'}
    +0854         if isfield(model.ec,i{1})
    +0855             model.ec.(i{1}) = str2double(model.ec.(i{1}));
    +0856         end
    +0857     end
    +0858     % Fill rxnEnzMat
    +0859     rxnIdx              = cellfun('isempty', enzStoich(:,1));
    +0860     enzStoich(rxnIdx,:) = '';
    +0861     rxnIdx              = cell2mat(enzStoich(:,1));
    +0862     [~,enzIdx]          = ismember(enzStoich(:,2),model.ec.enzymes);
    +0863     coeffs              = cell2mat(enzStoich(:,3));
    +0864     model.ec.rxnEnzMat  = zeros(numel(model.ec.rxns), numel(model.ec.genes));
    +0865     linearIndices       = sub2ind([numel(model.ec.rxns), numel(model.ec.genes)], rxnIdx, enzIdx);
    +0866     model.ec.rxnEnzMat(linearIndices) = coeffs;
    +0867     %Parse ec-codes
    +0868     if ~isempty(ecGecko)
    +0869         locs = cell2mat(ecGecko(:,1));
    +0870         for i=unique(locs)'
    +0871             ecGeckoCat=strjoin(ecGecko(locs==i,2),';');
    +0872             model.ec.eccodes{i,1}=ecGeckoCat;
    +0873         end
    +0874         emptyEc=cellfun('isempty',model.ec.eccodes);
    +0875         model.ec.eccodes(emptyEc)={''};
    +0876     end
    +0877 end
    +0878 
    +0879 if verbose
    +0880     fprintf(' Done!\n');
    +0881 end
    +0882 end
    +0883 
    +0884 function model = emptyOrFill(model,field,emptyEntry,type,keepEmpty)
    +0885 if nargin<5
    +0886     keepEmpty=false;
    +0887 end
    +0888 if isnumeric(emptyEntry)
    +0889     emptyCells=isempty(model.(field));
    +0890 else
    +0891     emptyCells=cellfun('isempty',model.(field));
    +0892 end
    +0893 if all(emptyCells) && ~keepEmpty
    +0894     model = rmfield(model, field);
    +0895 elseif numel(model.(field))<numel(model.(type))
    +0896     model.(field)(end+1:numel(model.(type)),1)=emptyEntry;
    +0897 end
    +0898 end
    +0899 
    +0900 function model = readFieldValue(model, fieldName, value, pos)
    +0901 if numel(model.(fieldName))<pos-1
    +0902     model.(fieldName)(end+1:pos,1) = {''};
    +0903 end
    +0904 model.(fieldName)(pos,1) = {value};
    +0905 end
    +0906 
    +0907 function model = appendNote(model, fieldName, raw, key, value, pos)
    +0908 % One line of a notes map: a 'note' entry adds its value (RAVEN's own note
    +0909 % text), another entry adds 'key: value', a list item adds its value to the
    +0910 % entry above it. Entries are separated by '; ', list items by ', '. A
    +0911 % quoted list item is read whole, as the key detection would split it at
    +0912 % a ': ' inside the quotes.
    +0913 item = regexp(raw, '^ +- ([''"])(.*)\1$', 'tokens', 'once');
    +0914 if ~isempty(item)
    +0915     key = '';
    +0916     value = item{2};
    +0917 end
    +0918 if strcmp(value, '!!omap')
    +0919     value = '';
    +0920 end
    +0921 text = '';
    +0922 if numel(model.(fieldName)) >= pos
    +0923     text = model.(fieldName){pos};
    +0924 end
    +0925 if isempty(key)
    +0926     if isempty(text) || endsWith(text, ':')
    +0927         text = strtrim([text ' ' value]);
    +0928     else
    +0929         text = [text ', ' value];
    +0930     end
    +0931 else
    +0932     if isempty(value)
    +0933         entry = [key ':'];
    +0934     elseif strcmp(key, 'note')
    +0935         entry = value;
    +0936     else
    +0937         entry = [key ': ' value];
    +0938     end
    +0939     if isempty(text)
    +0940         text = entry;
    +0941     else
    +0942         text = [text '; ' entry];
    +0943     end
    +0944 end
    +0945 model = readFieldValue(model, fieldName, text, pos);
    +0946 end
    +0947 
    +0948 function [miriams,miriamKey,entryNumber] = gatherAnnotation(pos,miriams,key,value,miriamKey,entryNumber)
    +0949 if isempty(key)
    +0950     key=miriamKey;
    +0951 else
    +0952     miriamKey=key;
    +0953 end
    +0954 if ~isempty(value)
    +0955     miriams(entryNumber,1:3) = {pos, key, strip(value)};
    +0956     entryNumber = entryNumber + 1;
    +0957 end
    +0958 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/io/writeExcel.html b/docs/html/io/writeExcel.html similarity index 100% rename from doc/io/writeExcel.html rename to docs/html/io/writeExcel.html diff --git a/doc/io/writeFasta.html b/docs/html/io/writeFasta.html similarity index 100% rename from doc/io/writeFasta.html rename to docs/html/io/writeFasta.html diff --git a/doc/io/writeYAMLmodel.html b/docs/html/io/writeYAMLmodel.html similarity index 61% rename from doc/io/writeYAMLmodel.html rename to docs/html/io/writeYAMLmodel.html index 89356722..9f4f19f0 100644 --- a/doc/io/writeYAMLmodel.html +++ b/docs/html/io/writeYAMLmodel.html @@ -4,7 +4,7 @@ Description of writeYAMLmodel - + @@ -21,18 +21,18 @@

    writeYAMLmodel

    PURPOSE ^

    -
    writeYAMLmodel Write a model to a yaml file in the standard cobra-format
    +
    writeYAMLmodel Write a model to a YAML file in the cobra-format !!omap structure.

    SYNOPSIS ^

    function writeYAMLmodel(model,varargin)

    DESCRIPTION ^

    -
     writeYAMLmodel  Write a model to a yaml file in the standard cobra-format
    - !!omap structure.
    +
     writeYAMLmodel  Write a model to a YAML file in the cobra-format !!omap structure.
     
      The format uses the standard !!omap layout, extended with RAVEN-only
      top-level per-entry keys (inchis, deltaG, metFrom, rxnFrom, references,
    - confidence_score, protein) and the GECKO ec-rxns / ec-enzymes sections.
    + confidence_score, protein, notes) and the GECKO ec-rxns and ec-enzymes
    + sections.
      Reaction EC numbers are written inside the `annotation` block as
      `ec-code`, not as a top-level reaction key.
     
    @@ -44,12 +44,11 @@ 

    DESCRIPTION ^SUBFUNCTIONS ^SOURCE CODE ^

    0001 function writeYAMLmodel(model,varargin)
    -0002 % writeYAMLmodel  Write a model to a yaml file in the standard cobra-format
    -0003 % !!omap structure.
    -0004 %
    -0005 % The format uses the standard !!omap layout, extended with RAVEN-only
    -0006 % top-level per-entry keys (inchis, deltaG, metFrom, rxnFrom, references,
    -0007 % confidence_score, protein) and the GECKO ec-rxns / ec-enzymes sections.
    +0002 % writeYAMLmodel  Write a model to a YAML file in the cobra-format !!omap structure.
    +0003 %
    +0004 % The format uses the standard !!omap layout, extended with RAVEN-only
    +0005 % top-level per-entry keys (inchis, deltaG, metFrom, rxnFrom, references,
    +0006 % confidence_score, protein, notes) and the GECKO ec-rxns and ec-enzymes
    +0007 % sections.
     0008 % Reaction EC numbers are written inside the `annotation` block as
     0009 % `ec-code`, not as a top-level reaction key.
     0010 %
    @@ -88,225 +87,225 @@ 

    SOURCE CODE ^% Name-Value Arguments 0017 % -------------------- 0018 % fileName : char -0019 % name that the file will have. A dialog window will open if no file -0020 % name is specified. -0021 % sortIds : logical -0022 % if metabolites, reactions, genes and compartments should be sorted -0023 % alphabetically by their identifier, otherwise they are kept in their -0024 % original order (default false). -0025 % -0026 % Examples -0027 % -------- -0028 % writeYAMLmodel(model,fileName,sortIds); -0029 p=parseRAVENargs(varargin, {'fileName',[]; 'sortIds',false}); -0030 fileName=p.fileName; sortIds=p.sortIds; -0031 if isempty(fileName) -0032 [fileName, pathName] = uiputfile({'*.yml;*.yaml'}, 'Select file for model export',[model.id '.yml']); -0033 if fileName == 0 -0034 error('You should provide a file location') -0035 else -0036 fileName = fullfile(pathName,fileName); -0037 end -0038 end -0039 fileName=char(fileName); -0040 if ~endsWith(fileName,{'.yml','.yaml'}) -0041 fileName = strcat(fileName,'.yml'); -0042 end -0043 -0044 %Check that model is in RAVEN format: -0045 if isfield(model,'rules') -0046 model = ravenCobraWrapper(model); -0047 end -0048 -0049 %Check that the model structure has no problems -0050 checkModelStruct(model); -0051 -0052 %Sort identifiers alphabetically -0053 if sortIds == true -0054 model = sortIdentifiers(model); -0055 end -0056 -0057 %Simplify Miriam fields: -0058 if isfield(model,'metMiriams') -0059 [model.newMetMiriams,model.newMetMiriamNames] = extractMiriam(model.metMiriams); -0060 end -0061 if isfield(model,'rxnMiriams') -0062 [model.newRxnMiriams,model.newRxnMiriamNames] = extractMiriam(model.rxnMiriams); -0063 end -0064 if isfield(model,'geneMiriams') -0065 [model.newGeneMiriams,model.newGeneMiriamNames] = extractMiriam(model.geneMiriams); -0066 end -0067 if isfield(model,'compMiriams') -0068 [model.newCompMiriams,model.newCompMiriamNames] = extractMiriam(model.compMiriams); -0069 end -0070 -0071 %Open file: -0072 fid = fopen(fileName,'wt'); -0073 if fid == -1 -0074 error(['Cannot write to ' fileName ', does the directory exist?']) -0075 end -0076 % Emit a bare `!!omap` root, with no document-start marker. -0077 fprintf(fid,'!!omap\n'); -0078 -0079 %Insert file header (metadata) -0080 writeMetadata(model,fid); -0081 -0082 %Metabolites: -0083 % Field order: -0084 % id, name, compartment, charge, formula, annotation, then RAVEN-only -0085 % extras (inchis, deltaG, metFrom, notes). -0086 % SMILES goes inside the annotation block, not at metabolite top level — -0087 % the reader still accepts top-level `smiles:` for backward compatibility -0088 % with older yeast-GEM files. -0089 fprintf(fid,'- metabolites:\n'); -0090 for i = 1:length(model.mets) -0091 fprintf(fid,' - !!omap\n'); -0092 writeField(model, fid, 'mets', 'txt', i, ' - id', 4) -0093 writeField(model, fid, 'metNames', 'txt', i, ' - name', 4) -0094 writeField(model, fid, 'metComps', 'txt', i, ' - compartment', 4) -0095 writeField(model, fid, 'metCharges', 'flt', i, ' - charge', 4) -0096 writeField(model, fid, 'metFormulas', 'txt', i, ' - formula', 4) -0097 writeAnnotation(model, fid, 'met', i) -0098 writeField(model, fid, 'inchis', 'txt', i, ' - inchis', 4) -0099 writeField(model, fid, 'metDeltaG', 'flt', i, ' - deltaG', 4) -0100 writeField(model, fid, 'metFrom', 'txt', i, ' - metFrom', 4) -0101 writeField(model, fid, 'metNotes', 'txt', i, ' - notes', 4) -0102 end -0103 -0104 %Reactions: -0105 % Field order: -0106 % id, name, metabolites, lower_bound, upper_bound, gene_reaction_rule, -0107 % objective_coefficient, subsystem, annotation (which carries the EC -0108 % numbers under `ec-code`), then RAVEN-only extras (references, rxnFrom, -0109 % deltaG, confidence_score, notes). The notes key is the canonical -0110 % `notes`, not `rxnNotes`; the reader still accepts the legacy key. -0111 fprintf(fid,'- reactions:\n'); -0112 for i = 1:length(model.rxns) -0113 fprintf(fid,' - !!omap\n'); -0114 writeField(model, fid, 'rxns', 'txt', i, ' - id', 4) -0115 writeField(model, fid, 'rxnNames', 'txt', i, ' - name', 4) -0116 writeField(model, fid, 'S', 'txt', i, ' - metabolites', 4) -0117 writeField(model, fid, 'lb', 'flt', i, ' - lower_bound', 4) -0118 writeField(model, fid, 'ub', 'flt', i, ' - upper_bound', 4) -0119 % An absent gene_reaction_rule reads back as '' regardless of whether -0120 % it was written, so dropping an empty one when writing is safe, and -0121 % keeps the file free of the resulting churn. -0122 writeField(model, fid, 'grRules', 'txt', i, ' - gene_reaction_rule', 4) -0123 if model.c(i)~=0 -0124 writeField(model, fid, 'c', 'flt', i, ' - objective_coefficient', 4) -0125 end -0126 writeField(model, fid, 'subSystems', 'txt', i, ' - subsystem', 4) -0127 writeAnnotation(model, fid, 'rxn', i) -0128 writeField(model, fid, 'rxnReferences', 'txt', i, ' - references', 4) -0129 writeField(model, fid, 'rxnFrom', 'txt', i, ' - rxnFrom', 4) -0130 writeField(model, fid, 'rxnDeltaG', 'flt', i, ' - deltaG', 4) -0131 writeField(model, fid, 'rxnConfidenceScores', 'flt', i, ' - confidence_score', 4) -0132 writeField(model, fid, 'rxnNotes', 'txt', i, ' - notes', 4) -0133 end -0134 -0135 %Genes: -0136 % For a gene-less model, write the flow-style empty list rather than a -0137 % bare `- genes:` with nothing following, which parses as `genes: null` -0138 % and would crash a reader that assumes a present key means a list. -0139 if isfield(model,'genes') && ~isempty(model.genes) -0140 fprintf(fid,'- genes:\n'); -0141 for i = 1:length(model.genes) -0142 fprintf(fid,' - !!omap\n'); -0143 writeField(model, fid, 'genes', 'txt', i, ' - id', 4) -0144 writeField(model, fid, 'geneShortNames', 'txt', i, ' - name', 4) -0145 writeAnnotationSimple(model, fid, 'geneMiriams', 'newGeneMiriams', 'newGeneMiriamNames', i) -0146 writeField(model, fid, 'proteins', 'txt', i, ' - protein', 4) -0147 end -0148 else -0149 fprintf(fid,'- genes: []\n'); -0150 end -0151 -0152 %Compartments: -0153 fprintf(fid,'- compartments: !!omap\n'); -0154 for i = 1:length(model.comps) -0155 writeField(model, fid, 'compNames', 'txt', i, [' - ' model.comps{i}], 2) -0156 writeAnnotationSimple(model, fid, 'compMiriams', 'newCompMiriams', 'newCompMiriamNames', i, ' ', 6) -0157 end +0019 % name of the file. A dialog window opens if no file name is specified. +0020 % sortIds : logical +0021 % true to sort metabolites, reactions, genes and compartments +0022 % alphabetically by their identifier. If false, they keep their original +0023 % order (default false). +0024 % +0025 % Examples +0026 % -------- +0027 % writeYAMLmodel(model,fileName,sortIds); +0028 p=parseRAVENargs(varargin, {'fileName',[]; 'sortIds',false}); +0029 fileName=p.fileName; sortIds=p.sortIds; +0030 if isempty(fileName) +0031 [fileName, pathName] = uiputfile({'*.yml;*.yaml'}, 'Select file for model export',[model.id '.yml']); +0032 if fileName == 0 +0033 error('You should provide a file location') +0034 else +0035 fileName = fullfile(pathName,fileName); +0036 end +0037 end +0038 fileName=char(fileName); +0039 if ~endsWith(fileName,{'.yml','.yaml'}) +0040 fileName = strcat(fileName,'.yml'); +0041 end +0042 +0043 %Check that model is in RAVEN format: +0044 if isfield(model,'rules') +0045 model = ravenCobraWrapper(model); +0046 end +0047 +0048 %Check that the model structure has no problems +0049 checkModelStruct(model); +0050 +0051 %Sort identifiers alphabetically +0052 if sortIds == true +0053 model = sortIdentifiers(model); +0054 end +0055 +0056 %Simplify Miriam fields: +0057 if isfield(model,'metMiriams') +0058 [model.newMetMiriams,model.newMetMiriamNames] = extractMiriam(model.metMiriams); +0059 end +0060 if isfield(model,'rxnMiriams') +0061 [model.newRxnMiriams,model.newRxnMiriamNames] = extractMiriam(model.rxnMiriams); +0062 end +0063 if isfield(model,'geneMiriams') +0064 [model.newGeneMiriams,model.newGeneMiriamNames] = extractMiriam(model.geneMiriams); +0065 end +0066 if isfield(model,'compMiriams') +0067 [model.newCompMiriams,model.newCompMiriamNames] = extractMiriam(model.compMiriams); +0068 end +0069 +0070 %Open file: +0071 fid = fopen(fileName,'wt'); +0072 if fid == -1 +0073 error(['Cannot write to ' fileName ', does the directory exist?']) +0074 end +0075 % Emit a bare `!!omap` root, with no document-start marker. +0076 fprintf(fid,'!!omap\n'); +0077 +0078 %Insert file header (metadata) +0079 writeMetadata(model,fid); +0080 +0081 %Metabolites: +0082 % Field order: +0083 % id, name, compartment, charge, formula, annotation, then RAVEN-only +0084 % extras (inchis, deltaG, metFrom, notes). +0085 % SMILES goes inside the annotation block, not at metabolite top level — +0086 % the reader still accepts top-level `smiles:` for backward compatibility +0087 % with older yeast-GEM files. +0088 fprintf(fid,'- metabolites:\n'); +0089 for i = 1:length(model.mets) +0090 fprintf(fid,' - !!omap\n'); +0091 writeField(model, fid, 'mets', 'txt', i, ' - id', 4) +0092 writeField(model, fid, 'metNames', 'txt', i, ' - name', 4) +0093 writeField(model, fid, 'metComps', 'txt', i, ' - compartment', 4) +0094 writeField(model, fid, 'metCharges', 'flt', i, ' - charge', 4) +0095 writeField(model, fid, 'metFormulas', 'txt', i, ' - formula', 4) +0096 writeAnnotation(model, fid, 'met', i) +0097 writeField(model, fid, 'inchis', 'txt', i, ' - inchis', 4) +0098 writeField(model, fid, 'metDeltaG', 'flt', i, ' - deltaG', 4) +0099 writeField(model, fid, 'metFrom', 'txt', i, ' - metFrom', 4) +0100 writeField(model, fid, 'metNotes', 'txt', i, ' - notes', 4) +0101 end +0102 +0103 %Reactions: +0104 % Field order: +0105 % id, name, metabolites, lower_bound, upper_bound, gene_reaction_rule, +0106 % objective_coefficient, subsystem, annotation (which carries the EC +0107 % numbers under `ec-code`), then RAVEN-only extras (references, rxnFrom, +0108 % deltaG, confidence_score, notes). The notes key is the canonical +0109 % `notes`, not `rxnNotes`; the reader still accepts the legacy key. +0110 fprintf(fid,'- reactions:\n'); +0111 for i = 1:length(model.rxns) +0112 fprintf(fid,' - !!omap\n'); +0113 writeField(model, fid, 'rxns', 'txt', i, ' - id', 4) +0114 writeField(model, fid, 'rxnNames', 'txt', i, ' - name', 4) +0115 writeField(model, fid, 'S', 'txt', i, ' - metabolites', 4) +0116 writeField(model, fid, 'lb', 'flt', i, ' - lower_bound', 4) +0117 writeField(model, fid, 'ub', 'flt', i, ' - upper_bound', 4) +0118 % An absent gene_reaction_rule reads back as '' regardless of whether +0119 % it was written, so dropping an empty one when writing is safe, and +0120 % keeps the file free of the resulting churn. +0121 writeField(model, fid, 'grRules', 'txt', i, ' - gene_reaction_rule', 4) +0122 if model.c(i)~=0 +0123 writeField(model, fid, 'c', 'flt', i, ' - objective_coefficient', 4) +0124 end +0125 writeField(model, fid, 'subSystems', 'txt', i, ' - subsystem', 4) +0126 writeAnnotation(model, fid, 'rxn', i) +0127 writeField(model, fid, 'rxnReferences', 'txt', i, ' - references', 4) +0128 writeField(model, fid, 'rxnFrom', 'txt', i, ' - rxnFrom', 4) +0129 writeField(model, fid, 'rxnDeltaG', 'flt', i, ' - deltaG', 4) +0130 writeField(model, fid, 'rxnConfidenceScores', 'flt', i, ' - confidence_score', 4) +0131 writeField(model, fid, 'rxnNotes', 'txt', i, ' - notes', 4) +0132 end +0133 +0134 %Genes: +0135 % For a gene-less model, write the flow-style empty list rather than a +0136 % bare `- genes:` with nothing following, which parses as `genes: null` +0137 % and would crash a reader that assumes a present key means a list. +0138 if isfield(model,'genes') && ~isempty(model.genes) +0139 fprintf(fid,'- genes:\n'); +0140 for i = 1:length(model.genes) +0141 fprintf(fid,' - !!omap\n'); +0142 writeField(model, fid, 'genes', 'txt', i, ' - id', 4) +0143 writeField(model, fid, 'geneShortNames', 'txt', i, ' - name', 4) +0144 writeAnnotationSimple(model, fid, 'geneMiriams', 'newGeneMiriams', 'newGeneMiriamNames', i) +0145 writeField(model, fid, 'proteins', 'txt', i, ' - protein', 4) +0146 end +0147 else +0148 fprintf(fid,'- genes: []\n'); +0149 end +0150 +0151 %Compartments: +0152 fprintf(fid,'- compartments: !!omap\n'); +0153 for i = 1:length(model.comps) +0154 writeField(model, fid, 'compNames', 'txt', i, [' - ' model.comps{i}], 2) +0155 writeAnnotationSimple(model, fid, 'compMiriams', 'newCompMiriams', 'newCompMiriamNames', i, ' ', 6) +0156 end +0157 0158 -0159 -0160 %EC-model: -0161 if isfield(model,'ec') -0162 % gecko_light flag at the top level (keeps the metaData block a pure -0163 % provenance container). The reader accepts both this key and the -0164 % legacy geckoLight key inside metaData. -0165 if model.ec.geckoLight; geckoLightStr = 'true'; else; geckoLightStr = 'false'; end -0166 fprintf(fid,'- gecko_light: %s\n', geckoLightStr); -0167 fprintf(fid,'- ec-rxns:\n'); -0168 for i = 1:length(model.ec.rxns) -0169 fprintf(fid,' - !!omap\n'); -0170 writeField(model.ec, fid, 'rxns', 'txt', i, ' - id', 4) -0171 writeField(model.ec, fid, 'kcat', 'flt', i, ' - kcat', 4) -0172 writeField(model.ec, fid, 'source', 'txt', i, ' - source', 4) -0173 writeField(model.ec, fid, 'notes', 'txt', i, ' - notes', 4) -0174 writeField(model.ec, fid, 'eccodes', 'txt', i, ' - eccodes', 4) -0175 writeField(model.ec, fid, 'rxnEnzMat', 'txt', i, ' - enzymes', 4) -0176 end -0177 -0178 fprintf(fid,'- ec-enzymes:\n'); -0179 for i = 1:length(model.ec.genes) -0180 fprintf(fid,' - !!omap\n'); -0181 writeField(model.ec, fid, 'genes', 'txt', i, ' - genes', 4) -0182 writeField(model.ec, fid, 'enzymes', 'txt', i, ' - enzymes', 4) -0183 writeField(model.ec, fid, 'mw', 'flt', i, ' - mw', 4) -0184 writeField(model.ec, fid, 'sequence', 'txt', i, ' - sequence', 4) -0185 writeField(model.ec, fid, 'concs', 'flt', i, ' - concs', 4) -0186 end -0187 end -0188 -0189 %Close file: -0190 fclose(fid); -0191 -0192 end -0193 -0194 function writeField(model,fid,fieldName,type,pos,name,keyIndent) -0195 %Writes a new line in the yaml file if the field exists and the field is -0196 %not empty at the corresponding position. It's recursive for the S -0197 %field (reaction stoichiometry). -0198 -0199 if isfield(model,fieldName) -0200 if strcmp(fieldName,'metComps') -0201 %metComps: write full name -0202 fieldName = 'comps'; -0203 pos = model.metComps(pos); -0204 end -0205 -0206 field = model.(fieldName); -0207 -0208 if strcmp(fieldName,'S') -0209 %S: create header & write each metabolite in a new line. Reactions -0210 %with no metabolites emit `metabolites: !!omap []` (the flow-style -0211 %empty omap representation) so the file remains a valid YAML 1.2 -0212 %document. -0213 if sum(field(:,pos) ~= 0) > 0 -0214 fprintf(fid,'%s: !!omap\n',name); -0215 model.mets = model.mets(field(:,pos) ~= 0); -0216 model.coeffs = field(field(:,pos) ~= 0,pos); -0217 %Sort metabolites: -0218 [model.mets,order] = sort(model.mets); -0219 model.coeffs = model.coeffs(order); -0220 for i = 1:length(model.mets) -0221 writeField(model, fid, 'coeffs', 'flt', i, [' - ' model.mets{i}], 6) -0222 end -0223 else -0224 fprintf(fid,'%s: !!omap []\n',name); -0225 end -0226 -0227 elseif strcmp(fieldName,'rxnEnzMat') -0228 %S: create header & write each enzyme in a new line -0229 fprintf(fid,'%s: !!omap\n',name); -0230 if sum(field(pos,:) ~= 0) > 0 +0159 %EC-model: +0160 if isfield(model,'ec') +0161 % gecko_light flag at the top level (keeps the metaData block a pure +0162 % provenance container). The reader accepts both this key and the +0163 % legacy geckoLight key inside metaData. +0164 if model.ec.geckoLight; geckoLightStr = 'true'; else; geckoLightStr = 'false'; end +0165 fprintf(fid,'- gecko_light: %s\n', geckoLightStr); +0166 fprintf(fid,'- ec-rxns:\n'); +0167 for i = 1:length(model.ec.rxns) +0168 fprintf(fid,' - !!omap\n'); +0169 writeField(model.ec, fid, 'rxns', 'txt', i, ' - id', 4) +0170 writeField(model.ec, fid, 'kcat', 'flt', i, ' - kcat', 4) +0171 writeField(model.ec, fid, 'source', 'txt', i, ' - source', 4) +0172 writeField(model.ec, fid, 'notes', 'txt', i, ' - notes', 4) +0173 writeField(model.ec, fid, 'eccodes', 'txt', i, ' - eccodes', 4) +0174 writeField(model.ec, fid, 'rxnEnzMat', 'txt', i, ' - enzymes', 4) +0175 end +0176 +0177 fprintf(fid,'- ec-enzymes:\n'); +0178 for i = 1:length(model.ec.genes) +0179 fprintf(fid,' - !!omap\n'); +0180 writeField(model.ec, fid, 'genes', 'txt', i, ' - genes', 4) +0181 writeField(model.ec, fid, 'enzymes', 'txt', i, ' - enzymes', 4) +0182 writeField(model.ec, fid, 'mw', 'flt', i, ' - mw', 4) +0183 writeField(model.ec, fid, 'sequence', 'txt', i, ' - sequence', 4) +0184 writeField(model.ec, fid, 'concs', 'flt', i, ' - concs', 4) +0185 end +0186 end +0187 +0188 %Close file: +0189 fclose(fid); +0190 +0191 end +0192 +0193 function writeField(model,fid,fieldName,type,pos,name,keyIndent) +0194 %Writes a new line in the yaml file if the field exists and the field is +0195 %not empty at the corresponding position. It's recursive for the S +0196 %field (reaction stoichiometry). +0197 +0198 if isfield(model,fieldName) +0199 if strcmp(fieldName,'metComps') +0200 %metComps: write full name +0201 fieldName = 'comps'; +0202 pos = model.metComps(pos); +0203 end +0204 +0205 field = model.(fieldName); +0206 +0207 if strcmp(fieldName,'S') +0208 %S: create header & write each metabolite in a new line. Reactions +0209 %with no metabolites emit `metabolites: !!omap []` (the flow-style +0210 %empty omap representation) so the file remains a valid YAML 1.2 +0211 %document. +0212 if sum(field(:,pos) ~= 0) > 0 +0213 fprintf(fid,'%s: !!omap\n',name); +0214 model.mets = model.mets(field(:,pos) ~= 0); +0215 model.coeffs = field(field(:,pos) ~= 0,pos); +0216 %Sort metabolites: +0217 [model.mets,order] = sort(model.mets); +0218 model.coeffs = model.coeffs(order); +0219 for i = 1:length(model.mets) +0220 writeField(model, fid, 'coeffs', 'flt', i, [' - ' model.mets{i}], 6) +0221 end +0222 else +0223 fprintf(fid,'%s: !!omap []\n',name); +0224 end +0225 +0226 elseif strcmp(fieldName,'rxnEnzMat') +0227 %enzymes: create header & write each enzyme in a new line. A +0228 %reaction without enzymes has no entry at all. +0229 if sum(field(pos,:) ~= 0) > 0 +0230 fprintf(fid,'%s: !!omap\n',name); 0231 model.enzymes = model.enzymes(field(pos,:) ~= 0); 0232 model.coeffs = field(pos,field(pos,:) ~= 0); 0233 %Sort metabolites: 0234 [model.enzymes,order] = sort(model.enzymes); 0235 model.coeffs = model.coeffs(order); 0236 for i = 1:length(model.enzymes) -0237 writeField(model, fid, 'coeffs', 'flt', i, [' - ' model.enzymes{i}], 4) +0237 writeField(model, fid, 'coeffs', 'flt', i, [' - ' model.enzymes{i}], 6) 0238 end 0239 end 0240 @@ -454,230 +453,241 @@

    SOURCE CODE ^if x == floor(x) && abs(x) < 1e16 0383 s = sprintf('%.1f', x); % e.g. 1000 -> "1000.0" 0384 else -0385 s = sprintf('%.15g', x); -0386 end -0387 end -0388 -0389 function writeAnnotationSimple(model, fid, miriamsField, miriamValsField, miriamNamesField, pos, indent, subIndent) -0390 % Emit a bare `annotation: !!omap` block from MIRIAM cross-references only -0391 % (used for genes and compartments, which carry no extra non-MIRIAM -0392 % annotation key and have no established reference format to follow — -0393 % compartments in particular are just a flat {id: name} map with no -0394 % per-compartment annotation at all — so the layout here follows only -0395 % readYAMLmodel's own expectation that a compartment's annotation -0396 % sub-entries are indented >=6 spaces). Keys are emitted in alphabetical -0397 % order, matching the sort order used elsewhere in this file. -0398 if nargin < 7 -0399 indent = ' '; -0400 end -0401 if nargin < 8 -0402 subIndent = length(indent)+2; -0403 end -0404 if ~isfield(model, miriamsField) || isempty(model.(miriamsField){pos}) -0405 return -0406 end -0407 names = model.(miriamNamesField); -0408 values = model.(miriamValsField); -0409 entries = collectMiriamEntries(names, values, pos); -0410 if isempty(entries) -0411 return -0412 end -0413 [~,order] = sort({entries.name}); -0414 entries = entries(order); -0415 fprintf(fid, '%s- annotation: !!omap\n', indent); -0416 emitAnnotationEntries(fid, entries, subIndent); -0417 end -0418 -0419 function entries = collectMiriamEntries(names, values, pos) -0420 entries = struct('name', {}, 'list', {}, 'forceList', {}); -0421 for j = 1:size(values, 2) -0422 v = values{pos, j}; -0423 if isempty(v); continue; end -0424 list = strip(strsplit(strrep(v, ' ', ''), ';')); -0425 entries(end+1) = struct('name', names{j}, 'list', {list}, 'forceList', false); %#ok<AGROW> -0426 end -0427 end -0428 -0429 function writeAnnotation(model, fid, kind, pos) -0430 % Emit the per-entry `annotation` block, fusing MIRIAM cross-references -0431 % with the non-MIRIAM annotation keys: `smiles` for metabolites and -0432 % `ec-code` (EC numbers) for reactions — both are written inside -0433 % `annotation` rather than as top-level entry keys. `ec-code` is always -0434 % emitted as a list, even when there is a single code. All keys (MIRIAMs -0435 % and the extra key together) are emitted in alphabetical order. -0436 switch kind -0437 case 'met' -0438 miriamsField = 'metMiriams'; -0439 miriamNamesField = 'newMetMiriamNames'; -0440 miriamValsField = 'newMetMiriams'; -0441 extraName = 'smiles'; -0442 extraField = 'metSmiles'; -0443 case 'rxn' -0444 miriamsField = 'rxnMiriams'; -0445 miriamNamesField = 'newRxnMiriamNames'; -0446 miriamValsField = 'newRxnMiriams'; -0447 extraName = 'ec-code'; -0448 extraField = 'eccodes'; -0449 otherwise -0450 error('writeAnnotation:unsupportedKind', 'Unsupported kind: %s', kind); -0451 end -0452 -0453 hasMiriams = isfield(model, miriamsField) && ~isempty(model.(miriamsField){pos}); -0454 hasExtra = isfield(model, extraField) && pos <= numel(model.(extraField)) ... -0455 && ~isempty(model.(extraField){pos}); -0456 -0457 if ~hasMiriams && ~hasExtra -0458 return; +0385 % Shortest text that reads back as exactly x (15 to 17 significant +0386 % digits), as Python's repr does, so 1/3 is written 0.3333333333333333 +0387 % and a value is never rounded on the way out. +0388 for digits = 15:17 +0389 s = sprintf(['%.' num2str(digits) 'g'], x); +0390 if str2double(s) == x +0391 break +0392 end +0393 end +0394 end +0395 end +0396 +0397 function writeAnnotationSimple(model, fid, miriamsField, miriamValsField, miriamNamesField, pos, indent, subIndent) +0398 % Emit a bare `annotation: !!omap` block from MIRIAM cross-references only +0399 % (used for genes and compartments, which carry no extra non-MIRIAM +0400 % annotation key and have no established reference format to follow — +0401 % compartments in particular are just a flat {id: name} map with no +0402 % per-compartment annotation at all — so the layout here follows only +0403 % readYAMLmodel's own expectation that a compartment's annotation +0404 % sub-entries are indented >=6 spaces). Keys are emitted in alphabetical +0405 % order, matching the sort order used elsewhere in this file. +0406 if nargin < 7 +0407 indent = ' '; +0408 end +0409 if nargin < 8 +0410 subIndent = length(indent)+2; +0411 end +0412 if ~isfield(model, miriamsField) || isempty(model.(miriamsField){pos}) +0413 return +0414 end +0415 names = model.(miriamNamesField); +0416 values = model.(miriamValsField); +0417 entries = collectMiriamEntries(names, values, pos); +0418 if isempty(entries) +0419 return +0420 end +0421 [~,order] = sort({entries.name}); +0422 entries = entries(order); +0423 fprintf(fid, '%s- annotation: !!omap\n', indent); +0424 emitAnnotationEntries(fid, entries, subIndent); +0425 end +0426 +0427 function entries = collectMiriamEntries(names, values, pos) +0428 entries = struct('name', {}, 'list', {}, 'forceList', {}); +0429 for j = 1:size(values, 2) +0430 v = values{pos, j}; +0431 if isempty(v); continue; end +0432 list = strip(strsplit(strrep(v, ' ', ''), ';')); +0433 entries(end+1) = struct('name', names{j}, 'list', {list}, 'forceList', false); %#ok<AGROW> +0434 end +0435 end +0436 +0437 function writeAnnotation(model, fid, kind, pos) +0438 % Emit the per-entry `annotation` block, fusing MIRIAM cross-references +0439 % with the non-MIRIAM annotation keys: `smiles` for metabolites and +0440 % `ec-code` (EC numbers) for reactions — both are written inside +0441 % `annotation` rather than as top-level entry keys. `ec-code` is always +0442 % emitted as a list, even when there is a single code. All keys (MIRIAMs +0443 % and the extra key together) are emitted in alphabetical order. +0444 switch kind +0445 case 'met' +0446 miriamsField = 'metMiriams'; +0447 miriamNamesField = 'newMetMiriamNames'; +0448 miriamValsField = 'newMetMiriams'; +0449 extraName = 'smiles'; +0450 extraField = 'metSmiles'; +0451 case 'rxn' +0452 miriamsField = 'rxnMiriams'; +0453 miriamNamesField = 'newRxnMiriamNames'; +0454 miriamValsField = 'newRxnMiriams'; +0455 extraName = 'ec-code'; +0456 extraField = 'eccodes'; +0457 otherwise +0458 error('writeAnnotation:unsupportedKind', 'Unsupported kind: %s', kind); 0459 end 0460 -0461 entries = struct('name', {}, 'list', {}, 'forceList', {}); -0462 if hasMiriams -0463 entries = collectMiriamEntries(model.(miriamNamesField), model.(miriamValsField), pos); -0464 end -0465 if hasExtra -0466 % EC numbers / SMILES are always emitted as a list, even when there is -0467 % a single value — unlike a MIRIAM entry, which collapses to a bare -0468 % scalar when there is only one. -0469 codes = strip(strsplit(strrep(model.(extraField){pos}, ' ', ''), ';')); -0470 codes = codes(~cellfun('isempty', codes)); -0471 if ~isempty(codes) -0472 entries(end+1) = struct('name', extraName, 'list', {codes}, 'forceList', true); %#ok<AGROW> -0473 end -0474 end -0475 if isempty(entries) -0476 return -0477 end -0478 [~,order] = sort({entries.name}); -0479 entries = entries(order); -0480 -0481 fprintf(fid, ' - annotation: !!omap\n'); -0482 emitAnnotationEntries(fid, entries, 6); -0483 end -0484 -0485 function emitAnnotationEntries(fid, entries, keyIndent) -0486 % Shared tail for writeAnnotation / writeAnnotationSimple: entries are -0487 % already sorted by name; emit each as a bare scalar (single value) or a -0488 % block list (multiple values), at the given key indent. -0489 pad = repmat(' ', 1, keyIndent); -0490 for e = entries -0491 if numel(e.list) == 1 && ~e.forceList -0492 emitScalarLine(fid, [pad '- ' e.name], e.list{1}, keyIndent) -0493 else -0494 fprintf(fid, '%s- %s:\n', pad, e.name); -0495 for k = 1:numel(e.list) -0496 emitListItem(fid, [pad ' -'], e.list{k}, keyIndent+2) -0497 end -0498 end -0499 end -0500 end -0501 -0502 function emitScalarLine(fid, prefix, value, keyIndent, bareNumeric) -0503 % Emit "PREFIX: VALUE\n" (double-quoted when needed), where PREFIX is the -0504 % " - key" part with no trailing colon and value is the raw, -0505 % unescaped scalar content. -0506 % -0507 % bareNumeric (default false): value is already a pre-formatted number -0508 % (from formatFloat) that must never be quoted — numbers are never -0509 % ambiguous once formatted. -0510 if nargin < 5 -0511 bareNumeric = false; -0512 end -0513 fprintf(fid, '%s:', prefix); -0514 emitScalarCore(fid, length(prefix)+1, value, keyIndent, bareNumeric) -0515 end -0516 -0517 function emitListItem(fid, dashPrefix, value, keyIndent) -0518 % Emit "DASHPREFIX VALUE\n" for a plain sequence entry (no key, just the -0519 % "- " marker) — e.g. a subsystem, EC code, SMILES or MIRIAM list item. -0520 % Unlike emitScalarLine, no colon is written. -0521 fprintf(fid, '%s', dashPrefix); -0522 emitScalarCore(fid, length(dashPrefix), value, keyIndent, false) +0461 hasMiriams = isfield(model, miriamsField) && ~isempty(model.(miriamsField){pos}); +0462 hasExtra = isfield(model, extraField) && pos <= numel(model.(extraField)) ... +0463 && ~isempty(model.(extraField){pos}); +0464 +0465 if ~hasMiriams && ~hasExtra +0466 return; +0467 end +0468 +0469 entries = struct('name', {}, 'list', {}, 'forceList', {}); +0470 if hasMiriams +0471 entries = collectMiriamEntries(model.(miriamNamesField), model.(miriamValsField), pos); +0472 end +0473 if hasExtra +0474 % EC numbers / SMILES are always emitted as a list, even when there is +0475 % a single value — unlike a MIRIAM entry, which collapses to a bare +0476 % scalar when there is only one. +0477 codes = strip(strsplit(strrep(model.(extraField){pos}, ' ', ''), ';')); +0478 codes = codes(~cellfun('isempty', codes)); +0479 if ~isempty(codes) +0480 entries(end+1) = struct('name', extraName, 'list', {codes}, 'forceList', true); %#ok<AGROW> +0481 end +0482 end +0483 if isempty(entries) +0484 return +0485 end +0486 [~,order] = sort({entries.name}); +0487 entries = entries(order); +0488 +0489 fprintf(fid, ' - annotation: !!omap\n'); +0490 emitAnnotationEntries(fid, entries, 6); +0491 end +0492 +0493 function emitAnnotationEntries(fid, entries, keyIndent) +0494 % Shared tail for writeAnnotation / writeAnnotationSimple: entries are +0495 % already sorted by name; emit each as a bare scalar (single value) or a +0496 % block list (multiple values), at the given key indent. +0497 pad = repmat(' ', 1, keyIndent); +0498 for e = entries +0499 if numel(e.list) == 1 && ~e.forceList +0500 emitScalarLine(fid, [pad '- ' e.name], e.list{1}, keyIndent) +0501 else +0502 fprintf(fid, '%s- %s:\n', pad, e.name); +0503 for k = 1:numel(e.list) +0504 emitListItem(fid, [pad ' -'], e.list{k}, keyIndent+2) +0505 end +0506 end +0507 end +0508 end +0509 +0510 function emitScalarLine(fid, prefix, value, keyIndent, bareNumeric) +0511 % Emit "PREFIX: VALUE\n" (double-quoted when needed), where PREFIX is the +0512 % " - key" part with no trailing colon and value is the raw, +0513 % unescaped scalar content. +0514 % +0515 % bareNumeric (default false): value is already a pre-formatted number +0516 % (from formatFloat) that must never be quoted — numbers are never +0517 % ambiguous once formatted. +0518 if nargin < 5 +0519 bareNumeric = false; +0520 end +0521 fprintf(fid, '%s:', prefix); +0522 emitScalarCore(fid, length(prefix)+1, value, keyIndent, bareNumeric) 0523 end 0524 -0525 function emitScalarCore(fid, ~, value, ~, bareNumeric) -0526 % Shared tail for emitScalarLine / emitListItem: writes a single space -0527 % after ':' or '-', then the value, double-quoted when needed (matching -0528 % Prettier's YAML default). No folding — this format never wraps a -0529 % scalar across lines, however long, so the two unused positional -0530 % arguments (column and key indent) only exist to keep every call site's -0531 % signature stable. -0532 if bareNumeric -0533 fprintf(fid, ' %s\n', value); -0534 return -0535 end -0536 if needsQuote(value) -0537 escaped = strrep(value, '\', '\\'); -0538 escaped = strrep(escaped, '"', '\"'); -0539 fprintf(fid, ' "%s"\n', escaped); -0540 else +0525 function emitListItem(fid, dashPrefix, value, keyIndent) +0526 % Emit "DASHPREFIX VALUE\n" for a plain sequence entry (no key, just the +0527 % "- " marker) — e.g. a subsystem, EC code, SMILES or MIRIAM list item. +0528 % Unlike emitScalarLine, no colon is written. +0529 fprintf(fid, '%s', dashPrefix); +0530 emitScalarCore(fid, length(dashPrefix), value, keyIndent, false) +0531 end +0532 +0533 function emitScalarCore(fid, ~, value, ~, bareNumeric) +0534 % Shared tail for emitScalarLine / emitListItem: writes a single space +0535 % after ':' or '-', then the value, double-quoted when needed (matching +0536 % Prettier's YAML default). No folding — this format never wraps a +0537 % scalar across lines, however long, so the two unused positional +0538 % arguments (column and key indent) only exist to keep every call site's +0539 % signature stable. +0540 if bareNumeric 0541 fprintf(fid, ' %s\n', value); -0542 end +0542 return 0543 end -0544 -0545 function tf = needsQuote(s) -0546 % Whether this scalar needs quoting when written: either because a plain -0547 % (bare) reading would resolve to a non-string YAML type (bool/int/float/ -0548 % null/timestamp — this writer never emits explicit type tags), or -0549 % because the text itself is not valid as a plain block scalar (leading -0550 % indicator character, a mid-string ": "/" #", leading/trailing -0551 % whitespace, or embedded control characters). Implements the YAML 1.2 -0552 % rules for resolving implicit scalar types and analyzing scalar style. -0553 if isempty(s) -0554 tf = true; -0555 return -0556 end -0557 -0558 resolverPattern = [ ... -0559 '^(true|True|TRUE|false|False|FALSE)$|' ... -0560 '^[-+]?[0-9][0-9_]*\.[0-9_]*([eE][-+]?[0-9]+)?$|' ... -0561 '^[-+]?[0-9][0-9_]*[eE][-+]?[0-9]+$|' ... -0562 '^[-+]?\.[0-9_]+([eE][-+][0-9]+)?$|' ... -0563 '^[-+]?\.(inf|Inf|INF)$|' ... -0564 '^\.(nan|NaN|NAN)$|' ... -0565 '^[-+]?0b[01_]+$|' ... -0566 '^[-+]?0o?[0-7_]+$|' ... -0567 '^[-+]?[0-9_]+$|' ... -0568 '^[-+]?0x[0-9a-fA-F_]+$|' ... -0569 '^(~|[Nn]ull|NULL)$|' ... -0570 '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' ... -0571 ]; -0572 if ~isempty(regexp(s, resolverPattern, 'once')) -0573 tf = true; -0574 return -0575 end -0576 -0577 first = s(1); -0578 if any(first == '#,[]{}&*!|>''"%@`') -0579 tf = true; return -0580 end -0581 followedByWs = (numel(s) == 1) || isWs(s(2)); -0582 if (first == '?' || first == ':') && followedByWs -0583 tf = true; return -0584 end -0585 if first == '-' && followedByWs -0586 tf = true; return -0587 end -0588 if isWs(first) || isWs(s(end)) -0589 tf = true; return -0590 end -0591 if any(s == char(10)) || any(s == char(13)) -0592 tf = true; return -0593 end -0594 for i = 2:numel(s) -0595 ch = s(i); -0596 if ch == ':' && ((i == numel(s)) || isWs(s(i+1))) -0597 tf = true; return -0598 end -0599 if ch == '#' && isWs(s(i-1)) -0600 tf = true; return -0601 end -0602 end -0603 tf = false; +0544 if needsQuote(value) +0545 escaped = strrep(value, '\', '\\'); +0546 escaped = strrep(escaped, '"', '\"'); +0547 fprintf(fid, ' "%s"\n', escaped); +0548 else +0549 fprintf(fid, ' %s\n', value); +0550 end +0551 end +0552 +0553 function tf = needsQuote(s) +0554 % Whether this scalar needs quoting when written: either because a plain +0555 % (bare) reading would resolve to a non-string YAML type (bool/int/float/ +0556 % null/timestamp — this writer never emits explicit type tags), or +0557 % because the text itself is not valid as a plain block scalar (leading +0558 % indicator character, a mid-string ": "/" #", leading/trailing +0559 % whitespace, or embedded control characters). Implements the YAML 1.2 +0560 % rules for resolving implicit scalar types and analyzing scalar style, +0561 % and also quotes the YAML 1.1 booleans y, n, yes, no, on and off in any +0562 % case, which YAML 1.1 loaders such as PyYAML read as booleans. +0563 if isempty(s) +0564 tf = true; +0565 return +0566 end +0567 +0568 resolverPattern = [ ... +0569 '^(true|True|TRUE|false|False|FALSE)$|' ... +0570 '^([Yy]|[Nn]|[Yy][Ee][Ss]|[Nn][Oo]|[Oo][Nn]|[Oo][Ff][Ff])$|' ... +0571 '^[-+]?[0-9][0-9_]*\.[0-9_]*([eE][-+]?[0-9]+)?$|' ... +0572 '^[-+]?[0-9][0-9_]*[eE][-+]?[0-9]+$|' ... +0573 '^[-+]?\.[0-9_]+([eE][-+][0-9]+)?$|' ... +0574 '^[-+]?\.(inf|Inf|INF)$|' ... +0575 '^\.(nan|NaN|NAN)$|' ... +0576 '^[-+]?0b[01_]+$|' ... +0577 '^[-+]?0o?[0-7_]+$|' ... +0578 '^[-+]?[0-9_]+$|' ... +0579 '^[-+]?0x[0-9a-fA-F_]+$|' ... +0580 '^(~|[Nn]ull|NULL)$|' ... +0581 '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' ... +0582 ]; +0583 if ~isempty(regexp(s, resolverPattern, 'once')) +0584 tf = true; +0585 return +0586 end +0587 +0588 first = s(1); +0589 if any(first == '#,[]{}&*!|>''"%@`') +0590 tf = true; return +0591 end +0592 followedByWs = (numel(s) == 1) || isWs(s(2)); +0593 if (first == '?' || first == ':') && followedByWs +0594 tf = true; return +0595 end +0596 if first == '-' && followedByWs +0597 tf = true; return +0598 end +0599 if isWs(first) || isWs(s(end)) +0600 tf = true; return +0601 end +0602 if any(s == char(10)) || any(s == char(13)) +0603 tf = true; return 0604 end -0605 -0606 function tf = isWs(ch) -0607 tf = ch == ' ' || ch == char(9) || ch == char(10) || ch == char(13); -0608 end

    +0605 for i = 2:numel(s) +0606 ch = s(i); +0607 if ch == ':' && ((i == numel(s)) || isWs(s(i+1))) +0608 tf = true; return +0609 end +0610 if ch == '#' && isWs(s(i-1)) +0611 tf = true; return +0612 end +0613 end +0614 tf = false; +0615 end +0616 +0617 function tf = isWs(ch) +0618 tf = ch == ' ' || ch == char(9) || ch == char(10) || ch == char(13); +0619 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/left.png b/docs/html/left.png similarity index 100% rename from doc/left.png rename to docs/html/left.png diff --git a/doc/linux.png b/docs/html/linux.png similarity index 100% rename from doc/linux.png rename to docs/html/linux.png diff --git a/doc/localization/assignCompartments.html b/docs/html/localization/assignCompartments.html similarity index 100% rename from doc/localization/assignCompartments.html rename to docs/html/localization/assignCompartments.html diff --git a/doc/localization/defaultCompartmentMap.html b/docs/html/localization/defaultCompartmentMap.html similarity index 64% rename from doc/localization/defaultCompartmentMap.html rename to docs/html/localization/defaultCompartmentMap.html index 13faeeed..6cd07188 100644 --- a/doc/localization/defaultCompartmentMap.html +++ b/docs/html/localization/defaultCompartmentMap.html @@ -4,7 +4,7 @@ Description of defaultCompartmentMap - + @@ -21,33 +21,33 @@

    defaultCompartmentMap

    PURPOSE ^

    -
    defaultCompartmentMap Default predictor/database compartment label -> model compartment id.
    +
    defaultCompartmentMap Default map from compartment labels to model compartment ids.

    SYNOPSIS ^

    function map = defaultCompartmentMap()

    DESCRIPTION ^

    -
     defaultCompartmentMap  Default predictor/database compartment label -> model compartment id.
    +
     defaultCompartmentMap  Default map from compartment labels to model compartment ids.
     
    - Returns a containers.Map from lower-case predictor / database compartment labels (as used by
    - DeepLoc, MULocDeep, COMPARTMENTS and UniProt) to compartment ids, tuned for yeast/fungal
    - models (e.g. yeast-GEM codes). Pass it (or your own map) to parseScores / getUniProtScores as
    + Returns a containers.Map from lower-case compartment labels of predictors and databases (as
    + used by DeepLoc, MULocDeep, COMPARTMENTS and UniProt) to compartment ids, for yeast and fungal
    + models (for example yeast-GEM codes). The map is passed to parseScores or getUniProtScores as
      'compartmentMap'.
     
    - The table below is also the worked example of how such a map is built, so read it as three
    - deliberate decisions rather than a lookup list:
    + The map holds three decisions:
     
    - - **Which compartments exist.** A label absent from the map is dropped along with its score
    -   column, so listing only the ids your model has is how unwanted compartments are excluded.
    -   'plastid' is absent because fungi lack one; a plant model would add it.
    - - **Which labels are the same thing.** Predictors disagree on wording, so 'cytoplasm' and
    -   'cytosol' both map to 'c', and 'mitochondrion' / 'mitochondria' / 'mitochondrial' all map
    -   to 'm'. Merged columns are combined by maximum, not sum.
    - - **Which distinct compartments to collapse.** 'lysosome' maps to 'v' not because it is a
    -   vacuole but because a yeast model has no lysosome and the vacuole is its functional
    -   equivalent. Change this if your model separates them.
    + - Compartments present. A label absent from the map is dropped along with its score column, so
    +   the map lists only the compartment ids of the model. 'plastid' is absent because fungi have
    +   no plastid; a plant model adds it.
    + - Labels that name the same compartment. Predictors differ in wording, so 'cytoplasm' and
    +   'cytosol' both map to 'c', and 'mitochondrion', 'mitochondria' and 'mitochondrial' all map
    +   to 'm'. Merged columns are combined by their maximum.
    + - Distinct compartments that are collapsed. 'lysosome' maps to 'v' because a yeast model has
    +   no lysosome and the vacuole is its functional equivalent. A model that separates the two
    +   needs a different mapping.
     
    - Extend a copy rather than editing this function, e.g.
    + Changes to the mapping go into a copy of the map, which leaves this function unedited, for
    + example:
     
          map = defaultCompartmentMap;
          map('cell wall') = 'ce';
    @@ -69,34 +69,34 @@ 

    CROSS-REFERENCE INFORMATION ^
 </ul>
 This function is called by:
 <ul style= -
  • getUniProtScores getUniProtScores Fetch curated subcellular locations from UniProt -> gene scoring structure.
  • parseScores parseScores Parse predictor / database output into a gene scoring structure (GSS).
  • +
  • getUniProtScores getUniProtScores Fetch curated subcellular locations from UniProt as a gene scoring structure.
  • parseScores parseScores Parse predictor or database output into a gene scoring structure (GSS).
  • SOURCE CODE ^

    0001 function map = defaultCompartmentMap()
    -0002 % defaultCompartmentMap  Default predictor/database compartment label -> model compartment id.
    +0002 % defaultCompartmentMap  Default map from compartment labels to model compartment ids.
     0003 %
    -0004 % Returns a containers.Map from lower-case predictor / database compartment labels (as used by
    -0005 % DeepLoc, MULocDeep, COMPARTMENTS and UniProt) to compartment ids, tuned for yeast/fungal
    -0006 % models (e.g. yeast-GEM codes). Pass it (or your own map) to parseScores / getUniProtScores as
    +0004 % Returns a containers.Map from lower-case compartment labels of predictors and databases (as
    +0005 % used by DeepLoc, MULocDeep, COMPARTMENTS and UniProt) to compartment ids, for yeast and fungal
    +0006 % models (for example yeast-GEM codes). The map is passed to parseScores or getUniProtScores as
     0007 % 'compartmentMap'.
     0008 %
    -0009 % The table below is also the worked example of how such a map is built, so read it as three
    -0010 % deliberate decisions rather than a lookup list:
    -0011 %
    -0012 % - **Which compartments exist.** A label absent from the map is dropped along with its score
    -0013 %   column, so listing only the ids your model has is how unwanted compartments are excluded.
    -0014 %   'plastid' is absent because fungi lack one; a plant model would add it.
    -0015 % - **Which labels are the same thing.** Predictors disagree on wording, so 'cytoplasm' and
    -0016 %   'cytosol' both map to 'c', and 'mitochondrion' / 'mitochondria' / 'mitochondrial' all map
    -0017 %   to 'm'. Merged columns are combined by maximum, not sum.
    -0018 % - **Which distinct compartments to collapse.** 'lysosome' maps to 'v' not because it is a
    -0019 %   vacuole but because a yeast model has no lysosome and the vacuole is its functional
    -0020 %   equivalent. Change this if your model separates them.
    -0021 %
    -0022 % Extend a copy rather than editing this function, e.g.
    +0009 % The map holds three decisions:
    +0010 %
    +0011 % - Compartments present. A label absent from the map is dropped along with its score column, so
    +0012 %   the map lists only the compartment ids of the model. 'plastid' is absent because fungi have
    +0013 %   no plastid; a plant model adds it.
    +0014 % - Labels that name the same compartment. Predictors differ in wording, so 'cytoplasm' and
    +0015 %   'cytosol' both map to 'c', and 'mitochondrion', 'mitochondria' and 'mitochondrial' all map
    +0016 %   to 'm'. Merged columns are combined by their maximum.
    +0017 % - Distinct compartments that are collapsed. 'lysosome' maps to 'v' because a yeast model has
    +0018 %   no lysosome and the vacuole is its functional equivalent. A model that separates the two
    +0019 %   needs a different mapping.
    +0020 %
    +0021 % Changes to the mapping go into a copy of the map, which leaves this function unedited, for
    +0022 % example:
     0023 %
     0024 %     map = defaultCompartmentMap;
     0025 %     map('cell wall') = 'ce';
    diff --git a/doc/localization/getUniProtScores.html b/docs/html/localization/getUniProtScores.html
    similarity index 65%
    rename from doc/localization/getUniProtScores.html
    rename to docs/html/localization/getUniProtScores.html
    index f63ec7b0..e3b7b277 100644
    --- a/doc/localization/getUniProtScores.html
    +++ b/docs/html/localization/getUniProtScores.html
    @@ -4,7 +4,7 @@
     
       Description of getUniProtScores
       
    -  
    +  
       
       
       
    @@ -21,44 +21,44 @@ 

    getUniProtScores

    PURPOSE ^

    -
    getUniProtScores Fetch curated subcellular locations from UniProt -> gene scoring structure.
    +
    getUniProtScores Fetch curated subcellular locations from UniProt as a gene scoring structure.

    SYNOPSIS ^

    function GSS = getUniProtScores(organism, varargin)

    DESCRIPTION ^

    -
     getUniProtScores  Fetch curated subcellular locations from UniProt -> gene scoring structure.
    +
     getUniProtScores  Fetch curated subcellular locations from UniProt as a gene scoring structure.
     
    - Queries the UniProtKB REST API for an organism's "Subcellular location" annotations and parses
    - them into the GSS consumed by predictLocalization (no manual export needed). UniProt's curated
    - location is qualitative, so each annotated compartment gets score 1.0 (a multi-located protein
    - lands in several). Requires an internet connection.
    + Queries the UniProtKB REST API for the "Subcellular location" annotations of an organism and
    + parses them into the GSS consumed by predictLocalization. No manual export is needed. UniProt
    + locations are qualitative, so each annotated compartment gets score 1.0 and a protein with
    + several annotated locations scores 1.0 in each of them. The function requires an internet
    + connection.
     
      Parameters
      ----------
      organism : double or char
    -     a UniProt organism / taxonomy id (e.g. 559292 for S. cerevisiae S288C).
    +     a UniProt organism (taxonomy) id (for example 559292 for S. cerevisiae S288C).
     
      Name-Value Arguments
      --------------------
      idField : char
    -     which identifier becomes the gene id: 'gene_oln' (ordered locus / ORF, the default, which
    -     matches yeast-GEM), 'accession', or 'gene_primary'.
    +     which identifier becomes the gene id: 'gene_oln' (ordered locus or ORF name, the default,
    +     which matches yeast-GEM), 'accession', or 'gene_primary'.
      reviewed : logical
    -     restrict to curated Swiss-Prot entries (default true).
    +     true to restrict the query to curated Swiss-Prot entries (default true).
      compartmentMap : containers.Map
    -     UniProt location term -> your model's compartment id (default
    -     defaultCompartmentMap). Here the map is not only a renaming: its keys are
    -     also the vocabulary searched for in UniProt's "Subcellular location [CC]"
    -     free text, matched whole-word and case insensitively, so they must be
    -     UniProt's own noun forms ('endoplasmic reticulum', not 'ER') and a term
    -     that is not a key is never looked for. Several keys may share one
    -     compartment id, which merges them; since every hit scores 1.0, merging
    -     here means "annotated to any of these counts as this compartment". One
    -     key cannot be split across two compartment ids - see parseScores for why,
    -     and for the recipe to duplicate a column on the returned GSS instead.
    +     maps UniProt location terms to the compartment ids of the model (default
    +     defaultCompartmentMap). The keys are also the vocabulary searched for in the
    +     "Subcellular location [CC]" free text of UniProt, matched as whole words and
    +     case insensitively. The keys must be the noun forms used by UniProt
    +     ('endoplasmic reticulum', not 'ER'), and a term that is not a key is not searched.
    +     Several keys may share one compartment id, which merges them; since every hit
    +     scores 1.0, a protein annotated to any of the merged terms scores 1.0 in that
    +     compartment. One key cannot map to two compartment ids; parseScores describes
    +     how to duplicate a column of the returned GSS instead.
      extraQuery : char
    -     an additional UniProt query clause, ANDed into the query (e.g. 'gene:CIT1').
    +     an additional UniProt query clause, combined with the query by AND (for example 'gene:CIT1').
     
      Returns
      -------
    @@ -80,7 +80,7 @@ 

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls:
      -
    • defaultCompartmentMap defaultCompartmentMap Default predictor/database compartment label -> model compartment id.
    • parseScores parseScores Parse predictor / database output into a gene scoring structure (GSS).
    +
  • defaultCompartmentMap defaultCompartmentMap Default map from compartment labels to model compartment ids.
  • parseScores parseScores Parse predictor or database output into a gene scoring structure (GSS).
  • This function is called by:
    @@ -92,38 +92,38 @@

    SUBFUNCTIONS ^SOURCE CODE ^

    0001 function GSS = getUniProtScores(organism, varargin)
    -0002 % getUniProtScores  Fetch curated subcellular locations from UniProt -> gene scoring structure.
    +0002 % getUniProtScores  Fetch curated subcellular locations from UniProt as a gene scoring structure.
     0003 %
    -0004 % Queries the UniProtKB REST API for an organism's "Subcellular location" annotations and parses
    -0005 % them into the GSS consumed by predictLocalization (no manual export needed). UniProt's curated
    -0006 % location is qualitative, so each annotated compartment gets score 1.0 (a multi-located protein
    -0007 % lands in several). Requires an internet connection.
    -0008 %
    -0009 % Parameters
    -0010 % ----------
    -0011 % organism : double or char
    -0012 %     a UniProt organism / taxonomy id (e.g. 559292 for S. cerevisiae S288C).
    -0013 %
    -0014 % Name-Value Arguments
    -0015 % --------------------
    -0016 % idField : char
    -0017 %     which identifier becomes the gene id: 'gene_oln' (ordered locus / ORF, the default, which
    -0018 %     matches yeast-GEM), 'accession', or 'gene_primary'.
    -0019 % reviewed : logical
    -0020 %     restrict to curated Swiss-Prot entries (default true).
    -0021 % compartmentMap : containers.Map
    -0022 %     UniProt location term -> your model's compartment id (default
    -0023 %     defaultCompartmentMap). Here the map is not only a renaming: its keys are
    -0024 %     also the vocabulary searched for in UniProt's "Subcellular location [CC]"
    -0025 %     free text, matched whole-word and case insensitively, so they must be
    -0026 %     UniProt's own noun forms ('endoplasmic reticulum', not 'ER') and a term
    -0027 %     that is not a key is never looked for. Several keys may share one
    -0028 %     compartment id, which merges them; since every hit scores 1.0, merging
    -0029 %     here means "annotated to any of these counts as this compartment". One
    -0030 %     key cannot be split across two compartment ids - see parseScores for why,
    -0031 %     and for the recipe to duplicate a column on the returned GSS instead.
    +0004 % Queries the UniProtKB REST API for the "Subcellular location" annotations of an organism and
    +0005 % parses them into the GSS consumed by predictLocalization. No manual export is needed. UniProt
    +0006 % locations are qualitative, so each annotated compartment gets score 1.0 and a protein with
    +0007 % several annotated locations scores 1.0 in each of them. The function requires an internet
    +0008 % connection.
    +0009 %
    +0010 % Parameters
    +0011 % ----------
    +0012 % organism : double or char
    +0013 %     a UniProt organism (taxonomy) id (for example 559292 for S. cerevisiae S288C).
    +0014 %
    +0015 % Name-Value Arguments
    +0016 % --------------------
    +0017 % idField : char
    +0018 %     which identifier becomes the gene id: 'gene_oln' (ordered locus or ORF name, the default,
    +0019 %     which matches yeast-GEM), 'accession', or 'gene_primary'.
    +0020 % reviewed : logical
    +0021 %     true to restrict the query to curated Swiss-Prot entries (default true).
    +0022 % compartmentMap : containers.Map
    +0023 %     maps UniProt location terms to the compartment ids of the model (default
    +0024 %     defaultCompartmentMap). The keys are also the vocabulary searched for in the
    +0025 %     "Subcellular location [CC]" free text of UniProt, matched as whole words and
    +0026 %     case insensitively. The keys must be the noun forms used by UniProt
    +0027 %     ('endoplasmic reticulum', not 'ER'), and a term that is not a key is not searched.
    +0028 %     Several keys may share one compartment id, which merges them; since every hit
    +0029 %     scores 1.0, a protein annotated to any of the merged terms scores 1.0 in that
    +0030 %     compartment. One key cannot map to two compartment ids; parseScores describes
    +0031 %     how to duplicate a column of the returned GSS instead.
     0032 % extraQuery : char
    -0033 %     an additional UniProt query clause, ANDed into the query (e.g. 'gene:CIT1').
    +0033 %     an additional UniProt query clause, combined with the query by AND (for example 'gene:CIT1').
     0034 %
     0035 % Returns
     0036 % -------
    diff --git a/doc/localization/index.html b/docs/html/localization/index.html
    similarity index 67%
    rename from doc/localization/index.html
    rename to docs/html/localization/index.html
    index 3011d3d9..75c636ec 100644
    --- a/doc/localization/index.html
    +++ b/docs/html/localization/index.html
    @@ -19,7 +19,7 @@ 

    Index for localization

    Matlab files in this directory:

    -
     assignCompartmentsassignCompartments Assign reactions to compartments by a functionality-constrained MILP.
     defaultCompartmentMapdefaultCompartmentMap Default predictor/database compartment label -> model compartment id.
     getUniProtScoresgetUniProtScores Fetch curated subcellular locations from UniProt -> gene scoring structure.
     parseScoresparseScores Parse predictor / database output into a gene scoring structure (GSS).
     predictLocalizationpredictLocalization Assign reactions to compartments using localization predictors.
    + assignCompartmentsassignCompartments Assign reactions to compartments by a functionality-constrained MILP.  defaultCompartmentMapdefaultCompartmentMap Default map from compartment labels to model compartment ids.  getUniProtScoresgetUniProtScores Fetch curated subcellular locations from UniProt as a gene scoring structure.  parseScoresparseScores Parse predictor or database output into a gene scoring structure (GSS).  predictLocalizationpredictLocalization Assign reactions to compartments using localization predictors. diff --git a/docs/html/localization/parseScores.html b/docs/html/localization/parseScores.html new file mode 100644 index 00000000..573d3c31 --- /dev/null +++ b/docs/html/localization/parseScores.html @@ -0,0 +1,448 @@ + + + + Description of parseScores + + + + + + + + + +
    Home > localization > parseScores.m
    + + + +

    parseScores +

    + +

    PURPOSE ^

    +
    parseScores Parse predictor or database output into a gene scoring structure (GSS).
    + +

    SYNOPSIS ^

    +
    function GSS = parseScores(inputFile, varargin)
    + +

    DESCRIPTION ^

    +
     parseScores  Parse predictor or database output into a gene scoring structure (GSS).
    +
    + Reads the output of a subcellular localisation predictor or evidence database and
    + builds the gene scoring structure consumed by predictLocalization. Scores are
    + normalised so that the highest score of each gene is 1.0.
    +
    + Parameters
    + ----------
    + inputFile : char
    +     the file with the predictor or database output.
    +
    + Name-Value Arguments
    + --------------------
    + predictor : char
    +     'deeploc' (DeepLoc 2 per-protein CSV, default), 'cello' (CELLO),
    +     'mulocdeep' (MULocDeep wide table: gene id + one probability column per
    +     compartment), 'compartments' (a COMPARTMENTS jensenlab.org channel TSV), or
    +     'uniprot' (a UniProtKB TSV export with a "Subcellular location [CC]" column).
    + compartmentMap : containers.Map
    +     maps the compartment labels of the predictor or database to the compartment ids of
    +     the model. Keys are the labels as the predictor writes them, in lower case; values
    +     are the compartment ids in the GSS. Optional for every predictor except 'uniprot',
    +     where the map also supplies the location vocabulary and defaults to
    +     defaultCompartmentMap. See Compartment mapping below.
    + idColumn : char or double
    +     column (name or 1-based index) holding the gene id for 'mulocdeep' and 'uniprot'
    +     (default: the first column). For yeast-GEM, the UniProt ordered-locus column holds
    +     the matching ids.
    + minConfidence : double
    +     for 'compartments', drop annotations with confidence below this (default 0).
    +
    + Returns
    + -------
    + GSS : struct
    +     a gene scoring structure for predictLocalization, with fields:
    +
    +     - genes : cell, one entry per gene.
    +     - compartments : cell, one entry per compartment column.
    +     - scores : double, numel(genes)-by-numel(compartments), normalised so
    +       that the highest score per gene is 1.0.
    +
    + Compartment mapping
    + -------------------
    + Predictors use their own compartment vocabulary ("Endoplasmic reticulum",
    + "Golgi apparatus", ...), which need not match the compartment ids of a model.
    + compartmentMap is a containers.Map that translates the labels; the translation
    + happens only there, so the returned GSS contains the ids of the map values.
    + defaultCompartmentMap returns an example map for yeast and fungal models, which can
    + be used as it is or as a template. Three rules apply:
    +
    + - A label that is not in the map is dropped, together with its whole score
    +   column. This excludes compartments that the model does not have
    +   (defaultCompartmentMap has no 'plastid' key, so plastid scores are dropped for a
    +   fungal model). Without a map, every label is kept unchanged.
    + - Several labels may share one compartment id, which merges their
    +   columns. The merged score of a gene is the maximum over the merged labels, not
    +   their sum: a gene predicted 0.7 cytoplasm and 0.4 cytosol scores 0.7 for 'c'.
    +   Sharing an id merges synonyms and collapses compartments that the model does not
    +   separate (defaultCompartmentMap sends both 'vacuole' and 'lysosome' to 'v').
    +   Column order follows the first appearance of each id.
    + - One label cannot map to two compartment ids: a containers.Map holds one
    +   value per key. A split is made on the returned scores. Duplicating the
    +   column afterwards lets a gene predicted "golgi" compete for both 'g' and 'v':
    +
    +       GSS = parseScores(file, 'compartmentMap', map);
    +       g = strcmp(GSS.compartments, 'g');
    +       GSS.compartments{end+1} = 'v';
    +       GSS.scores(:,end+1) = GSS.scores(:,g);
    +
    +   The two columns are then independent: predictLocalization assigns each gene
    +   to one compartment, so it picks whichever of 'g' and 'v' keeps the network
    +   better connected. Halving the copied column gives the evidence half the
    +   weight in the second compartment.
    +
    + For 'uniprot', the keys are also the terms searched for in the
    + "Subcellular location [CC]" free text, matched as whole words and case
    + insensitively. The keys must be the noun forms used by UniProt. A term that is
    + not a key is not searched.
    +
    + Examples
    + --------
    +     GSS = parseScores(file, 'predictor', 'deeploc');
    +     GSS = parseScores(file, 'predictor', 'deeploc', 'compartmentMap', defaultCompartmentMap);
    +     GSS = parseScores(file, 'predictor', 'uniprot', 'idColumn', 'Gene Names (ordered locus)');
    +
    + See also
    + --------
    + predictLocalization, getUniProtScores, defaultCompartmentMap
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • defaultCompartmentMap defaultCompartmentMap Default map from compartment labels to model compartment ids.
    +This function is called by: +
      +
    • getUniProtScores getUniProtScores Fetch curated subcellular locations from UniProt as a gene scoring structure.
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function GSS = parseScores(inputFile, varargin)
    +0002 % parseScores  Parse predictor or database output into a gene scoring structure (GSS).
    +0003 %
    +0004 % Reads the output of a subcellular localisation predictor or evidence database and
    +0005 % builds the gene scoring structure consumed by predictLocalization. Scores are
    +0006 % normalised so that the highest score of each gene is 1.0.
    +0007 %
    +0008 % Parameters
    +0009 % ----------
    +0010 % inputFile : char
    +0011 %     the file with the predictor or database output.
    +0012 %
    +0013 % Name-Value Arguments
    +0014 % --------------------
    +0015 % predictor : char
    +0016 %     'deeploc' (DeepLoc 2 per-protein CSV, default), 'cello' (CELLO),
    +0017 %     'mulocdeep' (MULocDeep wide table: gene id + one probability column per
    +0018 %     compartment), 'compartments' (a COMPARTMENTS jensenlab.org channel TSV), or
    +0019 %     'uniprot' (a UniProtKB TSV export with a "Subcellular location [CC]" column).
    +0020 % compartmentMap : containers.Map
    +0021 %     maps the compartment labels of the predictor or database to the compartment ids of
    +0022 %     the model. Keys are the labels as the predictor writes them, in lower case; values
    +0023 %     are the compartment ids in the GSS. Optional for every predictor except 'uniprot',
    +0024 %     where the map also supplies the location vocabulary and defaults to
    +0025 %     defaultCompartmentMap. See Compartment mapping below.
    +0026 % idColumn : char or double
    +0027 %     column (name or 1-based index) holding the gene id for 'mulocdeep' and 'uniprot'
    +0028 %     (default: the first column). For yeast-GEM, the UniProt ordered-locus column holds
    +0029 %     the matching ids.
    +0030 % minConfidence : double
    +0031 %     for 'compartments', drop annotations with confidence below this (default 0).
    +0032 %
    +0033 % Returns
    +0034 % -------
    +0035 % GSS : struct
    +0036 %     a gene scoring structure for predictLocalization, with fields:
    +0037 %
    +0038 %     - genes : cell, one entry per gene.
    +0039 %     - compartments : cell, one entry per compartment column.
    +0040 %     - scores : double, numel(genes)-by-numel(compartments), normalised so
    +0041 %       that the highest score per gene is 1.0.
    +0042 %
    +0043 % Compartment mapping
    +0044 % -------------------
    +0045 % Predictors use their own compartment vocabulary ("Endoplasmic reticulum",
    +0046 % "Golgi apparatus", ...), which need not match the compartment ids of a model.
    +0047 % compartmentMap is a containers.Map that translates the labels; the translation
    +0048 % happens only there, so the returned GSS contains the ids of the map values.
    +0049 % defaultCompartmentMap returns an example map for yeast and fungal models, which can
    +0050 % be used as it is or as a template. Three rules apply:
    +0051 %
    +0052 % - A label that is not in the map is dropped, together with its whole score
    +0053 %   column. This excludes compartments that the model does not have
    +0054 %   (defaultCompartmentMap has no 'plastid' key, so plastid scores are dropped for a
    +0055 %   fungal model). Without a map, every label is kept unchanged.
    +0056 % - Several labels may share one compartment id, which merges their
    +0057 %   columns. The merged score of a gene is the maximum over the merged labels, not
    +0058 %   their sum: a gene predicted 0.7 cytoplasm and 0.4 cytosol scores 0.7 for 'c'.
    +0059 %   Sharing an id merges synonyms and collapses compartments that the model does not
    +0060 %   separate (defaultCompartmentMap sends both 'vacuole' and 'lysosome' to 'v').
    +0061 %   Column order follows the first appearance of each id.
    +0062 % - One label cannot map to two compartment ids: a containers.Map holds one
    +0063 %   value per key. A split is made on the returned scores. Duplicating the
    +0064 %   column afterwards lets a gene predicted "golgi" compete for both 'g' and 'v':
    +0065 %
    +0066 %       GSS = parseScores(file, 'compartmentMap', map);
    +0067 %       g = strcmp(GSS.compartments, 'g');
    +0068 %       GSS.compartments{end+1} = 'v';
    +0069 %       GSS.scores(:,end+1) = GSS.scores(:,g);
    +0070 %
    +0071 %   The two columns are then independent: predictLocalization assigns each gene
    +0072 %   to one compartment, so it picks whichever of 'g' and 'v' keeps the network
    +0073 %   better connected. Halving the copied column gives the evidence half the
    +0074 %   weight in the second compartment.
    +0075 %
    +0076 % For 'uniprot', the keys are also the terms searched for in the
    +0077 % "Subcellular location [CC]" free text, matched as whole words and case
    +0078 % insensitively. The keys must be the noun forms used by UniProt. A term that is
    +0079 % not a key is not searched.
    +0080 %
    +0081 % Examples
    +0082 % --------
    +0083 %     GSS = parseScores(file, 'predictor', 'deeploc');
    +0084 %     GSS = parseScores(file, 'predictor', 'deeploc', 'compartmentMap', defaultCompartmentMap);
    +0085 %     GSS = parseScores(file, 'predictor', 'uniprot', 'idColumn', 'Gene Names (ordered locus)');
    +0086 %
    +0087 % See also
    +0088 % --------
    +0089 % predictLocalization, getUniProtScores, defaultCompartmentMap
    +0090 
    +0091 p = parseRAVENargs(varargin, {'predictor',[]; 'compartmentMap',[]; ...
    +0092                               'idColumn',[]; 'minConfidence',0});
    +0093 predictor = p.predictor;
    +0094 if isempty(predictor); predictor = 'deeploc'; else; predictor = char(predictor); end
    +0095 compartmentMap = p.compartmentMap;
    +0096 
    +0097 if ~isfile(inputFile)
    +0098     error('RAVEN:badInput', 'Could not open file %s', char(string(inputFile)));
    +0099 end
    +0100 
    +0101 switch lower(predictor)
    +0102     case 'cello'
    +0103         GSS = i_parseCello(inputFile);
    +0104     case 'deeploc'
    +0105         GSS = i_parseDeepLoc(inputFile);
    +0106     case 'mulocdeep'
    +0107         GSS = i_parseWide(inputFile, p.idColumn);
    +0108     case 'compartments'
    +0109         GSS = i_parseCompartments(inputFile, p.minConfidence);
    +0110     case 'uniprot'
    +0111         if isempty(compartmentMap); compartmentMap = defaultCompartmentMap(); end
    +0112         GSS = i_parseUniProt(inputFile, p.idColumn, compartmentMap);
    +0113         compartmentMap = [];   % already applied during parsing
    +0114     case 'wolf'
    +0115         error('RAVEN:badInput', ['WoLF PSORT support has been removed. Use a modern ' ...
    +0116             'predictor (''deeploc'', ''mulocdeep''), the ''compartments'' database, or ' ...
    +0117             '''uniprot'' (see getUniProtScores).']);
    +0118     otherwise
    +0119         error('RAVEN:badInput', 'Unknown predictor "%s"', predictor);
    +0120 end
    +0121 
    +0122 % Remove duplicate genes
    +0123 [~, J, K] = unique(GSS.genes);
    +0124 if numel(J) ~= numel(K)
    +0125     warning('RAVEN:warning', 'There are duplicate genes in the input file');
    +0126     GSS.genes = GSS.genes(J);
    +0127     GSS.scores = GSS.scores(J,:);
    +0128 end
    +0129 
    +0130 % Optional renaming of compartments to model ids (already done for 'uniprot')
    +0131 if ~isempty(compartmentMap)
    +0132     GSS = i_applyCompartmentMap(GSS, compartmentMap);
    +0133 end
    +0134 
    +0135 if isempty(GSS.genes) || isempty(GSS.compartments)
    +0136     warning('RAVEN:warning', ['parseScores produced an empty gene scoring structure ' ...
    +0137         '(no genes or no compartments matched). Check the input file and, if used, that ' ...
    +0138         'compartmentMap labels match the predictor/database compartment names.']);
    +0139 end
    +0140 
    +0141 % Normalize so the best score per gene is 1.0 (genes with no positive evidence untouched)
    +0142 m = max(GSS.scores, [], 2);
    +0143 m(m <= 0) = 1;
    +0144 GSS.scores = bsxfun(@times, GSS.scores, 1./m);
    +0145 end
    +0146 
    +0147 % ------------------------------------------------------------------------- parsers
    +0148 
    +0149 function GSS = i_parseCello(inputFile)
    +0150 fid = fopen(inputFile, 'r');
    +0151 tline = fgetl(fid);
    +0152 tline = regexprep(tline, '^.+#Combined:\t', '');
    +0153 tline = regexprep(tline, '\t#Most-likely-Location.+', '');
    +0154 GSS.compartments = transpose(regexp(tline, '\t', 'split'));
    +0155 GSS.genes = {}; GSS.scores = [];
    +0156 row = 0;
    +0157 while true
    +0158     tline = fgetl(fid);
    +0159     if ~ischar(tline); break; end
    +0160     tline = regexprep(tline, '^.+:\t', '');
    +0161     tline = regexprep(tline, ' .+', '');
    +0162     tline = regexp(tline, '\t', 'split');
    +0163     if numel(tline) < numel(GSS.compartments)
    +0164         error('RAVEN:badInput', 'Malformed CELLO row %d (fewer fields than compartments)', row+1);
    +0165     end
    +0166     row = row + 1;
    +0167     GSS.scores(row,:) = str2double(tline(1:numel(GSS.compartments)));
    +0168     GSS.genes{row,1} = tline{1,end};
    +0169 end
    +0170 fclose(fid);
    +0171 end
    +0172 
    +0173 function GSS = i_parseDeepLoc(inputFile)
    +0174 fid = fopen(inputFile, 'r');
    +0175 tline = fgetl(fid);
    +0176 comps = regexp(tline, ',', 'split');
    +0177 GSS.compartments = transpose(comps(4:end));   % cols 1-3 are Protein_ID/Localizations/Signals
    +0178 GSS.genes = {}; GSS.scores = [];
    +0179 row = 0;
    +0180 while true
    +0181     tline = fgetl(fid);
    +0182     if ~ischar(tline); break; end
    +0183     parts = regexp(tline, ',', 'split');
    +0184     row = row + 1;
    +0185     GSS.scores(row,:) = str2double(parts(4:end));
    +0186     GSS.genes{row,1} = parts{1,1};
    +0187 end
    +0188 fclose(fid);
    +0189 end
    +0190 
    +0191 function GSS = i_parseWide(inputFile, idColumn)
    +0192 % MULocDeep / generic wide table: gene id column + one numeric column per compartment.
    +0193 T = readtable(inputFile, 'FileType', 'text', 'VariableNamingRule', 'preserve');
    +0194 idx = i_idColumnIndex(T, idColumn);
    +0195 GSS.genes = cellstr(string(T{:,idx}));
    +0196 rest = setdiff(1:width(T), idx, 'stable');
    +0197 GSS.compartments = transpose(T.Properties.VariableNames(rest));
    +0198 GSS.scores = i_numericMatrix(T, rest);
    +0199 end
    +0200 
    +0201 function GSS = i_parseCompartments(inputFile, minConfidence)
    +0202 % COMPARTMENTS channel TSV (no header): col1 gene, col4 GO term name, last col confidence.
    +0203 T = readtable(inputFile, 'FileType', 'text', 'Delimiter', '\t', 'ReadVariableNames', false);
    +0204 if width(T) < 5
    +0205     error('RAVEN:badInput', 'Expected a COMPARTMENTS full TSV (>=5 columns)');
    +0206 end
    +0207 genes = string(T{:,1});
    +0208 comps = string(T{:,4});
    +0209 score = T{:,end};
    +0210 if ~isnumeric(score); score = str2double(string(score)); end
    +0211 keep = ~isnan(score) & score >= minConfidence;
    +0212 genes = genes(keep); comps = comps(keep); score = score(keep);
    +0213 
    +0214 ug = unique(genes, 'stable');
    +0215 uc = unique(comps, 'stable');
    +0216 M = zeros(numel(ug), numel(uc));
    +0217 [~, gi] = ismember(genes, ug);
    +0218 [~, ci] = ismember(comps, uc);
    +0219 for k = 1:numel(genes)
    +0220     if M(gi(k), ci(k)) < score(k); M(gi(k), ci(k)) = score(k); end
    +0221 end
    +0222 GSS.genes = cellstr(ug);
    +0223 GSS.compartments = cellstr(uc);
    +0224 GSS.scores = M;
    +0225 end
    +0226 
    +0227 function GSS = i_parseUniProt(inputFile, idColumn, map)
    +0228 % UniProtKB TSV export: scan the "Subcellular location [CC]" text for known terms.
    +0229 T = readtable(inputFile, 'FileType', 'text', 'Delimiter', '\t', 'VariableNamingRule', 'preserve');
    +0230 vn = string(T.Properties.VariableNames);
    +0231 locIdx = find(contains(lower(vn), 'subcellular'), 1);
    +0232 if isempty(locIdx)
    +0233     error('RAVEN:badInput', 'No "Subcellular location" column in the UniProt file');
    +0234 end
    +0235 idIdx = i_idColumnIndex(T, idColumn);
    +0236 ids = string(T{:,idIdx});
    +0237 loc = string(T{:,locIdx});
    +0238 
    +0239 labels = lower(string(keys(map)));
    +0240 codesForLabels = string(values(map));
    +0241 codeList = unique(codesForLabels, 'stable');
    +0242 % Whole-word patterns so a short label (e.g. 'cytoplasm') does not match inside a longer,
    +0243 % distinct location term (e.g. 'Cytoplasmic vesicle'). Labels must be the UniProt noun forms.
    +0244 patterns = "\<" + arrayfun(@(s) string(regexptranslate('escape', char(s))), labels) + "\>";
    +0245 
    +0246 M = zeros(numel(ids), numel(codeList));
    +0247 hasHit = false(numel(ids), 1);
    +0248 for i = 1:numel(ids)
    +0249     txt = lower(loc(i));
    +0250     txt = regexprep(txt, '\{[^}]*\}', ' ');         % drop {ECO:...} evidence
    +0251     % Split into per-isoform blocks and drop the Note=... free text WITHIN each block, so a
    +0252     % later isoform's locations survive and Note prose is never read as a location.
    +0253     blocks = regexp(char(txt), '\[isoform[^\]]*\]:', 'split');
    +0254     for b = 1:numel(blocks)
    +0255         seg = regexprep(blocks{b}, 'note=.*', ' ');
    +0256         for j = 1:numel(labels)
    +0257             if ~isempty(regexp(seg, patterns(j), 'once'))
    +0258                 c = find(codeList == codesForLabels(j), 1);
    +0259                 M(i, c) = 1; hasHit(i) = true;
    +0260             end
    +0261         end
    +0262     end
    +0263 end
    +0264 keep = hasHit & strlength(strtrim(ids)) > 0;
    +0265 GSS.genes = cellstr(ids(keep));
    +0266 GSS.compartments = cellstr(codeList);
    +0267 GSS.scores = M(keep, :);
    +0268 end
    +0269 
    +0270 % ------------------------------------------------------------------------- helpers
    +0271 
    +0272 function idx = i_idColumnIndex(T, idColumn)
    +0273 if isempty(idColumn)
    +0274     idx = 1;
    +0275 elseif isnumeric(idColumn)
    +0276     idx = idColumn;
    +0277 else
    +0278     idx = find(strcmp(T.Properties.VariableNames, char(idColumn)), 1);
    +0279     if isempty(idx)
    +0280         error('RAVEN:badInput', 'idColumn "%s" not found', char(idColumn));
    +0281     end
    +0282 end
    +0283 end
    +0284 
    +0285 function M = i_numericMatrix(T, cols)
    +0286 M = zeros(height(T), numel(cols));
    +0287 for k = 1:numel(cols)
    +0288     col = T{:, cols(k)};
    +0289     if ~isnumeric(col); col = str2double(string(col)); end
    +0290     M(:, k) = col;
    +0291 end
    +0292 end
    +0293 
    +0294 function GSS = i_applyCompartmentMap(GSS, map)
    +0295 labels = lower(string(keys(map)));
    +0296 codes = string(values(map));
    +0297 old = lower(string(GSS.compartments));
    +0298 mapped = strings(numel(old), 1);
    +0299 keepCol = false(numel(old), 1);
    +0300 for i = 1:numel(old)
    +0301     j = find(labels == old(i), 1);
    +0302     if ~isempty(j); mapped(i) = codes(j); keepCol(i) = true; end
    +0303 end
    +0304 usedCodes = unique(mapped(keepCol), 'stable');
    +0305 M = zeros(numel(GSS.genes), numel(usedCodes));
    +0306 for c = 1:numel(usedCodes)
    +0307     cols = keepCol & mapped == usedCodes(c);
    +0308     M(:, c) = max(GSS.scores(:, cols), [], 2);
    +0309 end
    +0310 GSS.compartments = cellstr(usedCodes);
    +0311 GSS.scores = M;
    +0312 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/localization/predictLocalization.html b/docs/html/localization/predictLocalization.html new file mode 100644 index 00000000..f7192815 --- /dev/null +++ b/docs/html/localization/predictLocalization.html @@ -0,0 +1,1263 @@ + + + + Description of predictLocalization + + + + + + + + + +
    Home > localization > predictLocalization.m
    + + + +

    predictLocalization +

    + +

    PURPOSE ^

    +
    predictLocalization Assign reactions to compartments using localization predictors.
    + +

    SYNOPSIS ^

    +
    function [outModel, geneLocalization, transportStruct, scores,removedRxns] = predictLocalization(model, GSS,defaultCompartment, varargin)
    + +

    DESCRIPTION ^

    +
     predictLocalization  Assign reactions to compartments using localization predictors.
    +
    + Assigns reactions to compartments so that the assignment agrees with the localization
    + predictors and the network stays connected.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure. If the model contains several compartments, they
    +     are merged.
    + GSS : struct
    +     a gene scoring structure as from parseScores.
    + defaultCompartment : char
    +     transport reactions are expressed as diffusion between the
    +     defaultCompartment and the others. The compartment is usually the cytosol. It must
    +     be one of GSS.compartments.
    +
    + Name-Value Arguments
    + --------------------
    + transportCost : double
    +     the cost for including a transport reaction. A scalar applies the same cost to all
    +     metabolites; a vector must have the same dimension as model.mets. A negative cost
    +     favours transport of the metabolite (default 0.5).
    + maxTime : double
    +     maximum optimisation time in minutes (default 15).
    + plotResults : logical
    +     true to plot the results during the optimisation (default false).
    +
    + Returns
    + -------
    + outModel : struct
    +     the resulting model structure.
    + geneLocalization : struct
    +     structure with the genes and their resulting localisation.
    + transportStruct : struct
    +     structure with the inferred transport reactions and the compartments between which
    +     they transport.
    + scores : struct
    +     structure with the total score history, the score based on gene localisation and
    +     the score based on the included transport reactions.
    + removedRxns : cell
    +     cell array with the ids of the reactions removed to obtain a connected input model.
    +
    + Notes
    + -----
    + The starting network must be connected when it is in one compartment. Unconnected
    + reactions are removed and returned in removedRxns. If many reactions are removed,
    + fillGaps can produce a more connected input model. The input model must not include
    + exchange, demand or sink reactions; otherwise the function provides no results.
    +
    + In the final model, every metabolite is produced in at least one reaction. Internal
    + loops can still make the model non-functional. Transport reactions are included only
    + as passive diffusion (A <=> B).
    +
    + The score of a model is the sum of the scores of all genes in their assigned
    + compartment minus the cost of all included transport reactions. A gene is assigned
    + to one compartment, which limits the problem size. The problem is solved by simulated
    + annealing.
    +
    + Examples
    + --------
    +     [outModel, geneLocalization, transportStruct, scores, removedRxns] = ...
    +         predictLocalization(model, GSS, defaultCompartment, ...
    +             'transportCost', transportCost, 'maxTime', maxTime, ...
    +             'plotResults', plotResults);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [outModel, geneLocalization, transportStruct, scores,...
    +0002     removedRxns] = predictLocalization(model, GSS,...
    +0003     defaultCompartment, varargin)
    +0004 % predictLocalization  Assign reactions to compartments using localization predictors.
    +0005 %
    +0006 % Assigns reactions to compartments so that the assignment agrees with the localization
    +0007 % predictors and the network stays connected.
    +0008 %
    +0009 % Parameters
    +0010 % ----------
    +0011 % model : struct
    +0012 %     a model structure. If the model contains several compartments, they
    +0013 %     are merged.
    +0014 % GSS : struct
    +0015 %     a gene scoring structure as from parseScores.
    +0016 % defaultCompartment : char
    +0017 %     transport reactions are expressed as diffusion between the
    +0018 %     defaultCompartment and the others. The compartment is usually the cytosol. It must
    +0019 %     be one of GSS.compartments.
    +0020 %
    +0021 % Name-Value Arguments
    +0022 % --------------------
    +0023 % transportCost : double
    +0024 %     the cost for including a transport reaction. A scalar applies the same cost to all
    +0025 %     metabolites; a vector must have the same dimension as model.mets. A negative cost
    +0026 %     favours transport of the metabolite (default 0.5).
    +0027 % maxTime : double
    +0028 %     maximum optimisation time in minutes (default 15).
    +0029 % plotResults : logical
    +0030 %     true to plot the results during the optimisation (default false).
    +0031 %
    +0032 % Returns
    +0033 % -------
    +0034 % outModel : struct
    +0035 %     the resulting model structure.
    +0036 % geneLocalization : struct
    +0037 %     structure with the genes and their resulting localisation.
    +0038 % transportStruct : struct
    +0039 %     structure with the inferred transport reactions and the compartments between which
    +0040 %     they transport.
    +0041 % scores : struct
    +0042 %     structure with the total score history, the score based on gene localisation and
    +0043 %     the score based on the included transport reactions.
    +0044 % removedRxns : cell
    +0045 %     cell array with the ids of the reactions removed to obtain a connected input model.
    +0046 %
    +0047 % Notes
    +0048 % -----
    +0049 % The starting network must be connected when it is in one compartment. Unconnected
    +0050 % reactions are removed and returned in removedRxns. If many reactions are removed,
    +0051 % fillGaps can produce a more connected input model. The input model must not include
    +0052 % exchange, demand or sink reactions; otherwise the function provides no results.
    +0053 %
    +0054 % In the final model, every metabolite is produced in at least one reaction. Internal
    +0055 % loops can still make the model non-functional. Transport reactions are included only
    +0056 % as passive diffusion (A <=> B).
    +0057 %
    +0058 % The score of a model is the sum of the scores of all genes in their assigned
    +0059 % compartment minus the cost of all included transport reactions. A gene is assigned
    +0060 % to one compartment, which limits the problem size. The problem is solved by simulated
    +0061 % annealing.
    +0062 %
    +0063 % Examples
    +0064 % --------
    +0065 %     [outModel, geneLocalization, transportStruct, scores, removedRxns] = ...
    +0066 %         predictLocalization(model, GSS, defaultCompartment, ...
    +0067 %             'transportCost', transportCost, 'maxTime', maxTime, ...
    +0068 %             'plotResults', plotResults);
    +0069 
    +0070 p=parseRAVENargs(varargin, {'transportCost',[]; 'maxTime',[]; 'plotResults',[]});
    +0071 transportCost=p.transportCost;
    +0072 maxTime=p.maxTime;
    +0073 plotResults=p.plotResults;
    +0074 
    +0075 if isempty(transportCost)
    +0076     transportCost=ones(numel(model.mets),1)*0.5;
    +0077 end
    +0078 if numel(transportCost)==1
    +0079     transportCost=ones(numel(model.mets),1)*transportCost;
    +0080 end
    +0081 transportCost=transportCost(:);
    +0082 
    +0083 if numel(transportCost)~=numel(model.mets)
    +0084     EM='The vector of transport costs must have the same dimension as model.mets';
    +0085     error('RAVEN:badInput', '%s', EM);
    +0086 end
    +0087 if isempty(maxTime)
    +0088     maxTime=15;
    +0089 end
    +0090 if isempty(plotResults)
    +0091     plotResults=false;
    +0092 end
    +0093 
    +0094 if isfield(model,'rxnComps')
    +0095     model=rmfield(model,'rxnComps');
    +0096     EM='The model structure contains information about reaction compartmentalization. This is not supported by this function. The rxnComps field has been deleted';
    +0097     warning('RAVEN:warning', '%s', EM);
    +0098 end
    +0099 if isfield(model,'geneComps')
    +0100     model=rmfield(model,'geneComps');
    +0101     EM='The model structure contains information about gene compartmentalization. This is not supported by this function. The geneComps field has been deleted';
    +0102     warning('RAVEN:warning', '%s', EM);
    +0103 end
    +0104 
    +0105 defaultCompartment=char(defaultCompartment);
    +0106 I=ismember(defaultCompartment,GSS.compartments);
    +0107 if I==false
    +0108     error('RAVEN:badInput', ...
    +0109         ['defaultCompartment ''%s'' not found in GSS. Available compartments: %s. ' ...
    +0110          '(If you mapped scores to model ids via compartmentMap, pass an id such as ''c''; ' ...
    +0111          'otherwise pass the raw predictor label.)'], ...
    +0112         defaultCompartment, strjoin(GSS.compartments(:)', ', '));
    +0113 end
    +0114 
    +0115 if numel(model.comps)>1
    +0116     EM='The model has several compartments. All compartments will be merged';
    +0117     warning('RAVEN:warning', '%s', EM);
    +0118     model=mergeCompartments(model,true,true);
    +0119 end
    +0120 
    +0121 noGenes = ismember(model.genes,GSS.genes);
    +0122 if ~all(noGenes)
    +0123     EM=['For ' num2str(numel(find(~noGenes))) ' of ' num2str(numel(model.genes)) ' model genes no data was found in GSS'];
    +0124     warning('RAVEN:warning', '%s', EM);
    +0125 end
    +0126 
    +0127 %***Begin formating the data structures
    +0128 
    +0129 %Expand the model so that iso-enzymes have different reactions
    +0130 model=expandModel(model);
    +0131 
    +0132 %Identify reactions that have to be deleted because the involved mets are
    +0133 %never produced. This is done in an iterative manner
    +0134 removedRxns={};
    +0135 %This is to keep track of which metabolites are removed in this step. It is
    +0136 %needed to adjust the transport costs
    +0137 originalModelMets=model.mets;
    +0138 while 1
    +0139     irrevModel=convertToIrrev(model);
    +0140     
    +0141     I=sum(irrevModel.S>0,2);
    +0142     
    +0143     %Pretend that the unconstrained metabolites are made enough
    +0144     if isfield(irrevModel,'unconstrained')
    +0145         I(irrevModel.unconstrained~=0)=2;
    +0146     end
    +0147     metsToDelete=false(numel(model.mets),1);
    +0148     
    +0149     %This is not very neat but iterate through each metabolite and check
    +0150     %whether it can be produced (without using only one isolated reversible
    +0151     %reaction)
    +0152     for i=1:numel(irrevModel.mets)
    +0153         %If something can be made in two reactions then everything is fine.
    +0154         %If it can be made in one reaction it is fine unless it is through
    +0155         %an isolated reversible reaction (which can act as a mini loop)
    +0156         if I(i)<2
    +0157             if I(i)==1
    +0158                 %Find the reaction where this metabolite is produced
    +0159                 [~, J]=find(irrevModel.S(i,:)>0);
    +0160                 
    +0161                 %Check the metabolites that are consumed in this reaction.
    +0162                 %The problem is if any of them is only produced in the
    +0163                 %opposite reversible reaction
    +0164                 K=irrevModel.S(:,J)<0;
    +0165                 check=find(K & I<=1);
    +0166                 
    +0167                 for j=1:numel(check)
    +0168                     %Find the reactions where it participates
    +0169                     [~, L]=find(irrevModel.S(check(j),:)>0);
    +0170                     
    +0171                     if ~isempty(L)
    +0172                         rxn=irrevModel.rxns(J);
    +0173                         rxnRev=irrevModel.rxns(L);
    +0174                         if strcmp(strrep(rxn,'_REV',''),strrep(rxnRev,'_REV',''))
    +0175                             metsToDelete(i)=true;
    +0176                         end
    +0177                     else
    +0178                         %If the metabolite was never produced then do
    +0179                         %nothing and deal with it when the loop gets there
    +0180                         continue;
    +0181                     end
    +0182                 end
    +0183             else
    +0184                 %Not made anywhere
    +0185                 metsToDelete(i)=true;
    +0186             end
    +0187         end
    +0188     end
    +0189     
    +0190     if any(metsToDelete)
    +0191         %Delete any reactions involving any of the metsToDelete
    +0192         [~, I]=find(model.S(metsToDelete,:));
    +0193         removedRxns=[removedRxns;model.rxns(I)];
    +0194         model=removeReactions(model,I,true,true);
    +0195     else
    +0196         %All bad reactions deleted
    +0197         break;
    +0198     end
    +0199 end
    +0200 
    +0201 %Adjust the transport costs
    +0202 transportCost=transportCost(ismember(originalModelMets,model.mets));
    +0203 
    +0204 %Assign fake genes to reactions without genes
    +0205 I=find(sum(model.rxnGeneMat,2)==0);
    +0206 for i=1:numel(I)
    +0207     model.genes=[model.genes;['&&FAKE&&' num2str(i)]];
    +0208     if isfield(model,'geneShortNames')
    +0209         model.geneShortNames=[model.geneShortNames;{''}];
    +0210     end
    +0211     if isfield(model,'geneMiriams')
    +0212         model.geneMiriams=[model.geneMiriams;{[]}];
    +0213     end
    +0214     if isfield(model,'proteins')
    +0215         model.proteins=[model.proteins;{[]}];
    +0216     end
    +0217     if isfield(model,'geneFrom')
    +0218         model.geneFrom=[model.geneFrom;{{'FAKE'}}];
    +0219     end
    +0220     model.rxnGeneMat(I(i),numel(model.genes))=1;
    +0221     model.grRules{I(i)}='';
    +0222 end
    +0223 
    +0224 %Update the GSS. All genes, fake or real, for which there is no evidence
    +0225 %gets a score 0.5 in all compartments
    +0226 I=setdiff(model.genes,GSS.genes);
    +0227 GSS.genes=[GSS.genes;I];
    +0228 GSS.scores=[GSS.scores;ones(numel(I),numel(GSS.compartments))*0.5];
    +0229 
    +0230 %Gene complexes should be moved together to be biologically
    +0231 %relevant. The average score for the genes is used for each compartment.
    +0232 %This is done by changing the model so that gene complexes are used as a
    +0233 %single gene name and then a score is calculated for that "gene".
    +0234 
    +0235 %Only "and"-relationships exist after expandModel
    +0236 genes=unique(model.grRules);
    +0237 nGenes=strrep(genes,'(','');
    +0238 nGenes=strrep(nGenes,')','');
    +0239 %nGenes=strrep(nGenes," and ","_and_");
    +0240 complexes=setdiff(nGenes,model.genes);
    +0241 if ~isempty(complexes)
    +0242     if isempty(complexes{1}) %Empty grRules also come up here
    +0243         complexes(1)=[];
    +0244     end
    +0245 end
    +0246 cScores=zeros(numel(complexes),numel(GSS.compartments));
    +0247 for i=1:numel(complexes)
    +0248     genesInComplex=regexp(complexes{i},' and ','split');
    +0249     
    +0250     %Find these genes in GSS
    +0251     [I, J]=ismember(genesInComplex,GSS.genes);
    +0252     
    +0253     if any(I)
    +0254         %Get the average of the genes that were found
    +0255         mScores=mean(GSS.scores(J(I),:));
    +0256         
    +0257         %And add 0.5 for the genes that were not found, to be
    +0258         %consistent with non-complexes
    +0259         mScores=(mScores.*sum(I)+(numel(genesInComplex)-sum(I))*0.5)/numel(genesInComplex);
    +0260     else
    +0261         EM=['Could not parse grRule "' complexes{i} '". Assigning score 0.0 in all compartments'];
    +0262         warning('RAVEN:warning', '%s', EM);
    +0263         mScores=ones(1,numel(genesInComplex))*0.5;
    +0264     end
    +0265     cScores(i,:)=mScores;
    +0266     
    +0267     %Add this complex as a new gene
    +0268     model.genes=[model.genes;complexes{i}];
    +0269     if isfield(model,'geneMiriams')
    +0270         model.geneMiriams=[model.geneMiriams;{[]}];
    +0271     end
    +0272     if isfield(model,'geneShortNames')
    +0273         model.geneShortNames=[model.geneShortNames;{''}];
    +0274     end
    +0275     if isfield(model,'proteins')
    +0276         model.proteins=[model.proteins;{''}];
    +0277     end
    +0278     if isfield(model,'geneFrom')
    +0279         model.geneFrom=[model.geneFrom;{'COMPLEX'}];
    +0280     end
    +0281     %Find the reactions which had the original complex and change them to
    +0282     %use the new "gene"
    +0283     I=ismember(model.grRules,['(' complexes{i} ')']);
    +0284     
    +0285     %Should check more carefully if there can be an error here
    +0286     if ~isempty(I)
    +0287         model.rxnGeneMat(I,:)=0; %Ok since the split on "or" was applied
    +0288         model.rxnGeneMat(I,numel(model.genes))=1;
    +0289     end
    +0290 end
    +0291 
    +0292 %Add the new "genes"
    +0293 GSS.genes=[GSS.genes;complexes];
    +0294 GSS.scores=[GSS.scores;cScores];
    +0295 
    +0296 %After merging the complexes it could happen that there are genes that are
    +0297 %no longer in use. Delete such genes
    +0298 model=removeReactions(model,{},false,true);
    +0299 
    +0300 %Exchange reactions, defined as involving an unconstrained metabolite, are
    +0301 %special in that they have to stay in the defaultCompartment. This means
    +0302 %that uptake/excretion of metabolites is always via the default
    +0303 %compartment. This is a small simplification, but should be valid in most
    +0304 %cases
    +0305 [~, I]=getExchangeRxns(model);
    +0306 
    +0307 %It will be easier later on if the same place. Put them in the beginning
    +0308 J=1:numel(model.rxns);
    +0309 J(I)=[];
    +0310 model=permuteModel(model,[I;J'],'rxns');
    +0311 
    +0312 %Number of exchange reactions
    +0313 nER=numel(I);
    +0314 
    +0315 %Also put the exchange metabolites in the beginning
    +0316 if isfield(model,'unconstrained')
    +0317     I=find(model.unconstrained);
    +0318     J=1:numel(model.mets);
    +0319     J(I)=[];
    +0320     model=permuteModel(model,[I;J'],'mets');
    +0321     %Also reorder the transport costs
    +0322     transportCost=transportCost([I;J']);
    +0323     %Number of exchange metabolites
    +0324     nEM=numel(I);
    +0325 else
    +0326     nEM=0;
    +0327 end
    +0328 
    +0329 %There is no point of having genes for exchange reactions, so delete them.
    +0330 %Also to make computations easier
    +0331 model.rxnGeneMat(1:nER,:)=0;
    +0332 model.grRules(1:nER)={''};
    +0333 
    +0334 %Remove unused genes
    +0335 model=removeReactions(model,{},false,true);
    +0336 
    +0337 %Remove genes with no match to the model and reorder so that the genes are
    +0338 %in the same order as model.genes. Since the fake genes are already added
    +0339 %so that all genes in model exist in GSS it is fine to do like this
    +0340 [~, J]=ismember(model.genes,GSS.genes);
    +0341 GSS.genes=model.genes;
    +0342 GSS.scores=GSS.scores(J,:);
    +0343 
    +0344 %Reorder the GSS so that the first index corresponds to the default
    +0345 %compartment
    +0346 [~, J]=ismember(defaultCompartment,GSS.compartments);
    +0347 reorder=1:numel(GSS.compartments);
    +0348 reorder(J)=[];
    +0349 reorder=[J reorder];
    +0350 GSS.scores=GSS.scores(:,reorder);
    +0351 GSS.compartments=GSS.compartments(reorder);
    +0352 
    +0353 %Since it is only checked whether the metabolites can be synthesized, there
    +0354 %is no need to care about the stoichiometry. Change to -1/1 to simplify
    +0355 %later. Keep the S matrix for later though
    +0356 oldS=model.S;
    +0357 model.S(model.S>0)=1;
    +0358 model.S(model.S<0)=-1;
    +0359 
    +0360 %Here is a bit of a trick. To avoid the recurring calculation which
    +0361 %reactions are reversible, the reversible reactions have the coefficients
    +0362 %-10/10 instead of -1/1
    +0363 model.S(:,model.rev==1)=model.S(:,model.rev==1).*10;
    +0364 
    +0365 %***Begin problem formulation
    +0366 
    +0367 %Some numbers that are good to have
    +0368 nRxns=numel(model.rxns)-nER; %Excluding exchange rxns
    +0369 nMets=numel(model.mets)-nEM; %Excluding exchange mets
    +0370 nGenes=numel(model.genes);
    +0371 nComps=numel(GSS.compartments);
    +0372 
    +0373 %Create a big stoichiometric matrix that will be the current model. In
    +0374 %order to have faster simulations the maximal model size is declared and
    +0375 %reactions are then moved within it.
    +0376 
    +0377 %First the original model (with the first nE being exchange rxns), then
    +0378 %reserve space for number of rxns minus exchange rxns for each non-default
    +0379 %compartment, then transport reactions for all non-exchange mets between
    +0380 %the default compartment and all others.
    +0381 %NOTE: Kept eye()*0 since eye() can be used to include all transport from
    +0382 %the beginning
    +0383 s=repmat(eye(nMets)*0,1,nComps-1);
    +0384 s=[zeros(numel(model.mets)-nMets,size(s,2));s];
    +0385 S=[model.S sparse(numel(model.mets),nRxns*(nComps-1)) s];
    +0386 s=[sparse(nMets*(nComps-1),numel(model.rxns)+nRxns*(nComps-1)) eye(nMets*(nComps-1))*0];
    +0387 S=[S;s];
    +0388 
    +0389 %Also replicate the transport costs
    +0390 transportCost=[transportCost(1:nEM);repmat(transportCost(nEM+1:end),nComps,1)];
    +0391 
    +0392 %Create a binary matrix that says where the genes are in the current
    +0393 %solution
    +0394 g2c=false(nGenes,nComps);
    +0395 %All genes start in the default compartment
    +0396 g2c(:,1)=true;
    +0397 
    +0398 %Start of main optimization loop
    +0399 tic;
    +0400 bestScore=-inf;
    +0401 bestS=[];
    +0402 bestg2c=[];
    +0403 
    +0404 %Temp for testing
    +0405 plotScore=[];
    +0406 nTrans=[];
    +0407 totScore=[];
    +0408 minScore=sum(min(GSS.scores,[],2));
    +0409 maxScore=sum(max(GSS.scores,[],2));
    +0410 
    +0411 while toc<maxTime*60
    +0412     %Pick a random gene, weighted by it is current score minus the best
    +0413     %score for that gene (often 1.0, but can be 0.5 for no genes or average
    +0414     %for complexes). Genes with bad fits are more likely to be moved. This
    +0415     %formulation never moves a gene from its best compartment. Therefore a
    +0416     %small uniform weight is added
    +0417     [I, J]=find(g2c);
    +0418     geneToMove=randsample(nGenes,1,true,max(GSS.scores(I,:),[],2)-GSS.scores(sub2ind(size(g2c),I,J))+0.1);
    +0419     
    +0420     %Sample among possible compartments to move to. Add a larger weight to
    +0421     %even out the odds a little. Also a way of getting rid of loops where
    +0422     %the same set of genes are moved back and forth several times
    +0423     toComp=randsample(nComps,1,true,GSS.scores(geneToMove,:)+0.2);
    +0424     
    +0425     %Check that it moves to a new compartment
    +0426     if toComp==find(g2c(geneToMove,:))
    +0427         continue;
    +0428     end
    +0429     
    +0430     %Moves the gene
    +0431     [newS, newg2c]=moveGene(S,model,g2c,geneToMove,toComp,nRxns,nMets);
    +0432     
    +0433     %Tries to connect the network. If this was not possible in 10
    +0434     %iterations, then abort. If more than 20 modifications were needed then
    +0435     %it is unlikely that it will be a lower score
    +0436     wasConnected=false;
    +0437     for j=1:10
    +0438         %Find the metabolites that are now unconnected
    +0439         unconnected=findUnconnected(newS,nEM);
    +0440         
    +0441         %Continue if there are still unconnected
    +0442         if any(unconnected)
    +0443             %For each gene find out how many of these could be connected if
    +0444             %the gene was moved and how many would be disconnected by
    +0445             %moving that gene
    +0446             [geneIndex, moveTo, deltaConnected, deltaScore]=selectGenes(newS,nEM,nMets,nER,nRxns,model,unconnected,g2c,GSS);
    +0447             
    +0448             %Score which gene would be the best to move. The highest
    +0449             %deltaScore is 1.0. It should be possible to move a gene from
    +0450             %worst to best compartment even if it disconnects, say, 1.5
    +0451             %more metabolites
    +0452             [score, I]=max(1.5*deltaScore+deltaConnected);
    +0453             
    +0454             %Check if it has to add a transport or if there is a gene that
    +0455             %could be moved order to have a more connected network
    +0456             hasToAddTransport=true;
    +0457             if ~isempty(deltaConnected)
    +0458                 if score>0
    +0459                     hasToAddTransport=false;
    +0460                 end
    +0461             end
    +0462             
    +0463             %If it is possible to move any gene to have a more
    +0464             %connected network, then move the best one
    +0465             if hasToAddTransport==false
    +0466                 [newS, newg2c]=moveGene(newS,model,g2c,geneIndex(I),moveTo(I),nRxns,nMets);
    +0467             else
    +0468                 %Choose a random unconnected metabolite that should be
    +0469                 %connected
    +0470                 transMet=unconnected(randsample(numel(unconnected),1));
    +0471                 
    +0472                 %First get where the metabolite is now
    +0473                 comps=ceil((transMet-nEM)/((size(S,1)-nEM)/nComps));
    +0474                 
    +0475                 %Find the corresponding metabolite index if it were in the
    +0476                 %default compartment
    +0477                 dcIndex=transMet-(comps-1)*nMets;
    +0478                 
    +0479                 %Then get the indexes of that metabolite in all
    +0480                 %compartments
    +0481                 allIndexes=dcIndex;
    +0482                 for k=1:nComps-1
    +0483                     allIndexes=[allIndexes;dcIndex+nMets*k];
    +0484                 end
    +0485                 
    +0486                 %It could be that some of these are not used in any
    +0487                 %reaction. Get only the ones which are
    +0488                 I=sum(newS(allIndexes,:)~=0,2)>0;
    +0489                 
    +0490                 %Then get the ones that are used but not in unconnected.
    +0491                 %These are metabolites that could potentially be
    +0492                 %transported to connect transMet
    +0493                 connectedUsed=setdiff(allIndexes(I),unconnected);
    +0494                 
    +0495                 %This may be an error but leave it for now. It seems to
    +0496                 %happen if nothing can be connected in one step
    +0497                 if isempty(connectedUsed)
    +0498                     break;
    +0499                 end
    +0500                 
    +0501                 %If transMet is in the default compartment then everything
    +0502                 %is fine, just connect it to a random one
    +0503                 if transMet==dcIndex
    +0504                     newS=addTransportToS(newS,nRxns,nER,nMets,nEM,nComps,transMet,connectedUsed(randsample(numel(connectedUsed),1)));
    +0505                 else
    +0506                     %If one of the connectedUsed is in the default
    +0507                     %compartment then connect to that one
    +0508                     I=connectedUsed(connectedUsed<(nMets+nEM));
    +0509                     if any(I)
    +0510                         newS=addTransportToS(newS,nRxns,nER,nMets,nEM,nComps,transMet,I(randsample(numel(I),1)));
    +0511                     else
    +0512                         %This is if the only way to connect it is by adding
    +0513                         %two transport reactions, going via the default
    +0514                         %compartment
    +0515                         break;
    +0516                     end
    +0517                 end
    +0518             end
    +0519         else
    +0520             wasConnected=true;
    +0521             break;
    +0522         end
    +0523     end
    +0524     
    +0525     %If the network was connected in a new way, it is possible that some
    +0526     %transport reactions are no longer needed. They should be removed
    +0527     if wasConnected==true
    +0528         %These are the metabolites that are being transported
    +0529         activeTransport=find(sum(newS(:,nER+nRxns*nComps+1:end),2));
    +0530         
    +0531         %Get the metabolites that are unconnected if transport was not used
    +0532         unconnected=findUnconnected(newS(:,1:nER+nRxns*nComps),nEM);
    +0533         
    +0534         %Find the transport reactions that are not needed and delete them
    +0535         I=setdiff(activeTransport,unconnected);
    +0536         
    +0537         %Since both metabolites in a transport rxns must be connected for
    +0538         %the reaction to be deleted, the sum over the colums should be 4
    +0539         newS(:,find(sum(newS(I,nER+nRxns*nComps+1:end))==4)+nER+nRxns*nComps)=0;
    +0540         
    +0541         %Score the solution and determine whether to keep it as a new
    +0542         %solution
    +0543         [score, geneScore, trCost]=scoreLocalization(newS,newg2c,GSS,transportCost);
    +0544         
    +0545         %If it was the best solution so far, keep it
    +0546         if score>bestScore
    +0547             bestScore=score;
    +0548             bestS=newS;
    +0549             bestg2c=newg2c;
    +0550         end
    +0551         
    +0552         %This should not be steepest descent later
    +0553         if score>=bestScore% || exp((score-bestScore)*7)>rand()
    +0554             plotScore=[plotScore;geneScore];
    +0555             nTrans=[nTrans;trCost];
    +0556             totScore=[totScore;score];
    +0557             S=newS;
    +0558             g2c=newg2c;
    +0559             
    +0560             if plotResults==true
    +0561                 subplot(3,2,1);
    +0562                 spy(S);
    +0563                 subplot(3,2,2);
    +0564                 plot(plotScore,'r');
    +0565                 xlabel('Gene score');
    +0566                 subplot(3,2,3);
    +0567                 plot((plotScore-minScore)/(maxScore-minScore),'r');
    +0568                 xlabel('Gene score relative to predictions');
    +0569                 subplot(3,2,4);
    +0570                 plot(nTrans,'g');
    +0571                 xlabel('Transport cost');
    +0572                 subplot(3,2,5);
    +0573                 plot(totScore,'b');
    +0574                 xlabel('Total score');
    +0575                 subplot(3,2,6);
    +0576                 pause(0.2);
    +0577             end
    +0578         end
    +0579     end
    +0580 end
    +0581 scores.totScore=score;
    +0582 scores.geneScore=geneScore;
    +0583 scores.transCost=trCost;
    +0584 
    +0585 %Find which metabolites are transported and to where
    +0586 [I, J]=find(bestS(nEM+1:nEM+nMets,end-nMets*(nComps-1)+1:end));
    +0587 J=ceil(J/nMets+1);
    +0588 transportStruct.mets=model.metNames(I+nEM);
    +0589 transportStruct.toComp=GSS.compartments(J);
    +0590 
    +0591 [I, J]=find(bestg2c);
    +0592 geneLocalization.genes=GSS.genes(I);
    +0593 geneLocalization.comps=GSS.compartments(J);
    +0594 
    +0595 %Resort the gene names
    +0596 [~, I]=sort(geneLocalization.genes);
    +0597 geneLocalization.genes=geneLocalization.genes(I);
    +0598 geneLocalization.comps=geneLocalization.comps(I);
    +0599 
    +0600 %Remove the fake genes
    +0601 I=strncmp('&&FAKE&&',geneLocalization.genes,8);
    +0602 geneLocalization.genes(I)=[];
    +0603 geneLocalization.comps(I)=[];
    +0604 
    +0605 %Put together the model. This is done by first duplicating the S matrix
    +0606 %into the different compartments. Then the transport reactions are added
    +0607 %based on transportStruct. By now model.S should have the same size as the
    +0608 %S matrix used in the optimization, but with conserved stoichiometry. In
    +0609 %the final step all reactions and metabolites that are not used in the S
    +0610 %matrix from the optimization are deleted from the model
    +0611 outModel=model;
    +0612 outModel.S=oldS;
    +0613 
    +0614 %This is the S matrix without exchange rxns or metabolites
    +0615 copyPart=outModel.S(nEM+1:end,nER+1:end);
    +0616 
    +0617 %Replicate to give the rxnGeneMat for the full system
    +0618 copyRxnGeneMat=outModel.rxnGeneMat(nER+1:end,:);
    +0619 outModel.rxnGeneMat=[outModel.rxnGeneMat;repmat(copyRxnGeneMat,nComps-1,1)];
    +0620 
    +0621 %First fix the compartments. The model is already ordered with the exchange
    +0622 %metabolites first. The original model may contain one or two compartments,
    +0623 %depending on whether any exchange metabolites are defined
    +0624 nStartComps=numel(outModel.comps);
    +0625 if nStartComps==1
    +0626     outModel.comps={'1'};
    +0627     outModel.compNames=GSS.compartments(1);
    +0628 else
    +0629     if model.metComps(1)==1
    +0630         outModel.compNames(1)=GSS.compartments(1);
    +0631     else
    +0632         outModel.compNames(2)=GSS.compartments(1);
    +0633     end
    +0634 end
    +0635 outModel.compNames=[outModel.compNames;GSS.compartments(2:end)];
    +0636 
    +0637 %Ugly little loop
    +0638 for i=1:numel(GSS.compartments)-1
    +0639     outModel.comps=[outModel.comps;num2str(numel(outModel.comps)+1)];
    +0640 end
    +0641 %This information is not known from the data, so empty fields are added
    +0642 outModel.compOutside=cell(numel(outModel.comps),1);
    +0643 outModel.compOutside(:)={''};
    +0644 
    +0645 for i=1:nComps-1
    +0646     outModel.S=[outModel.S sparse(size(outModel.S,1),nRxns)];
    +0647     outModel.S=[outModel.S;[sparse(nMets,nRxns*i+nER) copyPart]];
    +0648     outModel.rxns=[outModel.rxns;strcat(outModel.rxns(nER+1:nER+nRxns),'_',GSS.compartments{i+1})];
    +0649     outModel.rxnNames=[outModel.rxnNames;strcat(outModel.rxnNames(nER+1:nER+nRxns),' (',GSS.compartments{i+1},')')];
    +0650     outModel.lb=[outModel.lb;outModel.lb(nER+1:nER+nRxns)];
    +0651     outModel.ub=[outModel.ub;outModel.ub(nER+1:nER+nRxns)];
    +0652     outModel.rev=[outModel.rev;outModel.rev(nER+1:nER+nRxns)];
    +0653     outModel.c=[outModel.c;outModel.c(nER+1:nER+nRxns)];
    +0654     if isfield(outModel,'grRules')
    +0655         outModel.grRules=[outModel.grRules;outModel.grRules(nER+1:nER+nRxns)];
    +0656     end
    +0657     if isfield(outModel,'subSystems')
    +0658         outModel.subSystems=[outModel.subSystems;outModel.subSystems(nER+1:nER+nRxns)];
    +0659     end
    +0660     if isfield(outModel,'eccodes')
    +0661         outModel.eccodes=[outModel.eccodes;outModel.eccodes(nER+1:nER+nRxns)];
    +0662     end
    +0663     if isfield(outModel,'rxnFrom')
    +0664         outModel.rxnFrom=[outModel.rxnFrom;outModel.rxnFrom(nER+1:nER+nRxns)];
    +0665     end
    +0666     if isfield(outModel,'rxnMiriams')
    +0667         outModel.rxnMiriams=[outModel.rxnMiriams;outModel.rxnMiriams(nER+1:nER+nRxns)];
    +0668     end
    +0669     if isfield(outModel,'rxnNotes')
    +0670         outModel.rxnNotes=[outModel.rxnNotes;outModel.rxnNotes(nER+1:nER+nRxns)];
    +0671     end
    +0672     if isfield(outModel,'rxnReferences')
    +0673         outModel.rxnReferences=[outModel.rxnReferences;outModel.rxnReferences(nER+1:nER+nRxns)];
    +0674     end
    +0675     if isfield(outModel,'rxnConfidenceScores')
    +0676         outModel.rxnConfidenceScores=[outModel.rxnConfidenceScores;outModel.rxnConfidenceScores(nER+1:nER+nRxns)];
    +0677     end
    +0678     if isfield(outModel,'rxnDeltaG')
    +0679         outModel.rxnDeltaG=[outModel.rxnDeltaG;outModel.rxnDeltaG(nER+1:nER+nRxns)];
    +0680     end
    +0681     outModel.mets=[outModel.mets;strcat(outModel.mets(nEM+1:nEM+nMets),'_',GSS.compartments{i+1})];
    +0682     outModel.metNames=[outModel.metNames;outModel.metNames(nEM+1:nEM+nMets)];
    +0683     outModel.b=[outModel.b;outModel.b(nEM+1:nEM+nMets,:)];
    +0684     I=ones(nMets,1)*nStartComps+i;
    +0685     outModel.metComps=[outModel.metComps;I];
    +0686     if isfield(outModel,'inchis')
    +0687         outModel.inchis=[outModel.inchis;outModel.inchis(nEM+1:nEM+nMets)];
    +0688     end
    +0689     if isfield(outModel,'metSmiles')
    +0690         outModel.metSmiles=[outModel.metSmiles;outModel.metSmiles(nEM+1:nEM+nMets)];
    +0691     end
    +0692     if isfield(outModel,'unconstrained')
    +0693         outModel.unconstrained=[outModel.unconstrained;outModel.unconstrained(nEM+1:nEM+nMets)];
    +0694     end
    +0695     if isfield(outModel,'metMiriams')
    +0696         outModel.metMiriams=[outModel.metMiriams;outModel.metMiriams(nEM+1:nEM+nMets)];
    +0697     end
    +0698     if isfield(outModel,'metFormulas')
    +0699         outModel.metFormulas=[outModel.metFormulas;outModel.metFormulas(nEM+1:nEM+nMets)];
    +0700     end
    +0701     if isfield(outModel,'metFrom')
    +0702         outModel.metFrom=[outModel.metFrom;outModel.metFrom(nEM+1:nEM+nMets)];
    +0703     end
    +0704     if isfield(outModel,'metCharges')
    +0705         outModel.metCharges=[outModel.metCharges;outModel.metCharges(nEM+1:nEM+nMets)];
    +0706     end
    +0707     if isfield(outModel,'metDeltaG')
    +0708         outModel.metDeltaG=[outModel.metDeltaG;outModel.metDeltaG(nEM+1:nEM+nMets)];
    +0709     end
    +0710 end
    +0711 
    +0712 %Add the transport reactions
    +0713 transS=bestS(:,numel(outModel.rxns)+1:end);
    +0714 J=sum(transS)>0; %Active rxns
    +0715 
    +0716 %Transport reactions are written in a different way compared to a "real"
    +0717 %stoichimetric matrix. This is to fix that
    +0718 transS(transS~=0)=1;
    +0719 transS(1:nEM+nMets,:)=transS(1:nEM+nMets,:)*-1;
    +0720 I=find(sum(transS>0,2));
    +0721 nTransRxns=numel(I);
    +0722 outModel.S=[outModel.S transS(:,J)];
    +0723 filler=ones(nTransRxns,1);
    +0724 outModel.lb=[outModel.lb;filler*-1000];
    +0725 outModel.ub=[outModel.ub;filler*1000];
    +0726 outModel.rev=[outModel.rev;filler];
    +0727 outModel.c=[outModel.c;filler*0];
    +0728 outModel.rxnGeneMat=[outModel.rxnGeneMat;sparse(nTransRxns,numel(outModel.genes))];
    +0729 
    +0730 for i=1:numel(I)
    +0731     outModel.rxns=[outModel.rxns;strcat('transport',num2str(i))];
    +0732     outModel.rxnNames=[outModel.rxnNames;['Transport of ',outModel.metNames{I(i)}]];
    +0733     if isfield(outModel,'grRules')
    +0734         outModel.grRules=[outModel.grRules;{''}];
    +0735     end
    +0736     if isfield(outModel,'rxnMiriams')
    +0737         outModel.rxnMiriams=[outModel.rxnMiriams;{[]}];
    +0738     end
    +0739     if isfield(outModel,'subSystems')
    +0740         if any(cellfun(@(x) iscell(x), outModel.subSystems))
    +0741             outModel.subSystems=[outModel.subSystems;{{'Inferred transport reactions'}}];
    +0742         else
    +0743             outModel.subSystems=[outModel.subSystems;{'Inferred transport reactions'}];
    +0744         end
    +0745     end
    +0746     if isfield(outModel,'eccodes')
    +0747         outModel.eccodes=[outModel.eccodes;{''}];
    +0748     end
    +0749     if isfield(outModel,'rxnFrom')
    +0750         outModel.rxnFrom=[outModel.rxnFrom;{''}];
    +0751     end
    +0752     if isfield(outModel,'rxnNotes')
    +0753         outModel.rxnNotes=[outModel.rxnNotes;{''}];
    +0754     end
    +0755     if isfield(outModel,'rxnReferences')
    +0756         outModel.rxnReferences=[outModel.rxnReferences;{''}];
    +0757     end
    +0758     if isfield(outModel,'rxnConfidenceScores')
    +0759         outModel.rxnConfidenceScores=[outModel.rxnConfidenceScores;NaN];
    +0760     end
    +0761     if isfield(outModel,'rxnDeltaG')
    +0762         outModel.rxnDeltaG=[outModel.rxnDeltaG;NaN];
    +0763     end
    +0764 end
    +0765 
    +0766 %Then remove all reactions and metabolites that are not used in the final
    +0767 %solution from the optimization
    +0768 [~, J]=find(bestS(:,1:nER+nComps*nRxns));
    +0769 K=true(numel(outModel.rxns),1);
    +0770 K(J)=false;
    +0771 K(end-nTransRxns+1:end)=false;
    +0772 outModel=removeReactions(outModel,K,true);
    +0773 
    +0774 %Remove all fake genes
    +0775 I=strncmp('&&FAKE&&',outModel.genes,8);
    +0776 outModel.genes(I)=[];
    +0777 if isfield(outModel,'geneMiriams')
    +0778     outModel.geneMiriams(I)=[];
    +0779 end
    +0780 if isfield(outModel,'geneShortNames')
    +0781     outModel.geneShortNames(I)=[];
    +0782 end
    +0783 if isfield(outModel,'proteins')
    +0784     outModel.proteins(I)=[];
    +0785 end
    +0786 outModel.rxnGeneMat(:,I)=[];
    +0787 
    +0788 %Fix grRules and reconstruct rxnGeneMat
    +0789 [grRules,rxnGeneMat] = standardizeGrRules(outModel,true);
    +0790 outModel.grRules = grRules;
    +0791 outModel.rxnGeneMat = rxnGeneMat;
    +0792 end
    +0793 
    +0794 %Moves a gene and all associated reactions from one compartment to another
    +0795 function [S, g2c]=moveGene(S,model,g2c,geneToMove,toComp,nRxns,nMets)
    +0796 %Find the current compartment and update to the new one
    +0797 currentComp=find(g2c(geneToMove,:));
    +0798 g2c(geneToMove,:)=false;
    +0799 g2c(geneToMove,toComp)=true;
    +0800 
    +0801 %Find the reactions in the original model that the gene controls
    +0802 [I, ~]=find(model.rxnGeneMat(:,geneToMove));
    +0803 
    +0804 %Calculate their current positions in the S matrix
    +0805 oldRxns=I+(currentComp-1)*nRxns;
    +0806 
    +0807 %And their new positions
    +0808 newRxns=I+(toComp-1)*nRxns;
    +0809 
    +0810 %The metabolite ids also have to be changed to match the new
    +0811 %compartment
    +0812 metChange=nMets*(toComp-currentComp);
    +0813 
    +0814 %Update the reactions
    +0815 [I, J, K]=find(S(:,oldRxns));
    +0816 I=I+metChange;
    +0817 
    +0818 %Move the reactions
    +0819 S(:,oldRxns)=0;
    +0820 S(sub2ind(size(S),I,newRxns(J)))=K;
    +0821 end
    +0822 
    +0823 %Finds which metabolites are unconnected, in the sense that they are never
    +0824 %a product or only a product in a reversible reaction where one reactant is
    +0825 %only a product in the opposite direction of that reaction. This function
    +0826 %ignores exchange metabolites. Returns a vector of metabolite indexes.
    +0827 %metsToCheck is an array of metabolite indexes to check for connectivity.
    +0828 %If not supplied then all metabolites are checked
    +0829 function unconnected=findUnconnected(S,nEM,metsToCheck)
    +0830 if nargin>2
    +0831     %Do this by deleting everything from the network that is not in
    +0832     %metsToCheck and that is not exchange metabolites
    +0833     I=false(size(S,1),1);
    +0834     I(1:nEM)=true;
    +0835     I(metsToCheck)=true;
    +0836     S=S(I,:);
    +0837 end
    +0838 
    +0839 em=false(size(S,1),1);
    +0840 em(1:nEM)=true;
    +0841 
    +0842 %Construct a matrix in which the reversible reactions are inverted
    +0843 I=sum(S>2,1) | sum(S>2,1);
    +0844 revS=S;
    +0845 revS(:,I)=revS(:,I)*-1;
    +0846 
    +0847 %First calculate the ones that are ok
    +0848 %Produced in 2 rxns, is exchange, is not used at all, is produced in
    +0849 %non-reversible, involved in more than 1 reversible reactions
    +0850 connected=sum(S>0,2)>1 | em | sum(S~=0,2)==0 | sum(S(:,~I)>0,2)>0 | sum(S(:,I)~=0,2)>1;
    +0851 
    +0852 %Then get the ones that are unconnected because they are never produced
    +0853 unconnected=sum(S>0 | revS>0,2)==0 & connected==false;
    +0854 
    +0855 %Then get the ones that are potentially unconnected
    +0856 maybeUnconnected=~connected & ~unconnected;
    +0857 %maybeUnconnected=find(maybeUnconnectedS);
    +0858 
    +0859 %The metabolites in maybeUnconnected are involved in one reversible
    +0860 %reaction and not produced in any other reaction. This means that the
    +0861 %reactions which have at least one met in maybeUnconnected as reactant and
    +0862 %one as product are unconnected. The metabolites in maybeUnconnected that
    +0863 %are present in those reactions are then dead ends
    +0864 deadRxns=any(S(maybeUnconnected,:)>0) & any(S(maybeUnconnected,:)<0);
    +0865 
    +0866 %Get the mets involved in any of those reactions
    +0867 problematic=any(S(:,deadRxns)~=0,2);
    +0868 
    +0869 %If any of these are in the maybeUnconnected list then the metabolite is
    +0870 %unconnected
    +0871 unconnected(problematic & maybeUnconnected)=true;
    +0872 
    +0873 %Map back to metsToCheck
    +0874 if nargin>2
    +0875     unconnected=metsToCheck(unconnected(nEM+1:end));
    +0876 else
    +0877     unconnected=find(unconnected);
    +0878 end
    +0879 end
    +0880 
    +0881 %Given a set of unconnected metabolites, this function tries to move each
    +0882 %gene that could connect any of them, calculates the number of newly
    +0883 %connected metabolites minus the number of newly disconnected metabolites.
    +0884 %As some metabolites are very connected, only 25 genes are checked. Genes
    +0885 %that have a low score in their current compartment are more likely to be
    +0886 %moved
    +0887 function [geneIndex, moveTo, deltaConnected, deltaScore]=selectGenes(S,nEM,nMets,nER,nRxns,model,unconnected,g2c,GSS)
    +0888 %If moveTo is 0 then the gene cannot connect any of the metabolites
    +0889 moveTo=zeros(numel(model.genes),1);
    +0890 deltaConnected=zeros(numel(model.genes),1);
    +0891 
    +0892 %First get where the metabolites are now
    +0893 nComps=size(g2c,2);
    +0894 comps=ceil((unconnected-nEM)/((size(S,1)-nEM)/nComps));
    +0895 
    +0896 %Find the corresponding metabolite indexes if they all were in the default
    +0897 %compartment
    +0898 dcIndexes=unique(unconnected-(comps-1)*nMets);
    +0899 
    +0900 %Then find them if they were in any other compartment
    +0901 allIndexes=dcIndexes;
    +0902 for i=1:nComps-1
    +0903     allIndexes=[allIndexes;dcIndexes+nMets*i];
    +0904 end
    +0905 
    +0906 %Also check which reversible reactions that could be used
    +0907 I=sum(S>2,1) | sum(S>2,1);
    +0908 revS=S;
    +0909 revS(:,I)=revS(:,I)*-1;
    +0910 
    +0911 %Find all reactions that could make any of the unconnected metabolites in
    +0912 %some other compartment
    +0913 newMets=setdiff(allIndexes,unconnected);
    +0914 [~, potential]=find(S(newMets,:)>0 | revS(newMets,:)>0);
    +0915 potential(potential<=nER | potential>nER+nRxns*nComps)=[]; %No exchange rxns or transport rxns
    +0916 
    +0917 %Map J to the real metabolic reactions in model
    +0918 rxnComps=ceil((potential-nER)/(nRxns));
    +0919 
    +0920 %Find the corresponding reaction indexes if they all were in the default
    +0921 %compartment
    +0922 dcRxnIndexes=potential-(rxnComps-1)*nRxns;
    +0923 
    +0924 %Get the genes for those reactions
    +0925 genes=find(sum(model.rxnGeneMat(dcRxnIndexes,:)>0,1));
    +0926 
    +0927 %For some cases there can be very many reactions to connect something. This
    +0928 %is in particular true in the beginning of the optimization if, say, ATP is
    +0929 %unconnected. Therefore limit the number of genes to be checked to 25.
    +0930 %Weigh so that genes with bad scores in their current compartment are more
    +0931 %likely to be moved.
    +0932 
    +0933 %Get scores for these genes
    +0934 [~, J]=find(g2c(genes,:));
    +0935 
    +0936 %Add a small weight so that genes in their best compartment could be moved
    +0937 %as well
    +0938 geneScores=GSS.scores(sub2ind(size(g2c),genes(:),J));
    +0939 modGeneScores=1.1-geneScores;
    +0940 if numel(genes)>25
    +0941     rGenes=genes(randsample(numel(genes),min(numel(genes),25),true,modGeneScores));
    +0942     
    +0943     %The sampling with weights could give duplicates
    +0944     rGenes=unique(rGenes);
    +0945     
    +0946     %Reorder the geneScores to match
    +0947     [~, I]=ismember(rGenes,genes);
    +0948     geneScores=geneScores(I);
    +0949     genes=rGenes;
    +0950 end
    +0951 for i=1:numel(genes)
    +0952     %Since one gene is moved at a time, only metabolites involved in any of
    +0953     %the reactions for that gene can become unconnected. This helps to
    +0954     %speed up the algorithm. First get all involved reactions in the
    +0955     %default compartment
    +0956     rxns=find(model.rxnGeneMat(:,genes(i)));
    +0957     
    +0958     %Then get their mets
    +0959     mets=find(sum(model.S(:,rxns)~=0,2)>0);
    +0960     
    +0961     %Then get their indexes in all compartments
    +0962     allIndexes=mets;
    +0963     for j=1:nComps-1
    +0964         allIndexes=[allIndexes;mets+nMets*j];
    +0965     end
    +0966     
    +0967     %Check which of the unconnected metabolites that these reactions
    +0968     %correspond to. This could have been done earlier, but it is fast. The
    +0969     %reversibility check is skipped because it is unlikely to be an issue
    +0970     %here. Worst case is that the gene is tested once to much
    +0971     [I, ~]=find(model.S(:,rxns));
    +0972     moveToComps=unique(comps(ismember(dcIndexes,I)));
    +0973     
    +0974     %Try to move the gene to each of the compartments
    +0975     bestMove=-inf;
    +0976     bestComp=[];
    +0977     for j=1:numel(moveToComps)
    +0978         newS=moveGene(S,model,g2c,genes(i),moveToComps(j),nRxns,nMets);
    +0979         
    +0980         %Check how many metabolites that are unconnected after moving the
    +0981         %gene
    +0982         dConnected=numel(unconnected)-numel(findUnconnected(newS,nEM,[allIndexes;unconnected]));
    +0983         if dConnected>bestMove
    +0984             bestMove=dConnected;
    +0985             bestComp=moveToComps(j);
    +0986         end
    +0987     end
    +0988     
    +0989     %Add the difference in connectivity and where the genes should be moved
    +0990     moveTo(genes(i))=bestComp;
    +0991     deltaConnected(genes(i))=bestMove;
    +0992 end
    +0993 
    +0994 %Finish up
    +0995 geneIndex=genes(:);
    +0996 moveTo=moveTo(geneIndex);
    +0997 deltaConnected=deltaConnected(geneIndex);
    +0998 deltaScore=GSS.scores(sub2ind(size(g2c),geneIndex(:),moveTo))-geneScores;
    +0999 end
    +1000 
    +1001 %Small function to add a transport reactions between two metabolites.
    +1002 %Transport reactions are written as having a coefficient 2.0 for both
    +1003 %reactant and product. This is not a "real" reaction, but since all normal
    +1004 %reactions have coefficient -1/1 or -10/10 it is a compact way of writing
    +1005 %it
    +1006 function S=addTransportToS(S,nRxns,nER,nMets,nEM,nComps,metA,metB)
    +1007 mets=[metA;metB];
    +1008 %Find the current compartments for the metabolites
    +1009 comps=ceil((mets-nEM)/((size(S,1)-nEM)/nComps));
    +1010 
    +1011 if sum(comps==1)~=1
    +1012     EM='Tried to create a transport reaction from a non-default compartment';
    +1013     error('RAVEN:badInput', '%s', EM);
    +1014 end
    +1015 
    +1016 %Calculate the reaction index
    +1017 rIndex=(nER+nRxns*nComps)+mets(comps~=1)-nEM-nMets;
    +1018 
    +1019 S(mets,rIndex)=2;
    +1020 end
    +1021 
    +1022 %Scores a network based on the localization of the genes and the number of
    +1023 %transporter reactions used
    +1024 function [score, geneScore, transportCost]=scoreLocalization(S,g2c,GSS,transportCost)
    +1025 [I, J]=find(g2c);
    +1026 geneScore=sum(GSS.scores(sub2ind(size(g2c),I,J)));
    +1027 [I, ~]=find(S==2);
    +1028 I=unique(I);
    +1029 transportCost=sum(transportCost(I));
    +1030 score=geneScore-transportCost;
    +1031 end
    +1032 
    +1033 % To avoid dependency on stats toolbox, use this alternative implementation
    +1034 % of randsample, source:
    +1035 % https://github.com/gpeyre/numerical-tours/blob/dacee30081c04ef5f67b26b387ead85f2b193af9/matlab/toolbox_signal/randsample.m
    +1036 function y = randsample(n, k, replace, w)
    +1037 %RANDSAMPLE Random sample, with or without replacement.
    +1038 %   Y = RANDSAMPLE(N,K) returns Y as a vector of K values sampled uniformly
    +1039 %   at random, without replacement, from the integers 1:N.
    +1040 %
    +1041 %   Y = RANDSAMPLE(POPULATION,K) returns K values sampled uniformly at
    +1042 %   random, without replacement, from the values in the vector POPULATION.
    +1043 %
    +1044 %   Y = RANDSAMPLE(...,REPLACE) returns a sample taken with replacement if
    +1045 %   REPLACE is true, or without replacement if REPLACE is false (the default).
    +1046 %
    +1047 %   Y = RANDSAMPLE(...,true,W) returns a weighted sample, using positive
    +1048 %   weights W, taken with replacement.  W is often a vector of probabilities.
    +1049 %   This function does not support weighted sampling without replacement.
    +1050 %
    +1051 %   Example:  Generate a random sequence of the characters ACGT, with
    +1052 %   replacement, according to specified probabilities.
    +1053 %
    +1054 %      R = randsample("ACGT",48,true,[0.15 0.35 0.35 0.15])
    +1055 %
    +1056 %   See also RAND, RANDPERM.
    +1057 
    +1058 %   Copyright 1993-2008 The MathWorks, Inc.
    +1059 %   $Revision: 1.1.4.3 $  $Date: 2008/12/01 08:09:34 $
    +1060 
    +1061 if nargin < 2
    +1062     error('stats:randsample:TooFewInputs','Requires two input arguments.');
    +1063 elseif numel(n) == 1
    +1064     population = [];
    +1065 else
    +1066     population = n;
    +1067     n = numel(population);
    +1068     if length(population)~=n
    +1069        error('stats:randsample:BadPopulation','POPULATION must be a vector.');
    +1070     end
    +1071 end
    +1072 
    +1073 if nargin < 3
    +1074     replace = false;
    +1075 end
    +1076 
    +1077 if nargin < 4
    +1078     w = [];
    +1079 elseif ~isempty(w)
    +1080     if length(w) ~= n
    +1081         if isempty(population)
    +1082             error('stats:randsample:InputSizeMismatch',...
    +1083                   'W must have length equal to N.');
    +1084         else
    +1085             error('stats:randsample:InputSizeMismatch',...
    +1086                   'W must have the same length as the population.');
    +1087         end
    +1088     else
    +1089         p = w(:)' / sum(w);
    +1090     end
    +1091 end
    +1092 
    +1093 switch replace
    +1094 
    +1095 % Sample with replacement
    +1096 case {true, 'true', 1}
    +1097     if isempty(w)
    +1098         y = ceil(n .* rand(k,1));
    +1099     else
    +1100         [dum, y] = histc(rand(k,1),[0 cumsum(p)]);
    +1101     end
    +1102 
    +1103 % Sample without replacement
    +1104 case {false, 'false', 0}
    +1105     if k > n
    +1106         if isempty(population)
    +1107             error('stats:randsample:SampleTooLarge',...
    +1108         'K must be less than or equal to N for sampling without replacement.');
    +1109         else
    +1110             error('stats:randsample:SampleTooLarge',...
    +1111                   'K must be less than or equal to the population size.');
    +1112         end
    +1113     end
    +1114 
    +1115     if isempty(w)
    +1116         % If the sample is a sizeable fraction of the population,
    +1117         % just randomize the whole population (which involves a full
    +1118         % sort of n random values), and take the first k.
    +1119         if 4*k > n
    +1120             rp = randperm(n);
    +1121             y = rp(1:k);
    +1122 
    +1123         % If the sample is a small fraction of the population, a full sort
    +1124         % is wasteful.  Repeatedly sample with replacement until there are
    +1125         % k unique values.
    +1126         else
    +1127             x = zeros(1,n); % flags
    +1128             sumx = 0;
    +1129             while sumx < k
    +1130                 x(ceil(n * rand(1,k-sumx))) = 1; % sample w/replacement
    +1131                 sumx = sum(x); % count how many unique elements so far
    +1132             end
    +1133             y = find(x > 0);
    +1134             y = y(randperm(k));
    +1135         end
    +1136     else
    +1137         error('stats:randsample:NoWeighting',...
    +1138               'Weighted sampling without replacement is not supported.');
    +1139     end
    +1140 otherwise
    +1141     error('stats:randsample:BadReplaceValue',...
    +1142           'REPLACE must be either true or false.');
    +1143 end
    +1144 
    +1145 if ~isempty(population)
    +1146     y = population(y);
    +1147 else
    +1148     y = y(:);
    +1149 end
    +1150 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/m2html.css b/docs/html/m2html.css similarity index 100% rename from doc/m2html.css rename to docs/html/m2html.css diff --git a/docs/html/manipulation/addExchangeRxns.html b/docs/html/manipulation/addExchangeRxns.html new file mode 100644 index 00000000..05cfefc3 --- /dev/null +++ b/docs/html/manipulation/addExchangeRxns.html @@ -0,0 +1,206 @@ + + + + Description of addExchangeRxns + + + + + + + + + +
    Home > manipulation > addExchangeRxns.m
    + + + +

    addExchangeRxns +

    + +

    PURPOSE ^

    +
    addExchangeRxns Add exchange reactions for some metabolites.
    + +

    SYNOPSIS ^

    +
    function [model, addedRxns]=addExchangeRxns(model,reactionType,varargin)
    + +

    DESCRIPTION ^

    +
     addExchangeRxns  Add exchange reactions for some metabolites.
    +
    + The function builds the stoichiometric columns directly from the metabolite list,
    + without the equation parsing of addRxns, which makes it faster for adding exchange
    + reactions. New reactions are
    + named "metName exchange (TYPE)" and have the ids "EXC_TYPE_METID", where TYPE is IN,
    + OUT or BOTH according to reactionType.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + reactionType : char
    +     the type of reactions to add:
    +
    +     - 'in'   : input reactions
    +     - 'out'  : output reactions
    +     - 'both' : reversible input and output reactions. The positive direction
    +       corresponds to output
    +
    + Name-Value Arguments
    + --------------------
    + mets : cell or logical or double
    +     a cell array of metabolite ids, a logical vector with one element per metabolite of
    +     the model, or a vector of metabolite indexes to add reactions for
    +     (default model.mets).
    +
    + Returns
    + -------
    + model : struct
    +     updated model structure.
    + addedRxns : cell
    +     ids of the added reactions.
    +
    + Examples
    + --------
    +     [model, addedRxns] = addExchangeRxns(model, reactionType, mets);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function [model, addedRxns]=addExchangeRxns(model,reactionType,varargin)
    +0002 % addExchangeRxns  Add exchange reactions for some metabolites.
    +0003 %
    +0004 % The function builds the stoichiometric columns directly from the metabolite list,
    +0005 % without the equation parsing of addRxns, which makes it faster for adding exchange
    +0006 % reactions. New reactions are
    +0007 % named "metName exchange (TYPE)" and have the ids "EXC_TYPE_METID", where TYPE is IN,
    +0008 % OUT or BOTH according to reactionType.
    +0009 %
    +0010 % Parameters
    +0011 % ----------
    +0012 % model : struct
    +0013 %     a model structure.
    +0014 % reactionType : char
    +0015 %     the type of reactions to add:
    +0016 %
    +0017 %     - 'in'   : input reactions
    +0018 %     - 'out'  : output reactions
    +0019 %     - 'both' : reversible input and output reactions. The positive direction
    +0020 %       corresponds to output
    +0021 %
    +0022 % Name-Value Arguments
    +0023 % --------------------
    +0024 % mets : cell or logical or double
    +0025 %     a cell array of metabolite ids, a logical vector with one element per metabolite of
    +0026 %     the model, or a vector of metabolite indexes to add reactions for
    +0027 %     (default model.mets).
    +0028 %
    +0029 % Returns
    +0030 % -------
    +0031 % model : struct
    +0032 %     updated model structure.
    +0033 % addedRxns : cell
    +0034 %     ids of the added reactions.
    +0035 %
    +0036 % Examples
    +0037 % --------
    +0038 %     [model, addedRxns] = addExchangeRxns(model, reactionType, mets);
    +0039 
    +0040 p=parseRAVENargs(varargin, {'mets',[]});
    +0041 mets=p.mets;
    +0042 if isempty(mets)
    +0043     mets=model.mets;
    +0044 elseif ~islogical(mets) && ~isnumeric(mets)
    +0045     mets=convertCharArray(mets);
    +0046 end
    +0047 J=getIndexes(model,mets,'mets',false);
    +0048 mets=model.mets(J);
    +0049 
    +0050 reactionType=char(upper(reactionType));
    +0051 
    +0052 %Production is positive for OUT and BOTH
    +0053 if strcmp(reactionType,'IN')
    +0054     I=speye(numel(model.mets));
    +0055 else
    +0056     I=speye(numel(model.mets))*-1;
    +0057 end
    +0058 I=I(:,J);
    +0059 
    +0060 %Add an I matrix which corresponds to production of all metabolites
    +0061 model.S=[model.S I];
    +0062 if strcmp(reactionType,'BOTH')
    +0063     model.lb=[model.lb;ones(numel(J),1)*-1000];
    +0064     model.rev=[model.rev;ones(numel(J),1)];
    +0065 else
    +0066     model.lb=[model.lb;zeros(numel(J),1)];
    +0067     model.rev=[model.rev;zeros(numel(J),1)];
    +0068 end
    +0069 model.ub=[model.ub;ones(numel(J),1)*1000];
    +0070 model.c=[model.c;zeros(numel(J),1)];
    +0071 
    +0072 filler=cell(numel(J),1);
    +0073 filler(:)={''};
    +0074 addedRxns=strcat('EXC_',reactionType,'_',mets);
    +0075 addedRxnNames=strcat(model.metNames(J),' exchange (',reactionType,')');
    +0076 model.rxns=[model.rxns;addedRxns];
    +0077 model.rxnNames=[model.rxnNames;addedRxnNames];
    +0078 
    +0079 if isfield(model,'eccodes')
    +0080     model.eccodes=[model.eccodes;filler];
    +0081 end
    +0082 if isfield(model,'subSystems')
    +0083     fillerSub = filler;
    +0084     if any(cellfun(@(x) iscell(x), model.subSystems))
    +0085         fillerSub(:)={{''}};
    +0086     end
    +0087     model.subSystems=[model.subSystems;fillerSub];
    +0088 end
    +0089 if isfield(model,'grRules')
    +0090     model.grRules=[model.grRules;filler];
    +0091 end
    +0092 if isfield(model,'rxnFrom')
    +0093     model.rxnFrom=[model.rxnFrom;filler];
    +0094 end
    +0095 if isfield(model,'rxnMiriams')
    +0096     model.rxnMiriams=[model.rxnMiriams;filler];
    +0097 end
    +0098 if isfield(model,'rxnGeneMat')
    +0099     model.rxnGeneMat=[model.rxnGeneMat;sparse(numel(J),numel(model.genes))];
    +0100 end
    +0101 if isfield(model,'rxnComps')
    +0102     model.rxnComps=[model.rxnComps;ones(numel(J),1)];
    +0103     fprintf('NOTE: The exchange reactions are assigned to the first compartment\n');
    +0104 end
    +0105 if isfield(model,'rxnNotes')
    +0106     model.rxnNotes=[model.rxnNotes;filler];
    +0107 end
    +0108 if isfield(model,'rxnReferences')
    +0109     model.rxnReferences=[model.rxnReferences;filler];
    +0110 end
    +0111 if isfield(model,'rxnConfidenceScores')
    +0112     model.rxnConfidenceScores=[model.rxnConfidenceScores;NaN(numel(J),1)];
    +0113 end
    +0114 if isfield(model,'rxnDeltaG')
    +0115     model.rxnDeltaG=[model.rxnDeltaG;NaN(numel(J),1)];
    +0116 end
    +0117 if isfield(model,'pwys')
    +0118     model.pwys=[model.pwys;filler];
    +0119 end
    +0120 if isfield(model,'spontaneous')
    +0121     model.spontaneous=[model.spontaneous;false(numel(J),1)];
    +0122 end
    +0123 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/addGenesRaven.html b/docs/html/manipulation/addGenesRaven.html new file mode 100644 index 00000000..e490b69b --- /dev/null +++ b/docs/html/manipulation/addGenesRaven.html @@ -0,0 +1,238 @@ + + + + Description of addGenesRaven + + + + + + + + + +
    Home > manipulation > addGenesRaven.m
    + + + +

    addGenesRaven +

    + +

    PURPOSE ^

    +
    addGenesRaven Add genes to a model.
    + +

    SYNOPSIS ^

    +
    function newModel=addGenesRaven(model,genesToAdd)
    + +

    DESCRIPTION ^

    +
     addGenesRaven  Add genes to a model.
    +
    + The function checks neither MIRIAM formats nor forbidden characters in gene ids.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + genesToAdd : struct
    +     the genes to add, which can have the following fields:
    +
    +     - genes : cell array with unique strings that identify each gene.
    +       The characters allowed in SBML ids (mainly a-z, 0-9 and "_") are the
    +       valid ones, but the function does not check them; they matter only for
    +       export to SBML
    +     - geneShortNames : cell array of gene abbreviations (optional,
    +       default "")
    +     - geneMiriams : cell array with MIRIAM structures (optional,
    +       default [])
    +     - proteins : cell array of the protein names associated with each gene
    +       (optional, default "")
    +
    + Returns
    + -------
    + newModel : struct
    +     an updated model structure.
    +
    + Examples
    + --------
    +     newModel = addGenesRaven(model, genesToAdd);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    • addRxns addRxns Add reactions to a model.
    • changeGrRules changeGrRules Change the grRules of multiple reactions.
    + + + + +

    SOURCE CODE ^

    +
    0001 function newModel=addGenesRaven(model,genesToAdd)
    +0002 % addGenesRaven  Add genes to a model.
    +0003 %
    +0004 % The function checks neither MIRIAM formats nor forbidden characters in gene ids.
    +0005 %
    +0006 % Parameters
    +0007 % ----------
    +0008 % model : struct
    +0009 %     a model structure.
    +0010 % genesToAdd : struct
    +0011 %     the genes to add, which can have the following fields:
    +0012 %
    +0013 %     - genes : cell array with unique strings that identify each gene.
    +0014 %       The characters allowed in SBML ids (mainly a-z, 0-9 and "_") are the
    +0015 %       valid ones, but the function does not check them; they matter only for
    +0016 %       export to SBML
    +0017 %     - geneShortNames : cell array of gene abbreviations (optional,
    +0018 %       default "")
    +0019 %     - geneMiriams : cell array with MIRIAM structures (optional,
    +0020 %       default [])
    +0021 %     - proteins : cell array of the protein names associated with each gene
    +0022 %       (optional, default "")
    +0023 %
    +0024 % Returns
    +0025 % -------
    +0026 % newModel : struct
    +0027 %     an updated model structure.
    +0028 %
    +0029 % Examples
    +0030 % --------
    +0031 %     newModel = addGenesRaven(model, genesToAdd);
    +0032 
    +0033 newModel=model;
    +0034 
    +0035 if isempty(genesToAdd)
    +0036     return;
    +0037 end
    +0038 
    +0039 %Check some stuff regarding the required fields
    +0040 if ~isfield(genesToAdd,'genes')
    +0041     EM='genes is a required field in genesToAdd';
    +0042     error('RAVEN:badInput', '%s', EM);
    +0043 else
    +0044     genesToAdd.genes=convertCharArray(genesToAdd.genes);
    +0045 end
    +0046 
    +0047 nOldGenes=numel(model.genes);
    +0048 largeFiller=cell(nOldGenes,1);
    +0049 largeFiller(:)={''};
    +0050 
    +0051 %Check that no gene ids are already present in the model
    +0052 I=ismember(genesToAdd.genes,model.genes);
    +0053 if all(I)
    +0054     warning('All genes in genesToAdd.genes are already present in model.genes');
    +0055     return
    +0056 elseif any(I)
    +0057     existingGenes=strjoin(genesToAdd.genes(I), ', ');
    +0058     warning(['The following genes are already present in model.genes and will therefore not be added: ', existingGenes])
    +0059     genesToAdd.genes(I)=[];
    +0060     if isfield(genesToAdd,'geneShortNames')
    +0061         genesToAdd.geneShortNames(I)=[];
    +0062     end
    +0063     if isfield(genesToAdd,'proteins')
    +0064         genesToAdd.proteins(I)=[];
    +0065     end
    +0066     if isfield(genesToAdd,'geneMiriams')
    +0067         genesToAdd.geneMiriams(I)=[];
    +0068     end
    +0069     if isfield(genesToAdd,'geneComps')
    +0070         genesToAdd.geneComps(I)=[];
    +0071     end
    +0072 end
    +0073 newModel.genes=[newModel.genes;genesToAdd.genes(:)];
    +0074 
    +0075 %Number of genes actually being added, now that any already-present genes
    +0076 %have been trimmed out; the per-field length checks below compare against
    +0077 %this
    +0078 nGenes=numel(genesToAdd.genes);
    +0079 filler=cell(nGenes,1);
    +0080 filler(:)={''};
    +0081 
    +0082 %Some more checks and if they pass then add each field to the structure
    +0083 if isfield(genesToAdd,'geneShortNames')
    +0084     genesToAdd.geneShortNames=convertCharArray(genesToAdd.geneShortNames);
    +0085     if numel(genesToAdd.geneShortNames)~=nGenes
    +0086         EM='genesToAdd.geneShortNames must have the same number of elements as genesToAdd.genes';
    +0087         error('RAVEN:badInput', '%s', EM);
    +0088     end
    +0089     %Add empty field if it does not exist
    +0090     if ~isfield(newModel,'geneShortNames')
    +0091         newModel.geneShortNames=largeFiller;
    +0092     end
    +0093     newModel.geneShortNames=[newModel.geneShortNames;genesToAdd.geneShortNames(:)];
    +0094 else
    +0095     %Add empty strings if the structure is in the model
    +0096     if isfield(newModel,'geneShortNames')
    +0097         newModel.geneShortNames=[newModel.geneShortNames;filler];
    +0098     end
    +0099 end
    +0100 if isfield(genesToAdd,'proteins')
    +0101     genesToAdd.proteins=convertCharArray(genesToAdd.proteins);
    +0102     if numel(genesToAdd.proteins)~=nGenes
    +0103         EM='genesToAdd.proteins must have the same number of elements as genesToAdd.genes';
    +0104         error('RAVEN:badInput', '%s', EM);
    +0105     end
    +0106     %Add empty field if it does not exist
    +0107     if ~isfield(newModel,'proteins')
    +0108         newModel.proteins=largeFiller;
    +0109     end
    +0110     newModel.proteins=[newModel.proteins;genesToAdd.proteins(:)];
    +0111 else
    +0112     %Add empty strings if the structure is in the model
    +0113     if isfield(newModel,'proteins')
    +0114         newModel.proteins=[newModel.proteins;filler];
    +0115     end
    +0116 end
    +0117 
    +0118 
    +0119 %Do not check the type of geneMiriams
    +0120 if isfield(genesToAdd,'geneMiriams')
    +0121     if numel(genesToAdd.geneMiriams)~=nGenes
    +0122         EM='genesToAdd.geneMiriams must have the same number of elements as genesToAdd.genes';
    +0123         error('RAVEN:badInput', '%s', EM);
    +0124     end
    +0125     %Add empty field if it does not exist
    +0126     if ~isfield(newModel,'geneMiriams')
    +0127         newModel.geneMiriams=cell(nOldGenes,1);
    +0128     end
    +0129     newModel.geneMiriams=[newModel.geneMiriams;genesToAdd.geneMiriams(:)];
    +0130 else
    +0131     if isfield(newModel,'geneMiriams')
    +0132         newModel.geneMiriams=[newModel.geneMiriams;cell(nGenes,1)];
    +0133     end
    +0134 end
    +0135 
    +0136 if isfield(genesToAdd,'geneComps')
    +0137     if numel(genesToAdd.geneComps)~=nGenes
    +0138         EM='genesToAdd.geneComps must have the same number of elements as genesToAdd.genes';
    +0139         error('RAVEN:badInput', '%s', EM);
    +0140     end
    +0141     %Add empty field if it does not exist
    +0142     if ~isfield(newModel,'geneComps')
    +0143         newModel.geneComps=ones(nOldGenes,1);
    +0144         EM='Adding genes with compartment information to a model without such information. All existing genes will be assigned to the first compartment';
    +0145         warning('RAVEN:warning', '%s', EM);
    +0146     end
    +0147     newModel.geneComps=[newModel.geneComps;genesToAdd.geneComps(:)];
    +0148 else
    +0149     if isfield(newModel,'geneComps')
    +0150         newModel.geneComps=[newModel.geneComps;ones(nGenes,1)];
    +0151         fprintf('NOTE: The added genes will be assigned to the first compartment\n');
    +0152     end
    +0153 end
    +0154 
    +0155 if isfield(newModel,'geneFrom')
    +0156     newModel.geneFrom=[newModel.geneFrom;filler];
    +0157 end
    +0158 
    +0159 if isfield(newModel,'rxnGeneMat')
    +0160     newModel.rxnGeneMat=[newModel.rxnGeneMat,zeros(length(newModel.rxns),nGenes)];
    +0161 end
    +0162 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/addMets.html b/docs/html/manipulation/addMets.html new file mode 100644 index 00000000..98f47576 --- /dev/null +++ b/docs/html/manipulation/addMets.html @@ -0,0 +1,485 @@ + + + + Description of addMets + + + + + + + + + +
    Home > manipulation > addMets.m
    + + + +

    addMets +

    + +

    PURPOSE ^

    +
    addMets Add metabolites to a model.
    + +

    SYNOPSIS ^

    +
    function newModel=addMets(model,metsToAdd,varargin)
    + +

    DESCRIPTION ^

    +
     addMets  Add metabolites to a model.
    +
    + The function checks neither MIRIAM formats nor forbidden characters in metabolite ids.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + metsToAdd : struct
    +     the metabolite structure, which can have the following fields:
    +
    +     - mets : cell array with unique strings that identify each
    +       metabolite (optional, default: ids numbered with the prefix defined
    +       below)
    +     - metNames : cell array with the names of each metabolite (optional if
    +       mets is given, default: the ids)
    +     - compartments : cell array with the compartment of each metabolite.
    +       Each entry must match model.comps. A single string, or a cell array
    +       with one entry, assigns all metabolites to that compartment
    +     - b : Nx1 or Nx2 matrix with equality constraints for each
    +       metabolite (optional, default 0)
    +     - unconstrained : vector describing if each metabolite is an exchange
    +       metabolite (1) or not (0) (optional, default 0)
    +     - inchis : cell array with InChI strings (optional, default "")
    +     - metSmiles : cell array with SMILES strings (optional, default "")
    +     - metFormulas : cell array with the formulas (optional, default "")
    +     - metMiriams : cell array with MIRIAM structures (optional,
    +       default [])
    +     - metCharges : metabolite charge (optional, default NaN)
    +     - metDeltaG : Gibbs free energy of formation at biochemical standard
    +       condition in kJ/mol (optional, default NaN)
    +     - metNotes : cell array with metabolite notes as strings (optional,
    +       default "")
    +
    + Name-Value Arguments
    + --------------------
    + copyInfo : logical
    +     true to copy the available annotation from the metabolite with the same name in
    +     another compartment of the model (default true).
    + prefix : char
    +     prefix of the generated metabolite ids when metsToAdd.mets is not specified. If ids
    +     with the prefix exist in the model, the numbering starts at the highest
    +     existing number plus 1 (default "m_").
    +
    + Returns
    + -------
    + newModel : struct
    +     an updated model structure.
    +
    + Examples
    + --------
    +     newModel = addMets(model, metsToAdd, copyInfo, prefix);
    +
    + Notes
    + -----
    + When multiple metabolites are added at once, metMiriams is a cell array with one MIRIAM
    + structure per metabolite (example with ChEBI and KEGG):
    +
    +     metsToAdd.metMiriams{1} = struct("name",{{"chebi";"kegg.compound"}},...
    +         "value",{{"CHEBI:18072";"C11821"}});
    +     metsToAdd.metMiriams{2} = struct("name",{{"chebi";"kegg.compound"}},...
    +         "value",{{"CHEBI:31132";"C12248"}});
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • generateNewIds generateNewIds Generate a list of new metabolite or reaction ids.
    +This function is called by: +
      +
    • addRxns addRxns Add reactions to a model.
    • addRxnsGenesMets addRxnsGenesMets Copy reactions from a source model into another model.
    • addTransport addTransport Add transport reactions between compartments.
    + + + + +

    SOURCE CODE ^

    +
    0001 function newModel=addMets(model,metsToAdd,varargin)
    +0002 % addMets  Add metabolites to a model.
    +0003 %
    +0004 % The function checks neither MIRIAM formats nor forbidden characters in metabolite ids.
    +0005 %
    +0006 % Parameters
    +0007 % ----------
    +0008 % model : struct
    +0009 %     a model structure.
    +0010 % metsToAdd : struct
    +0011 %     the metabolite structure, which can have the following fields:
    +0012 %
    +0013 %     - mets : cell array with unique strings that identify each
    +0014 %       metabolite (optional, default: ids numbered with the prefix defined
    +0015 %       below)
    +0016 %     - metNames : cell array with the names of each metabolite (optional if
    +0017 %       mets is given, default: the ids)
    +0018 %     - compartments : cell array with the compartment of each metabolite.
    +0019 %       Each entry must match model.comps. A single string, or a cell array
    +0020 %       with one entry, assigns all metabolites to that compartment
    +0021 %     - b : Nx1 or Nx2 matrix with equality constraints for each
    +0022 %       metabolite (optional, default 0)
    +0023 %     - unconstrained : vector describing if each metabolite is an exchange
    +0024 %       metabolite (1) or not (0) (optional, default 0)
    +0025 %     - inchis : cell array with InChI strings (optional, default "")
    +0026 %     - metSmiles : cell array with SMILES strings (optional, default "")
    +0027 %     - metFormulas : cell array with the formulas (optional, default "")
    +0028 %     - metMiriams : cell array with MIRIAM structures (optional,
    +0029 %       default [])
    +0030 %     - metCharges : metabolite charge (optional, default NaN)
    +0031 %     - metDeltaG : Gibbs free energy of formation at biochemical standard
    +0032 %       condition in kJ/mol (optional, default NaN)
    +0033 %     - metNotes : cell array with metabolite notes as strings (optional,
    +0034 %       default "")
    +0035 %
    +0036 % Name-Value Arguments
    +0037 % --------------------
    +0038 % copyInfo : logical
    +0039 %     true to copy the available annotation from the metabolite with the same name in
    +0040 %     another compartment of the model (default true).
    +0041 % prefix : char
    +0042 %     prefix of the generated metabolite ids when metsToAdd.mets is not specified. If ids
    +0043 %     with the prefix exist in the model, the numbering starts at the highest
    +0044 %     existing number plus 1 (default "m_").
    +0045 %
    +0046 % Returns
    +0047 % -------
    +0048 % newModel : struct
    +0049 %     an updated model structure.
    +0050 %
    +0051 % Examples
    +0052 % --------
    +0053 %     newModel = addMets(model, metsToAdd, copyInfo, prefix);
    +0054 %
    +0055 % Notes
    +0056 % -----
    +0057 % When multiple metabolites are added at once, metMiriams is a cell array with one MIRIAM
    +0058 % structure per metabolite (example with ChEBI and KEGG):
    +0059 %
    +0060 %     metsToAdd.metMiriams{1} = struct("name",{{"chebi";"kegg.compound"}},...
    +0061 %         "value",{{"CHEBI:18072";"C11821"}});
    +0062 %     metsToAdd.metMiriams{2} = struct("name",{{"chebi";"kegg.compound"}},...
    +0063 %         "value",{{"CHEBI:31132";"C12248"}});
    +0064 
    +0065 p=parseRAVENargs(varargin, {'copyInfo',true; 'prefix','m_'});
    +0066 copyInfo=p.copyInfo;
    +0067 prefix=char(p.prefix);
    +0068 
    +0069 newModel=model;
    +0070 
    +0071 if isempty(metsToAdd)
    +0072     return;
    +0073 end
    +0074 
    +0075 %Check some stuff regarding the required fields
    +0076 if ~isfield(metsToAdd,'mets')
    +0077     metsToAdd.metNames=convertCharArray(metsToAdd.metNames);
    +0078     metsToAdd.mets=generateNewIds(newModel,'mets',prefix,numel(metsToAdd.metNames));
    +0079 else
    +0080     metsToAdd.mets=convertCharArray(metsToAdd.mets);
    +0081 end
    +0082 if ~isfield(metsToAdd,'metNames')
    +0083     metsToAdd.metNames=metsToAdd.mets;
    +0084 else
    +0085     metsToAdd.metNames=convertCharArray(metsToAdd.metNames);
    +0086 end
    +0087 if ~isfield(metsToAdd,'compartments')
    +0088     EM='compartments is a required field in metsToAdd';
    +0089     error('RAVEN:badInput', '%s', EM);
    +0090 else
    +0091     metsToAdd.compartments=convertCharArray(metsToAdd.compartments);
    +0092     %If only one compartment is given, assume it is for all metabolites
    +0093     if numel(metsToAdd.compartments)==1 && numel(metsToAdd.mets)>1
    +0094         temp=cell(numel(metsToAdd.mets),1);
    +0095         temp(:)=metsToAdd.compartments;
    +0096         metsToAdd.compartments=temp;
    +0097     end
    +0098 end
    +0099 
    +0100 %Number of metabolites
    +0101 nMets=numel(metsToAdd.mets);
    +0102 nOldMets=numel(model.mets);
    +0103 filler=cell(nMets,1);
    +0104 filler(:)={''};
    +0105 largeFiller=cell(nOldMets,1);
    +0106 largeFiller(:)={''};
    +0107 
    +0108 %Check that no metabolite ids are already present in the model
    +0109 I=ismember(metsToAdd.mets,model.mets);
    +0110 if any(I)
    +0111     error('One or more elements in metsToAdd.mets are already present in model.mets: %s',...
    +0112         strjoin(metsToAdd.mets(I),', '));
    +0113 else
    +0114     newModel.mets=[newModel.mets;metsToAdd.mets(:)];
    +0115 end
    +0116 
    +0117 %Check that all the compartments could be found
    +0118 [I, compMap]=ismember(metsToAdd.compartments,model.comps);
    +0119 if ~all(I)
    +0120     EM='metsToAdd.compartments must match model.comps';
    +0121     error('RAVEN:badInput', '%s', EM);
    +0122 end
    +0123 
    +0124 %Check that the metabolite names are not present in the same compartment.
    +0125 %Not the neatest way maybe..
    +0126 t1=strcat(metsToAdd.metNames(:),'***',metsToAdd.compartments(:));
    +0127 t2=strcat(model.metNames,'***',model.comps(model.metComps));
    +0128 if any(ismember(t1,t2))
    +0129     EM='One or more elements in metsToAdd.metNames already exist in the same compartments as the one it is being added to';
    +0130     error('RAVEN:badInput', '%s', EM);
    +0131 end
    +0132 
    +0133 %Some more checks and if they pass then add each field to the structure
    +0134 if numel(metsToAdd.metNames)~=nMets
    +0135     EM='metsToAdd.metNames must have the same number of elements as metsToAdd.mets';
    +0136     error('RAVEN:badInput', '%s', EM);
    +0137 else
    +0138     newModel.metNames=[newModel.metNames;metsToAdd.metNames(:)];
    +0139 end
    +0140 
    +0141 if numel(compMap)~=nMets
    +0142     EM='metsToAdd.compartments must have the same number of elements as metsToAdd.mets';
    +0143     error('RAVEN:badInput', '%s', EM);
    +0144 else
    +0145     newModel.metComps=[newModel.metComps;compMap(:)];
    +0146 end
    +0147 
    +0148 if isfield(metsToAdd,'b')
    +0149     if size(metsToAdd.b,1)~=nMets
    +0150         EM='metsToAdd.b must have the same number of elements as metsToAdd.mets';
    +0151         error('RAVEN:badInput', '%s', EM);
    +0152     else
    +0153         %Add empty field if it does not exist
    +0154         if ~isfield(newModel,'b')
    +0155             newModel.b=zeros(nOldMets,1);
    +0156         end
    +0157         
    +0158         %If the original is only one vector
    +0159         if size(metsToAdd.b,2)>size(newModel.b,2)
    +0160             newModel.b=[newModel.b newModel.b];
    +0161         end
    +0162         %Add the new ones
    +0163         newModel.b=[newModel.b;metsToAdd.b];
    +0164     end
    +0165 else
    +0166     if isfield(newModel,'b')
    +0167         %Add the default
    +0168         newModel.b=[newModel.b;zeros(nMets,size(newModel.b,2))];
    +0169     end
    +0170 end
    +0171 
    +0172 if isfield(metsToAdd,'unconstrained')
    +0173     if numel(metsToAdd.unconstrained)~=nMets
    +0174         EM='metsToAdd.unconstrained must have the same number of elements as metsToAdd.mets';
    +0175         error('RAVEN:badInput', '%s', EM);
    +0176     else
    +0177         %Add empty field if it does not exist
    +0178         if ~isfield(newModel,'unconstrained')
    +0179             newModel.unconstrained=zeros(nOldMets,1);
    +0180         end
    +0181         
    +0182         %Add the new ones
    +0183         newModel.unconstrained=[newModel.unconstrained;metsToAdd.unconstrained(:)];
    +0184     end
    +0185 else
    +0186     if isfield(newModel,'unconstrained')
    +0187         %Add the default
    +0188         newModel.unconstrained=[newModel.unconstrained;zeros(nMets,1)];
    +0189     end
    +0190 end
    +0191 
    +0192 if isfield(metsToAdd,'inchis')
    +0193     metsToAdd.inchis=convertCharArray(metsToAdd.inchis);
    +0194     if numel(metsToAdd.inchis)~=nMets
    +0195         EM='metsToAdd.inchis must have the same number of elements as metsToAdd.mets';
    +0196         error('RAVEN:badInput', '%s', EM);
    +0197     end
    +0198     %Add empty field if it does not exist
    +0199     if ~isfield(newModel,'inchis')
    +0200         newModel.inchis=largeFiller;
    +0201     end
    +0202     newModel.inchis=[newModel.inchis;metsToAdd.inchis(:)];
    +0203 else
    +0204     %Add empty strings if the structure is in the model
    +0205     if isfield(newModel,'inchis')
    +0206         newModel.inchis=[newModel.inchis;filler];
    +0207     end
    +0208 end
    +0209 
    +0210 
    +0211 if isfield(metsToAdd,'metSmiles')
    +0212     metsToAdd.metSmiles=convertCharArray(metsToAdd.metSmiles);
    +0213     if numel(metsToAdd.metSmiles)~=nMets
    +0214         EM='metsToAdd.metSmiles must have the same number of elements as metsToAdd.mets';
    +0215         error('RAVEN:badInput', '%s', EM);
    +0216     end
    +0217     %Add empty field if it does not exist
    +0218     if ~isfield(newModel,'metSmiles')
    +0219         newModel.metSmiles=largeFiller;
    +0220     end
    +0221     newModel.metSmiles=[newModel.metSmiles;metsToAdd.metSmiles(:)];
    +0222 else
    +0223     %Add empty strings if the structure is in the model
    +0224     if isfield(newModel,'metSmiles')
    +0225         newModel.metSmiles=[newModel.metSmiles;filler];
    +0226     end
    +0227 end
    +0228 
    +0229 if isfield(metsToAdd,'metFormulas')
    +0230     metsToAdd.metFormulas=convertCharArray(metsToAdd.metFormulas);
    +0231     if numel(metsToAdd.metFormulas)~=nMets
    +0232         EM='metsToAdd.metFormulas must have the same number of elements as metsToAdd.mets';
    +0233         error('RAVEN:badInput', '%s', EM);
    +0234     end
    +0235     %Add empty field if it does not exist
    +0236     if ~isfield(newModel,'metFormulas')
    +0237         newModel.metFormulas=largeFiller;
    +0238     end
    +0239     newModel.metFormulas=[newModel.metFormulas;metsToAdd.metFormulas(:)];
    +0240 else
    +0241     %Add default
    +0242     if isfield(newModel,'metFormulas')
    +0243         newModel.metFormulas=[newModel.metFormulas;filler];
    +0244     end
    +0245 end
    +0246 
    +0247 if isfield(metsToAdd,'metCharges')
    +0248     if numel(metsToAdd.metCharges)~=nMets
    +0249         EM='metsToAdd.metCharges must have the same number of elements as metsToAdd.mets';
    +0250         error('RAVEN:badInput', '%s', EM);
    +0251     end
    +0252     if ~isnumeric(metsToAdd.metCharges)
    +0253         EM='metsToAdd.metCharges must be of type "double"';
    +0254         error('RAVEN:badInput', '%s', EM);
    +0255     end
    +0256     if ~isfield(newModel,'metCharges')
    +0257         newModel.metCharges=NaN(numel(largeFiller),1);
    +0258     end
    +0259     newModel.metCharges=[newModel.metCharges;metsToAdd.metCharges(:)];
    +0260 else
    +0261     %Add default
    +0262     if isfield(newModel,'metCharges')
    +0263         newModel.metCharges=[newModel.metCharges;NaN(numel(filler),1)];
    +0264     end
    +0265 end
    +0266 
    +0267 if isfield(metsToAdd,'metDeltaG')
    +0268     if numel(metsToAdd.metDeltaG)~=nMets
    +0269         EM='metsToAdd.metDeltaG must have the same number of elements as metsToAdd.mets';
    +0270         error('RAVEN:badInput', '%s', EM);
    +0271     end
    +0272     if ~isnumeric(metsToAdd.metDeltaG)
    +0273         EM='metsToAdd.metDeltaG must be of type "double"';
    +0274         error('RAVEN:badInput', '%s', EM);
    +0275     end
    +0276     if ~isfield(newModel,'metDeltaG')
    +0277         newModel.metDeltaG=NaN(numel(largeFiller),1);
    +0278     end
    +0279     newModel.metDeltaG=[newModel.metDeltaG;metsToAdd.metDeltaG(:)];
    +0280 else
    +0281     %Add default
    +0282     if isfield(newModel,'metDeltaG')
    +0283         newModel.metDeltaG=[newModel.metDeltaG;NaN(numel(filler),1)];
    +0284     end
    +0285 end
    +0286 
    +0287 
    +0288 if isfield(metsToAdd,'metNotes')
    +0289     metsToAdd.metNotes=convertCharArray(metsToAdd.metNotes);
    +0290     if numel(metsToAdd.metNotes)==1 && numel(metsToAdd.mets)>1
    +0291         temp=cell(numel(metsToAdd.mets),1);
    +0292         temp(:)=metsToAdd.metNotes;
    +0293         metsToAdd.metNotes=temp;
    +0294     end
    +0295     if numel(metsToAdd.metNotes)~=nMets
    +0296         EM='metsToAdd.metNotes must have the same number of elements as metsToAdd.mets';
    +0297         error('RAVEN:badInput', '%s', EM);
    +0298     end
    +0299     %Add empty field if it does not exist
    +0300     if ~isfield(newModel,'metNotes')
    +0301         newModel.metNotes=largeFiller;
    +0302     end
    +0303     newModel.metNotes=[newModel.metNotes;metsToAdd.metNotes(:)];
    +0304 else
    +0305     %Add empty strings if the structure is in the model
    +0306     if isfield(newModel,'metNotes')
    +0307         newModel.metNotes=[newModel.metNotes;filler];
    +0308     end
    +0309 end
    +0310 
    +0311 %Do not check the type of metMiriams
    +0312 if isfield(metsToAdd,'metMiriams')
    +0313     if numel(metsToAdd.metMiriams)==1 && numel(metsToAdd.mets)>1
    +0314         temp=cell(numel(metsToAdd.mets),1);
    +0315         temp(:)={metsToAdd.metMiriams};
    +0316         metsToAdd.metMiriams=temp;
    +0317     end
    +0318     if numel(metsToAdd.metMiriams)~=nMets
    +0319         EM='metsToAdd.metMiriams must have the same number of elements as metsToAdd.mets';
    +0320         error('RAVEN:badInput', '%s', EM);
    +0321     end
    +0322     %Add empty field if it does not exist
    +0323     if ~isfield(newModel,'metMiriams')
    +0324         newModel.metMiriams=cell(nOldMets,1);
    +0325     end
    +0326     newModel.metMiriams=[newModel.metMiriams;metsToAdd.metMiriams(:)];
    +0327 else
    +0328     if isfield(newModel,'metMiriams')
    +0329         newModel.metMiriams=[newModel.metMiriams;cell(nMets,1)];
    +0330     end
    +0331 end
    +0332 
    +0333 if isfield(newModel,'metFrom')
    +0334     newModel.metFrom=[newModel.metFrom;filler];
    +0335 end
    +0336 
    +0337 %Expand the S matrix
    +0338 newModel.S=[newModel.S;sparse(nMets,size(newModel.S,2))];
    +0339 
    +0340 if copyInfo==true
    +0341     [I, J]=ismember(metsToAdd.metNames,model.metNames);
    +0342     J=J(I);
    +0343     %I is the indexes of the new metabolites for which a metabolite with
    +0344     %the same name exists
    +0345     I=find(I)+nOldMets;
    +0346     %Go through each of the added mets and copy annotation if it does not
    +0347     %exist
    +0348     for i=1:numel(I)
    +0349         if isfield(newModel,'inchis')
    +0350             if isempty(newModel.inchis{I(i)})
    +0351                 newModel.inchis(I(i))=newModel.inchis(J(i));
    +0352             end
    +0353         end
    +0354         if isfield(newModel,'metSmiles')
    +0355             if isempty(newModel.metSmiles{I(i)})
    +0356                 newModel.metSmiles(I(i))=newModel.metSmiles(J(i));
    +0357             end
    +0358         end        
    +0359         if isfield(newModel,'metFormulas')
    +0360             if isempty(newModel.metFormulas{I(i)})
    +0361                 newModel.metFormulas(I(i))=newModel.metFormulas(J(i));
    +0362             end
    +0363         end
    +0364         if isfield(newModel,'metMiriams')
    +0365             if isempty(newModel.metMiriams{I(i)})
    +0366                 newModel.metMiriams(I(i))=newModel.metMiriams(J(i));
    +0367             end
    +0368         end
    +0369         if isfield(newModel,'metCharges')
    +0370             newModel.metCharges(I(i))=newModel.metCharges(J(i));
    +0371         end
    +0372         if isfield(newModel,'metDeltaG')
    +0373             newModel.metDeltaG(I(i))=newModel.metDeltaG(J(i));
    +0374         end
    +0375     end
    +0376 end
    +0377 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/addRxns.html b/docs/html/manipulation/addRxns.html new file mode 100644 index 00000000..2da6093f --- /dev/null +++ b/docs/html/manipulation/addRxns.html @@ -0,0 +1,887 @@ + + + + Description of addRxns + + + + + + + + + +
    Home > manipulation > addRxns.m
    + + + +

    addRxns +

    + +

    PURPOSE ^

    +
    addRxns Add reactions to a model.
    + +

    SYNOPSIS ^

    +
    function newModel=addRxns(model,rxnsToAdd,varargin)
    + +

    DESCRIPTION ^

    +
     addRxns  Add reactions to a model.
    +
    + The function checks that grRules has one entry per reaction and that every gene of
    + a rule is in model.genes (an error otherwise, unless allowNewGenes is true).
    + standardizeGrRules then writes the rules in the standard format.
    +
    + New metabolites receive the available annotation of the metabolite with the same name
    + in another compartment, when one exists.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + rxnsToAdd : struct
    +     the reaction structure, which can have the following fields:
    +
    +     - rxns : cell array with unique strings that identify each reaction
    +     - equations : cell array with equation strings. Decimal coefficients
    +       are expressed as "1.2". Reversibility is indicated by "<=>" or "=>"
    +     - mets : (alternative to equations) cell array with the metabolites
    +       involved in each reaction as nested arrays, for example
    +       {{"met1","met2"},{"met1","met3","met4"}}. For a single added
    +       reaction, it can be a string array: {"met1","met2"}
    +     - stoichCoeffs : (alternative to equations) cell array with the
    +       corresponding stoichiometries as nested vectors, for example
    +       {[-1,+2],[-1,-1,+1]}. For a single added reaction, it can be a
    +       vector: [-1,+2]
    +     - rxnNames : cell array with the names of each reaction (optional,
    +       default "")
    +     - lb : vector with the lower bounds (optional, default
    +       model.annotation.defaultLB or -inf for reversible reactions and 0
    +       for irreversible reactions when "equations" is used. When "mets" and
    +       "stoichCoeffs" are used, the default for all reactions is
    +       model.annotation.defaultLB or -inf)
    +     - ub : vector with the upper bounds (optional, default
    +       model.annotation.defaultUB or inf)
    +     - c : vector with the objective function coefficients (optional,
    +       default 0)
    +     - eccodes : cell array with the EC-numbers for each reaction. Delimit
    +       several EC-numbers with ";" (optional, default "")
    +     - subSystems : cell array with the subsystems for each reaction
    +       (optional, default "")
    +     - grRules : cell array with the gene-reaction relationship for each
    +       reaction. "(A and B) or (C)" means that the reaction is
    +       catalysed by a complex of A and B or by C on its own. All genes must
    +       be present in model.genes, unless allowNewGenes is true; addGenesRaven
    +       adds genes (optional, default "")
    +     - rxnMiriams : cell array with Miriam structures (optional,
    +       default [])
    +     - rxnComps : cell array with compartments (as in model.comps)
    +       (optional, default {})
    +     - rxnNotes : cell array with reaction notes (optional, default "")
    +     - rxnDeltaG : Gibbs free energy at biochemical standard condition in
    +       kJ/mol (optional, default NaN)
    +     - rxnReferences : cell array with reaction references (optional,
    +       default "")
    +     - rxnConfidenceScores : vector with reaction confidence scores
    +       (optional, default NaN)
    +
    + Name-Value Arguments
    + --------------------
    + eqnType : double or char
    +     the interpretation of the equation strings (default 1). The values are the
    +     integers 1, 2 and 3, or the string aliases "id", "name" and "name[comp]":
    +
    +     - 1 or "id" : the metabolites are matched to model.mets. New
    +       metabolites (if allowed) are added to "compartment"
    +     - 2 or "name" : the metabolites are matched to model.metNames and all
    +       metabolites are assigned to "compartment". New metabolites get
    +       ids generated by generateNewIds with the prefix "m_" (or the
    +       prefix given in allowNewMets), numbered from the highest existing
    +       number with that prefix plus 1
    +     - 3 or "name[comp]" : the metabolites are written as "metNames[comps]".
    +       Only compartments in model.comps are allowed. New metabolites get
    +       ids generated in the same way as for eqnType 2
    + compartment : char
    +     the compartment of the metabolites for eqnType=2 and of new metabolites for
    +     eqnType=1. Must match model.comps. Not used for eqnType=3.
    + allowNewMets : logical or char
    +     true to allow the function to add new metabolites. A string also allows it and
    +     is used as the prefix of the new metabolite ids. The function calls addMets
    +     with copyInfo true, so a new metabolite takes the charge and deltaG from a
    +     metabolite with the same name in another compartment, and InChI, SMILES,
    +     formula and MIRIAM annotation from it when its own are empty. When the
    +     annotation of new metabolites is to be set explicitly, or when they are to be
    +     exchange (unconstrained) metabolites, adding them with addMets first gives
    +     that control (default false).
    + allowNewGenes : logical
    +     true to allow the function to add new genes (default false).
    +
    + Returns
    + -------
    + newModel : struct
    +     an updated model structure.
    +
    + Examples
    + --------
    +     newModel = addRxns(model, rxnsToAdd, eqnType, compartment, ...
    +                        allowNewMets, allowNewGenes);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    • addRxnsGenesMets addRxnsGenesMets Copy reactions from a source model into another model.
    • changeRxns changeRxns Modify the equations of reactions in a model.
    + + + + +

    SOURCE CODE ^

    +
    0001 function newModel=addRxns(model,rxnsToAdd,varargin)
    +0002 % addRxns  Add reactions to a model.
    +0003 %
    +0004 % The function checks that grRules has one entry per reaction and that every gene of
    +0005 % a rule is in model.genes (an error otherwise, unless allowNewGenes is true).
    +0006 % standardizeGrRules then writes the rules in the standard format.
    +0007 %
    +0008 % New metabolites receive the available annotation of the metabolite with the same name
    +0009 % in another compartment, when one exists.
    +0010 %
    +0011 % Parameters
    +0012 % ----------
    +0013 % model : struct
    +0014 %     a model structure.
    +0015 % rxnsToAdd : struct
    +0016 %     the reaction structure, which can have the following fields:
    +0017 %
    +0018 %     - rxns : cell array with unique strings that identify each reaction
    +0019 %     - equations : cell array with equation strings. Decimal coefficients
    +0020 %       are expressed as "1.2". Reversibility is indicated by "<=>" or "=>"
    +0021 %     - mets : (alternative to equations) cell array with the metabolites
    +0022 %       involved in each reaction as nested arrays, for example
    +0023 %       {{"met1","met2"},{"met1","met3","met4"}}. For a single added
    +0024 %       reaction, it can be a string array: {"met1","met2"}
    +0025 %     - stoichCoeffs : (alternative to equations) cell array with the
    +0026 %       corresponding stoichiometries as nested vectors, for example
    +0027 %       {[-1,+2],[-1,-1,+1]}. For a single added reaction, it can be a
    +0028 %       vector: [-1,+2]
    +0029 %     - rxnNames : cell array with the names of each reaction (optional,
    +0030 %       default "")
    +0031 %     - lb : vector with the lower bounds (optional, default
    +0032 %       model.annotation.defaultLB or -inf for reversible reactions and 0
    +0033 %       for irreversible reactions when "equations" is used. When "mets" and
    +0034 %       "stoichCoeffs" are used, the default for all reactions is
    +0035 %       model.annotation.defaultLB or -inf)
    +0036 %     - ub : vector with the upper bounds (optional, default
    +0037 %       model.annotation.defaultUB or inf)
    +0038 %     - c : vector with the objective function coefficients (optional,
    +0039 %       default 0)
    +0040 %     - eccodes : cell array with the EC-numbers for each reaction. Delimit
    +0041 %       several EC-numbers with ";" (optional, default "")
    +0042 %     - subSystems : cell array with the subsystems for each reaction
    +0043 %       (optional, default "")
    +0044 %     - grRules : cell array with the gene-reaction relationship for each
    +0045 %       reaction. "(A and B) or (C)" means that the reaction is
    +0046 %       catalysed by a complex of A and B or by C on its own. All genes must
    +0047 %       be present in model.genes, unless allowNewGenes is true; addGenesRaven
    +0048 %       adds genes (optional, default "")
    +0049 %     - rxnMiriams : cell array with Miriam structures (optional,
    +0050 %       default [])
    +0051 %     - rxnComps : cell array with compartments (as in model.comps)
    +0052 %       (optional, default {})
    +0053 %     - rxnNotes : cell array with reaction notes (optional, default "")
    +0054 %     - rxnDeltaG : Gibbs free energy at biochemical standard condition in
    +0055 %       kJ/mol (optional, default NaN)
    +0056 %     - rxnReferences : cell array with reaction references (optional,
    +0057 %       default "")
    +0058 %     - rxnConfidenceScores : vector with reaction confidence scores
    +0059 %       (optional, default NaN)
    +0060 %
    +0061 % Name-Value Arguments
    +0062 % --------------------
    +0063 % eqnType : double or char
    +0064 %     the interpretation of the equation strings (default 1). The values are the
    +0065 %     integers 1, 2 and 3, or the string aliases "id", "name" and "name[comp]":
    +0066 %
    +0067 %     - 1 or "id" : the metabolites are matched to model.mets. New
    +0068 %       metabolites (if allowed) are added to "compartment"
    +0069 %     - 2 or "name" : the metabolites are matched to model.metNames and all
    +0070 %       metabolites are assigned to "compartment". New metabolites get
    +0071 %       ids generated by generateNewIds with the prefix "m_" (or the
    +0072 %       prefix given in allowNewMets), numbered from the highest existing
    +0073 %       number with that prefix plus 1
    +0074 %     - 3 or "name[comp]" : the metabolites are written as "metNames[comps]".
    +0075 %       Only compartments in model.comps are allowed. New metabolites get
    +0076 %       ids generated in the same way as for eqnType 2
    +0077 % compartment : char
    +0078 %     the compartment of the metabolites for eqnType=2 and of new metabolites for
    +0079 %     eqnType=1. Must match model.comps. Not used for eqnType=3.
    +0080 % allowNewMets : logical or char
    +0081 %     true to allow the function to add new metabolites. A string also allows it and
    +0082 %     is used as the prefix of the new metabolite ids. The function calls addMets
    +0083 %     with copyInfo true, so a new metabolite takes the charge and deltaG from a
    +0084 %     metabolite with the same name in another compartment, and InChI, SMILES,
    +0085 %     formula and MIRIAM annotation from it when its own are empty. When the
    +0086 %     annotation of new metabolites is to be set explicitly, or when they are to be
    +0087 %     exchange (unconstrained) metabolites, adding them with addMets first gives
    +0088 %     that control (default false).
    +0089 % allowNewGenes : logical
    +0090 %     true to allow the function to add new genes (default false).
    +0091 %
    +0092 % Returns
    +0093 % -------
    +0094 % newModel : struct
    +0095 %     an updated model structure.
    +0096 %
    +0097 % Examples
    +0098 % --------
    +0099 %     newModel = addRxns(model, rxnsToAdd, eqnType, compartment, ...
    +0100 %                        allowNewMets, allowNewGenes);
    +0101 
    +0102 p=parseRAVENargs(varargin, {'eqnType',1; 'compartment',[]; 'allowNewMets',false; 'allowNewGenes',false});
    +0103 eqnType=p.eqnType;
    +0104 if ischar(eqnType) || isstring(eqnType)
    +0105     switch lower(char(eqnType))
    +0106         case 'id'
    +0107             eqnType=1;
    +0108         case 'name'
    +0109             eqnType=2;
    +0110         case 'name[comp]'
    +0111             eqnType=3;
    +0112         otherwise
    +0113             error('RAVEN:badInput', '%s', ['Unknown eqnType string "' char(eqnType) '". Use ''id'', ''name'', or ''name[comp]''.']);
    +0114     end
    +0115 end
    +0116 if ~isnumeric(eqnType)
    +0117     EM='eqnType must be numeric or one of the string aliases ''id'', ''name'', ''name[comp]''';
    +0118     error('RAVEN:badInput', '%s', EM);
    +0119 elseif ~ismember(eqnType,[1 2 3])
    +0120     EM='eqnType must be 1, 2, or 3';
    +0121     error('RAVEN:badInput', '%s', EM);
    +0122 end
    +0123 compartment=p.compartment;
    +0124 if ~isempty(compartment)
    +0125     compartment=char(compartment);
    +0126 end
    +0127 allowNewMets=p.allowNewMets;
    +0128 if ~islogical(allowNewMets)
    +0129     allowNewMets=char(allowNewMets);
    +0130 end
    +0131 allowNewGenes=p.allowNewGenes;
    +0132 
    +0133 if allowNewGenes & isfield(rxnsToAdd,'grRules')
    +0134     % getGenesFromGrRules splits only on space-bounded "and"/"or" (or their
    +0135     % "&"/"|" equivalents), so a gene id that merely contains "and"/"or" as
    +0136     % a substring (e.g. "band1", "orfeo2") is not mistaken for an operator
    +0137     % the way a plain regexp split on the bare words would.
    +0138     genesToAdd.genes = getGenesFromGrRules(convertCharArray(rxnsToAdd.grRules));
    +0139     genesToAdd.genes = setdiff(genesToAdd.genes,model.genes); % Only keep new genes
    +0140     if isfield(model,'geneComps')
    +0141         % No compartment information is available for a gene parsed out of
    +0142         % a grRule, so it is assigned to the first compartment, matching
    +0143         % addGenesRaven's own default for a gene added with none specified.
    +0144         genesToAdd.geneComps(1:numel(genesToAdd.genes)) = repmat(1,numel(genesToAdd.genes),1);
    +0145     end
    +0146     if ~isempty(genesToAdd.genes)
    +0147         fprintf('\nNew genes added to the model:\n')
    +0148         %A new gene id is arbitrary text and may contain "%"; print as
    +0149         %literal data rather than as an fprintf format string.
    +0150         fprintf('%s\n', strjoin(genesToAdd.genes,newline))
    +0151         newModel=addGenesRaven(model,genesToAdd);
    +0152     else
    +0153         newModel=model;
    +0154     end
    +0155 else
    +0156     newModel=model;
    +0157 end
    +0158 
    +0159 %If no reactions should be added
    +0160 if isempty(rxnsToAdd)
    +0161     return;
    +0162 end
    +0163 
    +0164 if eqnType==2 || (eqnType==1 && allowNewMets==true)
    +0165     compartment=char(compartment);
    +0166     if ~ismember(compartment,model.comps)
    +0167         EM='compartment must match one of the compartments in model.comps';
    +0168         error('RAVEN:badInput', '%s', EM);
    +0169     end
    +0170 end
    +0171 
    +0172 if ~isfield(rxnsToAdd,'rxns')
    +0173     EM='rxns is a required field in rxnsToAdd';
    +0174     error('RAVEN:badInput', '%s', EM);
    +0175 else
    +0176     rxnsToAdd.rxns=convertCharArray(rxnsToAdd.rxns);
    +0177     %To fit with some later printing
    +0178     rxnsToAdd.rxns=rxnsToAdd.rxns(:);
    +0179 end
    +0180 if ~(isfield(rxnsToAdd,'equations') || (isfield(rxnsToAdd,'mets') && isfield(rxnsToAdd,'stoichCoeffs')))
    +0181     EM='Either "equations" or "mets"+"stoichCoeffs" are required fields in rxnsToAdd';
    +0182     error('RAVEN:badInput', '%s', EM);
    +0183 end
    +0184 
    +0185 if any(ismember(rxnsToAdd.rxns,model.rxns))
    +0186     EM='One or more reaction id was already present in the model. Use changeRxns or remove the existing reactions before adding new ones';
    +0187     error('RAVEN:badInput', '%s', EM);
    +0188 end
    +0189 
    +0190 %Normal case: equations provided
    +0191 if isfield(rxnsToAdd,'equations')
    +0192     rxnsToAdd.equations=convertCharArray(rxnsToAdd.equations);
    +0193 
    +0194     %Alternative case: mets+stoichiometry provided
    +0195 else
    +0196     %In the case of 1 rxn added (array of strings + vector), transform to
    +0197     %cells of length=1:
    +0198     if iscellstr(rxnsToAdd.mets)
    +0199         rxnsToAdd.mets={rxnsToAdd.mets};
    +0200     end
    +0201     if isnumeric(rxnsToAdd.stoichCoeffs)
    +0202         rxnsToAdd.stoichCoeffs = {rxnsToAdd.stoichCoeffs};
    +0203     end
    +0204     %Now both rxnsToAdd.mets and rxnsToAdd.stoichCoeffs should be cell
    +0205     %arrays & of the same size:
    +0206     if ~iscell(rxnsToAdd.mets) || ~iscell(rxnsToAdd.stoichCoeffs)
    +0207         EM='rxnsToAdd.mets & rxnsToAdd.stoichCoeffs must be cell arrays';
    +0208         error('RAVEN:badInput', '%s', EM);
    +0209     elseif length(rxnsToAdd.stoichCoeffs) ~= length(rxnsToAdd.mets)
    +0210         EM = 'rxnsToAdd.stoichCoeffs must have the same number of elements as rxnsToAdd.mets';
    +0211         error('RAVEN:badInput', '%s', EM);
    +0212     end
    +0213     %In this case we need lb to decide if the reaction is reversible or not:
    +0214     if ~isfield(rxnsToAdd,'lb')
    +0215         %Fill with standard if it does not exist
    +0216         rxnsToAdd.lb=-inf(size(rxnsToAdd.mets));
    +0217     elseif ~isnumeric(rxnsToAdd.lb)
    +0218         EM = 'rxnsToAdd.lb must be a vector';
    +0219         error('RAVEN:badInput', '%s', EM);
    +0220     elseif length(rxnsToAdd.lb) ~= length(rxnsToAdd.mets)
    +0221         EM = 'rxnsToAdd.lb must have the same number of elements as rxnsToAdd.mets';
    +0222         error('RAVEN:badInput', '%s', EM);
    +0223     end
    +0224     %Now we construct equations, to comply with the rest of the script:
    +0225     rxnsToAdd.equations = cell(size(rxnsToAdd.mets));
    +0226     for i = 1:length(rxnsToAdd.mets)
    +0227         mets         = rxnsToAdd.mets{i};
    +0228         stoichCoeffs = rxnsToAdd.stoichCoeffs{i};
    +0229         if isfield(rxnsToAdd,'ub')
    +0230             isrev        = rxnsToAdd.lb(i) < 0 && rxnsToAdd.ub(i) > 0;
    +0231         else
    +0232             isrev        = rxnsToAdd.lb(i) < 0;
    +0233         end
    +0234         rxnsToAdd.equations{i} = buildEquation(mets,stoichCoeffs,isrev);
    +0235     end
    +0236 end
    +0237 
    +0238 nRxns=numel(rxnsToAdd.rxns);
    +0239 nOldRxns=numel(model.rxns);
    +0240 filler=cell(nRxns,1);
    +0241 filler(:)={''};
    +0242 cellfiller=cell(nRxns,1);
    +0243 cellfiller(:)={{''}};
    +0244 largeFiller=cell(nOldRxns,1);
    +0245 largeFiller(:)={''};
    +0246 largeCellFiller=cell(nOldRxns,1);
    +0247 largeCellFiller(:)={{''}};
    +0248 
    +0249 %***Add everything to the model except for the equations.
    +0250 if numel(rxnsToAdd.equations)~=nRxns
    +0251     EM='rxnsToAdd.equations must have the same number of elements as rxnsToAdd.rxns';
    +0252     error('RAVEN:badInput', '%s', EM);
    +0253 end
    +0254 
    +0255 %Parse the equations. This is done at this early stage since I need the
    +0256 %reversibility info
    +0257 [S, mets, badRxns, reversible]=constructS(rxnsToAdd.equations);
    +0258 if any(badRxns)
    +0259     EM='The following equations have one or more metabolites both as substrate and product. Only the net equations will be added:';
    +0260     warning('RAVEN:warning', '%s', ravenList(EM, rxnsToAdd.rxns(badRxns)));
    +0261 end
    +0262 
    +0263 newModel.rev=[newModel.rev;reversible];
    +0264 newModel.rxns=[newModel.rxns;rxnsToAdd.rxns(:)];
    +0265 
    +0266 if isfield(rxnsToAdd,'rxnNames')
    +0267     rxnsToAdd.rxnNames=convertCharArray(rxnsToAdd.rxnNames);
    +0268     if numel(rxnsToAdd.rxnNames)~=nRxns
    +0269         EM='rxnsToAdd.rxnNames must have the same number of elements as rxnsToAdd.rxns';
    +0270         error('RAVEN:badInput', '%s', EM);
    +0271     end
    +0272     %Fill with standard if it does not exist
    +0273     if ~isfield(newModel,'rxnNames')
    +0274         newModel.rxnNames=largeFiller;
    +0275     end
    +0276     newModel.rxnNames=[newModel.rxnNames;rxnsToAdd.rxnNames(:)];
    +0277 else
    +0278     %Fill with standard if it does not exist
    +0279     if isfield(newModel,'rxnNames')
    +0280         newModel.rxnNames=[newModel.rxnNames;filler];
    +0281     end
    +0282 end
    +0283 
    +0284 if isfield(newModel,'annotation') & isfield(newModel.annotation,'defaultLB')
    +0285     newLb=newModel.annotation.defaultLB;
    +0286 else
    +0287     newLb=-inf;
    +0288 end
    +0289 
    +0290 if isfield(rxnsToAdd,'lb')
    +0291     if numel(rxnsToAdd.lb)~=nRxns
    +0292         EM='rxnsToAdd.lb must have the same number of elements as rxnsToAdd.rxns';
    +0293         error('RAVEN:badInput', '%s', EM);
    +0294     end
    +0295     %Fill with standard if it does not exist
    +0296     if ~isfield(newModel,'lb')
    +0297         newModel.lb=zeros(nOldRxns,1);
    +0298         newModel.lb(newModel.rev~=0)=newLb;
    +0299     end
    +0300     newModel.lb=[newModel.lb;rxnsToAdd.lb(:)];
    +0301 else
    +0302     %Fill with standard if it does not exist
    +0303     if isfield(newModel,'lb')
    +0304         I=zeros(nRxns,1);
    +0305         I(reversible~=0)=newLb;
    +0306         newModel.lb=[newModel.lb;I];
    +0307     end
    +0308 end
    +0309 
    +0310 if isfield(newModel,'annotation') & isfield(newModel.annotation,'defaultUB')
    +0311     newUb=newModel.annotation.defaultUB;
    +0312 else
    +0313     newUb=inf;
    +0314 end
    +0315 
    +0316 if isfield(rxnsToAdd,'ub')
    +0317     if numel(rxnsToAdd.ub)~=nRxns
    +0318         EM='rxnsToAdd.ub must have the same number of elements as rxnsToAdd.rxns';
    +0319         error('RAVEN:badInput', '%s', EM);
    +0320     end
    +0321     %Fill with standard if it does not exist
    +0322     if ~isfield(newModel,'ub')
    +0323         newModel.ub=repmat(newUb,nOldRxns,1);
    +0324     end
    +0325     newModel.ub=[newModel.ub;rxnsToAdd.ub(:)];
    +0326 else
    +0327     %Fill with standard if it does not exist
    +0328     if isfield(newModel,'ub')
    +0329         newModel.ub=[newModel.ub;repmat(newUb,nRxns,1)];
    +0330     end
    +0331 end
    +0332 
    +0333 if isfield(rxnsToAdd,'c')
    +0334     if numel(rxnsToAdd.c)~=nRxns
    +0335         EM='rxnsToAdd.c must have the same number of elements as rxnsToAdd.rxns';
    +0336         error('RAVEN:badInput', '%s', EM);
    +0337     end
    +0338     %Fill with standard if it does not exist
    +0339     if ~isfield(newModel,'c')
    +0340         newModel.c=zeros(nOldRxns,1);
    +0341     end
    +0342     newModel.c=[newModel.c;rxnsToAdd.c(:)];
    +0343 else
    +0344     %Fill with standard if it does not exist
    +0345     if isfield(newModel,'c')
    +0346         newModel.c=[newModel.c;zeros(nRxns,1)];
    +0347     end
    +0348 end
    +0349 
    +0350 if isfield(rxnsToAdd,'eccodes')
    +0351     rxnsToAdd.eccodes=convertCharArray(rxnsToAdd.eccodes);
    +0352     if numel(rxnsToAdd.eccodes)~=nRxns
    +0353         EM='rxnsToAdd.eccodes must have the same number of elements as rxnsToAdd.rxns';
    +0354         error('RAVEN:badInput', '%s', EM);
    +0355     end
    +0356     %Fill with standard if it does not exist
    +0357     if ~isfield(newModel,'eccodes')
    +0358         newModel.eccodes=largeFiller;
    +0359     end
    +0360     newModel.eccodes=[newModel.eccodes;rxnsToAdd.eccodes(:)];
    +0361 else
    +0362     %Fill with standard if it does not exist
    +0363     if isfield(newModel,'eccodes')
    +0364         newModel.eccodes=[newModel.eccodes;filler];
    +0365     end
    +0366 end
    +0367 
    +0368 if isfield(rxnsToAdd,'subSystems')
    +0369     % Has to be cell array
    +0370     if ischar(rxnsToAdd.subSystems)
    +0371         rxnsToAdd.subSystems = {rxnsToAdd.subSystems};
    +0372     end
    +0373     % If all nested cells are 1x1, then unnest
    +0374     if all(cellfun(@(x) iscell(x) && isscalar(x), rxnsToAdd.subSystems))
    +0375         rxnsToAdd.subSystems = transpose([rxnsToAdd.subSystems{:}]);
    +0376     end
    +0377     % Cell array should now be as simple as possible. Check if it is nested
    +0378     subSysRxnsNested = any(cellfun(@(x) iscell(x), rxnsToAdd.subSystems));
    +0379     if isfield(newModel,'subSystems')
    +0380         subSysModelNested = any(cellfun(@(x) iscell(x), newModel.subSystems));
    +0381     else
    +0382         subSysModelNested = subSysRxnsNested;
    +0383         if subSysRxnsNested
    +0384             newModel.subSystems=largeCellFiller;
    +0385         else
    +0386             newModel.subSystems=largeFiller;
    +0387         end
    +0388     end
    +0389     if subSysRxnsNested && ~subSysModelNested
    +0390         % Make all existing subSystems nested
    +0391         newModel.subSystems = cellfun(@(x) {x}, newModel.subSystems, 'uni', 0);
    +0392     elseif ~subSysRxnsNested && subSysModelNested
    +0393         rxnsToAdd.subSystems = cellfun(@(x) {x}, rxnsToAdd.subSystems, 'uni', 0);
    +0394     end
    +0395     if numel(rxnsToAdd.subSystems)~=nRxns
    +0396         EM='rxnsToAdd.subSystems must have the same number of elements as rxnsToAdd.rxns';
    +0397         error('RAVEN:badInput', '%s', EM);
    +0398     end
    +0399     newModel.subSystems=[newModel.subSystems;rxnsToAdd.subSystems(:)];
    +0400 else
    +0401     %Fill with standard if it does not exist
    +0402     if isfield(newModel,'subSystems')
    +0403         if any(cellfun(@(x) iscell(x), newModel.subSystems))
    +0404             newModel.subSystems=[newModel.subSystems;cellfiller];
    +0405         else
    +0406             newModel.subSystems=[newModel.subSystems;filler];
    +0407         end
    +0408     end
    +0409 end
    +0410 if isfield(rxnsToAdd,'rxnMiriams')
    +0411     if numel(rxnsToAdd.rxnMiriams)~=nRxns
    +0412         EM='rxnsToAdd.rxnMiriams must have the same number of elements as rxnsToAdd.rxns';
    +0413         error('RAVEN:badInput', '%s', EM);
    +0414     end
    +0415     %Fill with standard if it does not exist
    +0416     if ~isfield(newModel,'rxnMiriams')
    +0417         newModel.rxnMiriams=cell(nOldRxns,1);
    +0418     end
    +0419     newModel.rxnMiriams=[newModel.rxnMiriams;rxnsToAdd.rxnMiriams(:)];
    +0420 else
    +0421     %Fill with standard if it does not exist
    +0422     if isfield(newModel,'rxnMiriams')
    +0423         newModel.rxnMiriams=[newModel.rxnMiriams;cell(nRxns,1)];
    +0424     end
    +0425 end
    +0426 if isfield(rxnsToAdd,'rxnComps')
    +0427     if numel(rxnsToAdd.rxnComps)~=nRxns
    +0428         EM='rxnsToAdd.rxnComps must have the same number of elements as rxnsToAdd.rxns';
    +0429         error('RAVEN:badInput', '%s', EM);
    +0430     end
    +0431     %Fill with standard if it does not exist
    +0432     if ~isfield(newModel,'rxnComps')
    +0433         newModel.rxnComps=ones(nOldRxns,1);
    +0434         EM='Adding reactions with compartment information to a model without such information. All existing reactions will be assigned to the first compartment';
    +0435         warning('RAVEN:warning', '%s', EM);
    +0436     end
    +0437     newModel.rxnComps=[newModel.rxnComps;rxnsToAdd.rxnComps(:)];
    +0438 else
    +0439     %Fill with standard if it does not exist
    +0440     if isfield(newModel,'rxnComps')
    +0441         newModel.rxnComps=[newModel.rxnComps;ones(nRxns,1)];
    +0442         %fprintf("NOTE: The added reactions will be assigned to the first
    +0443         %compartment\n");
    +0444     end
    +0445 end
    +0446 if isfield(rxnsToAdd,'grRules')
    +0447     rxnsToAdd.grRules=convertCharArray(rxnsToAdd.grRules);
    +0448     if numel(rxnsToAdd.grRules)~=nRxns
    +0449         EM='rxnsToAdd.grRules must have the same number of elements as rxnsToAdd.rxns';
    +0450         error('RAVEN:badInput', '%s', EM);
    +0451     end
    +0452     %Fill with standard if it does not exist
    +0453     if ~isfield(newModel,'grRules')
    +0454         newModel.grRules=largeFiller;
    +0455     end
    +0456     newModel.grRules=[newModel.grRules;rxnsToAdd.grRules(:)];
    +0457 else
    +0458     %Fill with standard if it does not exist
    +0459     if isfield(newModel,'grRules')
    +0460         newModel.grRules=[newModel.grRules;filler];
    +0461     end
    +0462 end
    +0463 
    +0464 if isfield(rxnsToAdd,'rxnFrom')
    +0465     rxnsToAdd.rxnFrom=convertCharArray(rxnsToAdd.rxnFrom);
    +0466     if numel(rxnsToAdd.rxnFrom)~=nRxns
    +0467         EM='rxnsToAdd.rxnFrom must have the same number of elements as rxnsToAdd.rxns';
    +0468         error('RAVEN:badInput', '%s', EM);
    +0469     end
    +0470     %Fill with standard if it does not exist
    +0471     if ~isfield(newModel,'rxnFrom')
    +0472         newModel.rxnFrom=largeFiller;
    +0473     end
    +0474     newModel.rxnFrom=[newModel.rxnFrom;rxnsToAdd.rxnFrom(:)];
    +0475 else
    +0476     %Fill with standard if it does not exist
    +0477     if isfield(newModel,'rxnFrom')
    +0478         newModel.rxnFrom=[newModel.rxnFrom;filler];
    +0479     end
    +0480 end
    +0481 
    +0482 if isfield(rxnsToAdd,'rxnNotes')
    +0483     rxnsToAdd.rxnNotes=convertCharArray(rxnsToAdd.rxnNotes);
    +0484     if numel(rxnsToAdd.rxnNotes)~=nRxns
    +0485         EM='rxnsToAdd.rxnNotes must have the same number of elements as rxnsToAdd.rxns';
    +0486         error('RAVEN:badInput', '%s', EM);
    +0487     end
    +0488     %Fill with standard if it does not exist
    +0489     if ~isfield(newModel,'rxnNotes')
    +0490         newModel.rxnNotes=largeFiller;
    +0491     end
    +0492     newModel.rxnNotes=[newModel.rxnNotes;rxnsToAdd.rxnNotes(:)];
    +0493 else
    +0494     %Fill with standard if it does not exist
    +0495     if isfield(newModel,'rxnNotes')
    +0496         newModel.rxnNotes=[newModel.rxnNotes;filler];
    +0497     end
    +0498 end
    +0499 
    +0500 if isfield(rxnsToAdd,'rxnReferences')
    +0501     rxnsToAdd.rxnReferences=convertCharArray(rxnsToAdd.rxnReferences);
    +0502     if numel(rxnsToAdd.rxnReferences)~=nRxns
    +0503         EM='rxnsToAdd.rxnReferences must have the same number of elements as rxnsToAdd.rxns';
    +0504         error('RAVEN:badInput', '%s', EM);
    +0505     end
    +0506     %Fill with standard if it does not exist
    +0507     if ~isfield(newModel,'rxnReferences')
    +0508         newModel.rxnReferences=largeFiller;
    +0509     end
    +0510     newModel.rxnReferences=[newModel.rxnReferences;rxnsToAdd.rxnReferences(:)];
    +0511 else
    +0512     %Fill with standard if it does not exist
    +0513     if isfield(newModel,'rxnReferences')
    +0514         newModel.rxnReferences=[newModel.rxnReferences;filler];
    +0515     end
    +0516 end
    +0517 
    +0518 if isfield(rxnsToAdd,'pwys')
    +0519     rxnsToAdd.pwys=convertCharArray(rxnsToAdd.pwys);
    +0520     if numel(rxnsToAdd.pwys)~=nRxns
    +0521         EM='rxnsToAdd.pwys must have the same number of elements as rxnsToAdd.rxns';
    +0522         error('RAVEN:badInput', '%s', EM);
    +0523     end
    +0524     %Fill with standard if it does not exist
    +0525     if ~isfield(newModel,'pwys')
    +0526         newModel.pwys=largeFiller;
    +0527     end
    +0528     newModel.pwys=[newModel.pwys;rxnsToAdd.pwys(:)];
    +0529 else
    +0530     %Fill with standard if it does not exist
    +0531     if isfield(newModel,'pwys')
    +0532         newModel.pwys=[newModel.pwys;filler];
    +0533     end
    +0534 end
    +0535 
    +0536 if isfield(rxnsToAdd,'spontaneous')
    +0537     if numel(rxnsToAdd.spontaneous)~=nRxns
    +0538         EM='rxnsToAdd.spontaneous must have the same number of elements as rxnsToAdd.rxns';
    +0539         error('RAVEN:badInput', '%s', EM);
    +0540     end
    +0541     %Fill with standard if it does not exist
    +0542     if ~isfield(newModel,'spontaneous')
    +0543         newModel.spontaneous=false(nOldRxns,1);
    +0544     end
    +0545     newModel.spontaneous=[newModel.spontaneous;logical(rxnsToAdd.spontaneous(:))];
    +0546 else
    +0547     %Fill with standard if it does not exist
    +0548     if isfield(newModel,'spontaneous')
    +0549         newModel.spontaneous=[newModel.spontaneous;false(nRxns,1)];
    +0550     end
    +0551 end
    +0552 
    +0553 if isfield(rxnsToAdd,'rxnConfidenceScores')
    +0554     if numel(rxnsToAdd.rxnConfidenceScores)~=nRxns
    +0555         EM='rxnsToAdd.rxnConfidenceScores must have the same number of elements as rxnsToAdd.rxns';
    +0556         error('RAVEN:badInput', '%s', EM);
    +0557     end
    +0558     %Fill with standard if it does not exist
    +0559     if ~isfield(newModel,'rxnConfidenceScores')
    +0560         newModel.rxnConfidenceScores=NaN(nOldRxns,1);
    +0561     end
    +0562     newModel.rxnConfidenceScores=[newModel.rxnConfidenceScores;rxnsToAdd.rxnConfidenceScores(:)];
    +0563 else
    +0564     %Fill with standard if it does not exist
    +0565     if isfield(newModel,'rxnConfidenceScores')
    +0566         newModel.rxnConfidenceScores=[newModel.rxnConfidenceScores;NaN(nRxns,1)];
    +0567     end
    +0568 end
    +0569 
    +0570 if isfield(rxnsToAdd,'rxnDeltaG')
    +0571     if numel(rxnsToAdd.rxnDeltaG)~=nRxns
    +0572         EM='rxnsToAdd.rxnDeltaG must have the same number of elements as rxnsToAdd.rxns';
    +0573         error('RAVEN:badInput', '%s', EM);
    +0574     end
    +0575     %Fill with standard if it does not exist
    +0576     if ~isfield(newModel,'rxnDeltaG')
    +0577         newModel.rxnDeltaG=NaN(nOldRxns,1);
    +0578     end
    +0579     newModel.rxnDeltaG=[newModel.rxnDeltaG;rxnsToAdd.rxnDeltaG(:)];
    +0580 else
    +0581     %Fill with standard if it does not exist
    +0582     if isfield(newModel,'rxnDeltaG')
    +0583         newModel.rxnDeltaG=[newModel.rxnDeltaG;NaN(nRxns,1)];
    +0584     end
    +0585 end
    +0586 
    +0587 
    +0588 %***Start parsing the equations and adding the info to the S matrix The
    +0589 %mets are matched to model.mets
    +0590 if eqnType==1
    +0591     [I, J]=ismember(mets,model.mets);
    +0592     if ~all(I)
    +0593         if allowNewMets==true || ischar(allowNewMets)
    +0594             %Add the new mets
    +0595             metsToAdd.mets=mets(~I);
    +0596             metsToAdd.metNames=metsToAdd.mets;
    +0597             metsToAdd.compartments=compartment;
    +0598             if ischar(allowNewMets)
    +0599                 newModel=addMets(newModel,metsToAdd,true,allowNewMets);
    +0600             else
    +0601                 newModel=addMets(newModel,metsToAdd,true);
    +0602             end
    +0603         else
    +0604             EM='One or more equations contain metabolites that are not in model.mets. Set allowNewMets to true to allow this function to add metabolites or use addMets to add them before calling this function. Are you sure that eqnType=1?';
    +0605             error('RAVEN:badInput', '%s', EM);
    +0606         end
    +0607     end
    +0608     %Calculate the indexes of the metabolites and add the info
    +0609     metIndexes=J;
    +0610     metIndexes(~I)=numel(newModel.mets)-sum(~I)+1:numel(newModel.mets);
    +0611 end
    +0612 
    +0613 %Do some stuff that is the same for eqnType=2 and eqnType=3
    +0614 if eqnType==2 || eqnType==3
    +0615     %For later..
    +0616     t2=strcat(model.metNames,'***',model.comps(model.metComps));
    +0617 end
    +0618 
    +0619 %The mets are matched to model.metNames and assigned to "compartment"
    +0620 if eqnType==2
    +0621     %%Check that the metabolite names are not present in the same
    +0622     %%compartment.
    +0623     %Not the neatest way maybe..
    +0624     t1=strcat(mets,'***',compartment);
    +0625     [I, J]=ismember(t1,t2);
    +0626     
    +0627     if ~all(I)
    +0628         if allowNewMets==true || ischar(allowNewMets)
    +0629             %Add the new mets
    +0630             metsToAdd.metNames=mets(~I);
    +0631             metsToAdd.compartments=compartment;
    +0632             if ischar(allowNewMets)
    +0633                 newModel=addMets(newModel,metsToAdd,true,allowNewMets);
    +0634             else
    +0635                 newModel=addMets(newModel,metsToAdd,true);
    +0636             end
    +0637         else
    +0638             EM='One or more equations contain metabolites that are not in model.mets. Set allowNewMets to true to allow this function to add metabolites or use addMets to add them before calling this function';
    +0639             error('RAVEN:badInput', '%s', EM);
    +0640         end
    +0641     end
    +0642     
    +0643     %Calculate the indexes of the metabolites
    +0644     metIndexes=J;
    +0645     metIndexes(~I)=numel(newModel.mets)-sum(~I)+1:numel(newModel.mets);
    +0646 end
    +0647 
    +0648 %The equations are on the form metNames[compName]
    +0649 if eqnType==3
    +0650     %Parse the metabolite names
    +0651     metNames=cell(numel(mets),1);
    +0652     compartments=metNames;
    +0653     for i=1:numel(mets)
    +0654         starts=max(strfind(mets{i},'['));
    +0655         ends=max(strfind(mets{i},']'));
    +0656         
    +0657         %Check that the formatting is correct
    +0658         if isempty(starts) || isempty(ends) || ends<numel(mets{i})
    +0659             EM=['The metabolite ' mets{i} ' is not correctly formatted for eqnType=3'];
    +0660             error('RAVEN:badInput', '%s', EM);
    +0661         end
    +0662         
    +0663         %Check that the compartment is correct
    +0664         compartments{i}=mets{i}(starts+1:ends-1);
    +0665         I=ismember(compartments(i),newModel.comps);
    +0666         if ~I
    +0667             EM=['The metabolite ' mets{i} ' has a compartment that is not in model.comps'];
    +0668             error('RAVEN:badInput', '%s', EM);
    +0669         end
    +0670         metNames{i}=mets{i}(1:starts-1);
    +0671     end
    +0672     
    +0673     %Check if the metabolite exists already
    +0674     t1=strcat(metNames,'***',compartments);
    +0675     [I, J]=ismember(t1,t2);
    +0676     
    +0677     if ~all(I)
    +0678         if allowNewMets==true | ischar(allowNewMets)
    +0679             %Add the new mets
    +0680             metsToAdd.metNames=metNames(~I);
    +0681             metsToAdd.compartments=compartments(~I);
    +0682             if ischar(allowNewMets)
    +0683                 newModel=addMets(newModel,metsToAdd,true,allowNewMets);
    +0684             else
    +0685                 newModel=addMets(newModel,metsToAdd,true);
    +0686             end
    +0687         else
    +0688             EM='One or more equations contain metabolites that are not in model.metNames. Set allowNewMets to true to allow this function to add metabolites or use addMets to add them before calling this function';
    +0689             error('RAVEN:badInput', '%s', EM);
    +0690         end
    +0691     end
    +0692     
    +0693     %Calculate the indexes of the metabolites
    +0694     metIndexes=J;
    +0695     metIndexes(~I)=numel(newModel.mets)-sum(~I)+1:numel(newModel.mets);
    +0696 end
    +0697 
    +0698 %Add the info to the stoichiometric matrix
    +0699 newModel.S=[newModel.S sparse(size(newModel.S,1),nRxns)];
    +0700 
    +0701 for i=1:nRxns
    +0702     newModel.S(metIndexes,nOldRxns+i)=S(:,i);
    +0703     %Parse the grRules and check whether all genes in grRules appear in
    +0704     %genes
    +0705     if isfield(newModel,'grRules')
    +0706         rule=newModel.grRules{nOldRxns+i};
    +0707         rule=strrep(rule,'(','');
    +0708         rule=strrep(rule,')','');
    +0709         rule=strrep(rule,' or ',' ');
    +0710         rule=strrep(rule,' and ',' ');
    +0711         genes=regexp(rule,' ','split');
    +0712         [I, J]=ismember(genes,newModel.genes);
    +0713         if ~all(I) && any(rule)
    +0714             EM=['Not all genes for reaction ' rxnsToAdd.rxns{i} ' were found in model.genes. If needed, add genes with addGenesRaven before calling this function, or set the ''allowNewGenes'' flag to true'];
    +0715             error('RAVEN:badInput', '%s', EM);
    +0716         end
    +0717     end
    +0718 end
    +0719 
    +0720 %Make temporary minimal model structure with only new rxns, to parse to
    +0721 %standardizeGrRules
    +0722 newRxnsModel.genes=newModel.genes;
    +0723 newRxnsModel.grRules=newModel.grRules(length(model.rxns)+1:end);
    +0724 newRxnsModel.rxns=newModel.rxns(length(model.rxns)+1:end);
    +0725 
    +0726 %Fix grRules and reconstruct rxnGeneMat
    +0727 [grRules,rxnGeneMat] = standardizeGrRules(newRxnsModel,true);
    +0728 newModel.rxnGeneMat = [newModel.rxnGeneMat; rxnGeneMat];
    +0729 newModel.grRules = [newModel.grRules(1:nOldRxns); grRules];
    +0730 
    +0731 if isfield(newModel,'equations')
    +0732     %Reuse the equations as given where available (preserves the caller's
    +0733     %own formatting); reconstruct them from the now-populated S otherwise,
    +0734     %e.g. when rxnsToAdd used mets/stoichCoeffs instead of equations.
    +0735     if isfield(rxnsToAdd,'equations')
    +0736         newEquations=convertCharArray(rxnsToAdd.equations);
    +0737     else
    +0738         newEquations=constructEquations(newModel,newModel.rxns(nOldRxns+1:end));
    +0739     end
    +0740     newModel.equations=[newModel.equations;newEquations(:)];
    +0741 end
    +0742 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/addRxnsGenesMets.html b/docs/html/manipulation/addRxnsGenesMets.html new file mode 100644 index 00000000..888ecdcf --- /dev/null +++ b/docs/html/manipulation/addRxnsGenesMets.html @@ -0,0 +1,296 @@ + + + + Description of addRxnsGenesMets + + + + + + + + + +
    Home > manipulation > addRxnsGenesMets.m
    + + + +

    addRxnsGenesMets +

    + +

    PURPOSE ^

    +
    addRxnsGenesMets Copy reactions from a source model into another model.
    + +

    SYNOPSIS ^

    +
    function model=addRxnsGenesMets(model,sourceModel,rxns,varargin)
    + +

    DESCRIPTION ^

    +
     addRxnsGenesMets  Copy reactions from a source model into another model.
    +
    + Copies reactions from a source model to a draft model, together with their new
    + metabolites and genes. Reactions that are already in the draft model are skipped.
    +
    + The draft model and the source model must follow the same metabolite and compartment
    + naming convention, because metabolites are matched only by metaboliteName[compartment].
    + The function can copy additional reactions from a source model to a draft model that
    + getModelFromHomology created from the same models.
    +
    + Parameters
    + ----------
    + model : struct
    +     the draft model that receives the reactions.
    + sourceModel : struct
    +     the model that the reactions and metabolites are copied from.
    + rxns : cell or char
    +     reaction ids from the source model. A string adds one reaction.
    +
    + Name-Value Arguments
    + --------------------
    + addGene : logical or char or cell
    +     three options (default false):
    +
    +     - false : no genes are annotated to the new reactions
    +     - true : grRules are copied from the sourceModel and new genes are
    +       added when required
    +     - string or cell array : the grRules of the new reactions, as a string or a
    +       cell array, and new genes are added when required
    + rxnNote : cell or char
    +     strings that explain why the reactions were copied, stored in rxnNotes of the new
    +     reactions. A string applies the same note to every new reaction
    +     (default "Added via addRxnsGenesMets()").
    + confidence : double
    +     integer confidence score for all new reactions, following
    +     doi:10.1038/nprot.2009.203 (default 0):
    +
    +     - 4 : biochemical data: direct evidence from enzyme assays
    +     - 3 : genetic data: knockout/-in or overexpression analysis
    +     - 2 : physiological data: indirect evidence, for example secretion products
    +       or defined medium requirement; sequence data: genome annotation
    +     - 1 : modelling data: required for a functional model, hypothetical
    +       reaction
    +     - 0 : no evidence
    +
    + Returns
    + -------
    + model : struct
    +     an updated model structure.
    +
    + Examples
    + --------
    +     newModel = addRxnsGenesMets(model, sourceModel, rxns, addGene, ...
    +                                 rxnNote, confidence);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • addMets addMets Add metabolites to a model.
    • addRxns addRxns Add reactions to a model.
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function model=addRxnsGenesMets(model,sourceModel,rxns,varargin)
    +0002 % addRxnsGenesMets  Copy reactions from a source model into another model.
    +0003 %
    +0004 % Copies reactions from a source model to a draft model, together with their new
    +0005 % metabolites and genes. Reactions that are already in the draft model are skipped.
    +0006 %
    +0007 % The draft model and the source model must follow the same metabolite and compartment
    +0008 % naming convention, because metabolites are matched only by metaboliteName[compartment].
    +0009 % The function can copy additional reactions from a source model to a draft model that
    +0010 % getModelFromHomology created from the same models.
    +0011 %
    +0012 % Parameters
    +0013 % ----------
    +0014 % model : struct
    +0015 %     the draft model that receives the reactions.
    +0016 % sourceModel : struct
    +0017 %     the model that the reactions and metabolites are copied from.
    +0018 % rxns : cell or char
    +0019 %     reaction ids from the source model. A string adds one reaction.
    +0020 %
    +0021 % Name-Value Arguments
    +0022 % --------------------
    +0023 % addGene : logical or char or cell
    +0024 %     three options (default false):
    +0025 %
    +0026 %     - false : no genes are annotated to the new reactions
    +0027 %     - true : grRules are copied from the sourceModel and new genes are
    +0028 %       added when required
    +0029 %     - string or cell array : the grRules of the new reactions, as a string or a
    +0030 %       cell array, and new genes are added when required
    +0031 % rxnNote : cell or char
    +0032 %     strings that explain why the reactions were copied, stored in rxnNotes of the new
    +0033 %     reactions. A string applies the same note to every new reaction
    +0034 %     (default "Added via addRxnsGenesMets()").
    +0035 % confidence : double
    +0036 %     integer confidence score for all new reactions, following
    +0037 %     doi:10.1038/nprot.2009.203 (default 0):
    +0038 %
    +0039 %     - 4 : biochemical data: direct evidence from enzyme assays
    +0040 %     - 3 : genetic data: knockout/-in or overexpression analysis
    +0041 %     - 2 : physiological data: indirect evidence, for example secretion products
    +0042 %       or defined medium requirement; sequence data: genome annotation
    +0043 %     - 1 : modelling data: required for a functional model, hypothetical
    +0044 %       reaction
    +0045 %     - 0 : no evidence
    +0046 %
    +0047 % Returns
    +0048 % -------
    +0049 % model : struct
    +0050 %     an updated model structure.
    +0051 %
    +0052 % Examples
    +0053 % --------
    +0054 %     newModel = addRxnsGenesMets(model, sourceModel, rxns, addGene, ...
    +0055 %                                 rxnNote, confidence);
    +0056 
    +0057 p=parseRAVENargs(varargin, {'addGene',false; 'rxnNote',[]; 'confidence',0});
    +0058 confidence=p.confidence;
    +0059 rxns=convertCharArray(rxns);
    +0060 if isempty(p.rxnNote)
    +0061     rxnNote={'Added via addRxnsGenesMets()'};
    +0062 else
    +0063     rxnNote=convertCharArray(p.rxnNote);
    +0064 end
    +0065 if numel(rxnNote)==1 && numel(rxns)>1
    +0066     rxnNoteArray=cell(1,numel(rxns));
    +0067     rxnNoteArray(:)=rxnNote;
    +0068     rxnNote=rxnNoteArray;
    +0069 end
    +0070 addGene=p.addGene;
    +0071 
    +0072 % Obtain indexes of reactions in source model
    +0073 [notNewRxn,oldRxn]=ismember(rxns,model.rxns);
    +0074 rxns=rxns(~notNewRxn);
    +0075 if isempty(rxns)
    +0076     error('All reactions are already in the model.');
    +0077 elseif ~isempty(notNewRxn)
    +0078     fprintf('The following reactions were already present in the model and will not be added:\n')
    +0079     %Reaction ids are arbitrary text and may contain "%"; print as
    +0080     %literal data rather than as an fprintf format string.
    +0081     fprintf('%s\n', strjoin(model.rxns(oldRxn(find(oldRxn))),newline))
    +0082 end
    +0083 
    +0084 [~, rxnIdx]=ismember(rxns,sourceModel.rxns); % Get rxnIDs
    +0085 if any(rxnIdx==0)
    +0086     error('RAVEN:badInput', '%s', ravenList('The following reaction IDs could not be found in the source model:', rxns(rxnIdx==0)));
    +0087 end
    +0088 
    +0089 % Add new metabolites
    +0090 metIdx=find(any(sourceModel.S(:,rxnIdx),2)); % Get metabolite IDs
    +0091 % Many of the metabolites in are already in the draft model, so only add
    +0092 % the new metabolites
    +0093 
    +0094 % Match by metNames[metComps]. First make these structures for each model.
    +0095 metCompsN =cellstr(num2str(model.metComps));
    +0096 map=containers.Map(cellstr(num2str(transpose([1:length(model.comps)]))),model.comps);
    +0097 metCompsN = map.values(metCompsN);
    +0098 metCompsN = strcat(model.metNames,'[',metCompsN,']');
    +0099 
    +0100 sourcemetCompsN=cellstr(num2str(sourceModel.metComps));
    +0101 map=containers.Map(cellstr(num2str(transpose([1:length(sourceModel.comps)]))),sourceModel.comps);
    +0102 sourcemetCompsN = map.values(sourcemetCompsN);
    +0103 sourcemetCompsN=strcat(sourceModel.metNames,'[',sourcemetCompsN,']');
    +0104 
    +0105 newMetCompsN=sourcemetCompsN(metIdx);
    +0106 notNewMet=newMetCompsN(ismember(newMetCompsN,metCompsN));
    +0107 
    +0108 if ~isempty(notNewMet)
    +0109     fprintf('\nThe following metabolites were already present in the model and will not be added:\n')
    +0110     %Metabolite names are arbitrary text and may contain "%"; print as
    +0111     %literal data rather than as an fprintf format string.
    +0112     fprintf('%s\n', strjoin(transpose(notNewMet),newline))
    +0113 end
    +0114 
    +0115 metIdx=metIdx(~ismember(sourcemetCompsN(metIdx),metCompsN));
    +0116 
    +0117 if ~isempty(metIdx)
    +0118     fprintf('\nThe following metabolites will be added to the model:\n')
    +0119     %Metabolite names are arbitrary text and may contain "%"; print as
    +0120     %literal data rather than as an fprintf format string.
    +0121     fprintf('%s\n', strjoin(transpose(sourcemetCompsN(metIdx)),newline))
    +0122     
    +0123     if isfield(sourceModel,'mets')
    +0124         metsToAdd.mets=sourceModel.mets(metIdx);
    +0125     end
    +0126     if isfield(sourceModel,'metNames')
    +0127         metsToAdd.metNames=sourceModel.metNames(metIdx);
    +0128     end
    +0129     if isfield(sourceModel,'metFormulas')
    +0130         metsToAdd.metFormulas=sourceModel.metFormulas(metIdx);
    +0131     end
    +0132     if isfield(sourceModel,'metCharges')
    +0133         metsToAdd.metCharges=sourceModel.metCharges(metIdx);
    +0134     end
    +0135     if isfield(sourceModel,'metMiriams')
    +0136         metsToAdd.metMiriams=sourceModel.metMiriams(metIdx);
    +0137     end
    +0138     if isfield(sourceModel,'metNotes')
    +0139         metsToAdd.metNotes=sourceModel.metNotes(metIdx);
    +0140     end
    +0141     if isfield(sourceModel,'inchis')
    +0142         metsToAdd.inchis=sourceModel.inchis(metIdx);
    +0143     end
    +0144     if isfield(sourceModel,'metSmiles')
    +0145         metsToAdd.metSmiles=sourceModel.metSmiles(metIdx);
    +0146     end
    +0147     if isfield(sourceModel,'metDeltaG')
    +0148         metsToAdd.metDeltaG=sourceModel.metDeltaG(metIdx);
    +0149     end
    +0150     
    +0151     metsToAdd.compartments=strtrim(cellstr(num2str(sourceModel.metComps(metIdx)))); % Convert from compartment string to compartment number
    +0152     [~,idx]=ismember(metsToAdd.compartments,strsplit(num2str(1:length(sourceModel.comps)))); % Match compartment number to compartment abbreviation
    +0153     metsToAdd.compartments=sourceModel.comps(idx); % Fill in compartment abbreviations
    +0154     
    +0155     model=addMets(model,metsToAdd);
    +0156 end
    +0157 fprintf('\nNumber of metabolites added to the model:\n')
    +0158 fprintf([num2str(numel(metIdx)),'\n'])
    +0159 
    +0160 % Add new genes
    +0161 if ~islogical(addGene) | addGene ~= false
    +0162     if ischar(addGene)
    +0163         rxnToAdd.grRules={addGene};
    +0164     elseif iscell(addGene)
    +0165         rxnToAdd.grRules=addGene(~notNewRxn); 
    +0166     else
    +0167         rxnToAdd.grRules=sourceModel.grRules(rxnIdx); % Get the relevant grRules
    +0168     end
    +0169 end
    +0170 % Add new reactions
    +0171 rxnToAdd.equations=constructEquations(sourceModel,rxnIdx);
    +0172 rxnToAdd.rxnNames=sourceModel.rxnNames(rxnIdx);
    +0173 rxnToAdd.rxns=sourceModel.rxns(rxnIdx);
    +0174 rxnToAdd.lb=sourceModel.lb(rxnIdx);
    +0175 rxnToAdd.ub=sourceModel.ub(rxnIdx);
    +0176 rxnToAdd.rxnNotes(:)=rxnNote(~notNewRxn);
    +0177 rxnToAdd.rxnConfidenceScores=NaN(1,numel(rxnToAdd.rxns));
    +0178 if ~isnumeric(confidence)
    +0179     EM='confidence score must be numeric';
    +0180     error('RAVEN:badInput', '%s', EM);
    +0181 end
    +0182 rxnToAdd.rxnConfidenceScores(:)=confidence;
    +0183 if isfield(sourceModel,'rxnDeltaG')
    +0184     rxnToAdd.rxnDeltaG=sourceModel.rxnDeltaG(rxnIdx);
    +0185 end
    +0186 if isfield(sourceModel,'subSystems')
    +0187     rxnToAdd.subSystems=sourceModel.subSystems(rxnIdx);
    +0188 end
    +0189 if isfield(sourceModel,'eccodes')
    +0190     rxnToAdd.eccodes=sourceModel.eccodes(rxnIdx);
    +0191 end
    +0192 model=addRxns(model,rxnToAdd,3,'',false,true);
    +0193 
    +0194 fprintf('\nNumber of reactions added to the model:\n')
    +0195 fprintf([num2str(numel(rxnIdx)),'\n'])
    +0196 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/addTransport.html b/docs/html/manipulation/addTransport.html new file mode 100644 index 00000000..244b4bb2 --- /dev/null +++ b/docs/html/manipulation/addTransport.html @@ -0,0 +1,294 @@ + + + + Description of addTransport + + + + + + + + + +
    Home > manipulation > addTransport.m
    + + + +

    addTransport +

    + +

    PURPOSE ^

    +
    addTransport Add transport reactions between compartments.
    + +

    SYNOPSIS ^

    +
    function [model, addedRxns]=addTransport(model,fromComp,toComps,varargin)
    + +

    DESCRIPTION ^

    +
     addTransport  Add transport reactions between compartments.
    +
    + The function builds the stoichiometric columns directly from the metabolite names,
    + without the equation parsing of addRxns, which makes it faster for adding transport
    + reactions. New reaction names
    + have the format "metaboliteName transport, fromCompName-toCompName", where the
    + compartment names are the entries of model.compNames. New reaction ids are numbered
    + sequentially with the prefix tr_ (tr_0001, tr_0002, ...).
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + fromComp : char
    +     the id of the compartment to transport from. Must match one entry of model.comps.
    + toComps : cell
    +     the ids of the compartments to transport to. Each must match an entry of
    +     model.comps.
    +
    + Name-Value Arguments
    + --------------------
    + metNames : cell
    +     the metabolite names to add transport for (default all metabolites
    +     in fromComp).
    + isRev : logical
    +     true if the transport reactions should be reversible (default true).
    + onlyToExisting : logical
    +     true to add transport of a metabolite only if the metabolite exists in the target
    +     compartment. If false, the missing metabolites are first added with addMets
    +     (default true).
    + prefix : char
    +     prefix of the reaction ids (default "tr_").
    +
    + Returns
    + -------
    + model : struct
    +     updated model structure.
    + addedRxns : cell
    +     ids of the added reactions.
    +
    + Examples
    + --------
    +     [model, addedRxns] = addTransport(model, fromComp, toComps, ...
    +                          metNames, isRev, onlyToExisting, prefix);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • addMets addMets Add metabolites to a model.
    • generateNewIds generateNewIds Generate a list of new metabolite or reaction ids.
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function [model, addedRxns]=addTransport(model,fromComp,toComps,varargin)
    +0002 % addTransport  Add transport reactions between compartments.
    +0003 %
    +0004 % The function builds the stoichiometric columns directly from the metabolite names,
    +0005 % without the equation parsing of addRxns, which makes it faster for adding transport
    +0006 % reactions. New reaction names
    +0007 % have the format "metaboliteName transport, fromCompName-toCompName", where the
    +0008 % compartment names are the entries of model.compNames. New reaction ids are numbered
    +0009 % sequentially with the prefix tr_ (tr_0001, tr_0002, ...).
    +0010 %
    +0011 % Parameters
    +0012 % ----------
    +0013 % model : struct
    +0014 %     a model structure.
    +0015 % fromComp : char
    +0016 %     the id of the compartment to transport from. Must match one entry of model.comps.
    +0017 % toComps : cell
    +0018 %     the ids of the compartments to transport to. Each must match an entry of
    +0019 %     model.comps.
    +0020 %
    +0021 % Name-Value Arguments
    +0022 % --------------------
    +0023 % metNames : cell
    +0024 %     the metabolite names to add transport for (default all metabolites
    +0025 %     in fromComp).
    +0026 % isRev : logical
    +0027 %     true if the transport reactions should be reversible (default true).
    +0028 % onlyToExisting : logical
    +0029 %     true to add transport of a metabolite only if the metabolite exists in the target
    +0030 %     compartment. If false, the missing metabolites are first added with addMets
    +0031 %     (default true).
    +0032 % prefix : char
    +0033 %     prefix of the reaction ids (default "tr_").
    +0034 %
    +0035 % Returns
    +0036 % -------
    +0037 % model : struct
    +0038 %     updated model structure.
    +0039 % addedRxns : cell
    +0040 %     ids of the added reactions.
    +0041 %
    +0042 % Examples
    +0043 % --------
    +0044 %     [model, addedRxns] = addTransport(model, fromComp, toComps, ...
    +0045 %                          metNames, isRev, onlyToExisting, prefix);
    +0046 
    +0047 fromComp=char(fromComp);
    +0048 [I, fromID]=ismember(model.comps,fromComp);
    +0049 fromID=find(fromID);
    +0050 if sum(I)~=1
    +0051     EM='fromComps must have exactly one match in model.comps';
    +0052     error('RAVEN:badInput', '%s', EM);
    +0053 end
    +0054 toComps=convertCharArray(toComps);
    +0055 [I, toIDs]=ismember(toComps,model.comps);
    +0056 if ~all(I)
    +0057     EM='All compartments in toComps must have a match in model.comps';
    +0058     error('RAVEN:badInput', '%s', EM);
    +0059 end
    +0060 p=parseRAVENargs(varargin, {'metNames',[]; 'isRev',true; 'onlyToExisting',true; 'prefix','tr_'});
    +0061 isRev=p.isRev;
    +0062 onlyToExisting=p.onlyToExisting;
    +0063 prefix=char(p.prefix);
    +0064 if isempty(p.metNames)
    +0065     %Find all metabolites in fromComp
    +0066     metNames=model.metNames(model.metComps==fromID);
    +0067 else
    +0068     metNames=convertCharArray(p.metNames);
    +0069 end
    +0070 %Normalise to a column vector regardless of the orientation the caller
    +0071 %passed metNames in (e.g. a literal {'a','b','c'} is a row): everything
    +0072 %derived from it below is concatenated against model fields, which are
    +0073 %always columns.
    +0074 metNames=metNames(:);
    +0075 
    +0076 %Check that the names are unique
    +0077 if numel(unique(metNames))~=numel(metNames)
    +0078     error('RAVEN:badInput', '%s', 'Not all metabolite names are unique');
    +0079 end
    +0080 
    +0081 %Get the indexes of the mets in fromComp
    +0082 I=find(model.metComps==fromID);
    +0083 [J, K]=ismember(metNames,model.metNames(I));
    +0084 if ~all(J)
    +0085     EM='Not all metabolites in metNames exist in fromComp';
    +0086     error('RAVEN:badInput', '%s', EM);
    +0087 end
    +0088 fromMets=I(K); %These are the ids of the metabolites to transport. The order corresponds to metNames
    +0089 addedRxns={};
    +0090 %Loop through and add for each compartment in toComps
    +0091 for i=1:numel(toComps)
    +0092     fromMetsInComp=fromMets; %If onlyToExisting==true then not all mets are transported to each compartment
    +0093     %Get the indexes of the mets in the compartment
    +0094     I=find(model.metComps==toIDs(i));
    +0095     [J, K]=ismember(metNames,model.metNames(I));
    +0096     if onlyToExisting==true || all(J)
    +0097         toMets=I(K(J)); %Only look at the existing ones
    +0098         fromMetsInComp=fromMetsInComp(J);
    +0099         namesInComp=metNames(J); %Matches fromMetsInComp/toMets below
    +0100     else
    +0101         %This is if not all metabolites exist in the target compartment,
    +0102         %and they should be added
    +0103         metsToAdd.metNames=metNames(J==0);
    +0104         metsToAdd.compartments=toComps{i};
    +0105         model=addMets(model,metsToAdd);
    +0106 
    +0107         %Redo the mapping when all mets are there. A bit lazy, but it is
    +0108         %fast anyways
    +0109         I=find(model.metComps==toIDs(i));
    +0110         [~, K]=ismember(metNames,model.metNames(I));
    +0111         toMets=I(K); %All are guaranteed to be found now
    +0112         namesInComp=metNames; %All requested metabolites now exist
    +0113     end
    +0114     
    +0115     %Construct the S matrix directly as sparse (indexed assignment on a
    +0116     %sparse matrix still overwrites rather than summing on a repeated
    +0117     %index, same as on a dense one): a dense (mets x newRxns) intermediate
    +0118     %is wasted allocation for a genome-scale model.
    +0119     nRxns=numel(fromMetsInComp);
    +0120     newS=sparse(numel(model.mets),nRxns);
    +0121     newS(sub2ind(size(newS),fromMetsInComp(:),(1:nRxns)'))=-1;
    +0122     newS(sub2ind(size(newS),toMets(:),(1:nRxns)'))=1;
    +0123 
    +0124     %Add the reactions
    +0125     model.S=[model.S newS];
    +0126     if isfield(model,'annotation') && isfield(model.annotation,'defaultLB')
    +0127         lb = model.annotation.defaultLB;
    +0128         ub = model.annotation.defaultUB;
    +0129     else
    +0130         lb = -inf;
    +0131         ub = inf;
    +0132     end
    +0133     if isRev==true
    +0134         model.lb=[model.lb;ones(nRxns,1)*lb];
    +0135         model.rev=[model.rev;ones(nRxns,1)];
    +0136     else
    +0137         model.lb=[model.lb;zeros(nRxns,1)];
    +0138         model.rev=[model.rev;zeros(nRxns,1)];
    +0139     end
    +0140     model.ub=[model.ub;ones(nRxns,1)*ub];
    +0141     model.c=[model.c;zeros(nRxns,1)];
    +0142     
    +0143     %Add annotation
    +0144     filler=cell(nRxns,1);
    +0145     filler(:)={''};
    +0146     addedRxnsID=generateNewIds(model,'rxns',prefix,nRxns);
    +0147     addedRxnsName=strcat(namesInComp, {' transport, '}, model.compNames(fromID), '-', model.compNames(toIDs(i)));
    +0148     addedRxnsName=addedRxnsName(:); %Defensive: keep column orientation regardless of namesInComp's
    +0149     model.rxns=[model.rxns;addedRxnsID];
    +0150     model.rxnNames=[model.rxnNames;addedRxnsName];
    +0151     
    +0152     if isfield(model,'eccodes')
    +0153         model.eccodes=[model.eccodes;filler];
    +0154     end
    +0155     if isfield(model,'subSystems')
    +0156         isNested = any(cellfun(@(x) iscell(x), model.subSystems));
    +0157         ssFiller = filler;
    +0158         if isRev==1
    +0159             ssFiller(:) = {['Transport between ' fromComp ' and ' toComps{i}]};
    +0160         else
    +0161             ssFiller(:) = {['Transport from ' fromComp ' to ' toComps{i}]};
    +0162         end
    +0163         if isNested
    +0164             ssFiller = cellfun(@(x) {x}, ssFiller, 'uni', 0);
    +0165         end
    +0166         model.subSystems=[model.subSystems;ssFiller];
    +0167     end
    +0168     if isfield(model,'grRules')
    +0169         model.grRules=[model.grRules;filler];
    +0170     end
    +0171     if isfield(model,'rxnFrom')
    +0172         model.rxnFrom=[model.rxnFrom;filler];
    +0173     end
    +0174     if isfield(model,'rxnMiriams')
    +0175         model.rxnMiriams=[model.rxnMiriams;cell(nRxns,1)];
    +0176     end
    +0177     if isfield(model,'rxnComps')
    +0178         model.rxnComps=[model.rxnComps;ones(nRxns,1)];
    +0179         fprintf('NOTE: The added transport reactions will be added to the first compartment\n');
    +0180     end
    +0181     if isfield(model,'rxnGeneMat')
    +0182         model.rxnGeneMat=[model.rxnGeneMat;sparse(nRxns,numel(model.genes))];
    +0183     end
    +0184     if isfield(model,'rxnNotes')
    +0185         model.rxnNotes=[model.rxnNotes;filler];
    +0186     end
    +0187     if isfield(model,'rxnReferences')
    +0188         model.rxnReferences=[model.rxnReferences;filler];
    +0189     end
    +0190     if isfield(model,'rxnConfidenceScores')
    +0191         model.rxnConfidenceScores=[model.rxnConfidenceScores;NaN(nRxns,1)];
    +0192     end
    +0193     if isfield(model,'rxnDeltaG')
    +0194         model.rxnDeltaG=[model.rxnDeltaG;NaN(nRxns,1)];
    +0195     end
    +0196     if isfield(model,'pwys')
    +0197         model.pwys=[model.pwys;filler];
    +0198     end
    +0199     if isfield(model,'spontaneous')
    +0200         model.spontaneous=[model.spontaneous;false(nRxns,1)];
    +0201     end
    +0202     addedRxns = [addedRxns; addedRxnsID];
    +0203 end
    +0204 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/changeGrRules.html b/docs/html/manipulation/changeGrRules.html new file mode 100644 index 00000000..3032223e --- /dev/null +++ b/docs/html/manipulation/changeGrRules.html @@ -0,0 +1,148 @@ + + + + Description of changeGrRules + + + + + + + + + +
    Home > manipulation > changeGrRules.m
    + + + +

    changeGrRules +

    + +

    PURPOSE ^

    +
    changeGrRules Change the grRules of multiple reactions.
    + +

    SYNOPSIS ^

    +
    function model = changeGrRules(model,rxns,grRules,varargin)
    + +

    DESCRIPTION ^

    +
     changeGrRules  Change the grRules of multiple reactions.
    +
    + Genes in grRules that are not in the model are added to the model.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure to change the gene association.
    + rxns : char or cell
    +     reaction IDs.
    + grRules : char or cell
    +     the replacement or additional gene association. ' and ' indicates subunits, ' or '
    +     indicates isoenzymes, and brackets '()' separate different instances. A single
    +     rule applies to all reactions in rxns.
    +
    + Name-Value Arguments
    + --------------------
    + replace : logical
    +     true to replace the old gene association with the new association. False to
    +     combine them as "(old) or (new)" (default true).
    +
    + Returns
    + -------
    + model : struct
    +     an updated model structure.
    +
    + Examples
    + --------
    +     model = changeGrRules(model, rxns, grRules, replace);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function model = changeGrRules(model,rxns,grRules,varargin)
    +0002 % changeGrRules  Change the grRules of multiple reactions.
    +0003 %
    +0004 % Genes in grRules that are not in the model are added to the model.
    +0005 %
    +0006 % Parameters
    +0007 % ----------
    +0008 % model : struct
    +0009 %     a model structure to change the gene association.
    +0010 % rxns : char or cell
    +0011 %     reaction IDs.
    +0012 % grRules : char or cell
    +0013 %     the replacement or additional gene association. ' and ' indicates subunits, ' or '
    +0014 %     indicates isoenzymes, and brackets '()' separate different instances. A single
    +0015 %     rule applies to all reactions in rxns.
    +0016 %
    +0017 % Name-Value Arguments
    +0018 % --------------------
    +0019 % replace : logical
    +0020 %     true to replace the old gene association with the new association. False to
    +0021 %     combine them as "(old) or (new)" (default true).
    +0022 %
    +0023 % Returns
    +0024 % -------
    +0025 % model : struct
    +0026 %     an updated model structure.
    +0027 %
    +0028 % Examples
    +0029 % --------
    +0030 %     model = changeGrRules(model, rxns, grRules, replace);
    +0031 
    +0032 p=parseRAVENargs(varargin, {'replace',true});
    +0033 replace=p.replace;
    +0034 
    +0035 rxns=convertCharArray(rxns);
    +0036 grRules=convertCharArray(grRules);
    +0037 
    +0038 if isscalar(grRules) && ~isscalar(rxns)
    +0039     grRules = repmat(grRules,1,numel(rxns));
    +0040 end
    +0041 if ~(numel(grRules)==numel(rxns))
    +0042     error('Number of rxns and grRules should be identical')
    +0043 end
    +0044 
    +0045 % Add genes to model
    +0046 geneList = getGenesFromGrRules(grRules);
    +0047 genesToAdd.genes=setdiff(geneList,model.genes); % Only keep the genes that are not yet part of the model.genes.
    +0048 if ~isempty(genesToAdd.genes)
    +0049     model=addGenesRaven(model,genesToAdd); % Add genes
    +0050 end
    +0051     
    +0052 % Find reaction and gene indices
    +0053 idx=getIndexes(model,rxns,'rxns');
    +0054 
    +0055 % Change gene associations
    +0056 if replace==true % Replace old gene associations
    +0057     model.grRules(idx)=grRules;
    +0058 else % Add gene associations, add new gene rules after 'OR'.
    +0059     % A reaction with no existing grRule yet gets the new rule directly:
    +0060     % wrapping it as '() or (...)' regardless left an empty first clause,
    +0061     % a string neither grRuleToDNF nor isDnfGrRule can parse.
    +0062     existingRules=model.grRules(idx);
    +0063     hasExisting=~cellfun(@isempty,existingRules);
    +0064     combined=grRules;
    +0065     combined(hasExisting)=strcat('(',existingRules(hasExisting),') or (',grRules(hasExisting),')');
    +0066     model.grRules(idx)=combined;
    +0067 end
    +0068 
    +0069 %Fix grRules and reconstruct rxnGeneMat
    +0070 [grRules,rxnGeneMat] = standardizeGrRules(model,true);
    +0071 model.grRules = grRules;
    +0072 model.rxnGeneMat = rxnGeneMat;
    +0073 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/changeRxns.html b/docs/html/manipulation/changeRxns.html new file mode 100644 index 00000000..9376d8c3 --- /dev/null +++ b/docs/html/manipulation/changeRxns.html @@ -0,0 +1,255 @@ + + + + Description of changeRxns + + + + + + + + + +
    Home > manipulation > changeRxns.m
    + + + +

    changeRxns +

    + +

    PURPOSE ^

    +
    changeRxns Modify the equations of reactions in a model.
    + +

    SYNOPSIS ^

    +
    function model=changeRxns(model,rxns,equations,varargin)
    + +

    DESCRIPTION ^

    +
     changeRxns  Modify the equations of reactions in a model.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + rxns : cell
    +     cell array with reaction ids.
    + equations : cell or struct
    +     cell array with equations, or a structure with the fields "mets" and
    +     "stoichCoeffs" as in addRxns, for example:
    +
    +     - equations.mets = {{"met1","met2"},{"met1","met3"}}
    +     - equations.stoichCoeffs = {[-1,+2],[-1,+1]}
    +
    + Name-Value Arguments
    + --------------------
    + eqnType : double
    +     the interpretation of the equation strings (default 1):
    +
    +     - 1 : the metabolites are matched to model.mets. New metabolites (if
    +       allowed) are added to "compartment".
    +     - 2 : the metabolites are matched to model.metNames and all
    +       metabolites are assigned to "compartment". New metabolites get
    +       ids generated by generateNewIds with the prefix "m_", numbered from the
    +       highest existing number with that prefix plus 1.
    +     - 3 : the metabolites are written as "metNames[comps]". Only
    +       compartments in model.comps are allowed. New metabolites get ids
    +       generated in the same way as for eqnType 2.
    + compartment : char
    +     the compartment of the metabolites for eqnType=2 and of new metabolites for
    +     eqnType=1. Must match model.comps. Not used for eqnType=3.
    + allowNewMets : logical
    +     true to allow the function to add new metabolites. The function calls addMets
    +     with copyInfo true, so a new metabolite takes the charge and deltaG from a
    +     metabolite with the same name in another compartment, and InChI, SMILES,
    +     formula and MIRIAM annotation from it when its own are empty. When the
    +     annotation of new metabolites is to be set explicitly, or when they are to be
    +     exchange (unconstrained) metabolites, adding them with addMets first gives
    +     that control (default false).
    +
    + Returns
    + -------
    + model : struct
    +     an updated model structure.
    +
    + Examples
    + --------
    +     model = changeRxns(model, rxns, equations, eqnType, compartment, allowNewMets);
    +
    + Notes
    + -----
    + The function keeps the bounds of the reactions. Changing an irreversible reaction to a
    + reversible one, or the reverse, changes the model.rev field but not model.lb and
    + model.ub, so the bounds still define the reversibility of the reaction. setParam
    + changes the bounds.
    +
    + New metabolites receive the annotation of the metabolite with the same name in
    + another compartment, when one exists (see allowNewMets).
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • addRxns addRxns Add reactions to a model.
    • permuteModel permuteModel Change the order of the reactions or metabolites in a model.
    • removeReactions removeReactions Delete a set of reactions from a model.
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function model=changeRxns(model,rxns,equations,varargin)
    +0002 % changeRxns  Modify the equations of reactions in a model.
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % model : struct
    +0007 %     a model structure.
    +0008 % rxns : cell
    +0009 %     cell array with reaction ids.
    +0010 % equations : cell or struct
    +0011 %     cell array with equations, or a structure with the fields "mets" and
    +0012 %     "stoichCoeffs" as in addRxns, for example:
    +0013 %
    +0014 %     - equations.mets = {{"met1","met2"},{"met1","met3"}}
    +0015 %     - equations.stoichCoeffs = {[-1,+2],[-1,+1]}
    +0016 %
    +0017 % Name-Value Arguments
    +0018 % --------------------
    +0019 % eqnType : double
    +0020 %     the interpretation of the equation strings (default 1):
    +0021 %
    +0022 %     - 1 : the metabolites are matched to model.mets. New metabolites (if
    +0023 %       allowed) are added to "compartment".
    +0024 %     - 2 : the metabolites are matched to model.metNames and all
    +0025 %       metabolites are assigned to "compartment". New metabolites get
    +0026 %       ids generated by generateNewIds with the prefix "m_", numbered from the
    +0027 %       highest existing number with that prefix plus 1.
    +0028 %     - 3 : the metabolites are written as "metNames[comps]". Only
    +0029 %       compartments in model.comps are allowed. New metabolites get ids
    +0030 %       generated in the same way as for eqnType 2.
    +0031 % compartment : char
    +0032 %     the compartment of the metabolites for eqnType=2 and of new metabolites for
    +0033 %     eqnType=1. Must match model.comps. Not used for eqnType=3.
    +0034 % allowNewMets : logical
    +0035 %     true to allow the function to add new metabolites. The function calls addMets
    +0036 %     with copyInfo true, so a new metabolite takes the charge and deltaG from a
    +0037 %     metabolite with the same name in another compartment, and InChI, SMILES,
    +0038 %     formula and MIRIAM annotation from it when its own are empty. When the
    +0039 %     annotation of new metabolites is to be set explicitly, or when they are to be
    +0040 %     exchange (unconstrained) metabolites, adding them with addMets first gives
    +0041 %     that control (default false).
    +0042 %
    +0043 % Returns
    +0044 % -------
    +0045 % model : struct
    +0046 %     an updated model structure.
    +0047 %
    +0048 % Examples
    +0049 % --------
    +0050 %     model = changeRxns(model, rxns, equations, eqnType, compartment, allowNewMets);
    +0051 %
    +0052 % Notes
    +0053 % -----
    +0054 % The function keeps the bounds of the reactions. Changing an irreversible reaction to a
    +0055 % reversible one, or the reverse, changes the model.rev field but not model.lb and
    +0056 % model.ub, so the bounds still define the reversibility of the reaction. setParam
    +0057 % changes the bounds.
    +0058 %
    +0059 % New metabolites receive the annotation of the metabolite with the same name in
    +0060 % another compartment, when one exists (see allowNewMets).
    +0061 
    +0062 p=parseRAVENargs(varargin, {'eqnType',1; 'compartment',[]; 'allowNewMets',false});
    +0063 eqnType=p.eqnType;
    +0064 compartment=p.compartment;
    +0065 allowNewMets=p.allowNewMets;
    +0066 
    +0067 rxns=convertCharArray(rxns);
    +0068 compartment=char(compartment);
    +0069 
    +0070 %Find the indexes of the reactions and throw an error if they are not all
    +0071 %found
    +0072 [I, J]=ismember(rxns,model.rxns);
    +0073 if ~all(I)
    +0074     EM='All reaction ids must exist in the model';
    +0075     error('RAVEN:badInput', '%s', EM);
    +0076 end
    +0077 
    +0078 %The reactions are changed in the following manner. First create a
    +0079 %rxns-structure by copying info from the model. Then remove the old
    +0080 %reactions. Then add the updated ones using addRxns. Lastly, the model is
    +0081 %reordered to match the original order. This is done like this to make use
    +0082 %of the advanced parsing of equations that addRxns use.
    +0083 rxnsToChange.rxns=rxns;
    +0084 if isfield(equations,'mets') && isfield(equations,'stoichCoeffs')
    +0085     rxnsToChange.mets=equations.mets;
    +0086     rxnsToChange.stoichCoeffs=equations.stoichCoeffs;
    +0087 else
    +0088     rxnsToChange.equations=convertCharArray(equations);
    +0089 end
    +0090 if isfield(model,'rxnNames')
    +0091     rxnsToChange.rxnNames=model.rxnNames(J);
    +0092 end
    +0093 if isfield(model,'lb')
    +0094     rxnsToChange.lb=model.lb(J);
    +0095 end
    +0096 if isfield(model,'ub')
    +0097     rxnsToChange.ub=model.ub(J);
    +0098 end
    +0099 if isfield(model,'c')
    +0100     rxnsToChange.c=model.c(J);
    +0101 end
    +0102 if isfield(model,'eccodes')
    +0103     rxnsToChange.eccodes=model.eccodes(J);
    +0104 end
    +0105 if isfield(model,'subSystems')
    +0106     rxnsToChange.subSystems=model.subSystems(J);
    +0107 end
    +0108 if isfield(model,'rxnComps')
    +0109     rxnsToChange.rxnComps=model.rxnComps(J);
    +0110 end
    +0111 if isfield(model,'grRules')
    +0112     rxnsToChange.grRules=model.grRules(J);
    +0113 end
    +0114 if isfield(model,'rxnFrom')
    +0115     rxnsToChange.rxnFrom=model.rxnFrom(J);
    +0116 end
    +0117 if isfield(model,'rxnScores')
    +0118     rxnsToChange.rxnScores=model.rxnScores(J);
    +0119 end
    +0120 if isfield(model,'rxnMiriams')
    +0121     rxnsToChange.rxnMiriams=model.rxnMiriams(J);
    +0122 end
    +0123 if isfield(model,'rxnNotes')
    +0124     rxnsToChange.rxnNotes=model.rxnNotes(J);
    +0125 end
    +0126 if isfield(model,'rxnReferences')
    +0127     rxnsToChange.rxnReferences=model.rxnReferences(J);
    +0128 end
    +0129 if isfield(model,'rxnConfidenceScores')
    +0130     rxnsToChange.rxnConfidenceScores=model.rxnConfidenceScores(J);
    +0131 end
    +0132 if isfield(model,'rxnDeltaG')
    +0133     rxnsToChange.rxnDeltaG=model.rxnDeltaG(J);
    +0134 end
    +0135 if isfield(model,'pwys')
    +0136     rxnsToChange.pwys=model.pwys(J);
    +0137 end
    +0138 
    +0139 %Calculate the new order of reactions
    +0140 order=ones(numel(model.rxns),1);
    +0141 order(J)=0;
    +0142 order=cumsum(order);
    +0143 order(J)=order(end)+1:order(end)+numel(rxns);
    +0144 
    +0145 %Remove the original reactions
    +0146 model=removeReactions(model,rxns);
    +0147 
    +0148 model=addRxns(model,rxnsToChange,eqnType,compartment,allowNewMets);
    +0149 model=permuteModel(model,order,'rxns');
    +0150 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/manipulation/closeModel.html b/docs/html/manipulation/closeModel.html similarity index 73% rename from doc/manipulation/closeModel.html rename to docs/html/manipulation/closeModel.html index 7cf4196c..189f0511 100644 --- a/doc/manipulation/closeModel.html +++ b/docs/html/manipulation/closeModel.html @@ -4,7 +4,7 @@ Description of closeModel - + @@ -21,15 +21,21 @@

    closeModel

    PURPOSE ^

    -
    closeModel Add boundary metabolites and their exchange reactions.
    +
    closeModel Add boundary metabolites to exchange reactions.

    SYNOPSIS ^

    function closedModel=closeModel(model)

    DESCRIPTION ^

    -
     closeModel  Add boundary metabolites and their exchange reactions.
    +
     closeModel  Add boundary metabolites to exchange reactions.
     
    - Adds boundary metabolites and their participation in exchange reactions.
    + Adds a compartment "b" (boundary) and a boundary metabolite in it for every reaction
    + that has metabolites on one side only (exchange, sink and demand reactions). The
    + boundary metabolite takes part in the reaction with the coefficient 1 or -1 and is
    + marked as unconstrained in model.unconstrained; all other metabolites are marked as
    + constrained. A reaction with one metabolite gets the boundary metabolite "<id>_b" with
    + the annotation of that metabolite; a reaction with several metabolites gets
    + "<reaction id>_b" without annotation.
     
      Parameters
      ----------
    @@ -59,46 +65,46 @@ 

    CROSS-REFERENCE INFORMATION ^
 
 <h2><a name=SOURCE CODE ^

    0001 function closedModel=closeModel(model)
    -0002 % closeModel  Add boundary metabolites and their exchange reactions.
    +0002 % closeModel  Add boundary metabolites to exchange reactions.
     0003 %
    -0004 % Adds boundary metabolites and their participation in exchange reactions.
    -0005 %
    -0006 % Parameters
    -0007 % ----------
    -0008 % model : struct
    -0009 %     a model structure.
    -0010 %
    -0011 % Returns
    -0012 % -------
    -0013 % closedModel : struct
    -0014 %     an updated model structure with boundary metabolites added.
    -0015 %
    -0016 % Examples
    -0017 % --------
    -0018 %     closedModel = closeModel(model);
    -0019 
    -0020 closedModel=model;
    -0021 
    -0022 closedModel.comps{numel(closedModel.comps)+1,1}='b';
    -0023 closedModel.compNames{numel(closedModel.compNames)+1,1}='boundary';
    -0024 if isfield(closedModel,'compMiriams')
    -0025     closedModel.compMiriams{numel(closedModel.compMiriams)+1,1}=[];
    -0026 end
    -0027 closedModel.unconstrained=zeros(numel(closedModel.mets),1);
    -0028 
    -0029 for i=1:numel(closedModel.rxns)
    -0030     col=closedModel.S(:,i);
    -0031     %A boundary/exchange/sink/demand reaction has metabolites on only one
    -0032     %side, same definition as getExchangeRxns: no substrates or no
    -0033     %products. The previous coefficient-magnitude rule (all nonzero
    -0034     %coefficients summing to 1 in absolute value) missed a scaled
    -0035     %single-metabolite sink such as "2 A =>" (sum 2) outright, and only
    -0036     %matched a multi-metabolite one-sided reaction such as
    -0037     %"0.5 A + 0.5 B =>" by coincidence, when its magnitudes summed to
    -0038     %exactly 1 --- and even then mishandled it below, since find() on such
    -0039     %a reaction returns more than one index and only the first metabolite
    -0040     %was ever used to build the boundary metabolite, silently dropping the
    -0041     %rest.
    +0004 % Adds a compartment "b" (boundary) and a boundary metabolite in it for every reaction
    +0005 % that has metabolites on one side only (exchange, sink and demand reactions). The
    +0006 % boundary metabolite takes part in the reaction with the coefficient 1 or -1 and is
    +0007 % marked as unconstrained in model.unconstrained; all other metabolites are marked as
    +0008 % constrained. A reaction with one metabolite gets the boundary metabolite "<id>_b" with
    +0009 % the annotation of that metabolite; a reaction with several metabolites gets
    +0010 % "<reaction id>_b" without annotation.
    +0011 %
    +0012 % Parameters
    +0013 % ----------
    +0014 % model : struct
    +0015 %     a model structure.
    +0016 %
    +0017 % Returns
    +0018 % -------
    +0019 % closedModel : struct
    +0020 %     an updated model structure with boundary metabolites added.
    +0021 %
    +0022 % Examples
    +0023 % --------
    +0024 %     closedModel = closeModel(model);
    +0025 
    +0026 closedModel=model;
    +0027 
    +0028 closedModel.comps{numel(closedModel.comps)+1,1}='b';
    +0029 closedModel.compNames{numel(closedModel.compNames)+1,1}='boundary';
    +0030 if isfield(closedModel,'compMiriams')
    +0031     closedModel.compMiriams{numel(closedModel.compMiriams)+1,1}=[];
    +0032 end
    +0033 closedModel.unconstrained=zeros(numel(closedModel.mets),1);
    +0034 
    +0035 for i=1:numel(closedModel.rxns)
    +0036     col=closedModel.S(:,i);
    +0037     %A boundary/exchange/sink/demand reaction has metabolites on only one
    +0038     %side, same definition as getExchangeRxns: no substrates or no
    +0039     %products. The coefficient magnitudes do not matter, so a scaled sink
    +0040     %such as "2 A =>" and a multi-metabolite one-sided reaction such as
    +0041     %"0.5 A + 0.5 B =>" are both boundary reactions.
     0042     if any(col>0) && any(col<0)
     0043         continue
     0044     end
    @@ -107,7 +113,7 @@ 

    SOURCE CODE ^continue 0048 end 0049 %A single metabolite names and inherits properties from that -0050 %metabolite, as before. Several metabolites on the one populated side +0050 %metabolite. Several metabolites on the one populated side 0051 %have no single metabolite to name the boundary after or copy 0052 %properties from, so it is named after the reaction instead and left 0053 %without those properties. diff --git a/docs/html/manipulation/contractModel.html b/docs/html/manipulation/contractModel.html new file mode 100644 index 00000000..b271ee38 --- /dev/null +++ b/docs/html/manipulation/contractModel.html @@ -0,0 +1,283 @@ + + + + Description of contractModel + + + + + + + + + +
    Home > manipulation > contractModel.m
    + + + +

    contractModel +

    + +

    PURPOSE ^

    +
    contractModel Contract a model by merging identical reactions.
    + +

    SYNOPSIS ^

    +
    function [reducedModel, removedRxns, indexedDuplicateRxns]=contractModel(model,varargin)
    + +

    DESCRIPTION ^

    +
     contractModel  Contract a model by merging identical reactions.
    +
    + simplifyModel runs contractModel for its deleteDuplicates option. If the duplicated
    + reactions have "_EXP_*" suffixes (where * is a digit), the model has passed through
    + expandModel, and the function removes these suffixes.
    +
    + The gene associations and EC numbers of merged reactions are combined. The merged
    + reaction keeps the widest bounds and the largest objective coefficient, and a warning
    + is given when the duplicates differ in these. findDuplicateRxns lists duplicate
    + reactions without changing the model.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + distReverse : logical
    +     true to treat reactions with the same metabolites but different reversibility as
    +     different reactions (default true).
    + mets : char or cell
    +     metabolite ids as a string or a cell array of strings; only the reactions that
    +     involve these metabolites are checked for duplication (default: all reactions).
    +     replaceMets uses this option.
    +
    + Returns
    + -------
    + reducedModel : struct
    +     a model structure without duplicate reactions.
    + removedRxns : cell
    +     cell array with the ids of the removed duplicate reactions.
    + indexedDuplicateRxns : cell
    +     cell array with one entry per reaction of reducedModel, holding the ids of the
    +     duplicates removed into that reaction (multiple ids separated by semicolon).
    +
    + Examples
    + --------
    +     [reducedModel, removedRxns, indexedDuplicateRxns] = contractModel(model, distReverse, mets);
    +
    + Notes
    + -----
    + The function may not work for grRules strings with nested expressions of "and" and "or".
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • removeReactions removeReactions Delete a set of reactions from a model.
    • sortModel sortModel Sort the metabolites and reactions of a model.
    • standardizeGrRules standardizeGrRules Standardise gene-reaction rules in a model.
    +This function is called by: +
      +
    • mergeCompartments mergeCompartments Merge all compartments in a model.
    • replaceMets replaceMets Replace a metabolite with another already in the model.
    • simplifyModel simplifyModel Simplify a model by deleting reactions and metabolites.
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [reducedModel, removedRxns, indexedDuplicateRxns]=contractModel(model,varargin)
    +0002 % contractModel  Contract a model by merging identical reactions.
    +0003 %
    +0004 % simplifyModel runs contractModel for its deleteDuplicates option. If the duplicated
    +0005 % reactions have "_EXP_*" suffixes (where * is a digit), the model has passed through
    +0006 % expandModel, and the function removes these suffixes.
    +0007 %
    +0008 % The gene associations and EC numbers of merged reactions are combined. The merged
    +0009 % reaction keeps the widest bounds and the largest objective coefficient, and a warning
    +0010 % is given when the duplicates differ in these. findDuplicateRxns lists duplicate
    +0011 % reactions without changing the model.
    +0012 %
    +0013 % Parameters
    +0014 % ----------
    +0015 % model : struct
    +0016 %     a model structure.
    +0017 %
    +0018 % Name-Value Arguments
    +0019 % --------------------
    +0020 % distReverse : logical
    +0021 %     true to treat reactions with the same metabolites but different reversibility as
    +0022 %     different reactions (default true).
    +0023 % mets : char or cell
    +0024 %     metabolite ids as a string or a cell array of strings; only the reactions that
    +0025 %     involve these metabolites are checked for duplication (default: all reactions).
    +0026 %     replaceMets uses this option.
    +0027 %
    +0028 % Returns
    +0029 % -------
    +0030 % reducedModel : struct
    +0031 %     a model structure without duplicate reactions.
    +0032 % removedRxns : cell
    +0033 %     cell array with the ids of the removed duplicate reactions.
    +0034 % indexedDuplicateRxns : cell
    +0035 %     cell array with one entry per reaction of reducedModel, holding the ids of the
    +0036 %     duplicates removed into that reaction (multiple ids separated by semicolon).
    +0037 %
    +0038 % Examples
    +0039 % --------
    +0040 %     [reducedModel, removedRxns, indexedDuplicateRxns] = contractModel(model, distReverse, mets);
    +0041 %
    +0042 % Notes
    +0043 % -----
    +0044 % The function may not work for grRules strings with nested expressions of "and" and "or".
    +0045 
    +0046 p=parseRAVENargs(varargin, {'distReverse',true; 'mets',[]});
    +0047 distReverse=p.distReverse;
    +0048 mets=p.mets;
    +0049 
    +0050 %First sort the model so that reversible reactions are in the same
    +0051 %direction
    +0052 modelS=sortModel(model,true);
    +0053 
    +0054 %Get a list of duplicate reactions
    +0055 if distReverse
    +0056     x=[modelS.S; model.rev']';
    +0057 else
    +0058     x=modelS.S';
    +0059 end
    +0060 if ~isempty(mets)
    +0061     toCheck = getIndexes(model,mets,'mets');
    +0062     [~, toCheck] = find(modelS.S(toCheck,:));
    +0063     x = [x, (1:size(x,1))']; % Make all other rxns unique by additional column
    +0064     x(toCheck,end) = 0;
    +0065 end
    +0066 
    +0067 [~,I,J] = unique(x,'rows','first');
    +0068 
    +0069 %Initialize cell array of indexedDuplicateRxns
    +0070 indexedDuplicateRxns=cell(numel(model.rxns),1);
    +0071 indexedDuplicateRxns(:)={''};
    +0072 
    +0073 duplicateRxns=setdiff(1:numel(model.rxns),I);
    +0074 mergeTo=I(J(duplicateRxns));
    +0075 
    +0076 mergedRxns=unique(mergeTo);
    +0077 
    +0078 %Now add all the info from this one. Print a warning if they have different
    +0079 %bounds or objective function coefficients. Uses the widest bounds and
    +0080 %largest magnitude of objective coefficient
    +0081 for i=1:numel(mergedRxns)
    +0082     duplRxn=transpose([mergedRxns(i),duplicateRxns(mergeTo==mergedRxns(i))]);
    +0083     if numel(unique(model.lb(duplRxn)))>1
    +0084         EM=['Duplicates of reaction ' model.rxns{mergedRxns(i)} ' have different lower bound. Uses the most negative/smallest lower bound'];
    +0085         warning('RAVEN:warning', '%s', EM);
    +0086         model.lb(mergedRxns(i))=min(model.lb(duplRxn));
    +0087     end
    +0088     if numel(unique(model.ub(duplRxn)))>1
    +0089         EM=['Duplicates of reaction ' model.rxns{mergedRxns(i)} ' have different upper bound. Uses the most positive/largest upper bound'];
    +0090         warning('RAVEN:warning', '%s', EM);
    +0091         model.ub(mergedRxns(i))=max(model.ub(duplRxn));
    +0092     end
    +0093     if numel(unique(model.c(duplRxn)))>1
    +0094         EM=['Duplicates of reaction ' model.rxns{mergedRxns(i)} ' has a different objective function coefficient. Uses the largest coefficient'];
    +0095         warning('RAVEN:warning', '%s', EM);
    +0096         model.c(mergedRxns(i))=max(model.c(duplRxn));
    +0097     end
    +0098     if isfield(model,'grRules') && any(~isempty(model.grRules(duplRxn)))
    +0099         rules=model.grRules(duplRxn);
    +0100         allRules={};
    +0101         for j=1:numel(rules)
    +0102             rules{j}=ignoreORinComplex(rules{j});
    +0103             allRules=[allRules regexp(rules{j},' or ','split')];
    +0104         end
    +0105         allRules=unique(allRules);
    +0106         allRules=strrep(allRules,'__OR__',' or ');
    +0107         andRules=contains(allRules,' and ');
    +0108         allRules(andRules)=strcat('(',allRules(andRules),')');
    +0109         if numel(allRules)==1
    +0110             model.grRules{mergedRxns(i)}=allRules{1};
    +0111         else
    +0112             model.grRules{mergedRxns(i)}=strjoin(allRules,' or ');
    +0113         end
    +0114     end    
    +0115     if isfield(model,'eccodes') && any(~isempty(model.eccodes(duplRxn)))
    +0116         codes=model.eccodes(duplRxn);
    +0117         allCodes={};
    +0118         for j=1:numel(codes)
    +0119             allCodes=[allCodes regexp(codes{j},';','split')];
    +0120         end
    +0121         allCodes=unique(allCodes);
    +0122         if numel(allCodes)==1
    +0123             model.eccodes{mergedRxns(i)}=allCodes{1};
    +0124         else
    +0125             model.eccodes{mergedRxns(i)}=strjoin(allCodes,';');
    +0126         end
    +0127     end
    +0128     %Generate indexedDuplicateRxns cell array
    +0129     if numel(duplRxn)==2
    +0130         indexedDuplicateRxns{duplRxn(1)}=model.rxns{duplRxn(2)};
    +0131     else
    +0132         indexedDuplicateRxns{duplRxn(1)}=strjoin(model.rxns(duplRxn(2:end)),';');
    +0133     end
    +0134     %If all reactions have _EXP_* (where * is digit) as suffix, then the
    +0135     %model had passed through expandModel(), and these suffixes are removed.
    +0136     if all(~cellfun(@isempty,regexp(model.rxns(duplRxn),'_EXP_\d+$')))
    +0137         model.rxns(duplRxn(1))=regexprep(model.rxns(duplRxn(1)),'_EXP_\d+$','');
    +0138     end
    +0139 end
    +0140 
    +0141 %Delete the duplicate reactions
    +0142 reducedModel=removeReactions(model,duplicateRxns);
    +0143 removedRxns=model.rxns(duplicateRxns);
    +0144 [~, index]=ismember(reducedModel.rxns,model.rxns);
    +0145 indexedDuplicateRxns=indexedDuplicateRxns(index);
    +0146 
    +0147 if isfield(reducedModel,'rxnGeneMat')
    +0148     %Fix grRules and reconstruct rxnGeneMat
    +0149     [grRules,rxnGeneMat] = standardizeGrRules(reducedModel,true);
    +0150     reducedModel.grRules = grRules;
    +0151     reducedModel.rxnGeneMat = rxnGeneMat;
    +0152 end
    +0153 end
    +0154 
    +0155 function grRule = ignoreORinComplex(grRule)
    +0156 %In a grRule, if OR relationship is nested in an AND relationship, then
    +0157 %obfuscate the OR before splitting isoenzymes
    +0158 grRule=['(' grRule ')'];
    +0159 brOpen=strfind(grRule,'(');
    +0160 brClose=strfind(grRule,')');
    +0161 andPos=strfind(grRule,' and ');
    +0162 %Find opening bracket closest before AND
    +0163 stillCapturing = 0;
    +0164 for i=1:numel(andPos)
    +0165     searchPos = andPos(i);
    +0166     while stillCapturing == 0
    +0167         closestOpen = brOpen(max(find(brOpen<searchPos)));
    +0168         inbetweenClose = brClose(brClose<searchPos & brClose>closestOpen);
    +0169         if ~isempty(inbetweenClose)
    +0170             searchPos=max(inbetweenClose);
    +0171         else
    +0172             stillCapturing = 1;
    +0173             beginPos = closestOpen;
    +0174         end
    +0175     end
    +0176     stillCapturing = 0;
    +0177     searchPos = andPos(i);
    +0178     while stillCapturing == 0
    +0179         closestClose = brClose(min(find(brClose>searchPos)));
    +0180         inbetweenOpen = brOpen(brOpen>searchPos & brOpen<closestOpen);
    +0181         if ~isempty(inbetweenOpen)
    +0182             searchPos=min(closestClose);
    +0183         else
    +0184             stillCapturing = 1;
    +0185             endPos = closestClose;
    +0186         end
    +0187     end
    +0188     replacePart=regexprep(grRule(beginPos:endPos),' or ','__OR__');
    +0189     grRule=strrep(grRule,grRule(beginPos:endPos),replacePart);
    +0190 end
    +0191 grRule=grRule(2:end-1);
    +0192 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/manipulation/convertToIrrev.html b/docs/html/manipulation/convertToIrrev.html similarity index 96% rename from doc/manipulation/convertToIrrev.html rename to docs/html/manipulation/convertToIrrev.html index 7cdedaed..7f1c33cc 100644 --- a/doc/manipulation/convertToIrrev.html +++ b/docs/html/manipulation/convertToIrrev.html @@ -30,8 +30,8 @@

    DESCRIPTION ^
     convertToIrrev  Convert a model to irreversible form.
     
      Reversible reactions are split into one forward and one reverse
    - reaction. The reverse reactions are saved as 'rxnID_REV'. A warning is
    - shown if some reaction identifiers already end with '_REV'.
    + reaction. The reverse reactions are named 'rxnID_REV'. A warning is
    + shown if some reaction ids already end with '_REV'.
     
      Parameters
      ----------
    @@ -41,7 +41,7 @@ 

    DESCRIPTION ^SOURCE CODE ^% convertToIrrev Convert a model to irreversible form. 0003 % 0004 % Reversible reactions are split into one forward and one reverse -0005 % reaction. The reverse reactions are saved as 'rxnID_REV'. A warning is -0006 % shown if some reaction identifiers already end with '_REV'. +0005 % reaction. The reverse reactions are named 'rxnID_REV'. A warning is +0006 % shown if some reaction ids already end with '_REV'. 0007 % 0008 % Parameters 0009 % ---------- @@ -88,7 +88,7 @@

    SOURCE CODE ^% Name-Value Arguments 0014 % -------------------- 0015 % rxns : cell -0016 % cell array with the reactions to split, if reversible (default +0016 % cell array with the ids of the reactions to split, if reversible (default 0017 % model.rxns). 0018 % 0019 % Returns diff --git a/docs/html/manipulation/copyToComps.html b/docs/html/manipulation/copyToComps.html new file mode 100644 index 00000000..84e36749 --- /dev/null +++ b/docs/html/manipulation/copyToComps.html @@ -0,0 +1,202 @@ + + + + Description of copyToComps + + + + + + + + + +
    Home > manipulation > copyToComps.m
    + + + +

    copyToComps +

    + +

    PURPOSE ^

    +
    copyToComps Copy reactions to new compartment(s).
    + +

    SYNOPSIS ^

    +
    function model=copyToComps(model,toComps,varargin)
    + +

    DESCRIPTION ^

    +
     copyToComps  Copy reactions to new compartment(s).
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + toComps : cell
    +     cell array of compartment ids. An id that is not in model.comps is added as a
    +     new compartment (see compNames and compOutside).
    +
    + Name-Value Arguments
    + --------------------
    + rxns : cell or logical or double
    +     a cell array of reaction ids, a logical vector with one element per reaction of
    +     the model, or a vector of reaction indexes to copy (default model.rxns).
    + deleteOriginal : logical
    +     true to remove the original reactions, which moves the reactions (default false).
    + compNames : cell
    +     cell array of compartment names. Used when new compartments are added
    +     (default toComps).
    + compOutside : cell
    +     cell array with the id (as in comps) of the compartment that surrounds each new
    +     compartment. Used when new compartments are added (default all {''}).
    +
    + Returns
    + -------
    + model : struct
    +     an updated model structure.
    +
    + Examples
    + --------
    +     model=copyToComps(model,toComps,rxns,deleteOriginal,compNames,compOutside);
    +
    + Notes
    + -----
    + New reactions and metabolites have the ids "id_toComps(i)".
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • mergeModels mergeModels Merge models into one model structure.
    • removeReactions removeReactions Delete a set of reactions from a model.
    +This function is called by: +
      +
    • mergeModels mergeModels Merge models into one model structure.
    + + + + +

    SOURCE CODE ^

    +
    0001 function model=copyToComps(model,toComps,varargin)
    +0002 % copyToComps  Copy reactions to new compartment(s).
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % model : struct
    +0007 %     a model structure.
    +0008 % toComps : cell
    +0009 %     cell array of compartment ids. An id that is not in model.comps is added as a
    +0010 %     new compartment (see compNames and compOutside).
    +0011 %
    +0012 % Name-Value Arguments
    +0013 % --------------------
    +0014 % rxns : cell or logical or double
    +0015 %     a cell array of reaction ids, a logical vector with one element per reaction of
    +0016 %     the model, or a vector of reaction indexes to copy (default model.rxns).
    +0017 % deleteOriginal : logical
    +0018 %     true to remove the original reactions, which moves the reactions (default false).
    +0019 % compNames : cell
    +0020 %     cell array of compartment names. Used when new compartments are added
    +0021 %     (default toComps).
    +0022 % compOutside : cell
    +0023 %     cell array with the id (as in comps) of the compartment that surrounds each new
    +0024 %     compartment. Used when new compartments are added (default all {''}).
    +0025 %
    +0026 % Returns
    +0027 % -------
    +0028 % model : struct
    +0029 %     an updated model structure.
    +0030 %
    +0031 % Examples
    +0032 % --------
    +0033 %     model=copyToComps(model,toComps,rxns,deleteOriginal,compNames,compOutside);
    +0034 %
    +0035 % Notes
    +0036 % -----
    +0037 % New reactions and metabolites have the ids "id_toComps(i)".
    +0038 
    +0039 p=parseRAVENargs(varargin, {'rxns',[]; 'deleteOriginal',false; 'compNames',[]; 'compOutside',[]});
    +0040 rxns=p.rxns;
    +0041 if isempty(rxns)
    +0042     rxns=model.rxns;
    +0043 elseif ~islogical(rxns) && ~isnumeric(rxns)
    +0044     rxns=convertCharArray(rxns);
    +0045 end
    +0046 deleteOriginal=p.deleteOriginal;
    +0047 compNames=p.compNames;
    +0048 if isempty(compNames)
    +0049     compNames=toComps;
    +0050 else
    +0051     compNames=convertCharArray(compNames);
    +0052 end
    +0053 compOutside=p.compOutside;
    +0054 if isempty(compOutside)
    +0055     % Default is {''} for every new compartment, not '': compOutside(i)
    +0056     % below indexes into it once per new compartment, which a bare ''
    +0057     % cannot support.
    +0058     compOutside=repmat({''},size(compNames));
    +0059 else
    +0060     compOutside=convertCharArray(compOutside);
    +0061     if length(compOutside) ~= length(compNames)
    +0062         error('compOutside and compNames should be of equal size.');
    +0063     end
    +0064 end
    +0065 
    +0066 originalID=model.id;
    +0067 originalName=model.name;
    +0068 
    +0069 rxns=getIndexes(model,rxns,'rxns');
    +0070 
    +0071 for i=1:numel(toComps)
    +0072     %Check if the compartment exists, otherwise add it
    +0073     [I,J]=ismember(toComps(i),model.comps);
    +0074     if I==false
    +0075         model.comps=[model.comps;toComps(i)];
    +0076         model.compNames=[model.compNames;compNames(i)];
    +0077         if isfield(model,'compOutside')
    +0078             model.compOutside=[model.compOutside;compOutside(i)];
    +0079         end
    +0080         if isfield(model,'compMiriams')
    +0081             model.compMiriams=[model.compMiriams;cell(1,1)];
    +0082         end
    +0083         J=numel(model.comps);
    +0084     end
    +0085     %Copy the reactions by making a model structure with only them, then
    +0086     %change the localization, and finally merge with the original model
    +0087     modelToAdd=model;
    +0088     modelToAdd=removeReactions(modelToAdd,setdiff(1:numel(model.rxns),rxns),true,true);
    +0089     modelToAdd.rxns=strcat(modelToAdd.rxns,'_',toComps(i));
    +0090     modelToAdd.mets=strcat(modelToAdd.mets,'_',toComps(i));
    +0091     modelToAdd.comps=modelToAdd.comps(J);
    +0092     modelToAdd.compNames=modelToAdd.compNames(J);
    +0093     if isfield(modelToAdd,'compOutside')
    +0094         modelToAdd.compOutside=modelToAdd.compOutside(J);
    +0095     end
    +0096     if isfield(modelToAdd,'compMiriams')
    +0097         modelToAdd.compMiriams=modelToAdd.compMiriams(J);
    +0098     end
    +0099     modelToAdd.metComps=ones(numel(modelToAdd.mets),1);
    +0100     if isfield(modelToAdd,'metFrom')
    +0101         modelToAdd = rmfield(modelToAdd,'metFrom');
    +0102     end
    +0103     if isfield(modelToAdd,'rxnFrom')
    +0104         modelToAdd = rmfield(modelToAdd,'rxnFrom');
    +0105     end
    +0106     if isfield(modelToAdd,'geneFrom')
    +0107         modelToAdd = rmfield(modelToAdd,'geneFrom');
    +0108     end
    +0109   
    +0110     %Merge the models
    +0111     model=mergeModels({model;modelToAdd},'metNames',[],true);
    +0112 end
    +0113 
    +0114 if deleteOriginal==true
    +0115     model=removeReactions(model,rxns,true,true,true); %Also delete unused compartments
    +0116 end
    +0117 
    +0118 model.id=originalID;
    +0119 model.name=originalName;
    +0120 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/manipulation/deleteUnusedGenes.html b/docs/html/manipulation/deleteUnusedGenes.html similarity index 100% rename from doc/manipulation/deleteUnusedGenes.html rename to docs/html/manipulation/deleteUnusedGenes.html diff --git a/doc/manipulation/expandModel.html b/docs/html/manipulation/expandModel.html similarity index 86% rename from doc/manipulation/expandModel.html rename to docs/html/manipulation/expandModel.html index c6974b0a..6f6371aa 100644 --- a/doc/manipulation/expandModel.html +++ b/docs/html/manipulation/expandModel.html @@ -29,8 +29,8 @@

    SYNOPSIS ^DESCRIPTION ^

     expandModel  Expand reactions that use several gene associations.
     
    - Each reaction that uses several gene associations is split into several
    - reactions, each under the control of only one gene.
    + Each reaction with several gene associations (isozymes) is split into one reaction per
    + association.
     
      Parameters
      ----------
    @@ -40,12 +40,12 @@ 

    DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • standardizeGrRules standardizeGrRules Standardise gene-reaction rules in a model.
  • This function is called by:
    @@ -76,8 +76,8 @@

    SOURCE CODE ^
    0001 function [newModel, rxnToCheck]=expandModel(model)
     0002 % expandModel  Expand reactions that use several gene associations.
     0003 %
    -0004 % Each reaction that uses several gene associations is split into several
    -0005 % reactions, each under the control of only one gene.
    +0004 % Each reaction with several gene associations (isozymes) is split into one reaction per
    +0005 % association.
     0006 %
     0007 % Parameters
     0008 % ----------
    @@ -87,12 +87,12 @@ 

    SOURCE CODE ^% Returns 0013 % ------- 0014 % newModel : struct -0015 % model structure with separate reactions for iso-enzymes, where the -0016 % reaction ids are renamed as id_EXP_1, id_EXP_2, etc. +0015 % model structure with separate reactions for isozymes. The ids of split reactions +0016 % are id_EXP_1, id_EXP_2 and so on, numbered per isozyme. 0017 % rxnToCheck : cell -0018 % cell array with original reaction identifiers for those whose grRule -0019 % was not in disjunctive normal form, and so had to be expanded by -0020 % distributivity, plus any whose grRule could not be parsed. +0018 % cell array with the original ids of the reactions whose grRule is not in +0019 % disjunctive normal form and was expanded by distributivity, and of the reactions +0020 % whose grRule cannot be parsed and is left unchanged. 0021 % 0022 % Examples 0023 % -------- @@ -101,11 +101,11 @@

    SOURCE CODE ^% Notes 0027 % ----- 0028 % The isozymes are the AND-clauses of the grRule in disjunctive normal form, -0029 % so nested expressions are expanded correctly: "g1 and (g2 or g3)" yields -0030 % "g1 and g2" and "g1 and g3". Reactions listed in rxnToCheck are expanded -0031 % correctly too; they are reported only because a rule needing -0032 % distributivity is often a sign that the GPR was not what its author -0033 % intended. Use standardizeGrRules/findPotentialErrors to inspect them. +0029 % so nested expressions are expanded: "g1 and (g2 or g3)" yields "g1 and g2" and +0030 % "g1 and g3". Reactions listed in rxnToCheck because of distributivity are expanded in +0031 % the same way; they are listed because a rule that needs distributivity can indicate +0032 % that the GPR differs from what its author intended. standardizeGrRules and +0033 % findPotentialErrors inspect them. 0034 0035 %Work out the isozymes of every reaction up front. The number of copies is 0036 %the number of DNF clauses, which is not the number of ' or ' substrings: diff --git a/doc/manipulation/findDuplicateRxns.html b/docs/html/manipulation/findDuplicateRxns.html similarity index 51% rename from doc/manipulation/findDuplicateRxns.html rename to docs/html/manipulation/findDuplicateRxns.html index 0d07e767..b6e2ca74 100644 --- a/doc/manipulation/findDuplicateRxns.html +++ b/docs/html/manipulation/findDuplicateRxns.html @@ -29,21 +29,19 @@

    SYNOPSIS ^DESCRIPTION ^

     findDuplicateRxns  Find reactions that share identical stoichiometry.
     
    - The upstream version of yeast-GEM's findDuplicatedRxns.
    -
    - Only stoichiometry is compared — bounds, GPRs, and annotations are
    - ignored. The default treats A→B and B→A as duplicates (typical curation
    - use case: "find reactions that could be merged").
    + Only the stoichiometry is compared; bounds, GPRs and annotations are ignored. With
    + the default ignoreDirection, A→B and B→A are duplicates. The pairs identify
    + reactions that could be merged (contractModel merges duplicate reactions).
     
      Parameters
      ----------
      model : struct
    -     RAVEN model struct.
    +     a model structure.
     
      Name-Value Arguments
      --------------------
      ignoreDirection : logical
    -     treat A→B and B→A as duplicates (default true).
    +     true to treat A→B and B→A as duplicates (default true).
     
      Returns
      -------
    @@ -73,65 +71,63 @@ 

    SOURCE CODE ^
    0001 function pairs = findDuplicateRxns(model, varargin)
     0002 % findDuplicateRxns  Find reactions that share identical stoichiometry.
     0003 %
    -0004 % The upstream version of yeast-GEM's findDuplicatedRxns.
    -0005 %
    -0006 % Only stoichiometry is compared — bounds, GPRs, and annotations are
    -0007 % ignored. The default treats A→B and B→A as duplicates (typical curation
    -0008 % use case: "find reactions that could be merged").
    -0009 %
    -0010 % Parameters
    -0011 % ----------
    -0012 % model : struct
    -0013 %     RAVEN model struct.
    -0014 %
    -0015 % Name-Value Arguments
    -0016 % --------------------
    -0017 % ignoreDirection : logical
    -0018 %     treat A→B and B→A as duplicates (default true).
    -0019 %
    -0020 % Returns
    -0021 % -------
    -0022 % pairs : double
    -0023 %     Nx2 numeric array of reaction-index pairs (i, j) where reactions i
    -0024 %     and j share the same (possibly negated) stoichiometry, with i < j.
    -0025 %     Empty if the model has no duplicates.
    -0026 %
    -0027 % Examples
    -0028 % --------
    -0029 %     pairs = findDuplicateRxns(model);
    -0030 %     pairs = findDuplicateRxns(model, false);
    -0031 
    -0032 p=parseRAVENargs(varargin, {'ignoreDirection',true});
    -0033 ignoreDirection=p.ignoreDirection;
    +0004 % Only the stoichiometry is compared; bounds, GPRs and annotations are ignored. With
    +0005 % the default ignoreDirection, A→B and B→A are duplicates. The pairs identify
    +0006 % reactions that could be merged (contractModel merges duplicate reactions).
    +0007 %
    +0008 % Parameters
    +0009 % ----------
    +0010 % model : struct
    +0011 %     a model structure.
    +0012 %
    +0013 % Name-Value Arguments
    +0014 % --------------------
    +0015 % ignoreDirection : logical
    +0016 %     true to treat A→B and B→A as duplicates (default true).
    +0017 %
    +0018 % Returns
    +0019 % -------
    +0020 % pairs : double
    +0021 %     Nx2 numeric array of reaction-index pairs (i, j) where reactions i
    +0022 %     and j share the same (possibly negated) stoichiometry, with i < j.
    +0023 %     Empty if the model has no duplicates.
    +0024 %
    +0025 % Examples
    +0026 % --------
    +0027 %     pairs = findDuplicateRxns(model);
    +0028 %     pairs = findDuplicateRxns(model, false);
    +0029 
    +0030 p=parseRAVENargs(varargin, {'ignoreDirection',true});
    +0031 ignoreDirection=p.ignoreDirection;
    +0032 
    +0033 S=model.S';  % nRxns x nMets, kept sparse: unique(...,'rows') accepts sparse input directly
     0034 
    -0035 S=model.S';  % nRxns x nMets, kept sparse: unique(...,'rows') accepts sparse input directly
    -0036 
    -0037 if ignoreDirection
    -0038     % Canonical form: flip rows whose first nonzero entry is negative so
    -0039     % that A->B and B->A hash to the same fingerprint.
    -0040     for i=1:size(S,1)
    -0041         k=find(S(i,:),1);
    -0042         if ~isempty(k) && S(i,k)<0
    -0043             S(i,:)=-S(i,:);
    -0044         end
    -0045     end
    -0046 end
    +0035 if ignoreDirection
    +0036     % Canonical form: flip rows whose first nonzero entry is negative so
    +0037     % that A->B and B->A hash to the same fingerprint.
    +0038     for i=1:size(S,1)
    +0039         k=find(S(i,:),1);
    +0040         if ~isempty(k) && S(i,k)<0
    +0041             S(i,:)=-S(i,:);
    +0042         end
    +0043     end
    +0044 end
    +0045 
    +0046 [~,~,ic]=unique(S,'rows');  % ic(i) = group label for reaction i
     0047 
    -0048 [~,~,ic]=unique(S,'rows');  % ic(i) = group label for reaction i
    -0049 
    -0050 pairs=zeros(0,2);
    -0051 for g=1:max(ic)
    -0052     idx=sort(find(ic==g));
    -0053     for ii=1:numel(idx)-1
    -0054         for jj=ii+1:numel(idx)
    -0055             pairs(end+1,:)=[idx(ii),idx(jj)]; %#ok<AGROW>
    -0056         end
    -0057     end
    -0058 end
    -0059 if ~isempty(pairs)
    -0060     pairs=sortrows(pairs);
    -0061 end
    -0062 end

    +0048 pairs=zeros(0,2); +0049 for g=1:max(ic) +0050 idx=sort(find(ic==g)); +0051 for ii=1:numel(idx)-1 +0052 for jj=ii+1:numel(idx) +0053 pairs(end+1,:)=[idx(ii),idx(jj)]; %#ok<AGROW> +0054 end +0055 end +0056 end +0057 if ~isempty(pairs) +0058 pairs=sortrows(pairs); +0059 end +0060 end


    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/manipulation/findPotentialErrors.html b/docs/html/manipulation/findPotentialErrors.html new file mode 100644 index 00000000..c1607bf2 --- /dev/null +++ b/docs/html/manipulation/findPotentialErrors.html @@ -0,0 +1,173 @@ + + + + Description of findPotentialErrors + + + + + + + + + +
    Home > manipulation > findPotentialErrors.m
    + + + +

    findPotentialErrors +

    + +

    PURPOSE ^

    +
    findPotentialErrors Identify reactions with non-DNF GPR rules.
    + +

    SYNOPSIS ^

    +
    function issues=findPotentialErrors(model)
    + +

    DESCRIPTION ^

    +
     findPotentialErrors  Identify reactions with non-DNF GPR rules.
    +
    + Detects grRules that are not in disjunctive normal form (DNF), that is, rules in
    + which an OR operator is nested inside an AND context. The RAVEN format requires DNF:
    + groups of genes that must all be present together (enzyme complexes) are written as
    + conjunctions, and the reaction is catalysed by at least one complex (a disjunction of
    + conjunctions).
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure with grRules and rxns fields.
    +
    + Returns
    + -------
    + issues : struct
    +     struct array with one element per problematic reaction and the fields:
    +
    +     - index  : double, 1-based index into model.rxns.
    +     - rxn    : char, reaction id.
    +     - grRule : char, the problematic grRule string.
    +     - reason : char, short explanation.
    +
    + Notes
    + -----
    + Detection walks the parse tree from parseGrRule, so gene ids that contain substrings
    + such as "and" or "or" are not misread as operators.
    +
    + A rule is non-DNF when an AND operator has an OR anywhere beneath it.
    + Bracketing alone does not make a rule non-DNF: "((G1 and G2) or G3)" and
    + "(G1 or G2) or (G3 and G4)" are both in DNF. standardizeGrRules attempts an
    + automatic repair.
    +
    + Rules that cannot be parsed at all are reported as issues too, with the
    + parser's message as the reason.
    +
    + Examples
    + --------
    +     issues = findPotentialErrors(model);
    +     if ~isempty(issues)
    +         fprintf("%d reactions with non-DNF grRules\n", numel(issues));
    +     end
    +     indexes2check = vertcat(issues.index);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: + + + + + +

    SOURCE CODE ^

    +
    0001 function issues=findPotentialErrors(model)
    +0002 % findPotentialErrors  Identify reactions with non-DNF GPR rules.
    +0003 %
    +0004 % Detects grRules that are not in disjunctive normal form (DNF), that is, rules in
    +0005 % which an OR operator is nested inside an AND context. The RAVEN format requires DNF:
    +0006 % groups of genes that must all be present together (enzyme complexes) are written as
    +0007 % conjunctions, and the reaction is catalysed by at least one complex (a disjunction of
    +0008 % conjunctions).
    +0009 %
    +0010 % Parameters
    +0011 % ----------
    +0012 % model : struct
    +0013 %     a model structure with grRules and rxns fields.
    +0014 %
    +0015 % Returns
    +0016 % -------
    +0017 % issues : struct
    +0018 %     struct array with one element per problematic reaction and the fields:
    +0019 %
    +0020 %     - index  : double, 1-based index into model.rxns.
    +0021 %     - rxn    : char, reaction id.
    +0022 %     - grRule : char, the problematic grRule string.
    +0023 %     - reason : char, short explanation.
    +0024 %
    +0025 % Notes
    +0026 % -----
    +0027 % Detection walks the parse tree from parseGrRule, so gene ids that contain substrings
    +0028 % such as "and" or "or" are not misread as operators.
    +0029 %
    +0030 % A rule is non-DNF when an AND operator has an OR anywhere beneath it.
    +0031 % Bracketing alone does not make a rule non-DNF: "((G1 and G2) or G3)" and
    +0032 % "(G1 or G2) or (G3 and G4)" are both in DNF. standardizeGrRules attempts an
    +0033 % automatic repair.
    +0034 %
    +0035 % Rules that cannot be parsed at all are reported as issues too, with the
    +0036 % parser's message as the reason.
    +0037 %
    +0038 % Examples
    +0039 % --------
    +0040 %     issues = findPotentialErrors(model);
    +0041 %     if ~isempty(issues)
    +0042 %         fprintf("%d reactions with non-DNF grRules\n", numel(issues));
    +0043 %     end
    +0044 %     indexes2check = vertcat(issues.index);
    +0045 
    +0046 issues=struct('index',{},'rxn',{},'grRule',{},'reason',{});
    +0047 
    +0048 if ~isfield(model,'grRules') || ~isfield(model,'rxns')
    +0049     return
    +0050 end
    +0051 
    +0052 for i=1:numel(model.grRules)
    +0053     rule=strtrim(model.grRules{i});
    +0054     if isempty(rule)
    +0055         continue
    +0056     end
    +0057 
    +0058     try
    +0059         isDnf=isDnfGrRule(rule);
    +0060     catch ME
    +0061         if ~strcmp(ME.identifier,'RAVEN:badGrRule')
    +0062             rethrow(ME)
    +0063         end
    +0064         % A rule that cannot be parsed is itself worth reporting: it is
    +0065         % certainly not usable by the isoenzyme/complex reasoning, and
    +0066         % silently skipping it would hide the very thing this function
    +0067         % exists to surface.
    +0068         issues(end+1,1)=struct( ...
    +0069             'index',  i, ...
    +0070             'rxn',    model.rxns{i}, ...
    +0071             'grRule', rule, ...
    +0072             'reason', ['Cannot be parsed: ' ME.message]); %#ok<AGROW>
    +0073         continue
    +0074     end
    +0075 
    +0076     if ~isDnf
    +0077         issues(end+1,1)=struct( ...
    +0078             'index',  i, ...
    +0079             'rxn',    model.rxns{i}, ...
    +0080             'grRule', rule, ...
    +0081             'reason', 'Non-DNF: OR operator nested inside AND context'); %#ok<AGROW>
    +0082     end
    +0083 end
    +0084 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/generateNewIds.html b/docs/html/manipulation/generateNewIds.html new file mode 100644 index 00000000..0c0b9413 --- /dev/null +++ b/docs/html/manipulation/generateNewIds.html @@ -0,0 +1,166 @@ + + + + Description of generateNewIds + + + + + + + + + +
    Home > manipulation > generateNewIds.m
    + + + +

    generateNewIds +

    + +

    PURPOSE ^

    +
    generateNewIds Generate a list of new metabolite or reaction ids.
    + +

    SYNOPSIS ^

    +
    function newIds=generateNewIds(model,type,prefix,varargin)
    + +

    DESCRIPTION ^

    +
     generateNewIds  Generate a list of new metabolite or reaction ids.
    +
    + The ids are numbered sequentially with a defined prefix. The highest existing number
    + among the ids of that type with the prefix is read from the model. Suffixes are
    + read with str2double. A suffix counts as a number when the result is a
    + finite integer value below 2^52; text, Inf, NaN, fractions (r_2.5) and
    + numbers of 2^52 or more (r_1e300, r_1e400) do not count.
    +
    + Parameters
    + ----------
    + model : struct
    +     model structure.
    + type : char
    +     type of id, 'rxns' or 'mets'.
    + prefix : char
    +     prefix to be used in all ids, for example 's_' or 'r_'.
    +
    + Name-Value Arguments
    + --------------------
    + quantity : double
    +     number of new ids to generate (default 1).
    + numLength : double
    +     length of the numerical part of the id; 4 gives ids like r_0001 and 6 gives ids like
    +     r_000001. If the prefix is used in the model, the length of the existing ids
    +     applies instead (default 4).
    +
    + Returns
    + -------
    + newIds : cell
    +     cell array with the generated ids.
    +
    + Examples
    + --------
    +     newIds = generateNewIds(model, type, prefix, quantity, numLength);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    • addMets addMets Add metabolites to a model.
    • addTransport addTransport Add transport reactions between compartments.
    + + + + +

    SOURCE CODE ^

    +
    0001 function newIds=generateNewIds(model,type,prefix,varargin)
    +0002 % generateNewIds  Generate a list of new metabolite or reaction ids.
    +0003 %
    +0004 % The ids are numbered sequentially with a defined prefix. The highest existing number
    +0005 % among the ids of that type with the prefix is read from the model. Suffixes are
    +0006 % read with str2double. A suffix counts as a number when the result is a
    +0007 % finite integer value below 2^52; text, Inf, NaN, fractions (r_2.5) and
    +0008 % numbers of 2^52 or more (r_1e300, r_1e400) do not count.
    +0009 %
    +0010 % Parameters
    +0011 % ----------
    +0012 % model : struct
    +0013 %     model structure.
    +0014 % type : char
    +0015 %     type of id, 'rxns' or 'mets'.
    +0016 % prefix : char
    +0017 %     prefix to be used in all ids, for example 's_' or 'r_'.
    +0018 %
    +0019 % Name-Value Arguments
    +0020 % --------------------
    +0021 % quantity : double
    +0022 %     number of new ids to generate (default 1).
    +0023 % numLength : double
    +0024 %     length of the numerical part of the id; 4 gives ids like r_0001 and 6 gives ids like
    +0025 %     r_000001. If the prefix is used in the model, the length of the existing ids
    +0026 %     applies instead (default 4).
    +0027 %
    +0028 % Returns
    +0029 % -------
    +0030 % newIds : cell
    +0031 %     cell array with the generated ids.
    +0032 %
    +0033 % Examples
    +0034 % --------
    +0035 %     newIds = generateNewIds(model, type, prefix, quantity, numLength);
    +0036 type=char(type);
    +0037 prefix=char(prefix);
    +0038 
    +0039 if type=='rxns'
    +0040     existingIds=model.rxns;
    +0041 elseif type=='mets'
    +0042     existingIds=model.mets;
    +0043 else
    +0044     error('type should be either ''rxns'' or ''mets''.')
    +0045 end
    +0046 p=parseRAVENargs(varargin, {'quantity',1; 'numLength',4});
    +0047 quantity=p.quantity;
    +0048 numLength=p.numLength;
    +0049 
    +0050 % Subset only existingIds that have the prefix. Escaped: prefix is
    +0051 % arbitrary caller-supplied text, not a regex pattern.
    +0052 escapedPrefix=regexptranslate('escape',prefix);
    +0053 existingIds=existingIds(~cellfun(@isempty,regexp(existingIds,['^' escapedPrefix])));
    +0054 
    +0055 if ~isempty(existingIds)
    +0056     existingIds=regexprep(existingIds,['^' escapedPrefix],'');
    +0057     numbers=str2double(existingIds);
    +0058     notInteger=~isfinite(numbers) | imag(numbers)~=0 | real(numbers)~=floor(real(numbers)) | abs(real(numbers))>=flintmax/2;
    +0059     numbers=real(numbers);
    +0060     numbers(notInteger)=NaN;
    +0061     if all(isnan(numbers))
    +0062         [~,I]=sort(existingIds);
    +0063         lastId=existingIds{I(end)};
    +0064         numLength=length(lastId);
    +0065         lastId=NaN;
    +0066     else
    +0067         [lastId,I]=max(numbers);
    +0068         numLength=length(existingIds{I});
    +0069     end
    +0070 else
    +0071     lastId=0;
    +0072     fprintf(['No ' type ' ids with prefix "' prefix ...
    +0073         '" currently exist in the model. The first new id will be "' ...
    +0074         [prefix,num2str(1,['%0' num2str(numLength) 'd'])] '"\n'],'%s')
    +0075 end
    +0076 
    +0077 if isnan(lastId)
    +0078     lastId = 0;
    +0079 end
    +0080 
    +0081 newIds=cell(quantity,1);
    +0082 
    +0083 for k=1:quantity
    +0084     newIds{k}=[prefix,num2str(k+lastId,['%0' num2str(numLength) 'd'])];
    +0085 end
    +0086 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/index.html b/docs/html/manipulation/index.html new file mode 100644 index 00000000..18a75fc2 --- /dev/null +++ b/docs/html/manipulation/index.html @@ -0,0 +1,29 @@ + + + + Index for Directory manipulation + + + + + + + + + + +
    < Master indexIndex for manipulation >
    + +

    Index for manipulation

    + +

    Matlab files in this directory:

    + +
     addExchangeRxnsaddExchangeRxns Add exchange reactions for some metabolites.
     addGenesRavenaddGenesRaven Add genes to a model.
     addMetsaddMets Add metabolites to a model.
     addRxnsaddRxns Add reactions to a model.
     addRxnsGenesMetsaddRxnsGenesMets Copy reactions from a source model into another model.
     addTransportaddTransport Add transport reactions between compartments.
     changeGrRuleschangeGrRules Change the grRules of multiple reactions.
     changeRxnschangeRxns Modify the equations of reactions in a model.
     closeModelcloseModel Add boundary metabolites to exchange reactions.
     contractModelcontractModel Contract a model by merging identical reactions.
     convertToIrrevconvertToIrrev Convert a model to irreversible form.
     copyToCompscopyToComps Copy reactions to new compartment(s).
     deleteUnusedGenesdeleteUnusedGenes Delete all genes not associated to any reaction.
     expandModelexpandModel Expand reactions that use several gene associations.
     findDuplicateRxnsfindDuplicateRxns Find reactions that share identical stoichiometry.
     findPotentialErrorsfindPotentialErrors Identify reactions with non-DNF GPR rules.
     generateNewIdsgenerateNewIds Generate a list of new metabolite or reaction ids.
     mergeCompartmentsmergeCompartments Merge all compartments in a model.
     mergeModelsmergeModels Merge models into one model structure.
     permuteModelpermuteModel Change the order of the reactions or metabolites in a model.
     removeBadRxnsremoveBadRxns Remove reactions that enable production or consumption from nothing.
     removeGenesremoveGenes Delete a set of genes from a model.
     removeLowScoreGenesremoveLowScoreGenes Remove low-score genes from a model.
     removeMetsremoveMets Delete a set of metabolites from a model.
     removeReactionsremoveReactions Delete a set of reactions from a model.
     replaceMetsreplaceMets Replace a metabolite with another already in the model.
     reverseRxnsreverseRxns Reverse the direction of reactions in a model.
     setExchangeBoundssetExchangeBounds Define exchange flux bounds for a set of metabolites.
     setParamsetParam Set parameters for reactions.
     simplifyModelsimplifyModel Simplify a model by deleting reactions and metabolites.
     sortIdentifierssortIdentifiers Sort model identifiers alphabetically.
     sortModelsortModel Sort the metabolites and reactions of a model.
     standardizeGrRulesstandardizeGrRules Standardise gene-reaction rules in a model.
    + + + + +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/mergeCompartments.html b/docs/html/manipulation/mergeCompartments.html new file mode 100644 index 00000000..2c650705 --- /dev/null +++ b/docs/html/manipulation/mergeCompartments.html @@ -0,0 +1,257 @@ + + + + Description of mergeCompartments + + + + + + + + + +
    Home > manipulation > mergeCompartments.m
    + + + +

    mergeCompartments +

    + +

    PURPOSE ^

    +
    mergeCompartments Merge all compartments in a model.
    + +

    SYNOPSIS ^

    +
    function [model, deletedRxns, duplicateRxns]=mergeCompartments(model,varargin)
    + +

    DESCRIPTION ^

    +
     mergeCompartments  Merge all compartments in a model.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + keepUnconstrained : logical
    +     true to keep the unconstrained metabolites in a separate compartment "b"
    +     (name "Unconstrained"). If they are merged, an exchange reaction between an
    +     unconstrained metabolite and a metabolite of the same name has no net
    +     stoichiometry after merging and is deleted (default false).
    + deleteRxnsWithOneMet : logical
    +     true to delete reactions that have one metabolite after merging and had more
    +     before. Such reactions arise from reactions such as A[c] + B[c] => A[m]. Some
    +     models balance hydrogen around each membrane with reactions like this
    +     (default false).
    + distReverse : logical
    +     true to treat reactions with the same metabolites but different reversibility as
    +     different reactions (default true).
    +
    + Returns
    + -------
    + model : struct
    +     a model with all reactions in one compartment.
    + deletedRxns : cell
    +     the ids of the reactions deleted because they have one metabolite after merging.
    + duplicateRxns : cell
    +     one entry per reaction of the returned model: '' when no duplicate was merged
    +     into it, otherwise the ids of the identical reactions from different
    +     compartments that were removed into it, joined by ";".
    +
    + Examples
    + --------
    +     [model, deletedRxns, duplicateRxns] = mergeCompartments(model);
    +
    + Notes
    + -----
    + Merges all compartments into one compartment "s" (name "System"). The merged model
    + can be used to test whether the network can synthesise all metabolites.
    +
    + Metabolites with the same name are merged into one. The merged metabolite
    + keeps the id and the other fields of one of the copies: the copy that is
    + already in compartment "s" if there is one, otherwise the copy whose id
    + comes first in character code order (for example ACCOA_c before ACCOA_m).
    + With keepUnconstrained only the copies that are not unconstrained are
    + candidates. If the metabolite ids contain the compartment, the ids of merged
    + metabolites can name a compartment that no longer exists.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • contractModel contractModel Contract a model by merging identical reactions.
    • removeMets removeMets Delete a set of metabolites from a model.
    • removeReactions removeReactions Delete a set of reactions from a model.
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function [model, deletedRxns, duplicateRxns]=mergeCompartments(model,varargin)
    +0002 % mergeCompartments  Merge all compartments in a model.
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % model : struct
    +0007 %     a model structure.
    +0008 %
    +0009 % Name-Value Arguments
    +0010 % --------------------
    +0011 % keepUnconstrained : logical
    +0012 %     true to keep the unconstrained metabolites in a separate compartment "b"
    +0013 %     (name "Unconstrained"). If they are merged, an exchange reaction between an
    +0014 %     unconstrained metabolite and a metabolite of the same name has no net
    +0015 %     stoichiometry after merging and is deleted (default false).
    +0016 % deleteRxnsWithOneMet : logical
    +0017 %     true to delete reactions that have one metabolite after merging and had more
    +0018 %     before. Such reactions arise from reactions such as A[c] + B[c] => A[m]. Some
    +0019 %     models balance hydrogen around each membrane with reactions like this
    +0020 %     (default false).
    +0021 % distReverse : logical
    +0022 %     true to treat reactions with the same metabolites but different reversibility as
    +0023 %     different reactions (default true).
    +0024 %
    +0025 % Returns
    +0026 % -------
    +0027 % model : struct
    +0028 %     a model with all reactions in one compartment.
    +0029 % deletedRxns : cell
    +0030 %     the ids of the reactions deleted because they have one metabolite after merging.
    +0031 % duplicateRxns : cell
    +0032 %     one entry per reaction of the returned model: '' when no duplicate was merged
    +0033 %     into it, otherwise the ids of the identical reactions from different
    +0034 %     compartments that were removed into it, joined by ";".
    +0035 %
    +0036 % Examples
    +0037 % --------
    +0038 %     [model, deletedRxns, duplicateRxns] = mergeCompartments(model);
    +0039 %
    +0040 % Notes
    +0041 % -----
    +0042 % Merges all compartments into one compartment "s" (name "System"). The merged model
    +0043 % can be used to test whether the network can synthesise all metabolites.
    +0044 %
    +0045 % Metabolites with the same name are merged into one. The merged metabolite
    +0046 % keeps the id and the other fields of one of the copies: the copy that is
    +0047 % already in compartment "s" if there is one, otherwise the copy whose id
    +0048 % comes first in character code order (for example ACCOA_c before ACCOA_m).
    +0049 % With keepUnconstrained only the copies that are not unconstrained are
    +0050 % candidates. If the metabolite ids contain the compartment, the ids of merged
    +0051 % metabolites can name a compartment that no longer exists.
    +0052 
    +0053 p=parseRAVENargs(varargin, {'keepUnconstrained',false; 'deleteRxnsWithOneMet',false; 'distReverse',true});
    +0054 keepUnconstrained=p.keepUnconstrained;
    +0055 deleteRxnsWithOneMet=p.deleteRxnsWithOneMet;
    +0056 distReverse=p.distReverse;
    +0057 
    +0058 if ~isfield(model,'unconstrained')
    +0059     keepUnconstrained=false;
    +0060 end
    +0061 
    +0062 %Keep track of which reactions only contained one metabolite before the
    +0063 %merging as they are probable exchange reactions.
    +0064 if deleteRxnsWithOneMet==true
    +0065     reservedRxns=model.rxns(sum(model.S~=0)==1);
    +0066     if ~isempty(reservedRxns) && ~isfield(model,'unconstrained')
    +0067         %If there is no unconstrained field these reactions are probably
    +0068         %exchange reactions and shall be kept. If not then print a warning
    +0069         EM='There are reactions with only one metabolite. Cannot determine whether they are exchange reactions since there is no unconstrained field';
    +0070         warning('RAVEN:warning', '%s', EM);
    +0071     end
    +0072 end
    +0073 
    +0074 %Loop through each metabolite name, and if a copy is not unconstrained then
    +0075 %change the S matrix to use the copy chosen as described in the Notes
    +0076 %Group metabolite indices by name once, rather than re-scanning all of
    +0077 %model.metNames (via ismember) for every unique name, which is quadratic
    +0078 %in a genome-scale model's (unique names x mets) count.
    +0079 [uNames,~,nameGroup]=unique(model.metNames);
    +0080 sameNameIdx=accumarray(nameGroup,(1:numel(nameGroup))',[numel(uNames) 1],@(v) {v});
    +0081 for i=1:numel(uNames)
    +0082     %Find all metabolites with this name..
    +0083     I=false(numel(model.metNames),1);
    +0084     I(sameNameIdx{i})=true;
    +0085 
    +0086     %Choose the copy that the other "un-unconstrained" copies are merged
    +0087     %into: one already in compartment "s" if there is one, otherwise the
    +0088     %one whose id sorts first.
    +0089     if keepUnconstrained==true
    +0090         candidates=find(I & model.unconstrained==false);
    +0091         
    +0092         %This could happen if there is only one metabolite and it is
    +0093         %unconstrained
    +0094         if isempty(candidates)
    +0095             continue;
    +0096         end
    +0097     else
    +0098         candidates=find(I);
    +0099     end
    +0100     inMerged=candidates(strcmp(model.comps(model.metComps(candidates)),'s'));
    +0101     if ~isempty(inMerged)
    +0102         candidates=inMerged;
    +0103     end
    +0104     [~,first]=sort(model.mets(candidates));
    +0105     mergeTo=candidates(first(1));
    +0106     I(mergeTo)=false; %Do not do anything for itself
    +0107     I=find(I);
    +0108     
    +0109     %Go through each of the metabolites with this name and update them to
    +0110     %mergeTo
    +0111     for j=1:numel(I)
    +0112         if keepUnconstrained==true && model.unconstrained(I(j))==true
    +0113             continue;
    +0114         end
    +0115         %Update the S matrix
    +0116         model.S(mergeTo,:)=model.S(mergeTo,:)+model.S(I(j),:);
    +0117         model.S(I(j),:)=0;
    +0118     end
    +0119 end
    +0120 
    +0121 %Remove all metabolites that are no longer used (with a bit of a trick)
    +0122 model=removeReactions(model,{},true);
    +0123 
    +0124 %Update all mets so that they are in compartment "s" with id "1"
    +0125 model.compNames={'System'};
    +0126 model.comps={'s'};
    +0127 model.compOutside={''};
    +0128 model.metComps=ones(numel(model.mets),1);
    +0129 %The per-compartment MIRIAM annotations no longer apply once every
    +0130 %compartment is merged into one, and leaving them would desync compMiriams
    +0131 %from the rebuilt comps. Drop them.
    +0132 if isfield(model,'compMiriams')
    +0133     model=rmfield(model,'compMiriams');
    +0134 end
    +0135 
    +0136 %Add exchange mets to another compartment "b" with id "2"
    +0137 if keepUnconstrained==true
    +0138     model.compNames=[model.compNames; {'Unconstrained'}];
    +0139     model.comps=[model.comps; {'b'}];
    +0140     model.compOutside=[model.compOutside; {'s'}];
    +0141     model.metComps(model.unconstrained~=0)=2;
    +0142 end
    +0143 
    +0144 %Transport reactions on the form A <=> B will have been deleted by the
    +0145 %merging. Remove those reactions
    +0146 model=removeMets(model,{},false,true,true);
    +0147 
    +0148 if deleteRxnsWithOneMet==true
    +0149     I=model.rxns(sum(model.S~=0)==1);
    +0150     
    +0151     %Delete the reactions that contain only one metabolite after the
    +0152     %merging but not before
    +0153     deletedRxns=setdiff(I,reservedRxns);
    +0154     model=removeReactions(model,deletedRxns,true,true);
    +0155 else
    +0156     deletedRxns={};
    +0157 end
    +0158 
    +0159 %And then finally merge the identical reactions
    +0160 [model, ~, duplicateRxns]=contractModel(model,distReverse);
    +0161 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/manipulation/mergeModels.html b/docs/html/manipulation/mergeModels.html similarity index 93% rename from doc/manipulation/mergeModels.html rename to docs/html/manipulation/mergeModels.html index 1758cfd1..53000f3a 100644 --- a/doc/manipulation/mergeModels.html +++ b/docs/html/manipulation/mergeModels.html @@ -29,10 +29,11 @@

    SYNOPSIS ^DESCRIPTION ^

     mergeModels  Merge models into one model structure.
     
    - Merges models into one model structure. Reactions are added without any
    - checks, so duplicate reactions might appear. Metabolites are matched by
    - their name and compartment (metaboliteName[comp]), while genes are matched
    - by their name.
    + Merges models into one model structure. Reactions are added without a check for
    + duplicate reactions, so duplicates can appear. A reaction id that already exists in
    + the merged model is renamed by appending "_" and the id of its source model.
    + Metabolites are matched by name and compartment (metaboliteName[comp]) and genes by
    + name.
     
      Parameters
      ----------
    @@ -42,23 +43,22 @@ 

    DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • copyToComps copyToComps Copy reactions to new compartment(s).
  • standardizeGrRules standardizeGrRules Standardise gene-reaction rules in a model.
  • This function is called by:
    • copyToComps copyToComps Copy reactions to new compartment(s).
    @@ -80,36 +80,36 @@

    SOURCE CODE ^
    0001 function model=mergeModels(models,varargin)
     0002 % mergeModels  Merge models into one model structure.
     0003 %
    -0004 % Merges models into one model structure. Reactions are added without any
    -0005 % checks, so duplicate reactions might appear. Metabolites are matched by
    -0006 % their name and compartment (metaboliteName[comp]), while genes are matched
    -0007 % by their name.
    -0008 %
    -0009 % Parameters
    -0010 % ----------
    -0011 % models : cell
    -0012 %     a cell array with model structures.
    -0013 %
    -0014 % Name-Value Arguments
    -0015 % --------------------
    -0016 % metParam : char
    -0017 %     string, metabolite name ("metNames") or ID ("mets") are used for
    -0018 %     matching (default "metNames").
    -0019 % supressWarnings : logical
    -0020 %     whether warnings should be supressed (default false).
    -0021 % copyToComps : logical
    -0022 %     internal flag used by copyToComps.m when it calls mergeModels to
    -0023 %     duplicate a model into a new compartment; leave at default when
    -0024 %     calling mergeModels directly (default false).
    -0025 %
    -0026 % Returns
    -0027 % -------
    -0028 % model : struct
    -0029 %     a model structure with the merged model. Follows the structure of
    -0030 %     normal models but also has "rxnFrom/metFrom/geneFrom" fields to
    -0031 %     indicate from which model each reaction/metabolite/gene was taken. If
    -0032 %     the model already has "rxnFrom/metFrom/geneFrom" fields, then these
    -0033 %     fields are not modified.
    +0004 % Merges models into one model structure. Reactions are added without a check for
    +0005 % duplicate reactions, so duplicates can appear. A reaction id that already exists in
    +0006 % the merged model is renamed by appending "_" and the id of its source model.
    +0007 % Metabolites are matched by name and compartment (metaboliteName[comp]) and genes by
    +0008 % name.
    +0009 %
    +0010 % Parameters
    +0011 % ----------
    +0012 % models : cell
    +0013 %     a cell array with model structures.
    +0014 %
    +0015 % Name-Value Arguments
    +0016 % --------------------
    +0017 % metParam : char
    +0018 %     the metabolite field used for matching: "metNames" (name and compartment) or
    +0019 %     "mets" (id) (default "metNames").
    +0020 % supressWarnings : logical
    +0021 %     true to suppress warnings (default false).
    +0022 % copyToComps : logical
    +0023 %     internal flag set by copyToComps when it calls mergeModels to duplicate a model into
    +0024 %     a new compartment. Left at the default when calling mergeModels directly
    +0025 %     (default false).
    +0026 %
    +0027 % Returns
    +0028 % -------
    +0029 % model : struct
    +0030 %     the merged model structure. In addition to the usual fields, it has the fields
    +0031 %     rxnFrom, metFrom and geneFrom, which hold the id of the model that each reaction,
    +0032 %     metabolite and gene was taken from. If a model already has these fields, its
    +0033 %     entries are not modified.
     0034 %
     0035 % Examples
     0036 % --------
    diff --git a/doc/manipulation/permuteModel.html b/docs/html/manipulation/permuteModel.html
    similarity index 98%
    rename from doc/manipulation/permuteModel.html
    rename to docs/html/manipulation/permuteModel.html
    index 716ee440..f42e9850 100644
    --- a/doc/manipulation/permuteModel.html
    +++ b/docs/html/manipulation/permuteModel.html
    @@ -56,7 +56,7 @@ 

    CROSS-REFERENCE INFORMATION ^
 </ul>
 This function is called by:
 <ul style= -
  • changeRxns changeRxns Modify the equations of reactions in a model.
  • sortIdentifiers sortIdentifiers Sort model identifiers alphabetically.
  • sortModel sortModel Standardize a model's internal ordering: by default orients
  • +
  • changeRxns changeRxns Modify the equations of reactions in a model.
  • sortIdentifiers sortIdentifiers Sort model identifiers alphabetically.
  • sortModel sortModel Sort the metabolites and reactions of a model.
  • diff --git a/docs/html/manipulation/removeBadRxns.html b/docs/html/manipulation/removeBadRxns.html new file mode 100644 index 00000000..36cddb9b --- /dev/null +++ b/docs/html/manipulation/removeBadRxns.html @@ -0,0 +1,367 @@ + + + + Description of removeBadRxns + + + + + + + + + +
    Home > manipulation > removeBadRxns.m
    + + + +

    removeBadRxns +

    + +

    PURPOSE ^

    +
    removeBadRxns Remove reactions that enable production or consumption from nothing.
    + +

    SYNOPSIS ^

    +
    function [newModel, removedRxns]=removeBadRxns(model,varargin)
    + +

    DESCRIPTION ^

    +
     removeBadRxns  Remove reactions that enable production or consumption from nothing.
    +
    + Iteratively removes reactions that enable production or consumption of a metabolite
    + without any uptake or excretion.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure. The model must not allow any uptake or excretion; the function
    +     warns about open exchange reactions when the model has no unconstrained field.
    +     importModel("filename", false) imports a model without uptake and excretion.
    +
    + Name-Value Arguments
    + --------------------
    + rxnRules : double
    +     which reactions may be removed (default 1):
    +
    +     - 1 : remove only unbalanced reactions
    +     - 2 : also remove reactions that could not be checked for mass
    +       balance
    +     - 3 : any reaction can be removed
    + ignoreMets : cell or logical or double
    +     a cell array of metabolite ids, a logical vector with one element per metabolite
    +     of the model, or a vector of metabolite indexes to exclude from the analysis
    +     (default []).
    + isNames : logical
    +     true if ignoreMets holds metabolite names (false: ids), which excludes the
    +     metabolites of that name in all compartments. Applies only if ignoreMets is a
    +     cell array (default false).
    + balanceElements : cell
    +     a cell array with the elements for which to balance the reactions, chosen from
    +     the elements defined in parseFormulas (default {"C";"P";"S";"N";"O"}).
    + refModel : struct
    +     a reference model, intended as a copy of model with uptake and excretion allowed
    +     and some objectives (such as production of biomass) constrained to a non-zero
    +     flux. A supplied reference model triggers a warning that the feature is not
    +     supported, followed by an error.
    + ignoreIntBounds : logical
    +     true to ignore internal bounds (including reversibility). Exchange reactions are
    +     not affected. Ignoring the bounds finds unbalanced solutions that the default
    +     constraints do not allow (default false).
    + printReport : logical
    +     true to print a report (default false).
    + seed : double
    +     seed for the random number generator. A seed makes the choice among several
    +     candidate reactions reproducible (default: the current rng state is not
    +     changed).
    +
    + Returns
    + -------
    + newModel : struct
    +     the model structure without the problematic reactions.
    + removedRxns : cell
    +     a cell array with the ids of the removed reactions.
    +
    + Notes
    + -----
    + The function removes reactions that enable production or consumption of metabolites
    + when no exchange reaction is used. Models inferred automatically from databases can
    + contain unbalanced reactions that allow net production or consumption of metabolites
    + without any consumption or excretion. One cause is a general compound that has a
    + different meaning in different reactions (as DNA has in these two reactions):
    +
    +     dATP + dGTP + dCTP + dTTP <=> DNA + 4 PPi
    +     0.25 dATP + 0.25 dGTP + 0.25 dCTP + 0.25 dTTP <=> DNA + PPi
    +
    + Such reactions are elementally unbalanced, but the model can contain further
    + unbalanced reactions that are not problematic. The function removes as few reactions
    + as possible so that the model cannot produce or consume anything from nothing. It
    + calls findLeakMetabolite repeatedly, selects at random one of the elementally
    + unbalanced reactions that carry flux in the solution, removes it, and iterates until
    + no metabolite can be produced or consumed. The 'produce' direction runs before the
    + 'consume' direction.
    +
    + Examples
    + --------
    +     [newModel, removedRxns] = removeBadRxns(model, rxnRules, ignoreMets, ...
    +         isNames, balanceElements, refModel, ignoreIntBounds, printReport);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function [newModel, removedRxns]=removeBadRxns(model,varargin)
    +0002 % removeBadRxns  Remove reactions that enable production or consumption from nothing.
    +0003 %
    +0004 % Iteratively removes reactions that enable production or consumption of a metabolite
    +0005 % without any uptake or excretion.
    +0006 %
    +0007 % Parameters
    +0008 % ----------
    +0009 % model : struct
    +0010 %     a model structure. The model must not allow any uptake or excretion; the function
    +0011 %     warns about open exchange reactions when the model has no unconstrained field.
    +0012 %     importModel("filename", false) imports a model without uptake and excretion.
    +0013 %
    +0014 % Name-Value Arguments
    +0015 % --------------------
    +0016 % rxnRules : double
    +0017 %     which reactions may be removed (default 1):
    +0018 %
    +0019 %     - 1 : remove only unbalanced reactions
    +0020 %     - 2 : also remove reactions that could not be checked for mass
    +0021 %       balance
    +0022 %     - 3 : any reaction can be removed
    +0023 % ignoreMets : cell or logical or double
    +0024 %     a cell array of metabolite ids, a logical vector with one element per metabolite
    +0025 %     of the model, or a vector of metabolite indexes to exclude from the analysis
    +0026 %     (default []).
    +0027 % isNames : logical
    +0028 %     true if ignoreMets holds metabolite names (false: ids), which excludes the
    +0029 %     metabolites of that name in all compartments. Applies only if ignoreMets is a
    +0030 %     cell array (default false).
    +0031 % balanceElements : cell
    +0032 %     a cell array with the elements for which to balance the reactions, chosen from
    +0033 %     the elements defined in parseFormulas (default {"C";"P";"S";"N";"O"}).
    +0034 % refModel : struct
    +0035 %     a reference model, intended as a copy of model with uptake and excretion allowed
    +0036 %     and some objectives (such as production of biomass) constrained to a non-zero
    +0037 %     flux. A supplied reference model triggers a warning that the feature is not
    +0038 %     supported, followed by an error.
    +0039 % ignoreIntBounds : logical
    +0040 %     true to ignore internal bounds (including reversibility). Exchange reactions are
    +0041 %     not affected. Ignoring the bounds finds unbalanced solutions that the default
    +0042 %     constraints do not allow (default false).
    +0043 % printReport : logical
    +0044 %     true to print a report (default false).
    +0045 % seed : double
    +0046 %     seed for the random number generator. A seed makes the choice among several
    +0047 %     candidate reactions reproducible (default: the current rng state is not
    +0048 %     changed).
    +0049 %
    +0050 % Returns
    +0051 % -------
    +0052 % newModel : struct
    +0053 %     the model structure without the problematic reactions.
    +0054 % removedRxns : cell
    +0055 %     a cell array with the ids of the removed reactions.
    +0056 %
    +0057 % Notes
    +0058 % -----
    +0059 % The function removes reactions that enable production or consumption of metabolites
    +0060 % when no exchange reaction is used. Models inferred automatically from databases can
    +0061 % contain unbalanced reactions that allow net production or consumption of metabolites
    +0062 % without any consumption or excretion. One cause is a general compound that has a
    +0063 % different meaning in different reactions (as DNA has in these two reactions):
    +0064 %
    +0065 %     dATP + dGTP + dCTP + dTTP <=> DNA + 4 PPi
    +0066 %     0.25 dATP + 0.25 dGTP + 0.25 dCTP + 0.25 dTTP <=> DNA + PPi
    +0067 %
    +0068 % Such reactions are elementally unbalanced, but the model can contain further
    +0069 % unbalanced reactions that are not problematic. The function removes as few reactions
    +0070 % as possible so that the model cannot produce or consume anything from nothing. It
    +0071 % calls findLeakMetabolite repeatedly, selects at random one of the elementally
    +0072 % unbalanced reactions that carry flux in the solution, removes it, and iterates until
    +0073 % no metabolite can be produced or consumed. The 'produce' direction runs before the
    +0074 % 'consume' direction.
    +0075 %
    +0076 % Examples
    +0077 % --------
    +0078 %     [newModel, removedRxns] = removeBadRxns(model, rxnRules, ignoreMets, ...
    +0079 %         isNames, balanceElements, refModel, ignoreIntBounds, printReport);
    +0080 
    +0081 p=parseRAVENargs(varargin, {'rxnRules',1; 'ignoreMets',[]; 'isNames',false; 'balanceElements',{'C';'P';'S';'N';'O'}; 'refModel',[]; 'ignoreIntBounds',false; 'printReport',false; 'seed',[]});
    +0082 if ~isempty(p.seed)
    +0083     rng(p.seed);
    +0084 end
    +0085 rxnRules=p.rxnRules;
    +0086 ignoreMets=p.ignoreMets;
    +0087 if ~isempty(ignoreMets) && ~islogical(ignoreMets) && ~isnumeric(ignoreMets)
    +0088     ignoreMets=convertCharArray(ignoreMets);
    +0089 end
    +0090 isNames=p.isNames;
    +0091 balanceElements=convertCharArray(p.balanceElements);
    +0092 refModel=p.refModel;
    +0093 if ~isempty(refModel)
    +0094     EM='The feature to supply a reference model is currently not supported';
    +0095     warning('RAVEN:warning', '%s', EM);
    +0096 end
    +0097 ignoreIntBounds=p.ignoreIntBounds;
    +0098 printReport=p.printReport;
    +0099 
    +0100 %Check if the model has open exchange reactions and print a warning in that
    +0101 %case
    +0102 if ~isfield(model,'unconstrained')
    +0103     [~, I]=getExchangeRxns(model);
    +0104     if any(I)
    +0105         if any(model.lb(I)~=0) || any(model.ub(I)~=0)
    +0106             EM='The model contains open exchange reactions. This is not the intended use of this function. Consider importing your model using importModel(filename,false)';
    +0107             warning('RAVEN:warning', '%s', EM);
    +0108         end
    +0109     end
    +0110 end
    +0111 
    +0112 %Check that the model is feasible
    +0113 sol=solveLP(model);
    +0114 if isempty(sol.f)
    +0115     EM='The model is not feasible. Consider removing lower bounds (such as ATP maintenance)';
    +0116     error('RAVEN:badInput', '%s', EM);
    +0117 end
    +0118 
    +0119 %Check that the reference model is feasible
    +0120 if any(refModel)
    +0121     sol=solveLP(refModel);
    +0122     if isempty(sol.f)
    +0123         EM='The reference model is not feasible';
    +0124         error('RAVEN:badInput', '%s', EM);
    +0125     end
    +0126 end
    +0127 
    +0128 %Initialize some stuff
    +0129 removedRxns={};
    +0130 balanceStructure=getElementalBalance(model);
    +0131 
    +0132 %Check which elements to balance for
    +0133 if ~isempty(setdiff(balanceElements,balanceStructure.elements.abbrevs))
    +0134     EM='Could not recognize all elements to balance for';
    +0135     error('RAVEN:badInput', '%s', EM);
    +0136 end
    +0137 bal=ismember(balanceStructure.elements.abbrevs,balanceElements);
    +0138 
    +0139 %Main loop. First run for production, second for consumption
    +0140 warned=false(2,1); %This is to prevent the same warning being printed multiple times if rxnRules==3
    +0141 for i=1:2
    +0142     while 1
    +0143         %Make some metabolite using as few reactions as possible
    +0144         if i==1
    +0145             [solution, metabolite]=findLeakMetabolite(model,'produce', ...
    +0146                 'ignoreMets',ignoreMets,'isNames',isNames,'minNrFluxes',false, ...
    +0147                 'allowExcretion',true,'ignoreIntBounds',ignoreIntBounds);
    +0148             if ~isempty(solution)
    +0149                 if printReport
    +0150                     fprintf(['Can make: ' model.metNames{metabolite(1)} '\n']);
    +0151                 end
    +0152             else
    +0153                 %If no solution could be found, then finish
    +0154                 break;
    +0155             end
    +0156         else
    +0157             [solution, metabolite]=findLeakMetabolite(model,'consume', ...
    +0158                 'ignoreMets',ignoreMets,'isNames',isNames,'minNrFluxes',false, ...
    +0159                 'ignoreIntBounds',ignoreIntBounds);
    +0160             if ~isempty(solution)
    +0161                 if printReport
    +0162                     fprintf(['Can consume: ' model.metNames{metabolite(1)} '\n']);
    +0163                 end
    +0164             else
    +0165                 %If no solution could be found, then finish
    +0166                 break;
    +0167             end
    +0168         end
    +0169         
    +0170         %Find all reactions that are unbalanced and still carry flux
    +0171         I=find(abs(solution)>10^-8 & balanceStructure.balanceStatus>=0 & ~all(balanceStructure.leftComp(:,bal)==balanceStructure.rightComp(:,bal),2));
    +0172         
    +0173         %If there are unbalanced rxns then delete one of them and iterate
    +0174         if any(I)
    +0175             rxnToRemove=I(randperm(numel(I),1));
    +0176         else
    +0177             %If there are no unbalanced rxns in the solution
    +0178             if rxnRules==1
    +0179                 if i==1
    +0180                     EM=['No unbalanced reactions were found in the solution, but the model can still make "' model.metNames{metabolite} '". Aborting search. Consider setting rxnRules to 2 or 3 for a more exhaustive search'];
    +0181                     warning('RAVEN:warning', '%s', EM);
    +0182                 else
    +0183                     EM=['No unbalanced reactions were found in the solution, but the model can still consume "' model.metNames{metabolite} '". Aborting search. Consider setting rxnRules to 2 or 3 for a more exhaustive search'];
    +0184                     warning('RAVEN:warning', '%s', EM);
    +0185                 end
    +0186                 break;
    +0187             else
    +0188                 %Find reactions which are not checked for mass balancing,
    +0189                 %but that still carry flux
    +0190                 I=find(abs(solution)>10^-8 & balanceStructure.balanceStatus<0);
    +0191                 
    +0192                 %If there are any such reactions, remove one of them and
    +0193                 %iterate
    +0194                 if any(I)
    +0195                     rxnToRemove=I(randperm(numel(I),1));
    +0196                 else
    +0197                     if rxnRules==2
    +0198                         %This happens when all reactions used are balanced
    +0199                         %according to the metabolite formulas. This cannot
    +0200                         %be, and indicates that one or more of the formulas
    +0201                         %are wrong. Print a warning and delete any reaction
    +0202                         %with flux
    +0203                         if i==1
    +0204                             EM=['No unbalanced or unparsable reactions were found in the solution, but the model can still make "' model.metNames{metabolite} '". Aborting search. Consider setting rxnRules to 3 for a more exhaustive search'];
    +0205                             warning('RAVEN:warning', '%s', EM);
    +0206                         else
    +0207                             EM=['No unbalanced or unparsable reactions were found in the solution, but the model can still consume "' model.metNames{metabolite} '". Aborting search. Consider setting rxnRules to 3 for a more exhaustive search'];
    +0208                             warning('RAVEN:warning', '%s', EM);
    +0209                         end
    +0210                         break;
    +0211                     else
    +0212                         if i==1
    +0213                             if warned(1)==false
    +0214                                 EM=['No unbalanced or unparsable reactions were found in the solution, but the model can still make "' model.metNames{metabolite} '". This indicates some error in the metabolite formulas. Removing random reactions in the solution'];
    +0215                                 warning('RAVEN:warning', '%s', EM);
    +0216                                 warned(1)=true;
    +0217                             end
    +0218                         else
    +0219                             if warned(2)==false
    +0220                                 EM=['No unbalanced or unparsable reactions were found in the solution, but the model can still consume "' model.metNames{metabolite} '". This indicates some error in the metabolite formulas. Removing random reactions in the solution'];
    +0221                                 warning('RAVEN:warning', '%s', EM);
    +0222                                 warned(2)=true;
    +0223                             end
    +0224                         end
    +0225                         I=find(abs(solution)>10^-8);
    +0226                         rxnToRemove=I(randperm(numel(I),1));
    +0227                     end
    +0228                 end
    +0229             end
    +0230         end
    +0231         removedRxns=[removedRxns;model.rxns(rxnToRemove)];
    +0232         if printReport
    +0233             fprintf(['\tRemoved: '  model.rxns{rxnToRemove} '\n']);
    +0234         end
    +0235         model=removeReactions(model,rxnToRemove);
    +0236         balanceStructure.balanceStatus(rxnToRemove)=[];
    +0237         balanceStructure.leftComp(rxnToRemove,:)=[];
    +0238         balanceStructure.rightComp(rxnToRemove,:)=[];
    +0239     end
    +0240 end
    +0241 
    +0242 newModel=model;
    +0243 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/removeGenes.html b/docs/html/manipulation/removeGenes.html new file mode 100644 index 00000000..41f41b71 --- /dev/null +++ b/docs/html/manipulation/removeGenes.html @@ -0,0 +1,275 @@ + + + + Description of removeGenes + + + + + + + + + +
    Home > manipulation > removeGenes.m
    + + + +

    removeGenes +

    + +

    PURPOSE ^

    +
    removeGenes Delete a set of genes from a model.
    + +

    SYNOPSIS ^

    +
    function reducedModel = removeGenes(model,genesToRemove,varargin)
    + +

    DESCRIPTION ^

    +
     removeGenes  Delete a set of genes from a model.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + genesToRemove : cell or logical or double
    +     either a cell array of gene IDs, a logical vector with the same number
    +     of elements as genes in the model, or a vector of indexes to remove.
    +
    + Name-Value Arguments
    + --------------------
    + removeUnusedMets : logical
    +     remove metabolites that no remaining reaction uses (default false).
    + removeBlockedRxns : logical
    +     remove reactions that cannot carry flux after the genes are deleted.
    +     When false, the bounds of these reactions are set to 0 (default false).
    + standardizeRules : logical
    +     write the gene rules in the standard format (default true).
    +
    + Returns
    + -------
    + reducedModel : struct
    +     an updated model structure. Genes in genesToRemove that are not in the
    +     model are ignored. When none of them is in the model, no gene, reaction
    +     or bound changes (with standardizeRules true the gene rules are still
    +     written in the standard format). Otherwise, besides genesToRemove, every
    +     gene that no remaining reaction used before the removal is deleted as
    +     well, also when it was not requested (for example a gene that no
    +     reaction uses). A gene that only occurs in a rule part removed with
    +     another gene stays, because the reaction used it before the removal
    +     (for example G3 in (G1 and G3) or G2 after removing G1). A gene also
    +     stays when its reaction remains, even if that reaction is blocked
    +     (removeBlockedRxns false), such as the other subunit of a complex that
    +     lost one.
    +
    + Examples
    + --------
    +     reducedModel = removeGenes(model, genesToRemove, removeUnusedMets, ...
    +                        removeBlockedRxns, standardizeRules);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function reducedModel = removeGenes(model,genesToRemove,varargin)
    +0002 % removeGenes  Delete a set of genes from a model.
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % model : struct
    +0007 %     a model structure.
    +0008 % genesToRemove : cell or logical or double
    +0009 %     either a cell array of gene IDs, a logical vector with the same number
    +0010 %     of elements as genes in the model, or a vector of indexes to remove.
    +0011 %
    +0012 % Name-Value Arguments
    +0013 % --------------------
    +0014 % removeUnusedMets : logical
    +0015 %     remove metabolites that no remaining reaction uses (default false).
    +0016 % removeBlockedRxns : logical
    +0017 %     remove reactions that cannot carry flux after the genes are deleted.
    +0018 %     When false, the bounds of these reactions are set to 0 (default false).
    +0019 % standardizeRules : logical
    +0020 %     write the gene rules in the standard format (default true).
    +0021 %
    +0022 % Returns
    +0023 % -------
    +0024 % reducedModel : struct
    +0025 %     an updated model structure. Genes in genesToRemove that are not in the
    +0026 %     model are ignored. When none of them is in the model, no gene, reaction
    +0027 %     or bound changes (with standardizeRules true the gene rules are still
    +0028 %     written in the standard format). Otherwise, besides genesToRemove, every
    +0029 %     gene that no remaining reaction used before the removal is deleted as
    +0030 %     well, also when it was not requested (for example a gene that no
    +0031 %     reaction uses). A gene that only occurs in a rule part removed with
    +0032 %     another gene stays, because the reaction used it before the removal
    +0033 %     (for example G3 in (G1 and G3) or G2 after removing G1). A gene also
    +0034 %     stays when its reaction remains, even if that reaction is blocked
    +0035 %     (removeBlockedRxns false), such as the other subunit of a complex that
    +0036 %     lost one.
    +0037 %
    +0038 % Examples
    +0039 % --------
    +0040 %     reducedModel = removeGenes(model, genesToRemove, removeUnusedMets, ...
    +0041 %                        removeBlockedRxns, standardizeRules);
    +0042 
    +0043 p=parseRAVENargs(varargin, {'removeUnusedMets',false; 'removeBlockedRxns',false; 'standardizeRules',true});
    +0044 removeUnusedMets=p.removeUnusedMets;
    +0045 removeBlockedRxns=p.removeBlockedRxns;
    +0046 standardizeRules=p.standardizeRules;
    +0047 %Format grRules and rxnGeneMatrix:
    +0048 if standardizeRules
    +0049     [grRules,rxnGeneMat,toCheck] = standardizeGrRules(model,true);
    +0050     model.grRules    = grRules;
    +0051     model.rxnGeneMat = rxnGeneMat;
    +0052 else
    +0053     toCheck    = [];
    +0054     rxnGeneMat = model.rxnGeneMat;
    +0055 end
    +0056 reducedModel = model;
    +0057 %Only remove genes that are actually in the model
    +0058 if ~(islogical(genesToRemove) || isnumeric(genesToRemove))
    +0059     genesToRemove=convertCharArray(genesToRemove);
    +0060     genesToRemove=genesToRemove(ismember(genesToRemove,model.genes));
    +0061 end
    +0062 if ~isempty(genesToRemove)
    +0063     indexesToRemove = getIndexes(model,genesToRemove,'genes');
    +0064     if ~isempty(indexesToRemove)
    +0065         %Make 0 corresponding columns from rxnGeneMat:
    +0066         reducedModel.rxnGeneMat(:,indexesToRemove) = 0;        
    +0067         genes = model.genes(indexesToRemove);
    +0068         %Check if conflicting grRules contain any of the genes to remove
    +0069         if ~isempty(toCheck)
    +0070             for i=1:numel(toCheck)
    +0071                 index   = toCheck(i);
    +0072                 gIdxs   = find(rxnGeneMat(index,:));
    +0073                 g2check = model.genes(gIdxs);
    +0074                 if any(ismember(g2check,genes))
    +0075                     warning(['Conflicting grRule #' num2str(index) ' (' model.rxns{index} ') contains at least one of the genes to be removed, this grRule will be bypassed in order to avoid logical errors'])
    +0076                 end
    +0077             end
    +0078         end
    +0079         canCarryFlux = true(size(model.rxns));       
    +0080         %Loop through genes and adapt rxns:
    +0081         for i = 1:length(genes)
    +0082             %Find all reactions for this gene and loop through them:
    +0083             geneRxns = find(rxnGeneMat(:,indexesToRemove(i)));
    +0084             if ~isempty(geneRxns)
    +0085                 for j = 1:numel(geneRxns)
    +0086                     index  = geneRxns(j);
    +0087                     grRule = reducedModel.grRules{index};
    +0088                     if ~ismember(index,toCheck) && canCarryFlux(index) && ~isempty(grRule)
    +0089                         %Check if rxn can carry flux without this gene:
    +0090                         canCarryFlux(index) = canRxnCarryFlux(grRule,genes{i});
    +0091                         %Adapt gene rule & gene matrix:
    +0092                         grRule = removeGeneFromRule(grRule,genes{i});
    +0093                         reducedModel.grRules{index} = grRule;
    +0094                     end
    +0095                 end
    +0096             end
    +0097         end
    +0098         %Delete or block the reactions that cannot carry flux:
    +0099         if removeBlockedRxns
    +0100             rxnsToRemove = reducedModel.rxns(~canCarryFlux);
    +0101             reducedModel = removeReactions(reducedModel,rxnsToRemove,removeUnusedMets,true);
    +0102         else
    +0103             reducedModel = removeReactions(reducedModel,[],removeUnusedMets,true);
    +0104             reducedModel.lb(~canCarryFlux) = 0;
    +0105             reducedModel.ub(~canCarryFlux) = 0;
    +0106         end
    +0107     end
    +0108 end
    +0109 %Format grRules and rxnGeneMatrix after all modifications
    +0110 if standardizeRules
    +0111     [grRules,rxnGeneMat] = standardizeGrRules(reducedModel,true);
    +0112     reducedModel.grRules = grRules;
    +0113     reducedModel.rxnGeneMat = rxnGeneMat;
    +0114 end
    +0115 end
    +0116 
    +0117 function canIt = canRxnCarryFlux(geneRule,geneToRemove)
    +0118 %Evaluate the rule with geneToRemove absent and every other gene present.
    +0119 %A complex (AND) needs all of its subunits, isozymes (OR) need only one.
    +0120 tree = parseGrRule(geneRule);
    +0121 if isempty(tree)
    +0122     canIt = true;
    +0123     return
    +0124 end
    +0125 canIt = evalWithout(tree,geneToRemove);
    +0126 end
    +0127 
    +0128 function tf = evalWithout(node,geneToRemove)
    +0129 switch node.type
    +0130     case 'gene'
    +0131         tf = ~strcmp(node.id,geneToRemove);
    +0132     case 'and'
    +0133         tf = all(cellfun(@(c) evalWithout(c,geneToRemove),node.children));
    +0134     case 'or'
    +0135         tf = any(cellfun(@(c) evalWithout(c,geneToRemove),node.children));
    +0136     otherwise
    +0137         error('RAVEN:badGrRule',['Unexpected grRule node type: ' node.type]);
    +0138 end
    +0139 end
    +0140 
    +0141 function geneRule = removeGeneFromRule(geneRule,geneToRemove)
    +0142 %This function receives a gene rule and it returns it without the chosen
    +0143 %gene. A complex that loses a subunit is dropped whole; an isozyme is
    +0144 %dropped from its OR without disturbing the alternatives.
    +0145 tree = parseGrRule(geneRule);
    +0146 geneRule = grRuleToString(pruneGene(tree,geneToRemove));
    +0147 end
    +0148 
    +0149 function node = pruneGene(node,geneToRemove)
    +0150 %Remove geneToRemove from the tree, returning [] when nothing is left that
    +0151 %can catalyse the reaction.
    +0152 if isempty(node)
    +0153     node = [];
    +0154     return
    +0155 end
    +0156 switch node.type
    +0157     case 'gene'
    +0158         if strcmp(node.id,geneToRemove)
    +0159             node = [];
    +0160         end
    +0161     case 'and'
    +0162         %A complex missing any subunit cannot form at all.
    +0163         for k = 1:numel(node.children)
    +0164             if isempty(pruneGene(node.children{k},geneToRemove))
    +0165                 node = [];
    +0166                 return
    +0167             end
    +0168         end
    +0169     case 'or'
    +0170         kept = {};
    +0171         for k = 1:numel(node.children)
    +0172             child = pruneGene(node.children{k},geneToRemove);
    +0173             if ~isempty(child)
    +0174                 kept{end+1} = child; %#ok<AGROW>
    +0175             end
    +0176         end
    +0177         if isempty(kept)
    +0178             node = [];
    +0179         elseif isscalar(kept)
    +0180             node = kept{1};
    +0181         else
    +0182             node.children = kept;
    +0183         end
    +0184     otherwise
    +0185         error('RAVEN:badGrRule',['Unexpected grRule node type: ' node.type]);
    +0186 end
    +0187 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/removeLowScoreGenes.html b/docs/html/manipulation/removeLowScoreGenes.html new file mode 100644 index 00000000..7a67b836 --- /dev/null +++ b/docs/html/manipulation/removeLowScoreGenes.html @@ -0,0 +1,474 @@ + + + + Description of removeLowScoreGenes + + + + + + + + + +
    Home > manipulation > removeLowScoreGenes.m
    + + + +

    removeLowScoreGenes +

    + +

    PURPOSE ^

    +
    removeLowScoreGenes Remove low-score genes from a model.
    + +

    SYNOPSIS ^

    +
    function [newModel,remGenes] = removeLowScoreGenes(model,geneScores,varargin)
    + +

    DESCRIPTION ^

    +
     removeLowScoreGenes  Remove low-score genes from a model.
    +
    + Removes genes from a model based on their scores, a step used by the tINIT
    + package. The function distinguishes isozymes from subunits of an enzyme
    + complex. Genes are removed from each grRule, subject to the following
    + conditions:
    +
    +     1) At least one gene must remain associated with the reaction.
    +     2) Genes involved in a complex (joined by ANDs) are not removed.
    +
    + Parameters
    + ----------
    + model : struct
    +     model structure from which genes are to be removed.
    + geneScores : double
    +     a vector of scores associated with the model genes, with the same
    +     dimensions as model.genes. Genes with a score of zero or above and genes
    +     without a score (NaN) remain in the rules; a gene that no rule uses
    +     afterwards is removed from model.genes. Genes with a negative score
    +     are removed from the rules when the conditions above allow it.
    +
    +     If all genes associated with a reaction have a negative score, the
    +     least-negative gene remains; in a tie, the gene listed first in the rule
    +     is kept.
    +
    +     A negative-scoring gene that is a subunit in a complex is not removed,
    +     but the entire complex may be removed. Example cases:
    +
    +     - Original: G1 or (G2 and G3 and G4); Negative: G1, G2; New: G2 and G3
    +       and G4.
    +     - Original: G1 or (G2 and G3) or (G4 and G5); Negative: G1, G2; New: G4
    +       and G5 [using default complexScoring].
    +     - Original: (G1 and (G2 or G3) and G4); Negative: G2; New: (G1 and G3
    +       and G4).
    +
    + Name-Value Arguments
    + --------------------
    + isozymeScoring : char
    +     method used to combine the scores of multiple isozymes: "min", "max",
    +     "median" or "average" (default "max").
    + complexScoring : char
    +     method used to combine the scores of enzyme complex subunits: "min",
    +     "max", "median" or "average" (default "min").
    +
    + Returns
    + -------
    + newModel : struct
    +     model with updated genes, grRules and rxnGeneMat fields, and with the
    +     gene-associated fields (geneShortNames, proteins, geneMiriams, geneFrom,
    +     geneComps) trimmed to the remaining genes.
    + remGenes : cell
    +     the genes removed from model.genes: negative-score genes removed from every
    +     grRule and genes that no rule uses, whatever their score. Negative-score
    +     genes that are removed from some grRules but not all are not included in
    +     this list.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [newModel,remGenes] = removeLowScoreGenes(model,geneScores,varargin)
    +0002 % removeLowScoreGenes  Remove low-score genes from a model.
    +0003 %
    +0004 % Removes genes from a model based on their scores, a step used by the tINIT
    +0005 % package. The function distinguishes isozymes from subunits of an enzyme
    +0006 % complex. Genes are removed from each grRule, subject to the following
    +0007 % conditions:
    +0008 %
    +0009 %     1) At least one gene must remain associated with the reaction.
    +0010 %     2) Genes involved in a complex (joined by ANDs) are not removed.
    +0011 %
    +0012 % Parameters
    +0013 % ----------
    +0014 % model : struct
    +0015 %     model structure from which genes are to be removed.
    +0016 % geneScores : double
    +0017 %     a vector of scores associated with the model genes, with the same
    +0018 %     dimensions as model.genes. Genes with a score of zero or above and genes
    +0019 %     without a score (NaN) remain in the rules; a gene that no rule uses
    +0020 %     afterwards is removed from model.genes. Genes with a negative score
    +0021 %     are removed from the rules when the conditions above allow it.
    +0022 %
    +0023 %     If all genes associated with a reaction have a negative score, the
    +0024 %     least-negative gene remains; in a tie, the gene listed first in the rule
    +0025 %     is kept.
    +0026 %
    +0027 %     A negative-scoring gene that is a subunit in a complex is not removed,
    +0028 %     but the entire complex may be removed. Example cases:
    +0029 %
    +0030 %     - Original: G1 or (G2 and G3 and G4); Negative: G1, G2; New: G2 and G3
    +0031 %       and G4.
    +0032 %     - Original: G1 or (G2 and G3) or (G4 and G5); Negative: G1, G2; New: G4
    +0033 %       and G5 [using default complexScoring].
    +0034 %     - Original: (G1 and (G2 or G3) and G4); Negative: G2; New: (G1 and G3
    +0035 %       and G4).
    +0036 %
    +0037 % Name-Value Arguments
    +0038 % --------------------
    +0039 % isozymeScoring : char
    +0040 %     method used to combine the scores of multiple isozymes: "min", "max",
    +0041 %     "median" or "average" (default "max").
    +0042 % complexScoring : char
    +0043 %     method used to combine the scores of enzyme complex subunits: "min",
    +0044 %     "max", "median" or "average" (default "min").
    +0045 %
    +0046 % Returns
    +0047 % -------
    +0048 % newModel : struct
    +0049 %     model with updated genes, grRules and rxnGeneMat fields, and with the
    +0050 %     gene-associated fields (geneShortNames, proteins, geneMiriams, geneFrom,
    +0051 %     geneComps) trimmed to the remaining genes.
    +0052 % remGenes : cell
    +0053 %     the genes removed from model.genes: negative-score genes removed from every
    +0054 %     grRule and genes that no rule uses, whatever their score. Negative-score
    +0055 %     genes that are removed from some grRules but not all are not included in
    +0056 %     this list.
    +0057 
    +0058 
    +0059 p=parseRAVENargs(varargin, {'isozymeScoring',[]; 'complexScoring','min'});
    +0060 isozymeScoring=p.isozymeScoring;
    +0061 complexScoring=p.complexScoring;
    +0062 if isempty(isozymeScoring)
    +0063     isozymeScoring = 'max';
    +0064 end
    +0065 
    +0066 if ~isequal(size(model.genes),size(geneScores))
    +0067     error('The dimensions of model genes and geneScores do not match.');
    +0068 end
    +0069 
    +0070 % convert logical operators to symbols
    +0071 grRules = model.grRules;
    +0072 if any(contains(grRules,{'&','|'}))
    +0073     symbolicRules = true;
    +0074 else
    +0075     symbolicRules = false;
    +0076 end
    +0077 grRules = regexprep(grRules,' and ',' & ');
    +0078 grRules = regexprep(grRules,' or ',' | ');
    +0079 
    +0080 % get unique list of grRules
    +0081 [uRules,~,rule_ind] = unique(grRules);
    +0082 
    +0083 % iterate through each rule
    +0084 newRules = uRules;  %initialize newRules
    +0085 for i = 1:numel(uRules)
    +0086     if isempty(uRules{i}) || ~contains(uRules{i},'|')
    +0087         continue
    +0088     elseif contains(uRules{i},'&')
    +0089         newRules{i} = processComplexRule(uRules{i},model.genes,geneScores,isozymeScoring,complexScoring);
    +0090     else
    +0091         newRules{i} = processSimpleRule(uRules{i},model.genes,geneScores,isozymeScoring,complexScoring);
    +0092     end
    +0093 end
    +0094 
    +0095 % re-map unique rules to model
    +0096 newModel = model;
    +0097 newModel.grRules = newRules(rule_ind);
    +0098 
    +0099 % restore original logical operator formatting if it was changed
    +0100 if ~symbolicRules
    +0101     newModel.grRules = regexprep(newModel.grRules, ' \| ', ' or ');
    +0102     newModel.grRules = regexprep(newModel.grRules, ' & ', ' and ');
    +0103 end
    +0104 
    +0105 % regenerate "genes" and "rxnGeneMat" model fields
    +0106 [genes,rxnGeneMat] = getGenesFromGrRules(newModel.grRules);
    +0107 
    +0108 % determine which of the original genes were removed
    +0109 remInd = ~ismember(model.genes,genes);
    +0110 remGenes = model.genes(remInd);
    +0111 
    +0112 % Keep the retained genes in their original order rather than the sorted
    +0113 % order returned by getGenesFromGrRules. Gene removal never introduces new
    +0114 % genes, so the remaining genes are model.genes(~remInd). Preserving this
    +0115 % order ensures the gene-associated fields trimmed below (geneShortNames,
    +0116 % proteins, etc.), which are indexed by remInd, stay aligned with genes.
    +0117 newModel.genes = model.genes(~remInd);
    +0118 [~,reorderInd] = ismember(newModel.genes,genes);
    +0119 newModel.rxnGeneMat = rxnGeneMat(:,reorderInd);
    +0120 
    +0121 if isfield(newModel,'geneShortNames')
    +0122     newModel.geneShortNames(remInd) = [];
    +0123 end
    +0124 if isfield(newModel,'proteins')
    +0125     newModel.proteins(remInd) = [];
    +0126 end
    +0127 if isfield(newModel,'geneMiriams')
    +0128     newModel.geneMiriams(remInd) = [];
    +0129 end
    +0130 if isfield(newModel,'geneFrom')
    +0131     newModel.geneFrom(remInd) = [];
    +0132 end
    +0133 if isfield(newModel,'geneComps')
    +0134     newModel.geneComps(remInd) = [];
    +0135 end
    +0136 
    +0137 
    +0138 end
    +0139 
    +0140 
    +0141 
    +0142 function [updatedRule,rScore] = processSimpleRule(rule,genes,gScores,isozymeScoring,complexScoring)
    +0143 % Either score or modify a reaction gene rule containig only ANDs or ORs.
    +0144 %
    +0145 % If the rule contains an enzyme complex (all ANDs), the complex will be
    +0146 % scored based on the score of its subunits. Subunits without a score (NaN)
    +0147 % will be excluded from the score calculation.
    +0148 %
    +0149 % If the rule contains only isozymes (all ORs), the negative-score genes
    +0150 % will be removed from the rule. Isozymes without a score (NaN) will not be
    +0151 % removed from the rule. The resuling rule will then be scored.
    +0152 
    +0153 
    +0154 % get IDs and indices of genes involved in rule
    +0155 ruleGenes = unique(regexp(rule,'[^&|\(\) ]+','match'),'stable');
    +0156 [~,geneInd] = ismember(ruleGenes,genes);
    +0157 
    +0158 % rules with one or no genes remain unchanged
    +0159 if numel(ruleGenes) < 2
    +0160     rScore = gScores(geneInd);
    +0161     updatedRule = rule;
    +0162     return
    +0163 end
    +0164 
    +0165 if ~contains(rule,'&')  % rule contains isozymes
    +0166     
    +0167     scoreMethod = isozymeScoring;
    +0168     negInd = gScores(geneInd) < 0;  % NaNs will return false here
    +0169     if all(negInd)
    +0170         % deterministic least-negative: sort descending; the stable sort
    +0171         % breaks ties by the order of the genes in the rule string
    +0172         [~,sortOrder] = sort(gScores(geneInd),'descend');
    +0173         maxInd = sortOrder(1);
    +0174         updatedRule = ruleGenes{maxInd};
    +0175     elseif sum(~negInd) == 1
    +0176         updatedRule = ruleGenes{~negInd};
    +0177     else
    +0178         updatedRule = strjoin(ruleGenes(~negInd),' | ');
    +0179         if startsWith(rule,'(')
    +0180             updatedRule = ['(',updatedRule,')'];
    +0181         end
    +0182     end
    +0183     
    +0184     % update ruleGenes and their indices
    +0185     ruleGenes = unique(regexp(updatedRule,'[^&|\(\) ]+','match'),'stable');
    +0186     [~,geneInd] = ismember(ruleGenes,genes);
    +0187     
    +0188 elseif ~contains(rule,'|')  % rule contains enzyme complex
    +0189     scoreMethod = complexScoring;
    +0190     updatedRule = rule;
    +0191 else
    +0192     error('This function cannot handle rules with both "OR" and "AND" expressions.');
    +0193 end
    +0194 
    +0195 % score rule
    +0196 switch lower(scoreMethod)
    +0197     case 'min'
    +0198         rScore = min(gScores(geneInd),[],'omitnan');
    +0199     case 'max'
    +0200         rScore = max(gScores(geneInd),[],'omitnan');
    +0201     case 'median'
    +0202         rScore = median(gScores(geneInd),'omitnan');
    +0203     case 'average'
    +0204         rScore = mean(gScores(geneInd),'omitnan');
    +0205 end
    +0206 
    +0207 end
    +0208 
    +0209 
    +0210 
    +0211 function updatedRule = processComplexRule(rule,genes,gScores,isozymeScoring,complexScoring)
    +0212 % Update reactions containing both AND and OR expressions via recursive descent.
    +0213 %
    +0214 % Walks the boolean parse tree: OR nodes (isozymes) prune negative-score
    +0215 % branches while keeping at least one; AND nodes (complexes) are scored as
    +0216 % a unit and may be removed at the enclosing OR level. Nested isozyme groups
    +0217 % within a complex are simplified in place.
    +0218 updatedRule = evalGPR(rule, genes, gScores, isozymeScoring, complexScoring);
    +0219 end
    +0220 
    +0221 
    +0222 
    +0223 function [rule, score] = evalGPR(expr, genes, gScores, isozymeScoring, complexScoring)
    +0224 % Recursively evaluate and simplify a GPR expression.
    +0225 % NaN-scored genes/complexes are not pruned (treated as non-negative).
    +0226 
    +0227 expr = strtrim(expr);
    +0228 
    +0229 % Strip redundant outer parentheses
    +0230 while numel(expr) >= 2 && expr(1) == '('
    +0231     closePos = findMatchingClose(expr, 1);
    +0232     if closePos == numel(expr)
    +0233         expr = strtrim(expr(2:end-1));
    +0234     else
    +0235         break
    +0236     end
    +0237 end
    +0238 
    +0239 % OR split: isozyme level
    +0240 orTerms = splitDepth0(expr, '|');
    +0241 if numel(orTerms) > 1
    +0242     termScores = nan(1, numel(orTerms));
    +0243     termRules  = cell(1,  numel(orTerms));
    +0244     for k = 1:numel(orTerms)
    +0245         [termRules{k}, termScores(k)] = evalGPR(orTerms{k}, genes, gScores, isozymeScoring, complexScoring);
    +0246     end
    +0247     % NaN-scored terms are kept; negatives pruned when alternatives exist
    +0248     negIdx = termScores < 0;  % NaN -> false, so NaN terms are retained
    +0249     if all(negIdx)
    +0250         % All terms negative: keep the least-negative (deterministic)
    +0251         [~, ord] = sort(termScores, 'descend');
    +0252         keepIdx  = ord(1);
    +0253     else
    +0254         keepIdx = find(~negIdx);
    +0255     end
    +0256     termRules  = termRules(keepIdx);
    +0257     termScores = termScores(keepIdx);
    +0258     rule  = joinOR(termRules);
    +0259     score = applyScoring(termScores, isozymeScoring);
    +0260     return
    +0261 end
    +0262 
    +0263 % AND split: complex level
    +0264 andTerms = splitDepth0(expr, '&');
    +0265 if numel(andTerms) > 1
    +0266     termScores = nan(1, numel(andTerms));
    +0267     termRules  = cell(1,  numel(andTerms));
    +0268     for k = 1:numel(andTerms)
    +0269         [termRules{k}, termScores(k)] = evalGPR(andTerms{k}, genes, gScores, isozymeScoring, complexScoring);
    +0270     end
    +0271     score = applyScoring(termScores, complexScoring);
    +0272     rule  = joinAND(termRules);
    +0273     return
    +0274 end
    +0275 
    +0276 % Leaf: single gene
    +0277 geneIdx = find(strcmp(genes, expr), 1);
    +0278 if ~isempty(geneIdx)
    +0279     score = gScores(geneIdx);
    +0280 else
    +0281     score = NaN;
    +0282 end
    +0283 rule = expr;
    +0284 end
    +0285 
    +0286 
    +0287 
    +0288 function terms = splitDepth0(expr, sep)
    +0289 % Split expr on ' sep ' at bracket depth 0.
    +0290 terms = {};
    +0291 depth = 0;
    +0292 start = 1;
    +0293 n     = numel(expr);
    +0294 i     = 1;
    +0295 while i <= n
    +0296     c = expr(i);
    +0297     if c == '('
    +0298         depth = depth + 1;
    +0299         i = i + 1;
    +0300     elseif c == ')'
    +0301         depth = depth - 1;
    +0302         i = i + 1;
    +0303     elseif c == sep && depth == 0 && i >= 2 && i <= n-1 && expr(i-1) == ' ' && expr(i+1) == ' '
    +0304         terms{end+1} = strtrim(expr(start:i-2));  %#ok<AGROW>
    +0305         start = i + 2;
    +0306         i     = i + 2;
    +0307     else
    +0308         i = i + 1;
    +0309     end
    +0310 end
    +0311 terms{end+1} = strtrim(expr(start:end));
    +0312 end
    +0313 
    +0314 
    +0315 
    +0316 function pos = findMatchingClose(expr, openPos)
    +0317 % Return position of ')' matching '(' at openPos.
    +0318 depth = 0;
    +0319 for i = openPos:numel(expr)
    +0320     if     expr(i) == '('; depth = depth + 1;
    +0321     elseif expr(i) == ')'; depth = depth - 1;
    +0322         if depth == 0; pos = i; return; end
    +0323     end
    +0324 end
    +0325 pos = numel(expr);
    +0326 end
    +0327 
    +0328 
    +0329 
    +0330 function rule = joinOR(rules)
    +0331 % Join rules with ' | '; wrap AND-containing subterms in parens.
    +0332 n = numel(rules);
    +0333 if n > 1
    +0334     for k = 1:n
    +0335         if contains(rules{k}, '&')
    +0336             rules{k} = ['(' rules{k} ')'];
    +0337         end
    +0338     end
    +0339 end
    +0340 rule = strjoin(rules, ' | ');
    +0341 end
    +0342 
    +0343 
    +0344 
    +0345 function rule = joinAND(rules)
    +0346 % Join rules with ' & '; wrap OR-containing subterms in parens.
    +0347 n = numel(rules);
    +0348 if n > 1
    +0349     for k = 1:n
    +0350         if contains(rules{k}, '|')
    +0351             rules{k} = ['(' rules{k} ')'];
    +0352         end
    +0353     end
    +0354 end
    +0355 rule = strjoin(rules, ' & ');
    +0356 end
    +0357 
    +0358 
    +0359 
    +0360 function s = applyScoring(scores, method)
    +0361 switch lower(method)
    +0362     case 'min';     s = min(scores, [], 'omitnan');
    +0363     case 'max';     s = max(scores, [], 'omitnan');
    +0364     case 'median';  s = median(scores, 'omitnan');
    +0365     case 'average'; s = mean(scores, 'omitnan');
    +0366     otherwise;      s = max(scores, [], 'omitnan');
    +0367 end
    +0368 end
    +0369 
    +0370 
    +0371
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/removeMets.html b/docs/html/manipulation/removeMets.html new file mode 100644 index 00000000..1cf98de4 --- /dev/null +++ b/docs/html/manipulation/removeMets.html @@ -0,0 +1,212 @@ + + + + Description of removeMets + + + + + + + + + +
    Home > manipulation > removeMets.m
    + + + +

    removeMets +

    + +

    PURPOSE ^

    +
    removeMets Delete a set of metabolites from a model.
    + +

    SYNOPSIS ^

    +
    function reducedModel=removeMets(model,metsToRemove,varargin)
    + +

    DESCRIPTION ^

    +
     removeMets  Delete a set of metabolites from a model.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + metsToRemove : cell or logical or double
    +     either a cell array of metabolite IDs, a logical vector with the same
    +     number of elements as metabolites in the model, or a vector of
    +     indexes to remove.
    +
    + Name-Value Arguments
    + --------------------
    + isNames : logical
    +     true if the supplied mets are metabolite names, false if they are IDs.
    +     All metabolites with a listed name are deleted, which removes
    +     metabolites in several compartments at once. Requires metsToRemove to
    +     be a cell array (default false).
    + removeUnusedRxns : logical
    +     remove reactions that involve no metabolite (default false).
    + removeUnusedGenes : logical
    +     remove genes that no remaining reaction uses. Takes effect only when
    +     removeUnusedRxns is true (default false).
    + removeUnusedComps : logical
    +     remove compartments that contain no metabolite (default false).
    +
    + Returns
    + -------
    + reducedModel : struct
    +     an updated model structure.
    +
    + Examples
    + --------
    +     reducedModel = removeMets(model, metsToRemove, isNames, ...
    +         removeUnusedRxns, removeUnusedGenes, removeUnusedComps);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    • mergeCompartments mergeCompartments Merge all compartments in a model.
    • removeReactions removeReactions Delete a set of reactions from a model.
    • simplifyModel simplifyModel Simplify a model by deleting reactions and metabolites.
    + + + + +

    SOURCE CODE ^

    +
    0001 function reducedModel=removeMets(model,metsToRemove,varargin)
    +0002 % removeMets  Delete a set of metabolites from a model.
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % model : struct
    +0007 %     a model structure.
    +0008 % metsToRemove : cell or logical or double
    +0009 %     either a cell array of metabolite IDs, a logical vector with the same
    +0010 %     number of elements as metabolites in the model, or a vector of
    +0011 %     indexes to remove.
    +0012 %
    +0013 % Name-Value Arguments
    +0014 % --------------------
    +0015 % isNames : logical
    +0016 %     true if the supplied mets are metabolite names, false if they are IDs.
    +0017 %     All metabolites with a listed name are deleted, which removes
    +0018 %     metabolites in several compartments at once. Requires metsToRemove to
    +0019 %     be a cell array (default false).
    +0020 % removeUnusedRxns : logical
    +0021 %     remove reactions that involve no metabolite (default false).
    +0022 % removeUnusedGenes : logical
    +0023 %     remove genes that no remaining reaction uses. Takes effect only when
    +0024 %     removeUnusedRxns is true (default false).
    +0025 % removeUnusedComps : logical
    +0026 %     remove compartments that contain no metabolite (default false).
    +0027 %
    +0028 % Returns
    +0029 % -------
    +0030 % reducedModel : struct
    +0031 %     an updated model structure.
    +0032 %
    +0033 % Examples
    +0034 % --------
    +0035 %     reducedModel = removeMets(model, metsToRemove, isNames, ...
    +0036 %         removeUnusedRxns, removeUnusedGenes, removeUnusedComps);
    +0037 if ~islogical(metsToRemove) && ~isnumeric(metsToRemove)
    +0038     metsToRemove=convertCharArray(metsToRemove);
    +0039 end
    +0040 
    +0041 p=parseRAVENargs(varargin, {'isNames',false; 'removeUnusedRxns',false; 'removeUnusedGenes',false; 'removeUnusedComps',false});
    +0042 isNames=p.isNames;
    +0043 removeUnusedRxns=p.removeUnusedRxns;
    +0044 removeUnusedGenes=p.removeUnusedGenes;
    +0045 removeUnusedComps=p.removeUnusedComps;
    +0046 
    +0047 %Check that metsToRemove is a cell array
    +0048 if isNames==true && ~iscell(metsToRemove)
    +0049     error('Must supply a cell array of strings if isNames=true');
    +0050 end
    +0051 
    +0052 reducedModel=model;
    +0053 
    +0054 if isNames==false
    +0055     indexesToDelete=getIndexes(model,metsToRemove,'mets');
    +0056 else
    +0057     indexesToDelete=[];
    +0058     for i=1:numel(metsToRemove)
    +0059         indexesToDelete=[indexesToDelete;find(strcmp(metsToRemove(i),model.metNames))];
    +0060     end
    +0061 end
    +0062 
    +0063 %Remove metabolites
    +0064 if ~isempty(indexesToDelete)
    +0065     reducedModel.mets(indexesToDelete)=[];
    +0066     reducedModel.S(indexesToDelete,:)=[];
    +0067     if isfield(reducedModel,'b')
    +0068         reducedModel.b(indexesToDelete,:)=[];
    +0069     end
    +0070     if isfield(reducedModel,'metNames')
    +0071         reducedModel.metNames(indexesToDelete)=[];
    +0072     end
    +0073     if isfield(reducedModel,'metComps')
    +0074         reducedModel.metComps(indexesToDelete)=[];
    +0075     end
    +0076     if isfield(reducedModel,'inchis')
    +0077         reducedModel.inchis(indexesToDelete)=[];
    +0078     end
    +0079     if isfield(reducedModel,'metSmiles')
    +0080         reducedModel.metSmiles(indexesToDelete)=[];
    +0081     end
    +0082     if isfield(reducedModel,'metFormulas')
    +0083         reducedModel.metFormulas(indexesToDelete)=[];
    +0084     end
    +0085     if isfield(reducedModel,'metMiriams')
    +0086         reducedModel.metMiriams(indexesToDelete)=[];
    +0087     end
    +0088     if isfield(reducedModel,'unconstrained')
    +0089         reducedModel.unconstrained(indexesToDelete)=[];
    +0090     end
    +0091     if isfield(reducedModel,'metFrom')
    +0092         reducedModel.metFrom(indexesToDelete)=[];
    +0093     end
    +0094     if isfield(reducedModel,'metCharges')
    +0095         reducedModel.metCharges(indexesToDelete)=[];
    +0096     end
    +0097     if isfield(reducedModel,'metDeltaG')
    +0098         reducedModel.metDeltaG(indexesToDelete)=[];
    +0099     end
    +0100     if isfield(reducedModel,'metNotes')
    +0101         reducedModel.metNotes(indexesToDelete)=[];
    +0102     end    
    +0103 end
    +0104 
    +0105 %Remove unused reactions
    +0106 if removeUnusedRxns==true
    +0107     %Get unused reactions
    +0108     [~, a]=find(reducedModel.S);
    +0109     rxnsToRemove=1:numel(reducedModel.rxns);
    +0110     rxnsToRemove(a)=[];
    +0111     reducedModel=removeReactions(reducedModel,rxnsToRemove,false,removeUnusedGenes);
    +0112 end
    +0113 
    +0114 %Remove unused compartments
    +0115 if removeUnusedComps==true
    +0116     oldComps=reducedModel.comps;
    +0117     I=ismember(1:numel(oldComps),reducedModel.metComps);
    +0118     if ~all(I)
    +0119         reducedModel.comps(~I)=[];
    +0120         reducedModel.compNames(~I)=[];
    +0121         if isfield(reducedModel,'compOutside')
    +0122             reducedModel.compOutside(~I)=[];
    +0123         end
    +0124         if isfield(reducedModel,'compMiriams')
    +0125             reducedModel.compMiriams(~I)=[];
    +0126         end
    +0127         [~, J]=ismember(oldComps(reducedModel.metComps),reducedModel.comps);
    +0128         reducedModel.metComps=J;
    +0129     end
    +0130 end
    +0131 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/removeReactions.html b/docs/html/manipulation/removeReactions.html new file mode 100644 index 00000000..1791ca47 --- /dev/null +++ b/docs/html/manipulation/removeReactions.html @@ -0,0 +1,169 @@ + + + + Description of removeReactions + + + + + + + + + +
    Home > manipulation > removeReactions.m
    + + + +

    removeReactions +

    + +

    PURPOSE ^

    +
    removeReactions Delete a set of reactions from a model.
    + +

    SYNOPSIS ^

    +
    function reducedModel=removeReactions(model,rxnsToRemove,varargin)
    + +

    DESCRIPTION ^

    +
     removeReactions  Delete a set of reactions from a model.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + rxnsToRemove : cell or logical or double
    +     either a cell array of reaction IDs, a logical vector with the same
    +     number of elements as reactions in the model, or a vector of indexes
    +     to remove.
    +
    + Name-Value Arguments
    + --------------------
    + removeUnusedMets : logical
    +     remove metabolites that no remaining reaction uses (default false).
    + removeUnusedGenes : logical
    +     remove genes that no remaining reaction uses (default false).
    + removeUnusedComps : logical
    +     remove compartments that contain no metabolite. Takes effect only when
    +     removeUnusedMets is true (default false).
    +
    + Returns
    + -------
    + reducedModel : struct
    +     an updated model structure.
    +
    + Examples
    + --------
    +     reducedModel = removeReactions(model, rxnsToRemove, removeUnusedMets, ...
    +         removeUnusedGenes, removeUnusedComps);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • removeMets removeMets Delete a set of metabolites from a model.
    +This function is called by: +
      +
    • changeRxns changeRxns Modify the equations of reactions in a model.
    • contractModel contractModel Contract a model by merging identical reactions.
    • copyToComps copyToComps Copy reactions to new compartment(s).
    • deleteUnusedGenes deleteUnusedGenes Delete all genes not associated to any reaction.
    • mergeCompartments mergeCompartments Merge all compartments in a model.
    • removeBadRxns removeBadRxns Remove reactions that enable production or consumption from nothing.
    • removeGenes removeGenes Delete a set of genes from a model.
    • removeMets removeMets Delete a set of metabolites from a model.
    • simplifyModel simplifyModel Simplify a model by deleting reactions and metabolites.
    + + + + +

    SOURCE CODE ^

    +
    0001 function reducedModel=removeReactions(model,rxnsToRemove,varargin)
    +0002 % removeReactions  Delete a set of reactions from a model.
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % model : struct
    +0007 %     a model structure.
    +0008 % rxnsToRemove : cell or logical or double
    +0009 %     either a cell array of reaction IDs, a logical vector with the same
    +0010 %     number of elements as reactions in the model, or a vector of indexes
    +0011 %     to remove.
    +0012 %
    +0013 % Name-Value Arguments
    +0014 % --------------------
    +0015 % removeUnusedMets : logical
    +0016 %     remove metabolites that no remaining reaction uses (default false).
    +0017 % removeUnusedGenes : logical
    +0018 %     remove genes that no remaining reaction uses (default false).
    +0019 % removeUnusedComps : logical
    +0020 %     remove compartments that contain no metabolite. Takes effect only when
    +0021 %     removeUnusedMets is true (default false).
    +0022 %
    +0023 % Returns
    +0024 % -------
    +0025 % reducedModel : struct
    +0026 %     an updated model structure.
    +0027 %
    +0028 % Examples
    +0029 % --------
    +0030 %     reducedModel = removeReactions(model, rxnsToRemove, removeUnusedMets, ...
    +0031 %         removeUnusedGenes, removeUnusedComps);
    +0032 
    +0033 p=parseRAVENargs(varargin, {'removeUnusedMets',false; 'removeUnusedGenes',false; 'removeUnusedComps',false});
    +0034 removeUnusedMets=p.removeUnusedMets;
    +0035 removeUnusedGenes=p.removeUnusedGenes;
    +0036 removeUnusedComps=p.removeUnusedComps;
    +0037 if ~islogical(rxnsToRemove) && ~isnumeric(rxnsToRemove)
    +0038     rxnsToRemove=convertCharArray(rxnsToRemove);
    +0039 end
    +0040 
    +0041 reducedModel=model;
    +0042 reg=ravenModelFields();
    +0043 
    +0044 if ~isempty(rxnsToRemove) || removeUnusedMets || removeUnusedGenes
    +0045     indexesToDelete=getIndexes(model,rxnsToRemove,'rxns');
    +0046     
    +0047     %Remove reactions
    +0048     if ~isempty(indexesToDelete)
    +0049         rxnReg=reg(strcmp({reg.type},'rxn'));
    +0050         for ri_=1:numel(rxnReg)
    +0051             fname=rxnReg(ri_).name;
    +0052             if isfield(reducedModel,fname)
    +0053                 reducedModel.(fname)(indexesToDelete)=[];
    +0054             end
    +0055         end
    +0056         if isfield(reducedModel,'S')
    +0057             reducedModel.S(:,indexesToDelete)=[];
    +0058         end
    +0059         if isfield(reducedModel,'rxnGeneMat')
    +0060             reducedModel.rxnGeneMat(indexesToDelete,:)=[];
    +0061         end
    +0062     end
    +0063     
    +0064     %Remove unused metabolites
    +0065     if removeUnusedMets==true
    +0066         if isfield(reducedModel,'S')
    +0067             [usedMets, ~]=find(reducedModel.S);
    +0068             unUsedMets=true(numel(reducedModel.mets),1);
    +0069             unUsedMets(usedMets)=false;
    +0070             reducedModel=removeMets(reducedModel,unUsedMets,false,false,false,removeUnusedComps);
    +0071         end
    +0072     end
    +0073     
    +0074     %Remove unused genes
    +0075     if removeUnusedGenes==true && isfield(reducedModel,'rxnGeneMat')
    +0076         %Find all genes that are not used
    +0077         [~, b]=find(reducedModel.rxnGeneMat);
    +0078         toKeep=false(numel(reducedModel.genes),1);
    +0079         toKeep(b)=true;
    +0080         
    +0081         geneReg=reg(strcmp({reg.type},'gene'));
    +0082         for ri_=1:numel(geneReg)
    +0083             fname=geneReg(ri_).name;
    +0084             if isfield(reducedModel,fname)
    +0085                 reducedModel.(fname)=reducedModel.(fname)(toKeep);
    +0086             end
    +0087         end
    +0088         reducedModel.rxnGeneMat=reducedModel.rxnGeneMat(:,toKeep);
    +0089     end
    +0090 else
    +0091     reducedModel=model;
    +0092 end
    +0093 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/replaceMets.html b/docs/html/manipulation/replaceMets.html new file mode 100644 index 00000000..094b1e43 --- /dev/null +++ b/docs/html/manipulation/replaceMets.html @@ -0,0 +1,290 @@ + + + + Description of replaceMets + + + + + + + + + +
    Home > manipulation > replaceMets.m
    + + + +

    replaceMets +

    + +

    PURPOSE ^

    +
    replaceMets Replace a metabolite with another already in the model.
    + +

    SYNOPSIS ^

    +
    function [model, removedRxns, idxDuplRxns]=replaceMets(model,metabolite,replacement,varargin)
    + +

    DESCRIPTION ^

    +
     replaceMets  Replace a metabolite with another already in the model.
    +
    + Replaces the name and annotation (formula, miriams, charge, deltaG, InChI
    + and SMILES) of a metabolite with those of a replacement metabolite that is
    + already in the model. If this results in duplicate metabolites, the S matrix
    + is updated to use one of them. With identifiers true the replacement
    + metabolite is kept; by name, the metabolite with the lowest index among those
    + of the same name and compartment is kept. At the end, contractModel is run to remove
    + duplicate reactions that this creates.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + metabolite : char
    +     name of the metabolite to be replaced (its identifier if identifiers is
    +     true).
    + replacement : char
    +     name of the replacement metabolite (its identifier if identifiers is
    +     true).
    +
    + Name-Value Arguments
    + --------------------
    + verbose : logical
    +     whether to print the ids of reactions that involve the replaced
    +     metabolite (default false).
    + identifiers : logical
    +     true if 'metabolite' and 'replacement' are metabolite identifiers,
    +     false if they are metabolite names (default false).
    +
    + Returns
    + -------
    + model : struct
    +     model structure with the selected metabolites replaced.
    + removedRxns : cell
    +     not assigned by the function; requesting this output raises an error.
    + idxDuplRxns : double
    +     not assigned by the function; requesting this output raises an error.
    +
    + Examples
    + --------
    +     [model, removedRxns, idxDuplRxns] = replaceMets(model, metabolite, ...
    +         replacement, verbose);
    +
    + Notes
    + -----
    + The function applies to a model that contains both 'oxygen' and 'o2' as
    + metabolite names. If 'oxygen' and 'o2' are identifiers, 'identifiers' is
    + set to true.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • contractModel contractModel Contract a model by merging identical reactions.
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function [model, removedRxns, idxDuplRxns]=replaceMets(model,metabolite,replacement,varargin)
    +0002 % replaceMets  Replace a metabolite with another already in the model.
    +0003 %
    +0004 % Replaces the name and annotation (formula, miriams, charge, deltaG, InChI
    +0005 % and SMILES) of a metabolite with those of a replacement metabolite that is
    +0006 % already in the model. If this results in duplicate metabolites, the S matrix
    +0007 % is updated to use one of them. With identifiers true the replacement
    +0008 % metabolite is kept; by name, the metabolite with the lowest index among those
    +0009 % of the same name and compartment is kept. At the end, contractModel is run to remove
    +0010 % duplicate reactions that this creates.
    +0011 %
    +0012 % Parameters
    +0013 % ----------
    +0014 % model : struct
    +0015 %     a model structure.
    +0016 % metabolite : char
    +0017 %     name of the metabolite to be replaced (its identifier if identifiers is
    +0018 %     true).
    +0019 % replacement : char
    +0020 %     name of the replacement metabolite (its identifier if identifiers is
    +0021 %     true).
    +0022 %
    +0023 % Name-Value Arguments
    +0024 % --------------------
    +0025 % verbose : logical
    +0026 %     whether to print the ids of reactions that involve the replaced
    +0027 %     metabolite (default false).
    +0028 % identifiers : logical
    +0029 %     true if 'metabolite' and 'replacement' are metabolite identifiers,
    +0030 %     false if they are metabolite names (default false).
    +0031 %
    +0032 % Returns
    +0033 % -------
    +0034 % model : struct
    +0035 %     model structure with the selected metabolites replaced.
    +0036 % removedRxns : cell
    +0037 %     not assigned by the function; requesting this output raises an error.
    +0038 % idxDuplRxns : double
    +0039 %     not assigned by the function; requesting this output raises an error.
    +0040 %
    +0041 % Examples
    +0042 % --------
    +0043 %     [model, removedRxns, idxDuplRxns] = replaceMets(model, metabolite, ...
    +0044 %         replacement, verbose);
    +0045 %
    +0046 % Notes
    +0047 % -----
    +0048 % The function applies to a model that contains both 'oxygen' and 'o2' as
    +0049 % metabolite names. If 'oxygen' and 'o2' are identifiers, 'identifiers' is
    +0050 % set to true.
    +0051 
    +0052 metabolite=char(metabolite);
    +0053 replacement=char(replacement);
    +0054 
    +0055 p=parseRAVENargs(varargin, {'verbose',false; 'identifiers',false});
    +0056 verbose=p.verbose;
    +0057 if isempty(verbose)
    +0058     verbose=false;
    +0059 end
    +0060 identifiers=p.identifiers;
    +0061 
    +0062 % Find occurence of replacement metabolites. Annotation will be taken from
    +0063 % first metabolite found.
    +0064 if identifiers
    +0065     repIdx = find(strcmp(replacement,model.mets));
    +0066 else
    +0067     repIdx = find(strcmp(replacement,model.metNames));
    +0068 end
    +0069 if isempty(repIdx)
    +0070     error('The replacement metabolite cannot be found in the model.');
    +0071 end
    +0072 % Captured by id, not position: idxDelete below removes rows, which would
    +0073 % otherwise shift repIdx out from under the metabolite it is meant to name
    +0074 repMetIds = model.mets(repIdx);
    +0075 
    +0076 % Change name and information from metabolite to replacement metabolite
    +0077 if identifiers
    +0078     metIdx = find(strcmp(metabolite,model.mets));
    +0079 else
    +0080     metIdx = find(strcmp(metabolite,model.metNames));
    +0081 end
    +0082 if isempty(metIdx)
    +0083     error('The to-be-replaced metabolite cannot be found in the model.');
    +0084 end
    +0085 
    +0086 % Either of the two rows can be the one that survives the merge: with identifiers the
    +0087 % replacement stays, by name the replaced metabolite is renamed and stays while the
    +0088 % replacement's row is deleted below. Both ids are kept so that contractModel is
    +0089 % restricted to the metabolite that is left.
    +0090 repMetIds = [repMetIds; model.mets(metIdx)];
    +0091 
    +0092 rxnsWithMet = find(model.S(metIdx,:));
    +0093 if verbose==true
    +0094     fprintf('\n\nThe following reactions contain the to-be-replaced metabolite as reactant:\n')
    +0095     %Reaction ids are arbitrary text and may contain "%"; print as
    +0096     %literal data rather than as an fprintf format string.
    +0097     fprintf('%s\n', strjoin(model.rxns(rxnsWithMet),newline))
    +0098 end
    +0099 
    +0100 model.metNames(metIdx) = model.metNames(repIdx(1));
    +0101 if isfield(model,'metFormulas')
    +0102     model.metFormulas(metIdx) = model.metFormulas(repIdx(1));
    +0103 end
    +0104 if isfield(model,'metMiriams')
    +0105     model.metMiriams(metIdx) = model.metMiriams(repIdx(1));
    +0106 end
    +0107 if isfield(model,'metCharges')
    +0108     model.metCharges(metIdx) = model.metCharges(repIdx(1));
    +0109 end
    +0110 if isfield(model,'metDeltaG')
    +0111     model.metDeltaG(metIdx) = model.metDeltaG(repIdx(1));
    +0112 end
    +0113 if isfield(model,'inchis')
    +0114     model.inchis(metIdx) = model.inchis(repIdx(1));
    +0115 end
    +0116 if isfield(model,'metSmiles')
    +0117     model.metSmiles(metIdx) = model.metSmiles(repIdx(1));
    +0118 end
    +0119 
    +0120 idxDelete=[];
    +0121 if identifiers
    +0122     originalStoch = model.S(metIdx,rxnsWithMet);
    +0123     % Add rather than overwrite: a reaction where the replacement
    +0124     % metabolite is already itself a participant must keep that
    +0125     % contribution, not have it clobbered by the replaced metabolite's
    +0126     model.S(repIdx,rxnsWithMet) = model.S(repIdx,rxnsWithMet) + originalStoch;
    +0127     model.S(metIdx,rxnsWithMet) = 0;
    +0128     idxDelete = metIdx;
    +0129 else
    +0130     % Run through replacement metabolites and their compartments. If any of the
    +0131     % to-be-replaced metabolites is already present (checked by
    +0132     % metaboliteName[compartment], then the replacement metabolite is kept and
    +0133     % the to-be-replace metabolite ID deleted.
    +0134     
    +0135     % Build list of metaboliteName[compartment]
    +0136     metCompsN =cellstr(num2str(model.metComps));
    +0137     map = containers.Map(cellstr(num2str(transpose(1:length(model.comps)))),model.comps);
    +0138     metCompsN = map.values(metCompsN);
    +0139     metCompsN = strcat(lower(model.metNames),'[',metCompsN,']');
    +0140     
    +0141     for i = 1:length(repIdx)
    +0142         metCompsNidx=find(strcmp(metCompsN(repIdx(i)), metCompsN));
    +0143         if length(metCompsNidx)>1
    +0144             for j = 2:length(metCompsNidx)
    +0145                 model.S(metCompsNidx(1),:) = model.S(metCompsNidx(1),:) + model.S(metCompsNidx(j),:);
    +0146                 idxDelete=[idxDelete; metCompsNidx(j)]; % Make list of metabolite IDs to delete
    +0147             end
    +0148         end
    +0149     end
    +0150 end
    +0151 
    +0152 if ~isempty(idxDelete)
    +0153     model.S(idxDelete,:) =[];
    +0154     model.mets(idxDelete) = [];
    +0155     model.metNames(idxDelete) = [];
    +0156     model.metComps(idxDelete) = [];
    +0157     model.b(idxDelete,:) = [];
    +0158     if isfield(model,'metFormulas')
    +0159         model.metFormulas(idxDelete) = [];
    +0160     end
    +0161     if isfield(model,'unconstrained')
    +0162         model.unconstrained(idxDelete) = [];
    +0163     end
    +0164     if isfield(model,'metMiriams')
    +0165         model.metMiriams(idxDelete) = [];
    +0166     end
    +0167     if isfield(model,'metCharges')
    +0168         model.metCharges(idxDelete) = [];
    +0169     end
    +0170     if isfield(model,'metDeltaG')
    +0171         model.metDeltaG(idxDelete) = [];
    +0172     end
    +0173     if isfield(model,'inchis')
    +0174         model.inchis(idxDelete) = [];
    +0175     end
    +0176     if isfield(model,'metSmiles')
    +0177         model.metSmiles(idxDelete) = [];
    +0178     end
    +0179     if isfield(model,'metFrom')
    +0180         model.metFrom(idxDelete) = [];
    +0181     end
    +0182 end
    +0183 
    +0184 % This could now have created duplicate reactions. Contract model.
    +0185 % repIdx may point past the end, or at the wrong row, once idxDelete has
    +0186 % shifted things above; re-resolve the metabolite that is left by id. An empty
    +0187 % list must not reach contractModel, which would then check every reaction.
    +0188 % distReverse is passed by name rather than positionally as [], which
    +0189 % parseRAVENargs would take literally instead of falling back to its
    +0190 % documented default of true.
    +0191 repIdx = find(ismember(model.mets, repMetIds));
    +0192 if ~isempty(repIdx)
    +0193     model=contractModel(model,'mets',repIdx);
    +0194 end
    +0195 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/reverseRxns.html b/docs/html/manipulation/reverseRxns.html new file mode 100644 index 00000000..c2dc6c95 --- /dev/null +++ b/docs/html/manipulation/reverseRxns.html @@ -0,0 +1,106 @@ + + + + Description of reverseRxns + + + + + + + + + +
    Home > manipulation > reverseRxns.m
    + + + +

    reverseRxns +

    + +

    PURPOSE ^

    +
    reverseRxns Reverse the direction of reactions in a model.
    + +

    SYNOPSIS ^

    +
    function model=reverseRxns(model, rxns)
    + +

    DESCRIPTION ^

    +
     reverseRxns  Reverse the direction of reactions in a model.
    +
    + Negates the stoichiometric coefficients and the objective coefficient of
    + the reactions, and sets the lower bound to the negated upper bound and the
    + upper bound to the negated lower bound. A flux in the new positive
    + direction equals a flux in the original negative direction. Reaction IDs
    + in rxns that are not in the model are ignored.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + rxns : cell
    +     IDs of the reactions to reverse.
    +
    + Returns
    + -------
    + model : struct
    +     an updated model structure.
    +
    + Examples
    + --------
    +     model = reverseRxns(model, rxns);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function model=reverseRxns(model, rxns)
    +0002 % reverseRxns  Reverse the direction of reactions in a model.
    +0003 %
    +0004 % Negates the stoichiometric coefficients and the objective coefficient of
    +0005 % the reactions, and sets the lower bound to the negated upper bound and the
    +0006 % upper bound to the negated lower bound. A flux in the new positive
    +0007 % direction equals a flux in the original negative direction. Reaction IDs
    +0008 % in rxns that are not in the model are ignored.
    +0009 %
    +0010 % Parameters
    +0011 % ----------
    +0012 % model : struct
    +0013 %     a model structure.
    +0014 % rxns : cell
    +0015 %     IDs of the reactions to reverse.
    +0016 %
    +0017 % Returns
    +0018 % -------
    +0019 % model : struct
    +0020 %     an updated model structure.
    +0021 %
    +0022 % Examples
    +0023 % --------
    +0024 %     model = reverseRxns(model, rxns);
    +0025 
    +0026 rxnInd = find(ismember(model.rxns,rxns));
    +0027 
    +0028 %reverse the reactions
    +0029 model.S(:,rxnInd) = model.S(:,rxnInd)*-1;
    +0030 %swap the bounds
    +0031 ub = model.ub(rxnInd);
    +0032 model.ub(rxnInd) = -model.lb(rxnInd);
    +0033 model.lb(rxnInd) = -ub;
    +0034 %flip the objective
    +0035 model.c(rxnInd) = -model.c(rxnInd);
    +0036 
    +0037 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/setExchangeBounds.html b/docs/html/manipulation/setExchangeBounds.html new file mode 100644 index 00000000..9b00c3bd --- /dev/null +++ b/docs/html/manipulation/setExchangeBounds.html @@ -0,0 +1,336 @@ + + + + Description of setExchangeBounds + + + + + + + + + +
    Home > manipulation > setExchangeBounds.m
    + + + +

    setExchangeBounds +

    + +

    PURPOSE ^

    +
    setExchangeBounds Define exchange flux bounds for a set of metabolites.
    + +

    SYNOPSIS ^

    +
    function [exchModel,unusedMets] = setExchangeBounds(model,varargin)
    + +

    DESCRIPTION ^

    +
     setExchangeBounds  Define exchange flux bounds for a set of metabolites.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + mets : cell or double
    +     a cell array of metabolite names (case insensitive) or metabolite
    +     IDs, or a vector of indices in model.mets (default all exchanged
    +     metabolites).
    + lb : double
    +     lower bound of exchange flux. Either a vector of bounds corresponding
    +     to each of the provided metabolites, or a single value that is applied
    +     to all (default model.annotation.defaultLB if it exists, otherwise
    +     -1000). Without mets, only a single value is accepted.
    + ub : double
    +     upper bound of exchange flux. Either a vector of bounds corresponding
    +     to each of the provided metabolites, or a single value that is applied
    +     to all (default model.annotation.defaultUB if it exists, otherwise
    +     1000). Without mets, only a single value is accepted.
    + closeOthers : logical
    +     close the exchange reactions of all exchanged metabolites that are not
    +     in the provided list. This prevents import of these metabolites and
    +     leaves their export unchanged. Set to false, with a warning, when the
    +     exchange reactions are not all written in the same direction
    +     (default true).
    + mediaOnly : logical
    +     only consider exchange reactions involving exchange to or from the
    +     extracellular (media) compartment. Reactions such as "sink" reactions
    +     that exchange metabolites directly with an intracellular compartment
    +     are therefore ignored, although "getExchangeRxns" identifies such
    +     reactions as exchange reactions. The extracellular compartment is the
    +     one named "extracellular" in the "compNames" field (case insensitive).
    +     The "compNames" and "metComps" fields are required; an error is thrown
    +     if either is missing or if no compartment has this name
    +     (default false).
    +
    + Returns
    + -------
    + exchModel : struct
    +     a model structure with updated exchange flux bounds for the provided
    +     set of metabolites.
    + unusedMets : cell
    +     metabolites in mets that were not used because they are not involved
    +     in any of the considered exchange reactions in the model.
    +
    + Examples
    + --------
    +     exchModel = setExchangeBounds(model, mets, lb, ub, closeOthers, ...
    +         mediaOnly);
    +
    + Notes
    + -----
    + Exchange reactions involving more than one metabolite are ignored.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • setParam setParam Set parameters for reactions.
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function [exchModel,unusedMets] = setExchangeBounds(model,varargin)
    +0002 % setExchangeBounds  Define exchange flux bounds for a set of metabolites.
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % model : struct
    +0007 %     a model structure.
    +0008 %
    +0009 % Name-Value Arguments
    +0010 % --------------------
    +0011 % mets : cell or double
    +0012 %     a cell array of metabolite names (case insensitive) or metabolite
    +0013 %     IDs, or a vector of indices in model.mets (default all exchanged
    +0014 %     metabolites).
    +0015 % lb : double
    +0016 %     lower bound of exchange flux. Either a vector of bounds corresponding
    +0017 %     to each of the provided metabolites, or a single value that is applied
    +0018 %     to all (default model.annotation.defaultLB if it exists, otherwise
    +0019 %     -1000). Without mets, only a single value is accepted.
    +0020 % ub : double
    +0021 %     upper bound of exchange flux. Either a vector of bounds corresponding
    +0022 %     to each of the provided metabolites, or a single value that is applied
    +0023 %     to all (default model.annotation.defaultUB if it exists, otherwise
    +0024 %     1000). Without mets, only a single value is accepted.
    +0025 % closeOthers : logical
    +0026 %     close the exchange reactions of all exchanged metabolites that are not
    +0027 %     in the provided list. This prevents import of these metabolites and
    +0028 %     leaves their export unchanged. Set to false, with a warning, when the
    +0029 %     exchange reactions are not all written in the same direction
    +0030 %     (default true).
    +0031 % mediaOnly : logical
    +0032 %     only consider exchange reactions involving exchange to or from the
    +0033 %     extracellular (media) compartment. Reactions such as "sink" reactions
    +0034 %     that exchange metabolites directly with an intracellular compartment
    +0035 %     are therefore ignored, although "getExchangeRxns" identifies such
    +0036 %     reactions as exchange reactions. The extracellular compartment is the
    +0037 %     one named "extracellular" in the "compNames" field (case insensitive).
    +0038 %     The "compNames" and "metComps" fields are required; an error is thrown
    +0039 %     if either is missing or if no compartment has this name
    +0040 %     (default false).
    +0041 %
    +0042 % Returns
    +0043 % -------
    +0044 % exchModel : struct
    +0045 %     a model structure with updated exchange flux bounds for the provided
    +0046 %     set of metabolites.
    +0047 % unusedMets : cell
    +0048 %     metabolites in mets that were not used because they are not involved
    +0049 %     in any of the considered exchange reactions in the model.
    +0050 %
    +0051 % Examples
    +0052 % --------
    +0053 %     exchModel = setExchangeBounds(model, mets, lb, ub, closeOthers, ...
    +0054 %         mediaOnly);
    +0055 %
    +0056 % Notes
    +0057 % -----
    +0058 % Exchange reactions involving more than one metabolite are ignored.
    +0059 
    +0060 
    +0061 % handle input arguments
    +0062 p=parseRAVENargs(varargin, {'mets',[]; 'lb',[]; 'ub',[]; 'closeOthers',true; 'mediaOnly',false});
    +0063 mets=p.mets;
    +0064 if ~isempty(mets) && ~islogical(mets) && ~isnumeric(mets)
    +0065     mets=convertCharArray(mets);
    +0066 end
    +0067 
    +0068 lb=p.lb;
    +0069 if isempty(lb)
    +0070     if isfield(model,'annotation') && isfield(model.annotation,'defaultLB')
    +0071         lb = model.annotation.defaultLB;
    +0072     else
    +0073         lb = -1000;
    +0074     end
    +0075 end
    +0076 
    +0077 ub=p.ub;
    +0078 if isempty(ub)
    +0079     if isfield(model,'annotation') && isfield(model.annotation,'defaultUB')
    +0080         ub = model.annotation.defaultUB;
    +0081     else
    +0082         ub = 1000;
    +0083     end
    +0084 end
    +0085 
    +0086 closeOthers=p.closeOthers;
    +0087 if isempty(closeOthers)
    +0088     closeOthers = true;
    +0089 end
    +0090 
    +0091 mediaOnly=p.mediaOnly;
    +0092 if mediaOnly
    +0093     if ~all(isfield(model,{'compNames','metComps'}))
    +0094         error('mediaOnly option requires the "compNames" and "metComps" model fields.');
    +0095     end
    +0096 end
    +0097 
    +0098 
    +0099 % for models with an "unconstrained" field, generate a version of the model
    +0100 % where all the unconstrained metabolites have stoichiometric coeffs set to
    +0101 % zero, but remain in the model to avoid index changes
    +0102 model_temp = model;
    +0103 if isfield(model,'unconstrained')
    +0104     model_temp.S(model_temp.unconstrained == 1,:) = 0;
    +0105 end
    +0106 
    +0107 % find exchange rxns, ignoring those involving more than one metabolite
    +0108 [~,exchRxnInd] = getExchangeRxns(model);
    +0109 exchRxnInd(sum(model_temp.S(:,exchRxnInd) ~= 0,1) > 1) = [];
    +0110 
    +0111 % find all exchanged metabolites
    +0112 exchMetInd = find(any(model_temp.S(:,exchRxnInd) ~= 0,2));
    +0113 
    +0114 if ( mediaOnly )
    +0115     % ignore exchanged metabolites in non-extracellular compartments and
    +0116     % any exchange reactions involving these metabolites
    +0117     [~,extCompInd] = ismember('extracellular',lower(model.compNames));
    +0118     if extCompInd > 0
    +0119         ignoreMet = (model.metComps(exchMetInd) ~= extCompInd);
    +0120     else
    +0121         error('Could not find any compartments named "extracellular".');
    +0122     end
    +0123     ignoreRxn = any(model.S(exchMetInd(ignoreMet),exchRxnInd) ~= 0,1);
    +0124     exchMetInd(ignoreMet) = [];
    +0125     exchRxnInd(ignoreRxn) = [];
    +0126 end
    +0127 
    +0128 % Check that all exchange reactions are formulated in the same direction.
    +0129 % If not, this means that negative flux indicates import for some exchange
    +0130 % reactions, but indicates export for others. Therefore, the LB and UB
    +0131 % would need to be specified differently depending on the exchange reaction
    +0132 % direction, which is error-prone.
    +0133 if all(all(model.S(exchMetInd,exchRxnInd) <= 0))
    +0134     importDir = 'backward';
    +0135 elseif all(all(model.S(exchMetInd,exchRxnInd) >= 0))
    +0136     importDir = 'forward';
    +0137 else
    +0138     fprintf('WARNING: Some exchange reactions differ in direction, and therefore have opposite meanings of LB and UB.\n');
    +0139     if closeOthers
    +0140         fprintf('         Therefore, the "closeOthers" option will be set to FALSE.\n');
    +0141     end
    +0142     closeOthers = false;
    +0143 end
    +0144 
    +0145 % prepare exchanged metabolites and bounds
    +0146 if ~isempty(mets)
    +0147     
    +0148     % prepare lb and ub variables
    +0149     if numel(lb) == 1
    +0150         lb = lb*ones(size(mets));
    +0151     elseif numel(lb) ~= numel(mets)
    +0152         error('lb must be a single value or a vector of equal length as mets');
    +0153     end
    +0154     if numel(ub) == 1
    +0155         ub = ub*ones(size(mets));
    +0156     elseif numel(ub) ~= numel(mets)
    +0157         error('ub must be a single value or a vector of equal length as mets');
    +0158     end
    +0159     
    +0160     % map provided mets to exchanged metabolites
    +0161     if isnumeric(mets)
    +0162         % mets are provided as indices
    +0163         [isExch,metInd] = ismember(mets,exchMetInd);    
    +0164     elseif sum(ismember(lower(mets),lower(model.metNames))) > sum(ismember(mets,model.mets))
    +0165         % assume that mets are provided as names
    +0166         [isExch,metInd] = ismember(lower(mets),lower(model.metNames(exchMetInd)));
    +0167     else
    +0168         % assume that mets are provided as met IDs
    +0169         [isExch,metInd] = ismember(mets,model.mets(exchMetInd));
    +0170     end
    +0171     
    +0172     % get exchanged met indices and determine unused (non-exchanged) mets
    +0173     unusedMets = mets(~isExch);
    +0174     exchMetInd = exchMetInd(metInd(isExch));
    +0175     
    +0176     % update bound vectors
    +0177     lb = lb(isExch);
    +0178     ub = ub(isExch);
    +0179     
    +0180 else
    +0181     % if no mets were provided, use all exchanged mets
    +0182     if numel(lb) > 1 || numel(ub) > 1
    +0183         error('Only one upper and one lower bound may be provide if metabolites are not specified.');
    +0184     else
    +0185         lb = lb*ones(size(exchMetInd));
    +0186         ub = ub*ones(size(exchMetInd));
    +0187     end
    +0188     unusedMets = {};
    +0189 end
    +0190 
    +0191 % check that at least one exchanged metabolite matches
    +0192 if isempty(exchMetInd)
    +0193     fprintf('None of the provided metabolites were found in any exchange reactions.\n');
    +0194     exchModel = model;
    +0195     return
    +0196 end
    +0197 
    +0198 % determine which metabolite is exchanged in each exchange reaction
    +0199 [metInd,rxnInd] = find(model_temp.S(exchMetInd,exchRxnInd) ~= 0);
    +0200 
    +0201 % check for any metabolites that are exchanged in more than one reaction.
    +0202 % metInd holds one entry per (metabolite,reaction) pair, so a metabolite
    +0203 % involved in more than one exchange reaction is a value that occurs more
    +0204 % than once in metInd. The occurrences are counted per unique value.
    +0205 [uniqueMetInd,~,ic] = unique(metInd);
    +0206 counts = accumarray(ic,1);
    +0207 multiMetInd = exchMetInd(uniqueMetInd(counts>1));
    +0208 if ~isempty(multiMetInd)
    +0209     fprintf('WARNING: The following metabolites are involved in more than one exchange reaction:\n');
    +0210     fprintf('\t%s\n',model.metNames{multiMetInd(1:min(numel(multiMetInd),10))});
    +0211     if numel(multiMetInd) > 10
    +0212         fprintf('\t... and %u more.\n',numel(multiMetInd)-10);
    +0213     end
    +0214 end
    +0215 
    +0216 % set exchange reaction bounds
    +0217 model = setParam(model,'lb',exchRxnInd(rxnInd),lb(metInd));
    +0218 model = setParam(model,'ub',exchRxnInd(rxnInd),ub(metInd));
    +0219 
    +0220 if closeOthers
    +0221     % constrain import of all other exchange reactions to zero
    +0222     constrainInd = setdiff(exchRxnInd,exchRxnInd(rxnInd));
    +0223     if strcmp(importDir,'backward')
    +0224         model = setParam(model,'lb',constrainInd,0);
    +0225     else
    +0226         model = setParam(model,'ub',constrainInd,0);
    +0227     end
    +0228 end
    +0229 
    +0230 % assign output model
    +0231 exchModel = model;
    +0232 
    +0233
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/setParam.html b/docs/html/manipulation/setParam.html new file mode 100644 index 00000000..67264fca --- /dev/null +++ b/docs/html/manipulation/setParam.html @@ -0,0 +1,236 @@ + + + + Description of setParam + + + + + + + + + +
    Home > manipulation > setParam.m
    + + + +

    setParam +

    + +

    PURPOSE ^

    +
    setParam Set parameters for reactions.
    + +

    SYNOPSIS ^

    +
    function model=setParam(model, paramType, rxnList, params, var)
    + +

    DESCRIPTION ^

    +
     setParam  Set parameters for reactions.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + paramType : char
    +     the type of parameter to set. The value is matched case-insensitively,
    +     except for the reset of the other objective coefficients under "obj"
    +     (below):
    +
    +     - "lb" : lower bound.
    +     - "ub" : upper bound.
    +     - "eq" : both upper and lower bound (equality constraint).
    +     - "obj" : objective coefficient. The objective coefficients of all
    +       other reactions are set to 0 only when every element of paramType
    +       contains "obj" in lower case; with another capitalisation, or when
    +       paramType also holds other types, the other coefficients keep their
    +       values.
    +     - "rev" : reversibility (only changes the model.rev field, does not
    +       affect model.lb and model.ub). A non-zero value sets the reaction
    +       reversible.
    +     - "var" : variance around measured bound.
    +     - "unc" : unconstrained, sets the lower and upper bound to
    +       model.annotation.defaultLB and model.annotation.defaultUB if these
    +       are defined, otherwise to -1000 and 1000.
    + rxnList : cell or double
    +     a cell array of reaction IDs or a vector with their corresponding
    +     indexes. IDs that are not in the model are ignored with a warning.
    + params : double
    +     a vector of the corresponding values, or a single value that is
    +     applied to all reactions in rxnList.
    + var : double, optional
    +     percentage of variance around measured value, if "var" is set as
    +     paramType. Defining "var" as 5 results in lb and ub at 97.5% and
    +     102.5% of the provided params value (if params value is negative,
    +     then lb and ub are 102.5% and 97.5%).
    +
    + Returns
    + -------
    + model : struct
    +     an updated model structure.
    +
    + Examples
    + --------
    +     model = setParam(model, paramType, rxnList, params, var);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    • setExchangeBounds setExchangeBounds Define exchange flux bounds for a set of metabolites.
    + + + + +

    SOURCE CODE ^

    +
    0001 function model=setParam(model, paramType, rxnList, params, var)
    +0002 % setParam  Set parameters for reactions.
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % model : struct
    +0007 %     a model structure.
    +0008 % paramType : char
    +0009 %     the type of parameter to set. The value is matched case-insensitively,
    +0010 %     except for the reset of the other objective coefficients under "obj"
    +0011 %     (below):
    +0012 %
    +0013 %     - "lb" : lower bound.
    +0014 %     - "ub" : upper bound.
    +0015 %     - "eq" : both upper and lower bound (equality constraint).
    +0016 %     - "obj" : objective coefficient. The objective coefficients of all
    +0017 %       other reactions are set to 0 only when every element of paramType
    +0018 %       contains "obj" in lower case; with another capitalisation, or when
    +0019 %       paramType also holds other types, the other coefficients keep their
    +0020 %       values.
    +0021 %     - "rev" : reversibility (only changes the model.rev field, does not
    +0022 %       affect model.lb and model.ub). A non-zero value sets the reaction
    +0023 %       reversible.
    +0024 %     - "var" : variance around measured bound.
    +0025 %     - "unc" : unconstrained, sets the lower and upper bound to
    +0026 %       model.annotation.defaultLB and model.annotation.defaultUB if these
    +0027 %       are defined, otherwise to -1000 and 1000.
    +0028 % rxnList : cell or double
    +0029 %     a cell array of reaction IDs or a vector with their corresponding
    +0030 %     indexes. IDs that are not in the model are ignored with a warning.
    +0031 % params : double
    +0032 %     a vector of the corresponding values, or a single value that is
    +0033 %     applied to all reactions in rxnList.
    +0034 % var : double, optional
    +0035 %     percentage of variance around measured value, if "var" is set as
    +0036 %     paramType. Defining "var" as 5 results in lb and ub at 97.5% and
    +0037 %     102.5% of the provided params value (if params value is negative,
    +0038 %     then lb and ub are 102.5% and 97.5%).
    +0039 %
    +0040 % Returns
    +0041 % -------
    +0042 % model : struct
    +0043 %     an updated model structure.
    +0044 %
    +0045 % Examples
    +0046 % --------
    +0047 %     model = setParam(model, paramType, rxnList, params, var);
    +0048 
    +0049 paramType=convertCharArray(paramType);
    +0050 if ~any(strcmpi(paramType,{'lb','ub','eq','obj','rev','var','unc'}))
    +0051     EM=['Incorrect parameter type: "' paramType{1} '"'];
    +0052     error('RAVEN:badInput', '%s', EM);
    +0053 end
    +0054 if isnumeric(rxnList) || islogical(rxnList)
    +0055     rxnList=model.rxns(rxnList);
    +0056 else
    +0057     rxnList=convertCharArray(rxnList);
    +0058 end
    +0059 if isempty(rxnList)
    +0060     return;
    +0061 end
    +0062 
    +0063 %Allow to set several parameters to the same value
    +0064 if numel(rxnList)~=numel(params) && numel(params)~=1
    +0065     EM='The number of parameter values and the number of reactions must be the same';
    +0066     error('RAVEN:badInput', '%s', EM);
    +0067 end
    +0068 
    +0069 if length(rxnList)>1
    +0070     if length(paramType)==1
    +0071         paramType(1:length(rxnList))=paramType;
    +0072     end
    +0073     if length(params)==1
    +0074         params(1:length(rxnList))=params;
    +0075     end
    +0076 end
    +0077 
    +0078 %Find the indexes for the reactions in rxnList. Do not use getIndexes
    +0079 %as we do not want to throw errors if matches fail
    +0080 indexes=zeros(numel(rxnList),1);
    +0081 
    +0082 [Lia,Locb] = ismember(rxnList,model.rxns);
    +0083 indexes(Lia) = Locb;
    +0084 if any(~Lia)
    +0085     params(~Lia)=[];
    +0086     indexes(~Lia)=[];
    +0087     paramType(~Lia)=[];
    +0088     warning('RAVEN:warning', '%s', ravenList('Reactions not present in model, will be ignored:', rxnList(~Lia)));
    +0089 end
    +0090 
    +0091 %Change the parameters
    +0092 if ~isempty(indexes)
    +0093     if contains(paramType,'obj')
    +0094         model.c=zeros(numel(model.c),1); % parameter is changed, not added
    +0095     end
    +0096     for j=1:length(paramType)
    +0097         if strcmpi(paramType{j},'eq')
    +0098             model.lb(indexes(j))=params(j);
    +0099             model.ub(indexes(j))=params(j);
    +0100         end
    +0101         if strcmpi(paramType{j},'lb')
    +0102             model.lb(indexes(j))=params(j);
    +0103         end
    +0104         if strcmpi(paramType{j},'ub')
    +0105             model.ub(indexes(j))=params(j);
    +0106         end
    +0107         if strcmpi(paramType{j},'obj')
    +0108             model.c(indexes(j))=params(j);
    +0109         end
    +0110         if strcmpi(paramType{j},'rev')
    +0111             %Non-zero values are interpreted as reversible
    +0112             model.rev(indexes(j))=params(j)~=0;
    +0113         end
    +0114         if strcmpi(paramType{j},'var')
    +0115             if params(j) < 0
    +0116                 model.lb(indexes(j)) = params(j) * (1+var/200);
    +0117                 model.ub(indexes(j)) = params(j) * (1-var/200);
    +0118             else
    +0119                 model.lb(indexes(j)) = params(j) * (1-var/200);
    +0120                 model.ub(indexes(j)) = params(j) * (1+var/200);
    +0121             end
    +0122         end
    +0123         if strcmpi(paramType{j},'unc')
    +0124             if isfield(model,'annotation') && isfield(model.annotation,'defaultLB')
    +0125                 lb = model.annotation.defaultLB;
    +0126             else
    +0127                 lb = -1000;
    +0128             end
    +0129             if isfield(model,'annotation') && isfield(model.annotation,'defaultUB')
    +0130                 ub = model.annotation.defaultUB;
    +0131             else
    +0132                 ub = 1000;
    +0133             end
    +0134             model.lb(indexes(j)) = lb;
    +0135             model.ub(indexes(j)) = ub;
    +0136         end
    +0137     end
    +0138 end
    +0139 if any(ismember(paramType,{'lb','ub','unc'}))
    +0140     invalidBound = model.lb(indexes) > model.ub(indexes);
    +0141     if any(invalidBound)
    +0142         error(['Invalid set of bounds for reaction(s): ', strjoin(model.rxns(indexes(invalidBound)),', '), '.'])
    +0143     end
    +0144 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/simplifyModel.html b/docs/html/manipulation/simplifyModel.html new file mode 100644 index 00000000..cf83096e --- /dev/null +++ b/docs/html/manipulation/simplifyModel.html @@ -0,0 +1,454 @@ + + + + Description of simplifyModel + + + + + + + + + +
    Home > manipulation > simplifyModel.m
    + + + +

    simplifyModel +

    + +

    PURPOSE ^

    +
    simplifyModel Simplify a model by deleting reactions and metabolites.
    + +

    SYNOPSIS ^

    +
    function [reducedModel, deletedReactions, deletedMetabolites]=simplifyModel(model,varargin)
    + +

    DESCRIPTION ^

    +
     simplifyModel  Simplify a model by deleting reactions and metabolites.
    +
    + Deletes reactions and metabolites that cannot carry flux. The deleted
    + reactions and metabolites identify different types of gaps.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + deleteUnconstrained : logical
    +     delete metabolites marked as unconstrained in model.unconstrained, and
    +     the compartments that are then left without metabolites (default true).
    + deleteDuplicates : logical
    +     delete all but the first occurrence of duplicate reactions, as found by
    +     contractModel. Duplicate reactions have the same stoichiometry and
    +     reversibility. The kept reaction takes the minimum lower bound, the
    +     maximum upper bound and the maximum objective coefficient of the
    +     duplicates, with a warning when these differ (default false).
    + deleteZeroInterval : logical
    +     delete reactions that are constrained to zero flux (default false).
    + deleteInaccessible : logical
    +     delete dead end reactions (default false).
    + deleteMinMax : logical
    +     delete reactions that cannot carry a flux, as determined by haveFlux
    +     under the bounds of the model. The run time grows with the number of
    +     reactions, since haveFlux solves linear programs (default false).
    + groupLinear : logical
    +     group linearly dependent pathways. The grouped model is irreversible,
    +     and all gene information (genes, rxnGeneMat, grRules and the
    +     gene-associated fields) is deleted (default false).
    + constrainReversible : logical
    +     check if there are reversible reactions which can only carry flux in
    +     one direction, and if so constrain them to be irreversible. A reversible
    +     reaction that is constrained to one direction is not split into two
    +     reactions when groupLinear converts the model to irreversible, so the
    +     metabolites that it connects can meet the condition for grouping (one
    +     producing and one consuming reaction) (default false).
    + reservedRxns : cell
    +     cell array with reaction IDs that are not deleted by
    +     deleteZeroInterval, deleteInaccessible and deleteMinMax and not merged
    +     by groupLinear (default none).
    + suppressWarnings : logical
    +     true if warnings should be suppressed (default false).
    +
    + Returns
    + -------
    + reducedModel : struct
    +     an updated model structure.
    + deletedReactions : cell
    +     a cell array with the IDs of all deleted reactions.
    + deletedMetabolites : cell
    +     a cell array with the IDs of all deleted metabolites.
    +
    + Examples
    + --------
    +     [reducedModel, deletedReactions, deletedMetabolites] = ...
    +         simplifyModel(model, deleteUnconstrained, deleteDuplicates, ...
    +         deleteZeroInterval, deleteInaccessible, deleteMinMax, ...
    +         groupLinear, constrainReversible, reservedRxns, suppressWarnings);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • contractModel contractModel Contract a model by merging identical reactions.
    • convertToIrrev convertToIrrev Convert a model to irreversible form.
    • removeMets removeMets Delete a set of metabolites from a model.
    • removeReactions removeReactions Delete a set of reactions from a model.
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [reducedModel, deletedReactions, deletedMetabolites]=simplifyModel(model,varargin)
    +0002 % simplifyModel  Simplify a model by deleting reactions and metabolites.
    +0003 %
    +0004 % Deletes reactions and metabolites that cannot carry flux. The deleted
    +0005 % reactions and metabolites identify different types of gaps.
    +0006 %
    +0007 % Parameters
    +0008 % ----------
    +0009 % model : struct
    +0010 %     a model structure.
    +0011 %
    +0012 % Name-Value Arguments
    +0013 % --------------------
    +0014 % deleteUnconstrained : logical
    +0015 %     delete metabolites marked as unconstrained in model.unconstrained, and
    +0016 %     the compartments that are then left without metabolites (default true).
    +0017 % deleteDuplicates : logical
    +0018 %     delete all but the first occurrence of duplicate reactions, as found by
    +0019 %     contractModel. Duplicate reactions have the same stoichiometry and
    +0020 %     reversibility. The kept reaction takes the minimum lower bound, the
    +0021 %     maximum upper bound and the maximum objective coefficient of the
    +0022 %     duplicates, with a warning when these differ (default false).
    +0023 % deleteZeroInterval : logical
    +0024 %     delete reactions that are constrained to zero flux (default false).
    +0025 % deleteInaccessible : logical
    +0026 %     delete dead end reactions (default false).
    +0027 % deleteMinMax : logical
    +0028 %     delete reactions that cannot carry a flux, as determined by haveFlux
    +0029 %     under the bounds of the model. The run time grows with the number of
    +0030 %     reactions, since haveFlux solves linear programs (default false).
    +0031 % groupLinear : logical
    +0032 %     group linearly dependent pathways. The grouped model is irreversible,
    +0033 %     and all gene information (genes, rxnGeneMat, grRules and the
    +0034 %     gene-associated fields) is deleted (default false).
    +0035 % constrainReversible : logical
    +0036 %     check if there are reversible reactions which can only carry flux in
    +0037 %     one direction, and if so constrain them to be irreversible. A reversible
    +0038 %     reaction that is constrained to one direction is not split into two
    +0039 %     reactions when groupLinear converts the model to irreversible, so the
    +0040 %     metabolites that it connects can meet the condition for grouping (one
    +0041 %     producing and one consuming reaction) (default false).
    +0042 % reservedRxns : cell
    +0043 %     cell array with reaction IDs that are not deleted by
    +0044 %     deleteZeroInterval, deleteInaccessible and deleteMinMax and not merged
    +0045 %     by groupLinear (default none).
    +0046 % suppressWarnings : logical
    +0047 %     true if warnings should be suppressed (default false).
    +0048 %
    +0049 % Returns
    +0050 % -------
    +0051 % reducedModel : struct
    +0052 %     an updated model structure.
    +0053 % deletedReactions : cell
    +0054 %     a cell array with the IDs of all deleted reactions.
    +0055 % deletedMetabolites : cell
    +0056 %     a cell array with the IDs of all deleted metabolites.
    +0057 %
    +0058 % Examples
    +0059 % --------
    +0060 %     [reducedModel, deletedReactions, deletedMetabolites] = ...
    +0061 %         simplifyModel(model, deleteUnconstrained, deleteDuplicates, ...
    +0062 %         deleteZeroInterval, deleteInaccessible, deleteMinMax, ...
    +0063 %         groupLinear, constrainReversible, reservedRxns, suppressWarnings);
    +0064 
    +0065 p=parseRAVENargs(varargin, {'deleteUnconstrained',true; 'deleteDuplicates',false; 'deleteZeroInterval',false; 'deleteInaccessible',false; 'deleteMinMax',false; 'groupLinear',false; 'constrainReversible',false; 'reservedRxns',[]; 'suppressWarnings',false});
    +0066 deleteUnconstrained=p.deleteUnconstrained;
    +0067 deleteDuplicates=p.deleteDuplicates;
    +0068 deleteZeroInterval=p.deleteZeroInterval;
    +0069 deleteInaccessible=p.deleteInaccessible;
    +0070 deleteMinMax=p.deleteMinMax;
    +0071 groupLinear=p.groupLinear;
    +0072 constrainReversible=p.constrainReversible;
    +0073 reservedRxns=p.reservedRxns;
    +0074 if ~isempty(reservedRxns)
    +0075     reservedRxns=convertCharArray(reservedRxns);
    +0076 end
    +0077 suppressWarnings=p.suppressWarnings;
    +0078 
    +0079 reducedModel=model;
    +0080 deletedReactions={};
    +0081 deletedMetabolites={};
    +0082 
    +0083 if deleteUnconstrained==true
    +0084     if isfield(reducedModel,'unconstrained')
    +0085         %Remove unbalanced metabolites
    +0086         deletedMetabolites=reducedModel.mets(reducedModel.unconstrained~=0);
    +0087         reducedModel=removeMets(reducedModel,reducedModel.unconstrained~=0,false,false,false,true);
    +0088         reducedModel=rmfield(reducedModel,'unconstrained');
    +0089     end
    +0090 end
    +0091 
    +0092 if deleteDuplicates==true
    +0093     %Merge duplicate reactions into the first occurrence and delete the
    +0094     %others. Reactions with the same stoichiometry and reversibility are
    +0095     %duplicates; the merged reaction keeps the widest bounds and the largest
    +0096     %objective coefficient (see contractModel)
    +0097     [reducedModel, rxnsToDelete, ~]=contractModel(reducedModel);
    +0098     deletedReactions=[deletedReactions; rxnsToDelete];
    +0099 end
    +0100 
    +0101 if deleteZeroInterval==true
    +0102     %Find all reactions where both LB and UB are 0
    +0103     zeroIntervalReactions=and(reducedModel.lb==0,reducedModel.ub==0);
    +0104     
    +0105     rxnsToDelete=setdiff(reducedModel.rxns(zeroIntervalReactions),reservedRxns);
    +0106     deletedReactions=[deletedReactions; rxnsToDelete];
    +0107     
    +0108     %Remove reactions
    +0109     reducedModel=removeReactions(reducedModel,rxnsToDelete);
    +0110     
    +0111     %Find metabolites that no longer are used and delete them
    +0112     notInUse=orphanedMets(reducedModel);
    +0113     deletedMetabolites=[deletedMetabolites;reducedModel.mets(notInUse)];
    +0114     
    +0115     %Remove metabolites
    +0116     reducedModel=removeMets(reducedModel,notInUse);
    +0117 end
    +0118 
    +0119 if deleteInaccessible==true
    +0120     %Print a warning if exchange metabolites have not been deleted yet. This
    +0121     %often means that the only allowed products are the metabolites that
    +0122     %are taken up be the system
    +0123     if isfield(reducedModel,'unconstrained') && suppressWarnings==false
    +0124         EM='Removing dead-end reactions before removing exchange metabolites';
    +0125         warning('RAVEN:warning', '%s', EM);
    +0126     end
    +0127     
    +0128     while true
    +0129         %Find all metabolites that are only reactants or only products.
    +0130         %Delete those metabolites and reactions. This is done until no more
    +0131         %such reactions are found
    +0132         
    +0133         %Construct a stoichiometric matrix where all reactions are written
    +0134         %as reversible
    +0135         
    +0136         %Add fake exchange reactions if model.b~=0
    +0137         in=any(reducedModel.b<0,2);
    +0138         out=any(reducedModel.b>0,2);
    +0139         I=speye(numel(reducedModel.mets));
    +0140         revS=[reducedModel.S,reducedModel.S(:,reducedModel.lb<0)*-1 I(:,in) I(:,out)*-1];
    +0141         
    +0142         metUsage=sum(abs(revS')>0);
    +0143         onlyProducts=sum(revS'>0) == metUsage;
    +0144         onlyReactants=sum(revS'<0) == metUsage;
    +0145         
    +0146         %Also remove metabolites that only participate in one reversible
    +0147         %reaction Do not remove the ones in one reversible reaction if is
    +0148         %also has a non-zero coefficient in model.b
    +0149         notInUse=onlyProducts | onlyReactants | (sum(abs(reducedModel.S')>0)<=1 & (~in & ~out)');
    +0150         deletedRxn=false;
    +0151         if any(notInUse)
    +0152             %Find their corresponding reactions
    +0153             rxnsNotInUse=sum(abs(reducedModel.S(notInUse,:))>0,1)>0;
    +0154             rxnsToDelete=setdiff(reducedModel.rxns(rxnsNotInUse),reservedRxns);
    +0155             deletedReactions=[deletedReactions; rxnsToDelete];
    +0156             
    +0157             %Remove reactions
    +0158             reducedModel=removeReactions(reducedModel,rxnsToDelete);
    +0159             
    +0160             %Remove metabolites. Recalculate since it could be that some
    +0161             %cannot be deleted due to reserved rxns
    +0162             notInUse=orphanedMets(reducedModel);
    +0163             deletedMetabolites=[deletedMetabolites; reducedModel.mets(notInUse)];
    +0164             reducedModel=removeMets(reducedModel,notInUse);
    +0165             
    +0166             %It could happen that it just finds reserved reactions and gets
    +0167             %stuck
    +0168             if ~isempty(rxnsToDelete)
    +0169                 deletedRxn=true;
    +0170             end
    +0171         else
    +0172             break;
    +0173         end
    +0174         if deletedRxn==false
    +0175             break;
    +0176         end
    +0177     end
    +0178 end
    +0179 
    +0180 if deleteMinMax==true
    +0181     %Get reactions that cannot carry fluxes. This should be done
    +0182     %algebraically if possible
    +0183     I=~haveFlux(reducedModel);
    +0184     
    +0185     %Remove reactions
    +0186     rxnsToDelete=setdiff(reducedModel.rxns(I),reservedRxns);
    +0187     deletedReactions=[deletedReactions; rxnsToDelete];
    +0188     reducedModel=removeReactions(reducedModel,rxnsToDelete);
    +0189     
    +0190     %Remove metabolites
    +0191     notInUse=orphanedMets(reducedModel);
    +0192     deletedMetabolites=[deletedMetabolites; reducedModel.mets(notInUse)];
    +0193     reducedModel=removeMets(reducedModel,notInUse);
    +0194 end
    +0195 
    +0196 if constrainReversible==true
    +0197     revs=find(reducedModel.rev);
    +0198     %getAllowedBounds treats an empty reaction list as all reactions
    +0199     if ~isempty(revs)
    +0200         [I,J]=getAllowedBounds(reducedModel,revs);
    +0201     
    +0202         I=abs(I);
    +0203         J=abs(J);
    +0204     
    +0205         %Get the "small" values
    +0206         K=I<10^-10;
    +0207         L=J<10^-10;
    +0208     
    +0209         %Keep the values where only one direction is zero (small)
    +0210         I(K==L)=[];
    +0211         J(K==L)=[];
    +0212         revs(K==L)=[];
    +0213     
    +0214         %Change the reversibility of the remaining reactions
    +0215         reducedModel.rev(revs(J>10^-10))=0; %Ignore reverse direction
    +0216         reducedModel.lb(revs(J>10^-10))=0;
    +0217     
    +0218         toSwitch=revs(I>10^-10);
    +0219         reducedModel.rev(toSwitch)=0; %Change directionality
    +0220         reducedModel.ub(toSwitch)=reducedModel.lb(toSwitch)*-1;
    +0221         reducedModel.lb(toSwitch)=0;
    +0222         reducedModel.S(:,toSwitch)=reducedModel.S(:,toSwitch).*-1;
    +0223         reducedModel.c(toSwitch)=reducedModel.c(toSwitch)*-1;
    +0224     end
    +0225 end
    +0226 if groupLinear==true
    +0227     if  suppressWarnings==false
    +0228         fprintf('NOTE: You have chosen to group linear reactions. This option does not keep track of gene/reaction associations when reactions are merged. Deleting all gene information\n');
    +0229     end
    +0230     
    +0231     reducedModel.genes={};
    +0232     reducedModel.rxnGeneMat=sparse(numel(reducedModel.rxns),0);
    +0233     reducedModel.grRules(:)={''};
    +0234     
    +0235     if isfield(reducedModel,'geneShortNames')
    +0236         reducedModel.geneShortNames={};
    +0237     end
    +0238     if isfield(reducedModel,'proteins')
    +0239         reducedModel.proteins={};
    +0240     end
    +0241     if isfield(reducedModel,'geneMiriams')
    +0242         reducedModel.geneMiriams={};
    +0243     end
    +0244     if isfield(reducedModel,'geneComps')
    +0245         reducedModel.geneComps=[];
    +0246     end
    +0247     
    +0248     %Convert the model to irreversible
    +0249     irrevModel=convertToIrrev(reducedModel);
    +0250     
    +0251     %Loop through and iteratively group linear reactions
    +0252     while 1
    +0253         %Get the banned reaction indexes. Note that the indexes will change
    +0254         %in each iteration, but the names will not as they will not be merged
    +0255         %with any other reaction
    +0256         bannedIndexes=getIndexes(irrevModel,reservedRxns,'rxns');
    +0257         
    +0258         %Select all metabolites that are only present as reactants/products
    +0259         %in one reaction
    +0260         singleNegative=find(sum(irrevModel.S'<0)==1);
    +0261         singlePositive=find(sum(irrevModel.S'>0)==1);
    +0262         
    +0263         %Retrieve the common metabolites
    +0264         common=intersect(singleNegative,singlePositive);
    +0265         
    +0266         mergedSome=false;
    +0267         
    +0268         %Loop through each of them and see if the reactions should be
    +0269         %merged
    +0270         for i=1:numel(common)
    +0271             involvedRxns=find(irrevModel.S(common(i),:));
    +0272             
    +0273             %Check so that one or both of the reactions have not been merged
    +0274             %already
    +0275             if numel(involvedRxns)==2 && isempty(intersect(bannedIndexes,involvedRxns))
    +0276                 %Calculate how many times the second reaction has to be
    +0277                 %multiplied before being merged with the first
    +0278                 stoichRatio=abs(irrevModel.S(common(i),involvedRxns(1))/irrevModel.S(common(i),involvedRxns(2)));
    +0279                 
    +0280                 %Add the second to the first
    +0281                 irrevModel.S(:,involvedRxns(1))=irrevModel.S(:,involvedRxns(1))+irrevModel.S(:,involvedRxns(2))*stoichRatio;
    +0282                 
    +0283                 %Clear the second reaction
    +0284                 irrevModel.S(:,involvedRxns(2))=0;
    +0285                 
    +0286                 %This is to prevent numerical issues. It should be 0
    +0287                 %already
    +0288                 irrevModel.S(common(i),involvedRxns(1))=0;
    +0289                 
    +0290                 %At this point the second reaction is certain to be deleted
    +0291                 %in a later step and can therefore be ignored
    +0292                 
    +0293                 %Recalculate the bounds for the new reaction. This can be
    +0294                 %problematic since the scale of the bounds may change
    +0295                 %dramatically. Let the most constraining reaction determine
    +0296                 %the new bound
    +0297                 lb1=irrevModel.lb(involvedRxns(1));
    +0298                 lb2=irrevModel.lb(involvedRxns(2));
    +0299                 ub1=irrevModel.ub(involvedRxns(1));
    +0300                 ub2=irrevModel.ub(involvedRxns(2));
    +0301                 
    +0302                 if lb2~=-inf
    +0303                     irrevModel.lb(involvedRxns(1))=max(lb1,lb2/stoichRatio);
    +0304                 end
    +0305                 if ub2~=inf
    +0306                     irrevModel.ub(involvedRxns(1))=min(ub1,ub2/stoichRatio);
    +0307                 end
    +0308                 
    +0309                 %Then recalculate the objective coefficient. The resulting
    +0310                 %coefficient is the weighted sum of the previous
    +0311                 irrevModel.c(involvedRxns(1))=irrevModel.c(involvedRxns(1))+irrevModel.c(involvedRxns(2))*stoichRatio;
    +0312                 
    +0313                 %Iterate again
    +0314                 mergedSome=true;
    +0315             end
    +0316         end
    +0317         
    +0318         %All possible reactions merged
    +0319         if mergedSome==false
    +0320             break;
    +0321         end
    +0322         
    +0323         %Now delete all reactions that involve no metabolites
    +0324         I=find(sum(irrevModel.S~=0)==0);
    +0325         
    +0326         %Remove reactions
    +0327         irrevModel=removeReactions(irrevModel,I);
    +0328         
    +0329         %Remove metabolites
    +0330         notInUse=orphanedMets(irrevModel);
    +0331         irrevModel=removeMets(irrevModel,notInUse);
    +0332     end
    +0333     
    +0334     reducedModel=irrevModel;
    +0335 end
    +0336 end
    +0337 
    +0338 function I=orphanedMets(model)
    +0339 %Metabolites that take part in no reaction, and can therefore be removed
    +0340 %without changing what the model can do. Metabolites with a non-zero
    +0341 %model.b are kept: b is a boundary condition on the metabolite, so removing
    +0342 %the row would drop that constraint along with it.
    +0343 I=sum(model.S~=0,2)==0 & ~any(model.b~=0,2);
    +0344 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/manipulation/sortIdentifiers.html b/docs/html/manipulation/sortIdentifiers.html similarity index 93% rename from doc/manipulation/sortIdentifiers.html rename to docs/html/manipulation/sortIdentifiers.html index 8571978d..a27c6bea 100644 --- a/doc/manipulation/sortIdentifiers.html +++ b/docs/html/manipulation/sortIdentifiers.html @@ -29,8 +29,8 @@

    SYNOPSIS ^DESCRIPTION ^

     sortIdentifiers  Sort model identifiers alphabetically.
     
    - Sort reactions, metabolites, genes and compartments alphabetically by
    - their identifier.
    + Sorts reactions, metabolites, genes (if the genes field exists) and
    + compartments alphabetically by their identifier.
     
      Parameters
      ----------
    @@ -62,8 +62,8 @@ 

    SOURCE CODE ^
    0001 function newModel = sortIdentifiers(model)
     0002 % sortIdentifiers  Sort model identifiers alphabetically.
     0003 %
    -0004 % Sort reactions, metabolites, genes and compartments alphabetically by
    -0005 % their identifier.
    +0004 % Sorts reactions, metabolites, genes (if the genes field exists) and
    +0005 % compartments alphabetically by their identifier.
     0006 %
     0007 % Parameters
     0008 % ----------
    diff --git a/docs/html/manipulation/sortModel.html b/docs/html/manipulation/sortModel.html
    new file mode 100644
    index 00000000..352468ae
    --- /dev/null
    +++ b/docs/html/manipulation/sortModel.html
    @@ -0,0 +1,301 @@
    +
    +
    +
    +  Description of sortModel
    +  
    +  
    +  
    +  
    +  
    +  
    +
    +
    +
    +
    Home > manipulation > sortModel.m
    + + + +

    sortModel +

    + +

    PURPOSE ^

    +
    sortModel Sort the metabolites and reactions of a model.
    + +

    SYNOPSIS ^

    +
    function model=sortModel(model,varargin)
    + +

    DESCRIPTION ^

    +
     sortModel  Sort the metabolites and reactions of a model.
    +
    + Depending on the options, the function sorts the metabolites by name and
    + compartment, orients reversible reactions consistently, and reorders the
    + reactions within each subsystem.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + sortReversible : logical
    +     orient each reversible reaction by changing the sign of its
    +     stoichiometric coefficients. The metabolite that is first in
    +     lexicographical order (metabolite name followed by compartment ID)
    +     becomes a reactant. Not applied when sortReactionOrder is true
    +     (default false).
    + sortMetName : logical
    +     sort the metabolites of the model by metabolite name followed by
    +     compartment ID (default false).
    + sortReactionOrder : logical
    +     reorder the reactions within each subsystem, such that reactions
    +     consuming a metabolite come after reactions producing it. The order is
    +     found by a random local search that also changes the direction of
    +     reversible reactions to match production and consumption direction.
    +     Requires the subSystems field. Subsystems with fewer than 2 or more
    +     than 250 reactions are not reordered (default false).
    + seed : double
    +     seed for the random number generator used by the sortReactionOrder
    +     local search, which makes its result reproducible across runs
    +     (default: the current rng state is not changed).
    +
    + Returns
    + -------
    + model : struct
    +     an updated model structure.
    +
    + Examples
    + --------
    +     model = sortModel(model, sortReversible, sortMetName, sortReactionOrder);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • permuteModel permuteModel Change the order of the reactions or metabolites in a model.
    +This function is called by: +
      +
    • contractModel contractModel Contract a model by merging identical reactions.
    + + + + +

    SOURCE CODE ^

    +
    0001 function model=sortModel(model,varargin)
    +0002 % sortModel  Sort the metabolites and reactions of a model.
    +0003 %
    +0004 % Depending on the options, the function sorts the metabolites by name and
    +0005 % compartment, orients reversible reactions consistently, and reorders the
    +0006 % reactions within each subsystem.
    +0007 %
    +0008 % Parameters
    +0009 % ----------
    +0010 % model : struct
    +0011 %     a model structure.
    +0012 %
    +0013 % Name-Value Arguments
    +0014 % --------------------
    +0015 % sortReversible : logical
    +0016 %     orient each reversible reaction by changing the sign of its
    +0017 %     stoichiometric coefficients. The metabolite that is first in
    +0018 %     lexicographical order (metabolite name followed by compartment ID)
    +0019 %     becomes a reactant. Not applied when sortReactionOrder is true
    +0020 %     (default false).
    +0021 % sortMetName : logical
    +0022 %     sort the metabolites of the model by metabolite name followed by
    +0023 %     compartment ID (default false).
    +0024 % sortReactionOrder : logical
    +0025 %     reorder the reactions within each subsystem, such that reactions
    +0026 %     consuming a metabolite come after reactions producing it. The order is
    +0027 %     found by a random local search that also changes the direction of
    +0028 %     reversible reactions to match production and consumption direction.
    +0029 %     Requires the subSystems field. Subsystems with fewer than 2 or more
    +0030 %     than 250 reactions are not reordered (default false).
    +0031 % seed : double
    +0032 %     seed for the random number generator used by the sortReactionOrder
    +0033 %     local search, which makes its result reproducible across runs
    +0034 %     (default: the current rng state is not changed).
    +0035 %
    +0036 % Returns
    +0037 % -------
    +0038 % model : struct
    +0039 %     an updated model structure.
    +0040 %
    +0041 % Examples
    +0042 % --------
    +0043 %     model = sortModel(model, sortReversible, sortMetName, sortReactionOrder);
    +0044 
    +0045 p=parseRAVENargs(varargin, {'sortReversible',false; 'sortMetName',false; 'sortReactionOrder',false; 'seed',[]});
    +0046 if ~isempty(p.seed)
    +0047     rng(p.seed);
    +0048 end
    +0049 sortReversible=p.sortReversible;
    +0050 sortMetName=p.sortMetName;
    +0051 sortReactionOrder=p.sortReactionOrder;
    +0052 
    +0053 if sortMetName==true
    +0054     %Assuming that metComps are the indexes.
    +0055     [~, metIndexes]=sort(strcat(model.metNames,'[',model.comps(model.metComps),']'));
    +0056     model=permuteModel(model,metIndexes,'mets');
    +0057 end
    +0058 
    +0059 if sortReversible==true && sortReactionOrder==false
    +0060     revIndexes=find(model.rev);
    +0061 
    +0062     for i=1:numel(revIndexes)
    +0063         %Create a cell array with all the metabolite names
    +0064         mets=find(model.S(:,revIndexes(i)));
    +0065         metNames=strcat(model.metNames(mets),model.comps(model.metComps(mets)));
    +0066         
    +0067         if iscellstr(metNames)
    +0068             [~, indexes]=sort(metNames);
    +0069             
    +0070             if model.S(mets(indexes(1)),revIndexes(i))>0
    +0071                 model.S(:,revIndexes(i))=model.S(:,revIndexes(i))*-1;
    +0072             end
    +0073         end
    +0074     end
    +0075 end
    +0076 
    +0077 if sortReactionOrder==true
    +0078     if ~isfield(model,'subSystems')
    +0079         EM='The model must contain a subSystems field in order to sort reaction order';
    +0080         error('RAVEN:badInput', '%s', EM);
    +0081     end
    +0082     
    +0083     subsystemsUnique='';
    +0084     subsystemsConcatenated='';
    +0085     for i=1:numel(model.subSystems)
    +0086         if ~iscell(model.subSystems{i})
    +0087             model.subSystems{i} = {model.subSystems{i}};
    +0088         end    
    +0089         subsystemsConcatenated{i,1}=strjoin(model.subSystems{i,1},';');
    +0090         if ~isempty(model.subSystems{i,1})
    +0091             for j=1:numel(model.subSystems{i,1})
    +0092                 subsystemsUnique{numel(subsystemsUnique)+1,1}=model.subSystems{i,1}{1,j};
    +0093             end
    +0094         end
    +0095     end
    +0096     subsystemsUnique=unique(subsystemsUnique);
    +0097     for i=1:numel(subsystemsUnique)
    +0098         %Get all reactions for that subsystem. The subsystem name is
    +0099         %escaped (it is arbitrary model text, not a regex pattern), and
    +0100         %the match is anchored to a whole ';'-delimited entry, since a
    +0101         %plain substring search would also match e.g. "Glycolysis /
    +0102         %Gluconeogenesis" when looking for "Glycolysis".
    +0103         pattern=['(^|;)' regexptranslate('escape',subsystemsUnique{i}) '(;|$)'];
    +0104         rxns=find(~cellfun(@isempty,regexp(subsystemsConcatenated,pattern)));
    +0105         
    +0106         %Temporarily ignore large subsystems because of inefficient
    +0107         %implementation
    +0108         if numel(rxns)<2 || numel(rxns)>250
    +0109             continue;
    +0110         end
    +0111         
    +0112         nRxns=numel(rxns);
    +0113         revRxns=rxns(model.rev(rxns)~=0);
    +0114 
    +0115         %This variable will hold the current reversibility directions of
    +0116         %the reversible reactions. 1 means the same direction as in the
    +0117         %original model and -1 means the opposite direction.
    +0118         oldRev=ones(numel(revRxns),1);
    +0119 
    +0120         %Work on just this subsystem's own (mets x nRxns) columns rather
    +0121         %than a full copy of model.S every iteration below: the score
    +0122         %only ever depends on these columns, whose involved-metabolite
    +0123         %rows and reversible-reaction positions are fixed for the whole
    +0124         %optimization (only their order/sign changes), so both can be
    +0125         %precomputed once instead of per iteration.
    +0126         subS=model.S(:,rxns);
    +0127         subS=subS(any(subS,2),:);
    +0128         [~,revLocalIdx]=ismember(revRxns,rxns);
    +0129 
    +0130         %The problem could be solved analytically, but a simple random
    +0131         %method is used instead. Two reactions are chosen randomly and
    +0132         %their positions are switched. A score is calculated based on the
    +0133         %number of metabolites that are produced before they are consumed.
    +0134         %If the perturbed model has a better or equal score than the
    +0135         %original the reaction order is switched. If no increase in score
    +0136         %has been seen after 1000*rxnsInSubsystem then the optimization is
    +0137         %terminated
    +0138 
    +0139         rxnOrder=1:nRxns;
    +0140         oldScore=-inf;
    +0141         counter=0;
    +0142         firstIter=true;
    +0143         while 1==1
    +0144             counter=counter+1;
    +0145             if counter==100*nRxns
    +0146                 break;
    +0147             end
    +0148 
    +0149             newRxnOrder=rxnOrder;
    +0150             rev=oldRev;
    +0151 
    +0152             if firstIter==false
    +0153                 y=randperm(nRxns,2);
    +0154 
    +0155                 newRxnOrder(y(1))=rxnOrder(y(2));
    +0156                 newRxnOrder(y(2))=rxnOrder(y(1));
    +0157 
    +0158                 %With a 50% chance, also switch the reversibility of one of
    +0159                 %the reactions
    +0160                 if rand()>0.5 && numel(rev)>1
    +0161                     n=randperm(numel(rev),1);
    +0162                     rev(n)=rev(n)*-1;
    +0163                 end
    +0164             end
    +0165             firstIter=false;
    +0166 
    +0167             tempS=subS;
    +0168 
    +0169             %Switch the directionalities
    +0170             for j=1:numel(rev)
    +0171                 if rev(j)==-1
    +0172                     tempS(:,revLocalIdx(j))=tempS(:,revLocalIdx(j)).*-1;
    +0173                 end
    +0174             end
    +0175 
    +0176             %Get the metabolites that are involved and when they are
    +0177             %produced/consumed
    +0178             s=tempS(:,newRxnOrder);
    +0179 
    +0180             %Add so that all mets are produced and consumed in the end
    +0181             s=[s ones(size(s,1),1) ones(size(s,1),1)*-1];
    +0182             
    +0183             %For each metabolite, find the reaction where it is first
    +0184             %produced and the reaction where it is first consumed
    +0185             s1=s>0;
    +0186             r1=arrayfun(@(x) find(s1(x,:),1,'first'),1:size(s1,1));
    +0187             s2=s<0;
    +0188             r2=arrayfun(@(x) find(s2(x,:),1,'first'),1:size(s2,1));
    +0189             
    +0190             score=sum(r1<r2);
    +0191             
    +0192             if score>=oldScore
    +0193                 if score>oldScore
    +0194                     counter=0;
    +0195                 end
    +0196                 oldScore=score;
    +0197                 oldRev=rev;
    +0198                 rxnOrder=newRxnOrder;
    +0199             end
    +0200         end
    +0201         
    +0202         %Update the model for this subsystem
    +0203         for j=1:numel(oldRev)
    +0204             if oldRev(j)==-1
    +0205                 model.S(:,revRxns(j))=model.S(:,revRxns(j)).*-1;
    +0206             end
    +0207         end
    +0208         order=1:numel(model.rxns);
    +0209         order(rxns)=rxns(rxnOrder);
    +0210         model=permuteModel(model, order, 'rxns');
    +0211     end
    +0212 end
    +0213 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/manipulation/standardizeGrRules.html b/docs/html/manipulation/standardizeGrRules.html new file mode 100644 index 00000000..fb21e618 --- /dev/null +++ b/docs/html/manipulation/standardizeGrRules.html @@ -0,0 +1,287 @@ + + + + Description of standardizeGrRules + + + + + + + + + +
    Home > manipulation > standardizeGrRules.m
    + + + +

    standardizeGrRules +

    + +

    PURPOSE ^

    +
    standardizeGrRules Standardise gene-reaction rules in a model.
    + +

    SYNOPSIS ^

    +
    function [grRules,rxnGeneMat,indexes2check] = standardizeGrRules(model,varargin)
    + +

    DESCRIPTION ^

    +
     standardizeGrRules  Standardise gene-reaction rules in a model.
    +
    + The grRules are standardised according to the following:
    +
    + - No overall containing brackets
    + - Only enzyme complexes are enclosed in brackets
    + - " and " and " or " strings are set to lowercase
    +
    + A rxnGeneMat matrix consistent with the standardised grRules is created.
    + grRules that are not in disjunctive normal form (an "or" nested inside an
    + "and") or that cannot be parsed are not standardised, apart from the
    + removal of double spaces, spaces inside brackets and uppercase operators.
    + The function throws an error if the model has no grRules field.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + embedded : logical
    +     if true, suppresses the console warning about grRules that are not in
    +     disjunctive normal form or cannot be parsed, for use when this
    +     function is called inside another RAVEN function that handles
    +     reporting itself (default false).
    +
    + Returns
    + -------
    + grRules : cell
    +     [nRxns x 1] cell array with the standardised grRules.
    + rxnGeneMat : double
    +     [nRxns x nGenes] sparse matrix consistent with the standardised
    +     grRules.
    + indexes2check : double
    +     indices of the grRules that are not in disjunctive normal form or
    +     cannot be parsed. These grRules need a manual check.
    +
    + Examples
    + --------
    +     [grRules, rxnGeneMat, indexes2check] = standardizeGrRules(model, embedded);
    +
    + Notes
    + -----
    + If used in a model reconstruction or modification pipeline, run this
    + function first.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    • addRxns addRxns Add reactions to a model.
    • changeGrRules changeGrRules Change the grRules of multiple reactions.
    • contractModel contractModel Contract a model by merging identical reactions.
    • expandModel expandModel Expand reactions that use several gene associations.
    • mergeModels mergeModels Merge models into one model structure.
    • removeGenes removeGenes Delete a set of genes from a model.
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [grRules,rxnGeneMat,indexes2check] = standardizeGrRules(model,varargin)
    +0002 % standardizeGrRules  Standardise gene-reaction rules in a model.
    +0003 %
    +0004 % The grRules are standardised according to the following:
    +0005 %
    +0006 % - No overall containing brackets
    +0007 % - Only enzyme complexes are enclosed in brackets
    +0008 % - " and " and " or " strings are set to lowercase
    +0009 %
    +0010 % A rxnGeneMat matrix consistent with the standardised grRules is created.
    +0011 % grRules that are not in disjunctive normal form (an "or" nested inside an
    +0012 % "and") or that cannot be parsed are not standardised, apart from the
    +0013 % removal of double spaces, spaces inside brackets and uppercase operators.
    +0014 % The function throws an error if the model has no grRules field.
    +0015 %
    +0016 % Parameters
    +0017 % ----------
    +0018 % model : struct
    +0019 %     a model structure.
    +0020 %
    +0021 % Name-Value Arguments
    +0022 % --------------------
    +0023 % embedded : logical
    +0024 %     if true, suppresses the console warning about grRules that are not in
    +0025 %     disjunctive normal form or cannot be parsed, for use when this
    +0026 %     function is called inside another RAVEN function that handles
    +0027 %     reporting itself (default false).
    +0028 %
    +0029 % Returns
    +0030 % -------
    +0031 % grRules : cell
    +0032 %     [nRxns x 1] cell array with the standardised grRules.
    +0033 % rxnGeneMat : double
    +0034 %     [nRxns x nGenes] sparse matrix consistent with the standardised
    +0035 %     grRules.
    +0036 % indexes2check : double
    +0037 %     indices of the grRules that are not in disjunctive normal form or
    +0038 %     cannot be parsed. These grRules need a manual check.
    +0039 %
    +0040 % Examples
    +0041 % --------
    +0042 %     [grRules, rxnGeneMat, indexes2check] = standardizeGrRules(model, embedded);
    +0043 %
    +0044 % Notes
    +0045 % -----
    +0046 % If used in a model reconstruction or modification pipeline, run this
    +0047 % function first.
    +0048 
    +0049 %Preallocate fields
    +0050 n          = length(model.rxns);
    +0051 [g,~]      = size(model.genes);
    +0052 rxnGeneMat = sparse(n,g);
    +0053 grRules    = cell(n,1);
    +0054 genes      = model.genes;
    +0055 p=parseRAVENargs(varargin, {'embedded',false});
    +0056 embedded=p.embedded;
    +0057 
    +0058 %Gene id -> index lookup, built once rather than linearly scanning all of
    +0059 %model.genes (via strcmpi) for every gene token of every reaction, which
    +0060 %is quadratic in genome-scale models.
    +0061 if isempty(genes)
    +0062     geneMap = containers.Map('KeyType','char','ValueType','double');
    +0063 else
    +0064     geneMap = containers.Map(lower(genes),num2cell(1:numel(genes)));
    +0065 end
    +0066 
    +0067 if isfield(model,'grRules')
    +0068     originalGrRules=model.grRules; 
    +0069     originalGrRules=grRulesPreparation(originalGrRules);
    +0070     %Search for potential logical errors in the grRules field
    +0071     tmpModel.rxns=model.rxns; tmpModel.grRules=originalGrRules;
    +0072     fpeIssues=findPotentialErrors(tmpModel);
    +0073     if isempty(fpeIssues)
    +0074         indexes2check=[];
    +0075     else
    +0076         indexes2check=vertcat(fpeIssues.index);
    +0077     end
    +0078     if ~isempty(indexes2check) && ~embedded
    +0079         %Escaped: rxn id and grRule are arbitrary model text and may
    +0080         %contain "%", which sprintf/warning would otherwise misread as a
    +0081         %format directive and truncate the rest of the message.
    +0082         esc=@(s) strrep(s,'%','%%');
    +0083         STR=['Potentially problematic ") AND (", ") AND" or "AND ("relat' ...
    +0084              'ionships found in\n\n'];
    +0085         for fpeI=1:numel(fpeIssues)
    +0086             STR=[STR '  - grRule #' esc(fpeIssues(fpeI).rxn) ': ' esc(fpeIssues(fpeI).grRule) '\n']; %#ok<AGROW>
    +0087         end
    +0088         STR=[STR '\n This kind of relationships should only be present ' ...
    +0089              'in reactions catalysed by complexes of isoenzymes e.g.\n\n' ...
    +0090              '  - (G1 or G2) and (G3 or G4)\n\n For these cases modify ' ...
    +0091              'the grRules manually, writing all the possible combinations ' ...
    +0092              'e.g.\n\n  - (G1 and G3) or (G1 and G4) or (G2 and G3) or ' ...
    +0093              '(G2 and G4)\n\n For other cases modify the correspondent ' ...
    +0094              'grRules avoiding:\n\n  1) Overall container brackets, e.g.' ...
    +0095              '\n        "(G1 and G2)" should be "G1 and G2"\n\n  2) ' ...
    +0096              'Single unit enzymes enclosed into brackets, e.g.\n        ' ...
    +0097              '"(G1)" should be "G1"\n\n  3) The use of uppercases for ' ...
    +0098              'logical operators, e.g.\n        "G1 OR G2" should be "G1 ' ...
    +0099              'or G2"\n\n  4) Unbalanced brackets, e.g.\n        "((G1 ' ...
    +0100              'and G2) or G3" should be "(G1 and G2) or G3"\n'];
    +0101         %warning() re-parses its argument as a format string too, so the
    +0102         %already-resolved text must be passed via '%s', not directly.
    +0103         warning('%s', sprintf(STR))
    +0104     end
    +0105     
    +0106     for i=1:length(originalGrRules)
    +0107         originalSTR = originalGrRules{i};
    +0108         grRules{i,:}  = originalSTR;
    +0109         %Non-empty grRules are splitted in all their different isoenzymes
    +0110         genesSets   = getSimpleGeneSets(originalSTR);
    +0111         rxnGeneMat  = modifyRxnGeneMat(genesSets,geneMap,rxnGeneMat,i);
    +0112         %standardize the non-conflicting grRules
    +0113         if ~ismember(i,indexes2check)
    +0114             newSTR = [];
    +0115             if ~isempty(genesSets)
    +0116                 for j=1:length(genesSets)
    +0117                     simpleSet  = genesSets{j};
    +0118                     %Enclose simpleSet in brackets
    +0119                     if length(genesSets)>1
    +0120                         if ~isempty(strfind(simpleSet,' and '))
    +0121                             simpleSet = horzcat('(',simpleSet,')');
    +0122                         end
    +0123                     end
    +0124                     %Separate genesSets in the substring (in case of
    +0125                     %isoenzymes)
    +0126                     if j<length(genesSets)
    +0127                         newSTR = [newSTR, simpleSet, ' or '];
    +0128                         %Add the last simpleSet
    +0129                     else
    +0130                         newSTR = [newSTR, simpleSet];
    +0131                     end
    +0132                 end
    +0133                 %Update grRule
    +0134                 grRules{i} = char(newSTR);
    +0135             end
    +0136         end
    +0137     end
    +0138 else
    +0139     error('The model does not have a grRules field.')
    +0140 end
    +0141 
    +0142 end
    +0143 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    +0144 %Function that gets a cell array with all the simple geneSets in a given
    +0145 %grRule string
    +0146 function genesSets = getSimpleGeneSets(originalSTR)
    +0147 genesSets  = [];
    +0148 if ~isempty(originalSTR)
    +0149     originalSTR = strtrim(originalSTR);
    +0150     %Remove all brackets
    +0151     originalSTR = strrep(originalSTR,'(','');
    +0152     originalSTR = strrep(originalSTR,')','');
    +0153     %Split all the different genesSets
    +0154     genesSets  = transpose(strsplit(originalSTR,' or '));
    +0155 end
    +0156 end
    +0157 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    +0158 %Function that gets a cell array of simple genes sets (single genes or
    +0159 %enzyme complexes) associated with the i-th reaction and modifies the
    +0160 %correspondent row in the rxnGeneMat accordingly.
    +0161 function rxnGeneMat = modifyRxnGeneMat(genesSets,geneMap,rxnGeneMat,i)
    +0162 
    +0163 if ~isempty(genesSets)
    +0164     for j=1:length(genesSets)
    +0165         simpleSet  = genesSets{j};
    +0166         %        rxnGeneMat = modifyRxnGeneMat(simpleSet,model.genes,rxnGeneMat,i);
    +0167         %Get individual genes
    +0168         STR   = strrep(simpleSet,') and (',' and ');
    +0169         genes = strsplit(STR,' ');
    +0170         for k=1:length(genes)
    +0171             if ~strcmpi(genes(k),' and ')
    +0172                 geneKey = lower(genes{k});
    +0173                 if isKey(geneMap,geneKey)
    +0174                     rxnGeneMat(i,geneMap(geneKey)) = 1;
    +0175                     %else
    +0176                     %In this case the gene should be added to the
    +0177                     %genes field (and to all of its dependencies)
    +0178                 end
    +0179             end
    +0180         end
    +0181     end
    +0182 end
    +0183 end
    +0184 
    +0185 function grRules = grRulesPreparation(grRules)
    +0186 %Remove unnecessary blanks
    +0187 grRules=strrep(grRules,'  ',' ');
    +0188 grRules=strrep(grRules,'( ','(');
    +0189 grRules=strrep(grRules,' )',')');
    +0190 % Make sure that AND and OR strings are in lowercase
    +0191 grRules=strrep(grRules,' AND ',' and ');
    +0192 grRules=strrep(grRules,' OR ',' or ');
    +0193 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/matlabicon.gif b/docs/html/matlabicon.gif similarity index 100% rename from doc/matlabicon.gif rename to docs/html/matlabicon.gif diff --git a/doc/mex.png b/docs/html/mex.png similarity index 100% rename from doc/mex.png rename to docs/html/mex.png diff --git a/doc/pcode.png b/docs/html/pcode.png similarity index 100% rename from doc/pcode.png rename to docs/html/pcode.png diff --git a/doc/queries/buildEquation.html b/docs/html/queries/buildEquation.html similarity index 54% rename from doc/queries/buildEquation.html rename to docs/html/queries/buildEquation.html index 9bc9baaf..9a198ea0 100644 --- a/doc/queries/buildEquation.html +++ b/docs/html/queries/buildEquation.html @@ -34,14 +34,17 @@

    DESCRIPTION ^SOURCE CODE ^% mets : cell 0007 % metabolites involved in the reaction. 0008 % stoichCoeffs : double -0009 % vector with corresponding stoichiometric coeffs. -0010 % isrev : logical -0011 % indicates if the reaction is reversible or not. -0012 % -0013 % Returns -0014 % ------- -0015 % equationString : char -0016 % equation as a string. -0017 % -0018 % Examples -0019 % -------- -0020 % equationString = buildEquation(mets, stoichCoeffs, isrev); -0021 -0022 mets=convertCharArray(mets); -0023 if ~isnumeric(stoichCoeffs) -0024 EM = 'stoichCoeffs must be a numeric vector'; -0025 error('RAVEN:badInput', '%s', EM); -0026 elseif ~islogical(isrev) -0027 EM = 'isrev must be a logical'; +0009 % vector with the corresponding stoichiometric coefficients, of the same +0010 % length as mets. Negative coefficients are reactants and positive +0011 % coefficients are products. +0012 % isrev : logical +0013 % true if the reaction is reversible (written with "<=>"), false if it +0014 % is irreversible (written with "=>"). +0015 % +0016 % Returns +0017 % ------- +0018 % equationString : char +0019 % the equation as a string. +0020 % +0021 % Examples +0022 % -------- +0023 % equationString = buildEquation(mets, stoichCoeffs, isrev); +0024 +0025 mets=convertCharArray(mets); +0026 if ~isnumeric(stoichCoeffs) +0027 EM = 'stoichCoeffs must be a numeric vector'; 0028 error('RAVEN:badInput', '%s', EM); -0029 elseif length(mets) ~= length(stoichCoeffs) -0030 EM = 'lengths of mets and stoichCoeffs should be the same'; +0029 elseif ~islogical(isrev) +0030 EM = 'isrev must be a logical'; 0031 error('RAVEN:badInput', '%s', EM); -0032 end -0033 -0034 %Reactant half: -0035 reactants = mets(stoichCoeffs<0); -0036 reactantsCoeffs = abs(stoichCoeffs(stoichCoeffs<0)); -0037 reactantsEqn = concatenateEquation(reactants,reactantsCoeffs); -0038 -0039 %Product half: -0040 products = mets(stoichCoeffs>0); -0041 productsCoeffs = abs(stoichCoeffs(stoichCoeffs>0)); -0042 productsEqn = concatenateEquation(products,productsCoeffs); -0043 -0044 %Full equation: -0045 if isrev -0046 equationString = [reactantsEqn ' <=> ' productsEqn]; -0047 else -0048 equationString = [reactantsEqn ' => ' productsEqn]; -0049 end -0050 -0051 end -0052 -0053 function eqn = concatenateEquation(mets,stoichCoeffs) -0054 %This function concatenates metabolites and stoich. coefficients to form -0055 %either the left or right side of the rxn equation -0056 eqn = ''; -0057 for i = 1:length(stoichCoeffs) -0058 if i == 1 -0059 plusString=''; -0060 else -0061 plusString=' + '; -0062 end -0063 stoich = stoichCoeffs(i); -0064 if stoich == 1 -0065 stoich = ''; -0066 else -0067 stoich = [num2str(stoich) ' ']; -0068 end -0069 eqn = [eqn plusString stoich mets{i}]; -0070 end -0071 end

    +0032 elseif length(mets) ~= length(stoichCoeffs) +0033 EM = 'lengths of mets and stoichCoeffs should be the same'; +0034 error('RAVEN:badInput', '%s', EM); +0035 end +0036 +0037 %Reactant half: +0038 reactants = mets(stoichCoeffs<0); +0039 reactantsCoeffs = abs(stoichCoeffs(stoichCoeffs<0)); +0040 reactantsEqn = concatenateEquation(reactants,reactantsCoeffs); +0041 +0042 %Product half: +0043 products = mets(stoichCoeffs>0); +0044 productsCoeffs = abs(stoichCoeffs(stoichCoeffs>0)); +0045 productsEqn = concatenateEquation(products,productsCoeffs); +0046 +0047 %Full equation: +0048 if isrev +0049 equationString = [reactantsEqn ' <=> ' productsEqn]; +0050 else +0051 equationString = [reactantsEqn ' => ' productsEqn]; +0052 end +0053 +0054 end +0055 +0056 function eqn = concatenateEquation(mets,stoichCoeffs) +0057 %This function concatenates metabolites and stoich. coefficients to form +0058 %either the left or right side of the rxn equation +0059 eqn = ''; +0060 for i = 1:length(stoichCoeffs) +0061 if i == 1 +0062 plusString=''; +0063 else +0064 plusString=' + '; +0065 end +0066 stoich = stoichCoeffs(i); +0067 if stoich == 1 +0068 stoich = ''; +0069 else +0070 stoich = [num2str(stoich) ' ']; +0071 end +0072 eqn = [eqn plusString stoich mets{i}]; +0073 end +0074 end


    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/queries/checkModelStruct.html b/docs/html/queries/checkModelStruct.html new file mode 100644 index 00000000..ebf79191 --- /dev/null +++ b/docs/html/queries/checkModelStruct.html @@ -0,0 +1,664 @@ + + + + Description of checkModelStruct + + + + + + + + + +
    Home > queries > checkModelStruct.m
    + + + +

    checkModelStruct +

    + +

    PURPOSE ^

    +
    checkModelStruct Check a model structure for errors and inconsistencies.
    + +

    SYNOPSIS ^

    +
    function issues=checkModelStruct(model,varargin)
    + +

    DESCRIPTION ^

    +
     checkModelStruct  Check a model structure for errors and inconsistencies.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + throwErrors : logical
    +     true if structural problems (wrong types, missing required fields,
    +     duplicate IDs and other problems of structure) should throw an error. Advisory notices such as
    +     unused elements and bound inconsistencies are always reported as
    +     warnings regardless of this flag (default true).
    + trimWarnings : logical
    +     true if a given warning or error lists at most 10 items, the last of
    +     which reports the number of remaining items (default true).
    +
    + Returns
    + -------
    + issues : struct
    +     When called with an output argument, the function returns a struct
    +     array with one element per finding and neither throws nor prints. The
    +     default behaviour without an output argument is to print warnings and
    +     throw errors. Fields:
    +
    +     - category (char): type of issue, one of "missing_field",
    +       "wrong_type", "empty_id", "duplicate", "invalid_id",
    +       "invalid_bounds", "unused", "objective", "gpr", "invalid_formula",
    +       "cross_reference" or "other".
    +     - target (char): the item involved (field name, reaction ID,
    +       metabolite ID or another item), or "" when not applicable.
    +     - message (char): the full diagnostic text.
    +
    + Notes
    + -----
    + The check is intended for use after importing or constructing a model and
    + before exporting a model to SBML format.
    +
    + Examples
    + --------
    +     checkModelStruct(model);
    +     checkModelStruct(model, "throwErrors", false);
    +     issues = checkModelStruct(model);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • getGenesFromGrRules getGenesFromGrRules Extract gene list and rxnGeneMat from grRules array.
    • parseFormulas parseFormulas Get the elemental composition from formulas.
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function issues=checkModelStruct(model,varargin)
    +0002 % checkModelStruct  Check a model structure for errors and inconsistencies.
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % model : struct
    +0007 %     a model structure.
    +0008 %
    +0009 % Name-Value Arguments
    +0010 % --------------------
    +0011 % throwErrors : logical
    +0012 %     true if structural problems (wrong types, missing required fields,
    +0013 %     duplicate IDs and other problems of structure) should throw an error. Advisory notices such as
    +0014 %     unused elements and bound inconsistencies are always reported as
    +0015 %     warnings regardless of this flag (default true).
    +0016 % trimWarnings : logical
    +0017 %     true if a given warning or error lists at most 10 items, the last of
    +0018 %     which reports the number of remaining items (default true).
    +0019 %
    +0020 % Returns
    +0021 % -------
    +0022 % issues : struct
    +0023 %     When called with an output argument, the function returns a struct
    +0024 %     array with one element per finding and neither throws nor prints. The
    +0025 %     default behaviour without an output argument is to print warnings and
    +0026 %     throw errors. Fields:
    +0027 %
    +0028 %     - category (char): type of issue, one of "missing_field",
    +0029 %       "wrong_type", "empty_id", "duplicate", "invalid_id",
    +0030 %       "invalid_bounds", "unused", "objective", "gpr", "invalid_formula",
    +0031 %       "cross_reference" or "other".
    +0032 %     - target (char): the item involved (field name, reaction ID,
    +0033 %       metabolite ID or another item), or "" when not applicable.
    +0034 %     - message (char): the full diagnostic text.
    +0035 %
    +0036 % Notes
    +0037 % -----
    +0038 % The check is intended for use after importing or constructing a model and
    +0039 % before exporting a model to SBML format.
    +0040 %
    +0041 % Examples
    +0042 % --------
    +0043 %     checkModelStruct(model);
    +0044 %     checkModelStruct(model, "throwErrors", false);
    +0045 %     issues = checkModelStruct(model);
    +0046 
    +0047 p=parseRAVENargs(varargin, {'throwErrors',true; 'trimWarnings',true});
    +0048 throwErrors=p.throwErrors;
    +0049 trimWarnings=p.trimWarnings;
    +0050 
    +0051 collecting = nargout > 0;
    +0052 issues = struct('category',{},'target',{},'message',{});
    +0053 
    +0054     function reportIssue(severity, msg, items)
    +0055         % Report one issue: accumulate in collect mode, or warn/error.
    +0056         % severity: "error" | "warning"
    +0057         % When items is supplied (3 args) but is empty, silently do nothing.
    +0058         listProvided = nargin >= 3;
    +0059         if nargin < 3; items = {}; end
    +0060         if ~isempty(items)
    +0061             items = convertCharArray(items);
    +0062         end
    +0063         if listProvided && isempty(items); return; end
    +0064         if trimWarnings && numel(items) > 10
    +0065             items{10} = sprintf('...and %d more', numel(items)-9);
    +0066             items(11:end) = [];
    +0067         end
    +0068         cat = issueCategory(msg);
    +0069         if collecting
    +0070             if isempty(items)
    +0071                 issues(end+1,1) = struct('category',cat,'target','','message',msg);
    +0072             else
    +0073                 for ki_ = 1:numel(items)
    +0074                     issues(end+1,1) = struct('category',cat,'target',items{ki_},'message',msg); %#ok<AGROW>
    +0075                 end
    +0076             end
    +0077             return
    +0078         end
    +0079         isErr = strcmp(severity,'error') && throwErrors;
    +0080         if isErr
    +0081             if isempty(items)
    +0082                 error('RAVEN:modelError', '%s', msg);
    +0083             else
    +0084                 error('RAVEN:modelError', '%s', ravenList(msg, items, false));
    +0085             end
    +0086         else
    +0087             if isempty(items)
    +0088                 warning('RAVEN:modelError', '%s', msg);
    +0089             else
    +0090                 warning('RAVEN:modelError', '%s', ravenList(msg, items, false));
    +0091             end
    +0092         end
    +0093     end
    +0094 
    +0095 %Missing elements — checked inline so missing fields do not cause cascading
    +0096 %errors in the rest of the body
    +0097 fields={'id';'name';'rxns';'mets';'S';'lb';'ub';'rev';'c';'b';'comps';'metComps'};
    +0098 missingField=false;
    +0099 for i=1:numel(fields)
    +0100     if ~isfield(model,fields{i})
    +0101         EM=['The model is missing the "' fields{i} '" field'];
    +0102         reportIssue('error',EM);
    +0103         missingField=true;
    +0104     end
    +0105 end
    +0106 if missingField
    +0107     return  % missing required fields would cause access errors below; this
    +0108             % must fire whenever a field is missing, not only when
    +0109             % collecting -- throwErrors=false otherwise warns here and
    +0110             % then crashes on the very field it just warned about
    +0111 end
    +0112 
    +0113 %Type check
    +0114 if ~ischar(model.id)
    +0115     EM='The "id" field must be a string';
    +0116     reportIssue('error',EM);
    +0117 end
    +0118 if ~ischar(model.name)
    +0119     EM='The "name" field must be a string';
    +0120     reportIssue('error',EM);
    +0121 end
    +0122 if ~iscellstr(model.rxns)
    +0123     EM='The "rxns" field must be a cell array of strings';
    +0124     reportIssue('error',EM);
    +0125 end
    +0126 if ~iscellstr(model.mets)
    +0127     EM='The "mets" field must be a cell array of strings';
    +0128     reportIssue('error',EM);
    +0129 end
    +0130 if ~isnumeric(model.S)
    +0131     EM='The "S" field must be of type "double"';
    +0132     reportIssue('error',EM);
    +0133 end
    +0134 if ~isnumeric(model.lb)
    +0135     EM='The "lb" field must be of type "double"';
    +0136     reportIssue('error',EM);
    +0137 end
    +0138 if ~isnumeric(model.ub)
    +0139     EM='The "ub" field must be of type "double"';
    +0140     reportIssue('error',EM);
    +0141 end
    +0142 if ~isnumeric(model.rev)
    +0143     EM='The "rev" field must be of type "double"';
    +0144     reportIssue('error',EM);
    +0145 end
    +0146 if ~isnumeric(model.c)
    +0147     EM='The "c" field must be of type "double"';
    +0148     reportIssue('error',EM);
    +0149 end
    +0150 if ~isnumeric(model.b)
    +0151     EM='The "b" field must be of type "double"';
    +0152     reportIssue('error',EM);
    +0153 end
    +0154 if ~iscellstr(model.comps)
    +0155     EM='The "comps" field must be a cell array of strings';
    +0156     reportIssue('error',EM);
    +0157 end
    +0158 if ~isnumeric(model.metComps)
    +0159     EM='The "metComps" field must be of type "double"';
    +0160     reportIssue('error',EM);
    +0161 end
    +0162 if isfield(model,'compNames')
    +0163     if ~iscellstr(model.compNames)
    +0164         EM='The "compNames" field must be a cell array of strings';
    +0165         reportIssue('error',EM);
    +0166     end
    +0167 end
    +0168 if isfield(model,'compOutside')
    +0169     if ~iscellstr(model.compOutside)
    +0170         EM='The "compOutside" field must be a cell array of strings';
    +0171         reportIssue('error',EM);
    +0172     end
    +0173 end
    +0174 if isfield(model,'rxnNames')
    +0175     if ~iscellstr(model.rxnNames)
    +0176         EM='The "rxnNames" field must be a cell array of strings';
    +0177         reportIssue('error',EM);
    +0178     end
    +0179 end
    +0180 if isfield(model,'metNames')
    +0181     if ~iscellstr(model.metNames)
    +0182         EM='The "metNames" field must be a cell array of strings';
    +0183         reportIssue('error',EM);
    +0184     end
    +0185 end
    +0186 if isfield(model,'genes')
    +0187     if ~iscellstr(model.genes)
    +0188         EM='The "genes" field must be a cell array of strings';
    +0189         reportIssue('error',EM);
    +0190     end
    +0191 end
    +0192 if isfield(model,'rxnGeneMat')
    +0193     if ~isnumeric(model.rxnGeneMat)
    +0194         EM='The "rxnGeneMat" field must be of type "double"';
    +0195         reportIssue('error',EM);
    +0196     end
    +0197 end
    +0198 if isfield(model,'grRules')
    +0199     if ~iscellstr(model.grRules)
    +0200         EM='The "grRules" field must be a cell array of strings';
    +0201         reportIssue('error',EM);
    +0202     end
    +0203     if ~isfield(model,'genes')
    +0204         EM='If "grRules" field exists, the model should also contain a "genes" field';
    +0205         reportIssue('error',EM);
    +0206     else
    +0207         %Erroneous grRules that start/end with OR/AND
    +0208         EM='The following reaction(s) have grRules that start or end with ''OR'' or ''AND'':';
    +0209         reportIssue('error',EM,model.rxns(startsWith(model.grRules,{'or ','and '}) | endsWith(model.grRules,{' or',' and'})));
    +0210         %grRules that are not in genes field
    +0211         geneList = getGenesFromGrRules(model.grRules);
    +0212         geneList = setdiff(unique(geneList),model.genes);
    +0213         if ~isempty(geneList)
    +0214             problemGrRules = model.rxns(contains(model.grRules,geneList));
    +0215             problemGrRules = strjoin(problemGrRules(:),'; ');
    +0216             EM=['The reaction(s) "' problemGrRules '" contain the following genes in its "grRules" field, but these are not in the "genes" field:'];
    +0217             reportIssue('error',EM,geneList);
    +0218         end
    +0219     end
    +0220 end
    +0221 if isfield(model,'rxnComps')
    +0222     if ~isnumeric(model.rxnComps)
    +0223         EM='The "rxnComps" field must be of type "double"';
    +0224         reportIssue('error',EM);
    +0225     end
    +0226 end
    +0227 if isfield(model,'inchis')
    +0228     if ~iscellstr(model.inchis)
    +0229         EM='The "inchis" field must be a cell array of strings';
    +0230         reportIssue('error',EM);
    +0231     end
    +0232 end
    +0233 if isfield(model,'metSmiles')
    +0234     if ~iscellstr(model.metSmiles)
    +0235         EM='The "metSmiles" field must be a cell array of strings';
    +0236         reportIssue('error',EM);
    +0237     end
    +0238 end
    +0239 if isfield(model,'metFormulas')
    +0240     if ~iscellstr(model.metFormulas)
    +0241         EM='The "metFormulas" field must be a cell array of strings';
    +0242         reportIssue('error',EM);
    +0243     end
    +0244 end
    +0245 if isfield(model,'metCharges')
    +0246     if ~isnumeric(model.metCharges)
    +0247         EM='The "metCharges" field must be a double';
    +0248         reportIssue('error',EM);
    +0249     end
    +0250 end
    +0251 if isfield(model,'metDeltaG')
    +0252     if ~isnumeric(model.metDeltaG)
    +0253         EM='The "metDeltaG" field must be a double';
    +0254         reportIssue('error',EM);
    +0255     end
    +0256 end
    +0257 if isfield(model,'subSystems')
    +0258     isNested  = any(cellfun(@(x) iscell(x), model.subSystems));
    +0259     isCellStr = any(cellfun(@(x) ischar(x), model.subSystems));
    +0260     if ~xor(isNested,isCellStr)
    +0261         EM='The "subSystems" field must be a cell array of chars, *or* a cell array of cell arrays of chars';
    +0262         reportIssue('error',EM);
    +0263     end
    +0264 end
    +0265 if isfield(model,'eccodes')
    +0266     if ~iscellstr(model.eccodes)
    +0267         EM='The "eccodes" field must be a cell array of strings';
    +0268         reportIssue('error',EM);
    +0269     end
    +0270 end
    +0271 if isfield(model,'unconstrained')
    +0272     if ~isnumeric(model.unconstrained)
    +0273         EM='The "unconstrained" field must be of type "double"';
    +0274         reportIssue('error',EM);
    +0275     end
    +0276 end
    +0277 if isfield(model,'rxnNotes')
    +0278     if ~iscellstr(model.rxnNotes)
    +0279         EM='The "rxnNotes" field must be a cell array of strings';
    +0280         reportIssue('error',EM);
    +0281     end
    +0282 end
    +0283 if isfield(model,'rxnReferences')
    +0284     if ~iscellstr(model.rxnReferences)
    +0285         EM='The "rxnReferences" field must be a cell array of strings';
    +0286         reportIssue('error',EM);
    +0287     end
    +0288 end
    +0289 if isfield(model,'rxnConfidenceScores')
    +0290     if ~isnumeric(model.rxnConfidenceScores)
    +0291         EM='The "rxnConfidenceScores" field must be a double';
    +0292         reportIssue('error',EM);
    +0293     end
    +0294 end
    +0295 if isfield(model,'rxnDeltaG')
    +0296     if ~isnumeric(model.rxnDeltaG)
    +0297         EM='The "rxnDeltaG" field must be a double';
    +0298         reportIssue('error',EM);
    +0299     end
    +0300 end
    +0301 
    +0302 %Empty strings
    +0303 if isempty(model.id)
    +0304     EM='The "id" field cannot be empty';
    +0305     reportIssue('error',EM);
    +0306 end
    +0307 if any(cellfun(@isempty,model.rxns))
    +0308     EM='The model contains empty reaction IDs';
    +0309     reportIssue('error',EM);
    +0310 end
    +0311 if any(cellfun(@isempty,model.mets))
    +0312     EM='The model contains empty metabolite IDs';
    +0313     reportIssue('error',EM);
    +0314 end
    +0315 if any(cellfun(@isempty,model.comps))
    +0316     EM='The model contains empty compartment IDs';
    +0317     reportIssue('error',EM);
    +0318 end
    +0319 if isfield(model,'metNames')
    +0320     EM='The following metabolites have empty names:';
    +0321     reportIssue('error',EM,model.mets(cellfun(@isempty,model.metNames)));
    +0322 end
    +0323 
    +0324 if isfield(model,'genes')
    +0325     if any(cellfun(@isempty,model.genes))
    +0326         EM='The model contains empty gene IDs';
    +0327         reportIssue('error',EM);
    +0328     end
    +0329 end
    +0330 
    +0331 %Validate format of ids
    +0332 fields      = {'rxns';'mets';'comps';'genes'};
    +0333 fieldNames  = {'reaction';'metabolite';'compartment';'gene'};
    +0334 fieldPrefix = {'R_';'M_';'C_';'G_'};
    +0335 for i=1:numel(fields)
    +0336     try
    +0337         numIDs = ~startsWith(model.(fields{i}),regexpPattern('^[a-zA-Z_]'));
    +0338     catch
    +0339         numIDs = [];
    +0340     end
    +0341     if any(numIDs)
    +0342         EM = ['The following ' fieldNames{i} ' identifiers do not start '...
    +0343             'with a letter or _ (conflicting with SBML specifications). '...
    +0344             'This does not impact RAVEN functionality, but be aware that '...
    +0345             'exportModel will automatically add ' fieldPrefix{i} ...
    +0346             ' prefixes to all ' fieldNames{i} ' identifiers:'];
    +0347         reportIssue('warning',EM,{model.(fields{i}){numIDs}});
    +0348     end
    +0349 end
    +0350 
    +0351 %Duplicates
    +0352 EM='The following reaction IDs are duplicates:';
    +0353 reportIssue('error',EM,model.rxns(duplicates(model.rxns)));
    +0354 EM='The following metabolite IDs are duplicates:';
    +0355 reportIssue('error',EM,model.mets(duplicates(model.mets)));
    +0356 EM='The following compartment IDs are duplicates:';
    +0357 reportIssue('error',EM,model.comps(duplicates(model.comps)));
    +0358 if isfield(model,'genes')
    +0359     EM='The following genes are duplicates:';
    +0360     reportIssue('error',EM,model.genes(duplicates(model.genes)));
    +0361 end
    +0362 if isfield(model,'metNames')
    +0363     metInComp=strcat(model.metNames,'[',model.comps(model.metComps),']');
    +0364     EM='The following metabolites already exist in the same compartment:';
    +0365     reportIssue('error',EM,metInComp(duplicates(metInComp)));
    +0366 end
    +0367 
    +0368 %Elements never used (always reported as warnings)
    +0369 EM='The following reactions are empty (no involved metabolites):';
    +0370 reportIssue('warning',EM,model.rxns(~any(model.S,1)));
    +0371 EM='The following metabolites are never used in a reaction:';
    +0372 reportIssue('warning',EM,model.mets(~any(model.S,2)));
    +0373 if isfield(model,'genes')
    +0374     EM='The following genes are not associated to a reaction:';
    +0375     reportIssue('warning',EM,model.genes(~any(model.rxnGeneMat,1)));
    +0376 end
    +0377 I=true(numel(model.comps),1);
    +0378 I(model.metComps)=false;
    +0379 EM='The following compartments contain no metabolites:';
    +0380 reportIssue('warning',EM,model.comps(I));
    +0381 
    +0382 %Contradicting bounds
    +0383 EM='The following reactions have contradicting bounds (lower bound is higher than upper bound):';
    +0384 reportIssue('error',EM,model.rxns(model.lb>model.ub));
    +0385 EM='The following reactions have lower and upper bounds that indicate reversibility, but are indicated as irreversible in model.rev:';
    +0386 reportIssue('warning',EM,model.rxns(model.lb < 0 & model.ub > 0 & model.rev==0));
    +0387 
    +0388 %Multiple or no objective functions not allowed in SBML L3V1 FBCv2
    +0389 if numel(find(model.c))>1
    +0390     EM='Multiple objective functions found. This might be intended, but results in FBCv2 non-compliant SBML file when exported';
    +0391     reportIssue('warning',EM,model.rxns(find(model.c)));
    +0392 elseif ~any(model.c)
    +0393     EM='No objective function found. This might be intended, but results in FBCv2 non-compliant SBML file when exported';
    +0394     reportIssue('warning',EM);
    +0395 end
    +0396 
    +0397 %Mapping of compartments
    +0398 if isfield(model,'compOutside')
    +0399     EM='The following compartments are in "compOutside" but not in "comps":';
    +0400     reportIssue('error',EM,setdiff(model.compOutside,[{''};model.comps]));
    +0401 end
    +0402 
    +0403 %Met names which start with number
    +0404 if isfield(model,'metNames')
    +0405     I=false(numel(model.metNames),1);
    +0406     for i=1:numel(model.metNames)
    +0407         index=strfind(model.metNames{i},' ');
    +0408         if any(index)
    +0409             if ~isnan(str2double(model.metNames{i}(1:index(1)-1)))
    +0410                 I(i)=true;
    +0411             end
    +0412         end
    +0413     end
    +0414     EM='The following metabolite names begin with a number directly followed by space, which could potentially cause problems:';
    +0415     reportIssue('warning',EM,model.metNames(I));
    +0416 end
    +0417 
    +0418 %Non-parseable composition
    +0419 if isfield(model,'metFormulas')
    +0420     [~, ~, exitFlag]=parseFormulas(model.metFormulas,true,false);
    +0421     EM='The composition for the following metabolites could not be parsed:';
    +0422     reportIssue('warning',EM,model.mets(exitFlag==-1));
    +0423 end
    +0424 
    +0425 %Check if there are metabolites with different names but the same MIRIAM
    +0426 %codes
    +0427 if isfield(model,'metMiriams')
    +0428     miriams=containers.Map();
    +0429     for i=1:numel(model.mets)
    +0430         if ~isempty(model.metMiriams{i})
    +0431             %Loop through and add for each miriam
    +0432             for j=1:numel(model.metMiriams{i}.name)
    +0433                 %Get existing metabolite indexes
    +0434                 current=strcat(model.metMiriams{i}.name{j},'/',model.metMiriams{i}.value{j});
    +0435                 if isKey(miriams,current)
    +0436                     existing=miriams(current);
    +0437                 else
    +0438                     existing=[];
    +0439                 end
    +0440                 miriams(current)=[existing;i];
    +0441             end
    +0442         end
    +0443     end
    +0444 
    +0445     %Get all keys
    +0446     allMiriams=keys(miriams);
    +0447 
    +0448     hasMultiple=false(numel(allMiriams),1);
    +0449     for i=1:numel(allMiriams)
    +0450         if numel(miriams(allMiriams{i}))>1
    +0451             %Check if they all have the same name
    +0452             if numel(unique(model.metNames(miriams(allMiriams{i}))))>1
    +0453                 % regexp returns [] (not a false-like 0) when there is no
    +0454                 % match, and ~[] is [] too, so the negation itself is
    +0455                 % never true: use isempty to actually test for "no match"
    +0456                 if isempty(regexp(allMiriams{i},'^sbo\/SBO:','once')) % SBO terms are expected to be multiple
    +0457                     hasMultiple(i)=true;
    +0458                 end
    +0459             end
    +0460         end
    +0461     end
    +0462 
    +0463     %Print output
    +0464     EM='The following MIRIAM strings are associated to more than one unique metabolite name:';
    +0465     reportIssue('warning',EM,allMiriams(hasMultiple));
    +0466 end
    +0467 
    +0468 %Check if there are metabolites with different names but the same InChI
    +0469 %codes
    +0470 if isfield(model,'inchis')
    +0471     inchis=containers.Map();
    +0472     for i=1:numel(model.mets)
    +0473         if ~isempty(model.inchis{i})
    +0474             %Get existing metabolite indexes
    +0475             if isKey(inchis,model.inchis{i})
    +0476                 existing=inchis(model.inchis{i});
    +0477             else
    +0478                 existing=[];
    +0479             end
    +0480             inchis(model.inchis{i})=[existing;i];
    +0481         end
    +0482     end
    +0483 
    +0484     %Get all keys
    +0485     allInchis=keys(inchis);
    +0486 
    +0487     hasMultiple=false(numel(allInchis),1);
    +0488     for i=1:numel(allInchis)
    +0489         if numel(inchis(allInchis{i}))>1
    +0490             %Check if they all have the same name
    +0491             if numel(unique(model.metNames(inchis(allInchis{i}))))>1
    +0492                 hasMultiple(i)=true;
    +0493             end
    +0494         end
    +0495     end
    +0496 
    +0497     %Print output
    +0498     EM='The following InChI strings are associated to more than one unique metabolite name:';
    +0499     reportIssue('warning',EM,allInchis(hasMultiple));
    +0500 end
    +0501 
    +0502 % %Check if there are metabolites with different names but the same SMILES
    +0503 % if isfield(model,"metSmiles")
    +0504 %     metSmiles=containers.Map();
    +0505 %     for i=1:numel(model.mets)
    +0506 %         if ~isempty(model.metSmiles{i})
    +0507 %             %Get existing metabolite indexes
    +0508 %             if isKey(metSmiles,model.metSmiles{i})
    +0509 %                 existing=metSmiles(model.metSmiles{i});
    +0510 %             else
    +0511 %                 existing=[];
    +0512 %             end
    +0513 %             metSmiles(model.metSmiles{i})=[existing;i];
    +0514 %         end
    +0515 %     end
    +0516 %
    +0517 %     %Get all keys
    +0518 %     allmetSmiles=keys(metSmiles);
    +0519 %
    +0520 %     hasMultiple=false(numel(metSmiles),1);
    +0521 %     for i=1:numel(metSmiles)
    +0522 %         if numel(metSmiles(metSmiles{i}))>1
    +0523 %             %Check if they all have the same name
    +0524 %             if numel(unique(model.metNames(metSmiles(allmetSmiles{i}))))>1
    +0525 %                 hasMultiple(i)=true;
    +0526 %             end
    +0527 %         end
    +0528 %     end
    +0529 %
    +0530 %     %Print output
    +0531 %     EM="The following metSmiles strings are associated to more than one unique metabolite name:";
    +0532 %     reportIssue("warning",EM,allmetSmiles(hasMultiple));
    +0533 % end
    +0534 end
    +0535 
    +0536 function I=duplicates(strings)
    +0537 I=false(numel(strings),1);
    +0538 [J, K]=unique(strings);
    +0539 if numel(J)~=numel(strings)
    +0540     L=1:numel(strings);
    +0541     L(K)=[];
    +0542     I(L)=true;
    +0543 end
    +0544 end
    +0545 
    +0546 function cat=issueCategory(msg)
    +0547 if contains(msg,'missing')
    +0548     cat='missing_field';
    +0549 elseif contains(msg,'must be') || contains(msg,'should')
    +0550     cat='wrong_type';
    +0551 elseif contains(msg,'empty')
    +0552     cat='empty_id';
    +0553 elseif contains(msg,'duplicate') || contains(msg,'already exist in the same')
    +0554     cat='duplicate';
    +0555 elseif contains(msg,'bound')
    +0556     cat='invalid_bounds';
    +0557 elseif contains(msg,'SBML') || contains(msg,'identifier') || contains(msg,'do not start with')
    +0558     cat='invalid_id';
    +0559 elseif contains(msg,'never used') || contains(msg,'not associated') || contains(msg,'contain no metabolites') || contains(msg,'are empty (no')
    +0560     cat='unused';
    +0561 elseif contains(msg,'objective')
    +0562     cat='objective';
    +0563 elseif contains(msg,'grRule') || contains(msg,'start or end with') || contains(msg,'"genes"')
    +0564     cat='gpr';
    +0565 elseif contains(msg,'could not be parsed') || contains(msg,'composition')
    +0566     cat='invalid_formula';
    +0567 elseif contains(msg,'MIRIAM') || contains(msg,'InChI') || contains(msg,'SMILES')
    +0568     cat='cross_reference';
    +0569 else
    +0570     cat='other';
    +0571 end
    +0572 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/queries/constructEquations.html b/docs/html/queries/constructEquations.html similarity index 56% rename from doc/queries/constructEquations.html rename to docs/html/queries/constructEquations.html index 1e0994eb..ebf8a9d4 100644 --- a/doc/queries/constructEquations.html +++ b/docs/html/queries/constructEquations.html @@ -46,21 +46,23 @@

    DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    @@ -80,7 +82,7 @@

    CROSS-REFERENCE INFORMATION ^
 <li><a href=buildEquation buildEquation Construct single equation string for a given reaction.
  • getIndexes getIndexes Retrieve the indexes for a list of reactions or metabolites.
  • This function is called by:
      -
    • printFluxes printFluxes Print reactions and fluxes to the screen or to a file.
    • printModelStats printModelStats Print some statistics about a model to the screen.
    +
  • printFluxes printFluxes Print reactions and fluxes to the screen or to a file.
  • printModelStats printModelStats Print statistics about a model to the screen.
  • @@ -106,89 +108,91 @@

    SOURCE CODE ^% sort reversible reactions so that the metabolite that is first in the 0019 % lexicographic order is a reactant (default false). 0020 % sortMetNames : logical -0021 % sort the metabolite names in the equation. Uses compartment even if -0022 % useComps is false (default false). +0021 % sort the metabolite names in the equation. The compartment is included +0022 % in the sorting even if useComps is false (default false). 0023 % useMetID : logical -0024 % use metabolite ID in generated equations (default false). -0025 % useFormula : logical -0026 % use metabolite formula in generated equations (default false). -0027 % useRevField : logical -0028 % use the model.rev field to indicate reaction reversibility, -0029 % alternatively this is determined from the model.ub and model.lb fields -0030 % (default true). -0031 % -0032 % Returns -0033 % ------- -0034 % equationStrings : cell -0035 % a cell array with equations. -0036 % -0037 % Examples -0038 % -------- -0039 % equationStrings = constructEquations(model, rxns, useComps, ... -0040 % sortRevRxns, sortMetNames, useMetID, useFormula, useRevField); -0041 % -0042 % Notes -0043 % ----- -0044 % If useRevField is false, then reactions should be organized in their -0045 % forward direction (e.g. ub = 1000 and lb = -1000/0) for the reversibility -0046 % to be correctly determined. -0047 -0048 p=parseRAVENargs(varargin, {'rxns',[]; 'useComps',true; 'sortRevRxns',false; 'sortMetNames',false; 'useMetID',false; 'useFormula',false; 'useRevField',true}); -0049 rxns=p.rxns; -0050 if isempty(rxns) -0051 rxns=model.rxns; -0052 elseif ~islogical(rxns) && ~isnumeric(rxns) -0053 rxns=convertCharArray(rxns); -0054 end -0055 useComps=p.useComps; -0056 sortRevRxns=p.sortRevRxns; -0057 sortMetNames=p.sortMetNames; -0058 useMetID=p.useMetID; -0059 useFormula=p.useFormula; -0060 useRevField=p.useRevField; -0061 -0062 %Sort reversible equations -0063 if sortRevRxns==true -0064 model=sortModel(model); -0065 end -0066 -0067 %Sort metabolite names, including compartment -0068 if sortMetNames==true -0069 model=sortModel(model,false,true); -0070 end -0071 -0072 Rindexes=getIndexes(model,rxns,'rxns'); +0024 % use metabolite ID in generated equations. Cannot be true together with +0025 % useFormula (default false). +0026 % useFormula : logical +0027 % use metabolite formula in generated equations. Cannot be true together +0028 % with useMetID (default false). +0029 % useRevField : logical +0030 % use the model.rev field to indicate reaction reversibility. When +0031 % false, a reaction is reversible if model.lb < 0 and model.ub > 0 +0032 % (default true). +0033 % +0034 % Returns +0035 % ------- +0036 % equationStrings : cell +0037 % a cell array with the equation of each reaction. +0038 % +0039 % Examples +0040 % -------- +0041 % equationStrings = constructEquations(model, rxns, useComps, ... +0042 % sortRevRxns, sortMetNames, useMetID, useFormula, useRevField); +0043 % +0044 % Notes +0045 % ----- +0046 % If useRevField is false, the reactions have to be written in their forward +0047 % direction (for example ub = 1000 and lb = -1000 or 0) for the reversibility +0048 % to be determined correctly. +0049 +0050 p=parseRAVENargs(varargin, {'rxns',[]; 'useComps',true; 'sortRevRxns',false; 'sortMetNames',false; 'useMetID',false; 'useFormula',false; 'useRevField',true}); +0051 rxns=p.rxns; +0052 if isempty(rxns) +0053 rxns=model.rxns; +0054 elseif ~islogical(rxns) && ~isnumeric(rxns) +0055 rxns=convertCharArray(rxns); +0056 end +0057 useComps=p.useComps; +0058 sortRevRxns=p.sortRevRxns; +0059 sortMetNames=p.sortMetNames; +0060 useMetID=p.useMetID; +0061 useFormula=p.useFormula; +0062 useRevField=p.useRevField; +0063 +0064 %Sort reversible equations +0065 if sortRevRxns==true +0066 model=sortModel(model,true); +0067 end +0068 +0069 %Sort metabolite names, including compartment +0070 if sortMetNames==true +0071 model=sortModel(model,false,true); +0072 end 0073 -0074 equationStrings=cell(numel(Rindexes),1); +0074 Rindexes=getIndexes(model,rxns,'rxns'); 0075 -0076 for i=1:numel(Rindexes) -0077 Mindexes=find(model.S(:,Rindexes(i))); -0078 %Define metabolites by id, formula or name, and with or without compartment: -0079 if useMetID==true && useFormula==false -0080 mets = model.mets(Mindexes); -0081 elseif useMetID==false && useFormula==true -0082 mets = strcat('[',model.metFormulas(Mindexes),']'); -0083 elseif useMetID==true && useFormula==true -0084 error('Arguments useMetID and useFormula cannot be both TRUE!'); -0085 else -0086 mets = model.metNames(Mindexes); -0087 end -0088 if useComps==true -0089 comps = model.comps(model.metComps(Mindexes)); -0090 mets = strcat(mets,'[',comps,']'); -0091 end -0092 %Define stoich coeffs and reversibility: -0093 stoichCoeffs = model.S(Mindexes,Rindexes(i)); -0094 if useRevField == true -0095 isrev = model.rev(Rindexes(i))==1; -0096 else -0097 isrev = model.lb(Rindexes(i))<0 & model.ub(Rindexes(i))>0; -0098 end -0099 -0100 %Construct equation: -0101 equationStrings{i} = buildEquation(mets,stoichCoeffs,isrev); -0102 end -0103 end

    +0076 equationStrings=cell(numel(Rindexes),1); +0077 +0078 for i=1:numel(Rindexes) +0079 Mindexes=find(model.S(:,Rindexes(i))); +0080 %Define metabolites by id, formula or name, and with or without compartment: +0081 if useMetID==true && useFormula==false +0082 mets = model.mets(Mindexes); +0083 elseif useMetID==false && useFormula==true +0084 mets = strcat('[',model.metFormulas(Mindexes),']'); +0085 elseif useMetID==true && useFormula==true +0086 error('Arguments useMetID and useFormula cannot be both TRUE!'); +0087 else +0088 mets = model.metNames(Mindexes); +0089 end +0090 if useComps==true +0091 comps = model.comps(model.metComps(Mindexes)); +0092 mets = strcat(mets,'[',comps,']'); +0093 end +0094 %Define stoich coeffs and reversibility: +0095 stoichCoeffs = model.S(Mindexes,Rindexes(i)); +0096 if useRevField == true +0097 isrev = model.rev(Rindexes(i))==1; +0098 else +0099 isrev = model.lb(Rindexes(i))<0 & model.ub(Rindexes(i))>0; +0100 end +0101 +0102 %Construct equation: +0103 equationStrings{i} = buildEquation(mets,stoichCoeffs,isrev); +0104 end +0105 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/queries/constructS.html b/docs/html/queries/constructS.html new file mode 100644 index 00000000..9b3a8e0f --- /dev/null +++ b/docs/html/queries/constructS.html @@ -0,0 +1,297 @@ + + + + Description of constructS + + + + + + + + + +
    Home > queries > constructS.m
    + + + +

    constructS +

    + +

    PURPOSE ^

    +
    constructS Construct a stoichiometric matrix from a cell array of equations.
    + +

    SYNOPSIS ^

    +
    function [S, mets, badRxns, reversible]=constructS(equations,varargin)
    + +

    DESCRIPTION ^

    +
     constructS  Construct a stoichiometric matrix from a cell array of equations.
    +
    + Parameters
    + ----------
    + equations : cell
    +     cell array of equations in the form "A + 2 B <=> 3 C", where <=>
    +     indicates reversible and => irreversible reactions.
    +
    + Name-Value Arguments
    + --------------------
    + mets : cell
    +     cell array of metabolites. All metabolites in the equations must be
    +     present in the list, otherwise an error is thrown (default generated
    +     from the equations).
    + rxns : cell
    +     cell array of reaction ids. Used to print reaction ids in error
    +     messages in place of equations (default []).
    +
    + Returns
    + -------
    + S : double
    +     the resulting stoichiometric matrix.
    + mets : cell
    +     cell array with the metabolites in the order of the rows of the S
    +     matrix.
    + badRxns : logical
    +     logical vector with true for the reactions in which one or more
    +     metabolites occur more than once, for example as both substrate and
    +     product. An example is the phosphotransferase reaction
    +     ATP + ADP <=> ADP + ATP, which gives an empty column in the
    +     stoichiometric matrix.
    + reversible : logical
    +     logical vector with true if the equation is reversible.
    +
    + Examples
    + --------
    +     [S, mets, badRxns, reversible] = constructS(equations, mets);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • parseRxnEqu parseRxnEqu Get all metabolite names from a cell array of equations.
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [S, mets, badRxns, reversible]=constructS(equations,varargin)
    +0002 % constructS  Construct a stoichiometric matrix from a cell array of equations.
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % equations : cell
    +0007 %     cell array of equations in the form "A + 2 B <=> 3 C", where <=>
    +0008 %     indicates reversible and => irreversible reactions.
    +0009 %
    +0010 % Name-Value Arguments
    +0011 % --------------------
    +0012 % mets : cell
    +0013 %     cell array of metabolites. All metabolites in the equations must be
    +0014 %     present in the list, otherwise an error is thrown (default generated
    +0015 %     from the equations).
    +0016 % rxns : cell
    +0017 %     cell array of reaction ids. Used to print reaction ids in error
    +0018 %     messages in place of equations (default []).
    +0019 %
    +0020 % Returns
    +0021 % -------
    +0022 % S : double
    +0023 %     the resulting stoichiometric matrix.
    +0024 % mets : cell
    +0025 %     cell array with the metabolites in the order of the rows of the S
    +0026 %     matrix.
    +0027 % badRxns : logical
    +0028 %     logical vector with true for the reactions in which one or more
    +0029 %     metabolites occur more than once, for example as both substrate and
    +0030 %     product. An example is the phosphotransferase reaction
    +0031 %     ATP + ADP <=> ADP + ATP, which gives an empty column in the
    +0032 %     stoichiometric matrix.
    +0033 % reversible : logical
    +0034 %     logical vector with true if the equation is reversible.
    +0035 %
    +0036 % Examples
    +0037 % --------
    +0038 %     [S, mets, badRxns, reversible] = constructS(equations, mets);
    +0039 
    +0040 equations=convertCharArray(equations);
    +0041 p=parseRAVENargs(varargin, {'mets',[]; 'rxns',[]});
    +0042 mets=p.mets;
    +0043 rxns=p.rxns;
    +0044 if ~isempty(mets)
    +0045     mets=convertCharArray(mets);
    +0046 end
    +0047 if ~isempty(rxns)
    +0048     rxns=convertCharArray(rxns);
    +0049 end
    +0050 
    +0051 badRxns=false(numel(equations),1);
    +0052 
    +0053 %Check that no equations are too short to have reversibility data
    +0054 I=cellfun(@numel,equations);
    +0055 I=find(I<4,1);
    +0056 if any(I)
    +0057     if isempty(rxns)
    +0058         EM=['The following equation does not have reversibility data: ' equations{I} ];
    +0059         error('RAVEN:badInput', '%s', EM);
    +0060     else
    +0061         EM=['The reaction ' rxns{I} ' does not have reversibility data'];
    +0062         error('RAVEN:badInput', '%s', EM);
    +0063     end
    +0064 end
    +0065 
    +0066 %Makes life a little easier
    +0067 equations=strtrim(equations);
    +0068 equations=fixEquations(equations);
    +0069 
    +0070 %A supplied list is authoritative, so an entry in it can be matched in full
    +0071 %before the leading number of "2 oxoglutarate" is read as a coefficient. A
    +0072 %list derived from the equations cannot disambiguate that.
    +0073 metsSupplied=~isempty(mets);
    +0074 if isempty(mets)
    +0075     mets=parseRxnEqu(equations);
    +0076 end
    +0077 
    +0078 %Get which reactions are reversible
    +0079 reversible=cellfun(@any,strfind(equations,' <=> '));
    +0080 
    +0081 %Make them all reversible. This is not all that neat, but nevermind
    +0082 equations=strrep(equations,' => ',' <=> ');
    +0083 
    +0084 %Replace the the plus signs with some weird character that will be used for
    +0085 %parsing
    +0086 equations=strrep(equations,' + ', '€');
    +0087 
    +0088 %Keep track of coefficients to be added to S-matrix
    +0089 metsToS = cell(100000,1);
    +0090 rxnsToS = zeros(100000,1);
    +0091 coefToS = zeros(100000,1);
    +0092 newEntry = 1;
    +0093 %Loop through the equations and add the info to the S matrix
    +0094 for i=1:numel(equations)
    +0095     %Start by finding the position of the (=> or <=>)
    +0096     arrowIndex=strfind(equations{i},' <=> ');
    +0097     
    +0098     if numel(arrowIndex)~=1
    +0099         if isempty(rxns)
    +0100             EM=['The following equation does not have reversibility data: ' equations{i} ];
    +0101             error('RAVEN:badInput', '%s', EM);
    +0102         else
    +0103             EM=['The reaction ' rxns{i} ' does not have reversibility data'];
    +0104             error('RAVEN:badInput', '%s', EM);
    +0105         end
    +0106     end
    +0107     
    +0108     reactants=regexp(equations{i}(1:arrowIndex-1),'€','split');
    +0109     products=regexp(equations{i}(arrowIndex+5:end),'€','split');
    +0110     
    +0111     %If the splitting character is at the end (if exchange rxns), then an
    +0112     %empty string will exist together with the real ones. Remove it
    +0113     reactants(cellfun(@isempty,reactants))=[];
    +0114     products(cellfun(@isempty,products))=[];
    +0115     
    +0116     %A vector where an element is -1 is the corresponding metabolite is a
    +0117     %reactant and 1 if it is a product
    +0118     multiplyWith=[ones(numel(reactants),1)*-1; ones(numel(products),1)];
    +0119     
    +0120     metabolites=strtrim([reactants products]);
    +0121     
    +0122     %Now loop through the reactants and see if the metabolite has a
    +0123     %coefficient (it will look as "number name")
    +0124     for j=1:numel(metabolites)
    +0125         space=strfind(metabolites{j},' ');
    +0126         
    +0127         if isempty(space)
    +0128             %No coefficient
    +0129             coeff=1;
    +0130             name=metabolites{j};
    +0131         elseif metsSupplied && any(strcmp(strtrim(metabolites{j}),mets))
    +0132             %The whole entry is a known metabolite, so its leading number is
    +0133             %part of its name ("2 oxoglutarate") rather than a coefficient
    +0134             coeff=1;
    +0135             name=strtrim(metabolites{j});
    +0136         else
    +0137             coeff=str2double(metabolites{j}(1:space(1)));
    +0138             
    +0139             %If it was not a coefficiant
    +0140             if isnan(coeff)
    +0141                 coeff=1;
    +0142                 name=strtrim(metabolites{j});
    +0143             else
    +0144                 name=strtrim(metabolites{j}(space(1)+1:end));
    +0145             end
    +0146         end
    +0147         
    +0148         %Find the name in the mets list [a b]=ismember(name,mets);
    +0149         metsToS{newEntry}=name;
    +0150         rxnsToS(newEntry)=i;
    +0151         coefToS(newEntry)=coeff*multiplyWith(j);
    +0152         newEntry=newEntry+1;
    +0153     end
    +0154 end
    +0155 %Remove unused fields
    +0156 metsToS(newEntry:end)=[];
    +0157 rxnsToS(newEntry:end)=[];
    +0158 coefToS(newEntry:end)=[];
    +0159 
    +0160 %Match to mets array
    +0161 [metsPresent,metsLoc]=ismember(metsToS,mets);
    +0162 
    +0163 %Find badRxns
    +0164 [~,I]=unique([rxnsToS,metsLoc],'rows','stable');
    +0165 x=1:length(rxnsToS);
    +0166 x(I)=[];
    +0167 x=unique(rxnsToS(x));
    +0168 badRxns(x)=true;
    +0169 
    +0170 if any(~metsPresent)
    +0171     if isempty(rxns)
    +0172         EM=['Could not find the following metabolites in the metabolite list: ',...
    +0173             strjoin(unique(metsToS(~metsPresent)),', ')];
    +0174         error('RAVEN:badInput','%s',EM)
    +0175     else
    +0176         %Escaped: metabolite/reaction names are arbitrary model text and
    +0177         %may contain "%", which the sprintf below (needed to interpret
    +0178         %the template's own "\n") would otherwise misread as a format
    +0179         %directive and truncate the rest of the message.
    +0180         esc=@(c) strrep(c,'%','%%');
    +0181         missingMet = find(~metsPresent);
    +0182         missingMet = strcat(esc(metsToS(missingMet)),' (reaction:',esc(rxns(rxnsToS(missingMet))),')\n');
    +0183         missingMet = strjoin(missingMet,'');
    +0184         EM=sprintf(['Could not find the following metabolites (reaction indicated) in the metabolite list: \n' ...
    +0185             missingMet]);
    +0186         error('RAVEN:badInput','%s',EM)
    +0187     end
    +0188 end
    +0189 %sparse() adds up repeated (metabolite,reaction) entries, which is what a
    +0190 %metabolite occurring more than once in one equation means: "2 H2O + H2O =>"
    +0191 %is a coefficient of 3, and a metabolite on both sides cancels to an empty
    +0192 %column, as badRxns above reports. Assigning into a preallocated matrix
    +0193 %instead kept only the last occurrence, and materialised the whole matrix
    +0194 %densely on the way.
    +0195 S=sparse(metsLoc,rxnsToS,coefToS,numel(mets),numel(equations));
    +0196 end
    +0197 
    +0198 function equ=fixEquations(equ)
    +0199 %If the equation starts with "=>" or "<=>" then add a space again. This is
    +0200 %an alternative way to represent uptake reactions. The opposite way for
    +0201 %producing reactions
    +0202 equ=equ(:);
    +0203 for i=1:numel(equ)
    +0204     if strcmp(equ{i}(1:2),'=>') || strcmp(equ{i}(1:3),'<=>')
    +0205         equ{i}=[' ' equ{i}];
    +0206     else
    +0207         if strcmp(equ{i}(end-1:end),'=>') || strcmp(equ{i}(end-2:end),'<=>')
    +0208             equ{i}=[equ{i} ' '];
    +0209         end
    +0210     end
    +0211 end
    +0212 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/queries/getAllRxnsFromGenes.html b/docs/html/queries/getAllRxnsFromGenes.html similarity index 61% rename from doc/queries/getAllRxnsFromGenes.html rename to docs/html/queries/getAllRxnsFromGenes.html index 28bc1c72..e7bc5f44 100644 --- a/doc/queries/getAllRxnsFromGenes.html +++ b/docs/html/queries/getAllRxnsFromGenes.html @@ -30,7 +30,8 @@

    DESCRIPTION ^
     getAllRxnsFromGenes  Find all reactions annotated by the genes of a set.
     
      Given a list of reactions, this function finds the associated genes in
    - the model and returns all reactions that are annotated by these genes.
    + the model and returns all reactions that are annotated by these genes,
    + including the given reactions.
     
      Parameters
      ----------
    @@ -43,9 +44,9 @@ 

    DESCRIPTION ^SOURCE CODE ^% getAllRxnsFromGenes Find all reactions annotated by the genes of a set. 0003 % 0004 % Given a list of reactions, this function finds the associated genes in -0005 % the model and returns all reactions that are annotated by these genes. -0006 % -0007 % Parameters -0008 % ---------- -0009 % model : struct -0010 % a model structure. -0011 % rxns : cell or logical or double -0012 % either a cell array of IDs, a logical vector with the same number of -0013 % elements as reactions in the model, or a vector of indexes. -0014 % -0015 % Returns -0016 % ------- -0017 % allRxns : cell or logical or double -0018 % either a cell array of IDs, a logical vector with the same number of -0019 % elements as reactions in the model, or a vector of indexes, -0020 % dependent on the format of rxns. -0021 % -0022 % Examples -0023 % -------- -0024 % allRxns = getAllRxnsFromGenes(model, rxns); -0025 -0026 if ~islogical(rxns) && ~isnumeric(rxns) -0027 rxns=convertCharArray(rxns); -0028 end -0029 -0030 rxnIdx=getIndexes(model,rxns,'rxns'); -0031 [~, geneIdx]=find(model.rxnGeneMat(rxnIdx,:)); -0032 [allRxns, ~]=find(model.rxnGeneMat(:,geneIdx)); -0033 if iscell(rxns) -0034 allRxns=unique([model.rxns(allRxns);model.rxns(rxnIdx)]); -0035 elseif isnumeric(rxns) || islogical(rxns) -0036 allRxns=unique([allRxns;rxnIdx]); -0037 if islogical(rxns) -0038 temp=false(numel(model.rxns),1); -0039 temp(allRxns)=true; -0040 allRxns=temp; -0041 end -0042 end -0043 end

    +0005 % the model and returns all reactions that are annotated by these genes, +0006 % including the given reactions. +0007 % +0008 % Parameters +0009 % ---------- +0010 % model : struct +0011 % a model structure. +0012 % rxns : cell or logical or double +0013 % either a cell array of IDs, a logical vector with the same number of +0014 % elements as reactions in the model, or a vector of indexes. +0015 % +0016 % Returns +0017 % ------- +0018 % allRxns : cell or logical or double +0019 % the reactions in the same format as rxns: a cell array of IDs (unique +0020 % and sorted), a logical vector with the same number of elements as +0021 % reactions in the model, or a vector of indexes (unique and sorted). +0022 % +0023 % Examples +0024 % -------- +0025 % allRxns = getAllRxnsFromGenes(model, rxns); +0026 +0027 if ~islogical(rxns) && ~isnumeric(rxns) +0028 rxns=convertCharArray(rxns); +0029 end +0030 +0031 rxnIdx=getIndexes(model,rxns,'rxns'); +0032 [~, geneIdx]=find(model.rxnGeneMat(rxnIdx,:)); +0033 [allRxns, ~]=find(model.rxnGeneMat(:,geneIdx)); +0034 if iscell(rxns) +0035 allRxns=unique([model.rxns(allRxns);model.rxns(rxnIdx)]); +0036 elseif isnumeric(rxns) || islogical(rxns) +0037 allRxns=unique([allRxns;rxnIdx]); +0038 if islogical(rxns) +0039 temp=false(numel(model.rxns),1); +0040 temp(allRxns)=true; +0041 allRxns=temp; +0042 end +0043 end +0044 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/queries/getElementalBalance.html b/docs/html/queries/getElementalBalance.html new file mode 100644 index 00000000..e71bc47c --- /dev/null +++ b/docs/html/queries/getElementalBalance.html @@ -0,0 +1,289 @@ + + + + Description of getElementalBalance + + + + + + + + + +
    Home > queries > getElementalBalance.m
    + + + +

    getElementalBalance +

    + +

    PURPOSE ^

    +
    getElementalBalance Check whether the reactions of a model are balanced.
    + +

    SYNOPSIS ^

    +
    function balanceStructure=getElementalBalance(model,varargin)
    + +

    DESCRIPTION ^

    +
     getElementalBalance  Check whether the reactions of a model are balanced.
    +
    + Checks whether the reactions are elementally balanced, using the
    + metFormulas field or, when that is absent, the inchis field. An error is
    + thrown if the model has neither field. The charge balance is reported
    + separately.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + rxns : cell or logical or double
    +     either a cell array of reaction IDs, a logical vector with the same
    +     number of elements as reactions in the model, or a vector of
    +     indexes. Only these reactions are checked (default model.rxns).
    + printUnbalanced : logical
    +     print warnings about the reactions that are unbalanced
    +     (default false).
    + printUnparsable : logical
    +     print warnings about the reactions that cannot be balanced because of
    +     missing information or parsing errors (default false).
    +
    + Returns
    + -------
    + balanceStructure : struct
    +     elemental balance structure with fields:
    +
    +     - balanceStatus : 1 if the reaction is balanced, 0 if it is
    +       unbalanced, -1 if it could not be balanced due to missing
    +       information (including a reaction without metabolites), -2 if it
    +       could not be balanced because a formula could not be parsed.
    +       Elemental only; charge is reported in chargeStatus
    +     - chargeStatus : 1 if the reaction is charge balanced, 0 if it is
    +       not, -1 if any participating metabolite has no charge, the reaction
    +       has no metabolites, or the model has no metCharges field; the
    +       charge balance is then unknown
    +     - chargeResidual : the sum of charges over the reaction, NaN where
    +       chargeStatus is -1
    +     - elements : struct with fields abbrevs (cell array with
    +       abbreviations for all used elements) and names (cell array with
    +       the names for all used elements)
    +     - leftComp : MxN matrix with the sum of coefficients for each of the
    +       elements (N) for the left side of the reactions (M)
    +     - rightComp : the corresponding matrix for the right side
    +
    + Examples
    + --------
    +     balanceStructure = getElementalBalance(model, rxns, printUnbalanced, printUnparsable);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • getIndexes getIndexes Retrieve the indexes for a list of reactions or metabolites.
    • parseFormulas parseFormulas Get the elemental composition from formulas.
    +This function is called by: +
      +
    • printFluxes printFluxes Print reactions and fluxes to the screen or to a file.
    • printModelStats printModelStats Print statistics about a model to the screen.
    + + + + +

    SOURCE CODE ^

    +
    0001 function balanceStructure=getElementalBalance(model,varargin)
    +0002 % getElementalBalance  Check whether the reactions of a model are balanced.
    +0003 %
    +0004 % Checks whether the reactions are elementally balanced, using the
    +0005 % metFormulas field or, when that is absent, the inchis field. An error is
    +0006 % thrown if the model has neither field. The charge balance is reported
    +0007 % separately.
    +0008 %
    +0009 % Parameters
    +0010 % ----------
    +0011 % model : struct
    +0012 %     a model structure.
    +0013 %
    +0014 % Name-Value Arguments
    +0015 % --------------------
    +0016 % rxns : cell or logical or double
    +0017 %     either a cell array of reaction IDs, a logical vector with the same
    +0018 %     number of elements as reactions in the model, or a vector of
    +0019 %     indexes. Only these reactions are checked (default model.rxns).
    +0020 % printUnbalanced : logical
    +0021 %     print warnings about the reactions that are unbalanced
    +0022 %     (default false).
    +0023 % printUnparsable : logical
    +0024 %     print warnings about the reactions that cannot be balanced because of
    +0025 %     missing information or parsing errors (default false).
    +0026 %
    +0027 % Returns
    +0028 % -------
    +0029 % balanceStructure : struct
    +0030 %     elemental balance structure with fields:
    +0031 %
    +0032 %     - balanceStatus : 1 if the reaction is balanced, 0 if it is
    +0033 %       unbalanced, -1 if it could not be balanced due to missing
    +0034 %       information (including a reaction without metabolites), -2 if it
    +0035 %       could not be balanced because a formula could not be parsed.
    +0036 %       Elemental only; charge is reported in chargeStatus
    +0037 %     - chargeStatus : 1 if the reaction is charge balanced, 0 if it is
    +0038 %       not, -1 if any participating metabolite has no charge, the reaction
    +0039 %       has no metabolites, or the model has no metCharges field; the
    +0040 %       charge balance is then unknown
    +0041 %     - chargeResidual : the sum of charges over the reaction, NaN where
    +0042 %       chargeStatus is -1
    +0043 %     - elements : struct with fields abbrevs (cell array with
    +0044 %       abbreviations for all used elements) and names (cell array with
    +0045 %       the names for all used elements)
    +0046 %     - leftComp : MxN matrix with the sum of coefficients for each of the
    +0047 %       elements (N) for the left side of the reactions (M)
    +0048 %     - rightComp : the corresponding matrix for the right side
    +0049 %
    +0050 % Examples
    +0051 % --------
    +0052 %     balanceStructure = getElementalBalance(model, rxns, printUnbalanced, printUnparsable);
    +0053 
    +0054 p=parseRAVENargs(varargin, {'rxns',[]; 'printUnbalanced',false; 'printUnparsable',false});
    +0055 rxns=p.rxns;
    +0056 if ~isempty(rxns) && ~islogical(rxns) && ~isnumeric(rxns)
    +0057     rxns=convertCharArray(rxns);
    +0058 end
    +0059 printUnbalanced=p.printUnbalanced;
    +0060 printUnparsable=p.printUnparsable;
    +0061 
    +0062 if ~isempty(rxns)
    +0063     indexes=~getIndexes(model,rxns,'rxns',true);
    +0064     model=removeReactions(model,indexes,true);
    +0065 end
    +0066 
    +0067 balanceStructure.balanceStatus=nan(numel(model.rxns),1);
    +0068 
    +0069 %Get the formulas
    +0070 if isfield(model,'metFormulas')
    +0071     [balanceStructure.elements, useMat, exitFlag]=parseFormulas(model.metFormulas, true);
    +0072 else
    +0073     if isfield(model,'inchis')
    +0074         [balanceStructure.elements, useMat, exitFlag]=parseFormulas(model.inchis, true,true);
    +0075     else
    +0076         EM='The model must contain either the "metFormulas" or the "inchis" field in order to test for elemental balancing';
    +0077         error('RAVEN:badInput', '%s', EM);
    +0078     end
    +0079 end
    +0080 
    +0081 balanceStructure.leftComp=zeros(numel(model.rxns),numel(balanceStructure.elements.names));
    +0082 balanceStructure.rightComp=balanceStructure.leftComp;
    +0083 
    +0084 %Look at the right and left side of the reactions separately
    +0085 S{1}=model.S;
    +0086 S{2}=model.S;
    +0087 S{1}(S{1}>0)=0; %Left side
    +0088 S{2}(S{2}<0)=0; %Right side
    +0089 S{1}=abs(S{1}); %Both should have positive values
    +0090 
    +0091 %First do it for left side and then for right
    +0092 for i=1:2
    +0093     for j=1:numel(model.rxns)
    +0094         %Get the balance status of the involved mets
    +0095         I=exitFlag(S{i}(:,j)~=0);
    +0096         if any(I==-1)
    +0097             balanceStructure.balanceStatus(j)=-2;
    +0098         end
    +0099         if any(I==0)
    +0100             %Do not change a more serious error to a less serious one
    +0101             balanceStructure.balanceStatus(j)=min(-1,balanceStructure.balanceStatus(j));
    +0102         end
    +0103     end
    +0104 end
    +0105 %Elemental composition per reaction, vectorized as a single matrix
    +0106 %product per side instead of a per-reaction, per-element loop (which is
    +0107 %quadratic in a genome-scale model's reaction and element counts).
    +0108 balanceStructure.leftComp=full(S{1}'*useMat);
    +0109 balanceStructure.rightComp=full(S{2}'*useMat);
    +0110 
    +0111 %Now compare the left and right sides to find which are unbalanced. This is
    +0112 %done even if the reaction as a whole could not be balanced
    +0113 total=abs(balanceStructure.rightComp-balanceStructure.leftComp)>10^-8; %To deal with roundoff error
    +0114 
    +0115 %Get which reactions are unbalanced. Do not change an error to just
    +0116 %unbalanced
    +0117 balanceStructure.balanceStatus(any(total,2))=min(balanceStructure.balanceStatus(any(total,2)),0);
    +0118 
    +0119 %Reactions with no metabolites are not balanced (empty S column skips both loops above)
    +0120 emptyRxns = full(sum(model.S ~= 0, 1).' == 0);
    +0121 balanceStructure.balanceStatus(emptyRxns) = min(-1, balanceStructure.balanceStatus(emptyRxns));
    +0122 
    +0123 %The remaining ones are all balanced
    +0124 balanceStructure.balanceStatus(isnan(balanceStructure.balanceStatus))=1;
    +0125 
    +0126 %Charge balance. This is reported separately from balanceStatus, which
    +0127 %callers such as removeBadRxns and printModelStats read as a purely
    +0128 %elemental verdict.
    +0129 balanceStructure.chargeStatus=zeros(numel(model.rxns),1);
    +0130 balanceStructure.chargeResidual=nan(numel(model.rxns),1);
    +0131 if ~isfield(model,'metCharges')
    +0132     balanceStructure.chargeStatus(:)=-1;
    +0133 else
    +0134     for j=1:numel(model.rxns)
    +0135         %Only the participating metabolites may be touched: S is sparse and
    +0136         %0*NaN is NaN, so a single unset charge anywhere in the model would
    +0137         %otherwise poison every reaction. Summing with 'omitnan' instead
    +0138         %would be worse, silently reporting an unknown residual as 0.
    +0139         idx=find(model.S(:,j));
    +0140         if isempty(idx) || any(isnan(model.metCharges(idx)))
    +0141             balanceStructure.chargeStatus(j)=-1;
    +0142         else
    +0143             balanceStructure.chargeResidual(j)=sum(full(model.S(idx,j)).*model.metCharges(idx));
    +0144             if abs(balanceStructure.chargeResidual(j))>10^-8 %Roundoff error
    +0145                 balanceStructure.chargeStatus(j)=0;
    +0146             else
    +0147                 balanceStructure.chargeStatus(j)=1;
    +0148             end
    +0149         end
    +0150     end
    +0151 end
    +0152 
    +0153 %Print warnings
    +0154 toPrint=[];
    +0155 if printUnbalanced==true
    +0156     toPrint=[toPrint;find(balanceStructure.balanceStatus==0)];
    +0157 end
    +0158 if printUnparsable==true
    +0159     toPrint=[toPrint;find(balanceStructure.balanceStatus<0)];
    +0160 end
    +0161 
    +0162 toPrint=sort(toPrint);
    +0163 for i=1:numel(toPrint)
    +0164     if balanceStructure.balanceStatus(toPrint(i))<0
    +0165         if balanceStructure.balanceStatus(toPrint(i))==-1
    +0166             EM=['The reaction ' model.rxns{toPrint(i)} ' could not be balanced due to missing information'];
    +0167             warning('RAVEN:warning', '%s', EM);
    +0168         else
    +0169             EM=['The reaction ' model.rxns{toPrint(i)} ' could not be balanced due to a parsing error'];
    +0170             warning('RAVEN:warning', '%s', EM);
    +0171         end
    +0172     else
    +0173         %Find the compounds that are not balanced
    +0174         notBalanced=find(total(toPrint(i),:));
    +0175         for j=1:numel(notBalanced)
    +0176             EM=['The reaction ' model.rxns{toPrint(i)} ' is not balanced with respect to ' balanceStructure.elements.names{notBalanced(j)}];
    +0177             warning('RAVEN:warning', '%s', EM);
    +0178         end
    +0179     end
    +0180 end
    +0181 
    +0182 % Re-order the structure entries so they are consistent with the ordering of
    +0183 % the input reaction indexes
    +0184 if ~isempty(rxns)
    +0185     rxns = getIndexes(model,rxns,'rxns');
    +0186     [~,i] = sort(rxns);
    +0187     balanceStructure.balanceStatus(i) = balanceStructure.balanceStatus;
    +0188     balanceStructure.chargeStatus(i) = balanceStructure.chargeStatus;
    +0189     balanceStructure.chargeResidual(i) = balanceStructure.chargeResidual;
    +0190     balanceStructure.leftComp(i,:) = balanceStructure.leftComp;
    +0191     balanceStructure.rightComp(i,:) = balanceStructure.rightComp;
    +0192 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/queries/getExchangeRxns.html b/docs/html/queries/getExchangeRxns.html new file mode 100644 index 00000000..cbcbe4ed --- /dev/null +++ b/docs/html/queries/getExchangeRxns.html @@ -0,0 +1,216 @@ + + + + Description of getExchangeRxns + + + + + + + + + +
    Home > queries > getExchangeRxns.m
    + + + +

    getExchangeRxns +

    + +

    PURPOSE ^

    +
    getExchangeRxns Retrieve the exchange reactions from a model.
    + +

    SYNOPSIS ^

    +
    function [exchangeRxns, exchangeRxnsIndexes, exchangedMets]=getExchangeRxns(model,varargin)
    + +

    DESCRIPTION ^

    +
     getExchangeRxns  Retrieve the exchange reactions from a model.
    +
    + Exchange reactions are identified by having either no substrates or no
    + products. In a model with metabolites marked in the unconstrained field,
    + the exchange reactions are the reactions that involve such a metabolite
    + (as substrate for 'in', as product for 'out').
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + reactionType : char
    +     which exchange reactions should be returned (default 'all'):
    +
    +     - 'all' : all reactions, irrespective of reaction bounds
    +     - 'uptake' : reactions with bounds that imply that only uptake is
    +       allowed. Reaction direction, upper and lower bounds are all
    +       considered
    +     - 'excrete' : reactions with bounds that imply that only excretion is
    +       allowed. Reaction direction, upper and lower bounds are all
    +       considered
    +     - 'reverse' : reactions with non-zero upper and lower bounds that
    +       imply that both uptake and excretion are allowed
    +     - 'blocked' : reactions that have zero upper and lower bounds, not
    +       allowing any flux
    +     - 'in' : reactions where the boundary metabolite is the substrate of
    +       the reaction; a positive flux value would imply uptake, but
    +       reaction bounds are not considered
    +     - 'out' : reactions where the boundary metabolite is the product of
    +       the reaction; a negative flux value would imply uptake, but
    +       reaction bounds are not considered
    +
    + Returns
    + -------
    + exchangeRxns : cell
    +     cell array with the IDs of the exchange reactions.
    + exchangeRxnsIndexes : double
    +     vector with the indexes of the exchange reactions.
    + exchangedMets : double
    +     vector with the index of the first metabolite with a non-zero
    +     coefficient in each exchange reaction (0 if there is none). Only
    +     computed when requested.
    +
    + Notes
    + -----
    + The union of 'in' and 'out' equals 'all'. Also, the union of 'uptake',
    + 'excrete', 'reverse' and 'blocked' equals 'all'.
    +
    + Examples
    + --------
    +     [exchangeRxns, exchangeRxnsIndexes] = getExchangeRxns(model, reactionType);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    • getTransportRxns getTransportRxns Retrieve the transport reactions from a model.
    • modelSummary modelSummary Print an overview of a RAVEN model and its flux state.
    • printFluxes printFluxes Print reactions and fluxes to the screen or to a file.
    + + + + +

    SOURCE CODE ^

    +
    0001 function [exchangeRxns, exchangeRxnsIndexes, exchangedMets]=getExchangeRxns(model,varargin)
    +0002 % getExchangeRxns  Retrieve the exchange reactions from a model.
    +0003 %
    +0004 % Exchange reactions are identified by having either no substrates or no
    +0005 % products. In a model with metabolites marked in the unconstrained field,
    +0006 % the exchange reactions are the reactions that involve such a metabolite
    +0007 % (as substrate for 'in', as product for 'out').
    +0008 %
    +0009 % Parameters
    +0010 % ----------
    +0011 % model : struct
    +0012 %     a model structure.
    +0013 %
    +0014 % Name-Value Arguments
    +0015 % --------------------
    +0016 % reactionType : char
    +0017 %     which exchange reactions should be returned (default 'all'):
    +0018 %
    +0019 %     - 'all' : all reactions, irrespective of reaction bounds
    +0020 %     - 'uptake' : reactions with bounds that imply that only uptake is
    +0021 %       allowed. Reaction direction, upper and lower bounds are all
    +0022 %       considered
    +0023 %     - 'excrete' : reactions with bounds that imply that only excretion is
    +0024 %       allowed. Reaction direction, upper and lower bounds are all
    +0025 %       considered
    +0026 %     - 'reverse' : reactions with non-zero upper and lower bounds that
    +0027 %       imply that both uptake and excretion are allowed
    +0028 %     - 'blocked' : reactions that have zero upper and lower bounds, not
    +0029 %       allowing any flux
    +0030 %     - 'in' : reactions where the boundary metabolite is the substrate of
    +0031 %       the reaction; a positive flux value would imply uptake, but
    +0032 %       reaction bounds are not considered
    +0033 %     - 'out' : reactions where the boundary metabolite is the product of
    +0034 %       the reaction; a negative flux value would imply uptake, but
    +0035 %       reaction bounds are not considered
    +0036 %
    +0037 % Returns
    +0038 % -------
    +0039 % exchangeRxns : cell
    +0040 %     cell array with the IDs of the exchange reactions.
    +0041 % exchangeRxnsIndexes : double
    +0042 %     vector with the indexes of the exchange reactions.
    +0043 % exchangedMets : double
    +0044 %     vector with the index of the first metabolite with a non-zero
    +0045 %     coefficient in each exchange reaction (0 if there is none). Only
    +0046 %     computed when requested.
    +0047 %
    +0048 % Notes
    +0049 % -----
    +0050 % The union of 'in' and 'out' equals 'all'. Also, the union of 'uptake',
    +0051 % 'excrete', 'reverse' and 'blocked' equals 'all'.
    +0052 %
    +0053 % Examples
    +0054 % --------
    +0055 %     [exchangeRxns, exchangeRxnsIndexes] = getExchangeRxns(model, reactionType);
    +0056 
    +0057 p=parseRAVENargs(varargin, {'reactionType','all'});
    +0058 reactionType=char(p.reactionType);
    +0059 
    +0060 % Find exchange reactions. Both branches must produce a column of reaction
    +0061 % indexes: every use below (concatenation, bound lookups, model.rxns
    +0062 % indexing) reads them as indexes, not as a mask over the reactions.
    +0063 if isfield(model, 'unconstrained') && any(model.unconstrained~=0)
    +0064     % With boundary metabolites present, an exchange reaction is one that
    +0065     % involves a boundary metabolite. Producing the boundary metabolite is
    +0066     % what leaves the reaction without a product once the boundary
    +0067     % metabolite is removed, which is the definition the branch below uses.
    +0068     boundaryMets = model.unconstrained~=0;
    +0069     [~, I]=find(model.S(boundaryMets,:)>0);
    +0070     hasNoProd = unique(I(:));
    +0071     [~, I]=find(model.S(boundaryMets,:)<0);
    +0072     hasNoSubs = unique(I(:));
    +0073 else
    +0074     hasNoProd = transpose(find(sum(model.S>0)==0));
    +0075     hasNoSubs = transpose(find(sum(model.S<0)==0));
    +0076 end
    +0077 allExch   = [hasNoProd; hasNoSubs];
    +0078 
    +0079 switch reactionType
    +0080     case {'both','all'} % For legacy reasons, 'both' is also allowed
    +0081         exchangeRxnsIndexes = allExch;
    +0082     case 'in'
    +0083         exchangeRxnsIndexes = hasNoSubs;
    +0084     case 'out'
    +0085         exchangeRxnsIndexes = hasNoProd;
    +0086     case 'blocked'
    +0087         exchangeRxnsIndexes = allExch(model.lb(allExch) == 0 & model.ub(allExch) == 0);
    +0088     case 'reverse'
    +0089         exchangeRxnsIndexes = allExch(model.lb(allExch) < 0 & model.ub(allExch) > 0);
    +0090     case 'uptake'
    +0091         
    +0092         exchangeRxnsIndexes = allExch([(model.lb(hasNoProd) < 0 & model.ub(hasNoProd) <= 0); ...
    +0093                               (model.lb(hasNoSubs) >= 0 & model.ub(hasNoSubs) > 0)]);
    +0094     case 'excrete'
    +0095         exchangeRxnsIndexes = allExch([(model.lb(hasNoProd) >= 0 & model.ub(hasNoProd) > 0); ...
    +0096                               (model.lb(hasNoSubs) < 0 & model.ub(hasNoSubs) <= 0)]);
    +0097     otherwise
    +0098         error('Invalid reactionType specified')
    +0099 end
    +0100 % unique rather than sort: a reaction that involves two boundary metabolites,
    +0101 % and an empty reaction (no substrates and no products), each appear in both
    +0102 % halves of allExch and would otherwise be reported twice.
    +0103 exchangeRxnsIndexes = unique(exchangeRxnsIndexes);
    +0104 exchangeRxns = model.rxns(exchangeRxnsIndexes);
    +0105 
    +0106 if nargout > 2
    +0107     nExch=numel(exchangeRxnsIndexes);
    +0108     exchangedMets=zeros(nExch,1);
    +0109     for ei=1:nExch
    +0110         k=find(model.S(:,exchangeRxnsIndexes(ei)),1);
    +0111         if ~isempty(k)
    +0112             exchangedMets(ei)=k;
    +0113         end
    +0114     end
    +0115 end
    +0116 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/queries/getGenesFromGrRules.html b/docs/html/queries/getGenesFromGrRules.html similarity index 78% rename from doc/queries/getGenesFromGrRules.html rename to docs/html/queries/getGenesFromGrRules.html index 374f2b27..281dedf2 100644 --- a/doc/queries/getGenesFromGrRules.html +++ b/docs/html/queries/getGenesFromGrRules.html @@ -33,25 +33,25 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 </ul>
 This function is called by:
 <ul style= -
  • checkModelStruct checkModelStruct Perform a number of checks to ensure a model structure is ok.
  • +
  • checkModelStruct checkModelStruct Check a model structure for errors and inconsistencies.
  • @@ -77,25 +77,25 @@

    SOURCE CODE ^% ---------- 0006 % grRules : cell 0007 % a cell array of model grRules, from which a list of genes is to be -0008 % extracted. NOTE: Boolean operators can be text ("and", "or") or -0009 % symbolic ("&", "|"), but there must be a space between operators and -0010 % gene names/IDs. -0011 % -0012 % Name-Value Arguments -0013 % -------------------- -0014 % originalGenes : cell -0015 % the original gene list from the model. If supplied, the genes parsed -0016 % from grRules must match it exactly (as a set), an error is thrown -0017 % otherwise, and the returned genes list follows originalGenes' order. -0018 % -0019 % Returns -0020 % ------- -0021 % genes : cell -0022 % a unique list of all gene IDs that appear in grRules. +0008 % extracted. Boolean operators can be text ("and", "or") or symbolic +0009 % ("&", "|"), with a space between operators and gene names or IDs. +0010 % +0011 % Name-Value Arguments +0012 % -------------------- +0013 % originalGenes : cell +0014 % the original gene list from the model. If supplied, the genes parsed +0015 % from grRules must match it as a set, otherwise an error is thrown, and +0016 % the returned genes list follows the order of originalGenes. +0017 % +0018 % Returns +0019 % ------- +0020 % genes : cell +0021 % a unique, sorted list of all gene IDs that appear in grRules (in the +0022 % order of originalGenes when supplied). 0023 % rxnGeneMat : double -0024 % (optional) a binary matrix indicating which genes participate in each -0025 % reaction, where rows correspond to reactions (entries in grRules) and -0026 % columns correspond to genes. +0024 % (optional) a sparse binary matrix indicating which genes participate in +0025 % each reaction, where rows correspond to reactions (entries in grRules) +0026 % and columns correspond to genes. Only computed when requested. 0027 % 0028 % Examples 0029 % -------- diff --git a/docs/html/queries/getIndexes.html b/docs/html/queries/getIndexes.html new file mode 100644 index 00000000..ef64d889 --- /dev/null +++ b/docs/html/queries/getIndexes.html @@ -0,0 +1,249 @@ + + + + Description of getIndexes + + + + + + + + + +
    Home > queries > getIndexes.m
    + + + +

    getIndexes +

    + +

    PURPOSE ^

    +
    getIndexes Retrieve the indexes for a list of reactions or metabolites.
    + +

    SYNOPSIS ^

    +
    function indexes=getIndexes(model, objects, type, varargin)
    + +

    DESCRIPTION ^

    +
     getIndexes  Retrieve the indexes for a list of reactions or metabolites.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + objects : cell or logical or double
    +     either a cell array of IDs, a logical vector with the same number of
    +     elements as objects of the given type in the model, or a vector of
    +     indexes. An error is raised if an ID is not found in the model, except
    +     for type 'metnames', where a name that is not found gives an empty
    +     index vector.
    + type : char
    +     'rxns', 'mets' or 'genes', depending on what to retrieve. 'metnames'
    +     queries metabolite names and returns, for each name, the indexes of all
    +     metabolites with that name. 'metcomps' queries specific
    +     metabolites and their compartments in the format metaboliteName[comp].
    +     An error is raised if such a name matches no metabolite, or more than
    +     one.
    +     If a model.ec structure exists (GECKO 3), 'ecenzymes', 'ecrxns' and
    +     'ecgenes' are also allowed.
    +
    + Name-Value Arguments
    + --------------------
    + returnLogical : logical
    +     sets whether to return a logical array or an array with the indexes
    +     (default false). It cannot be true for type 'metnames', which raises
    +     the error RAVEN:badInput.
    +
    + Returns
    + -------
    + indexes : logical or double
    +     a logical array if returnLogical is true, otherwise a double array. For
    +     'metnames' with several names, a cell array of double vectors.
    +
    + Notes
    + -----
    + If 'ecenzymes', 'ecrxns' or 'ecgenes' are used with a GECKO 3 model, then
    + the indexes are from the model.ec.enzymes, model.ec.rxns or model.ec.genes
    + fields, respectively.
    +
    + Examples
    + --------
    +     indexes = getIndexes(model, objects, type, returnLogical);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: + + + + + +

    SOURCE CODE ^

    +
    0001 function indexes=getIndexes(model, objects, type, varargin)
    +0002 % getIndexes  Retrieve the indexes for a list of reactions or metabolites.
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % model : struct
    +0007 %     a model structure.
    +0008 % objects : cell or logical or double
    +0009 %     either a cell array of IDs, a logical vector with the same number of
    +0010 %     elements as objects of the given type in the model, or a vector of
    +0011 %     indexes. An error is raised if an ID is not found in the model, except
    +0012 %     for type 'metnames', where a name that is not found gives an empty
    +0013 %     index vector.
    +0014 % type : char
    +0015 %     'rxns', 'mets' or 'genes', depending on what to retrieve. 'metnames'
    +0016 %     queries metabolite names and returns, for each name, the indexes of all
    +0017 %     metabolites with that name. 'metcomps' queries specific
    +0018 %     metabolites and their compartments in the format metaboliteName[comp].
    +0019 %     An error is raised if such a name matches no metabolite, or more than
    +0020 %     one.
    +0021 %     If a model.ec structure exists (GECKO 3), 'ecenzymes', 'ecrxns' and
    +0022 %     'ecgenes' are also allowed.
    +0023 %
    +0024 % Name-Value Arguments
    +0025 % --------------------
    +0026 % returnLogical : logical
    +0027 %     sets whether to return a logical array or an array with the indexes
    +0028 %     (default false). It cannot be true for type 'metnames', which raises
    +0029 %     the error RAVEN:badInput.
    +0030 %
    +0031 % Returns
    +0032 % -------
    +0033 % indexes : logical or double
    +0034 %     a logical array if returnLogical is true, otherwise a double array. For
    +0035 %     'metnames' with several names, a cell array of double vectors.
    +0036 %
    +0037 % Notes
    +0038 % -----
    +0039 % If 'ecenzymes', 'ecrxns' or 'ecgenes' are used with a GECKO 3 model, then
    +0040 % the indexes are from the model.ec.enzymes, model.ec.rxns or model.ec.genes
    +0041 % fields, respectively.
    +0042 %
    +0043 % Examples
    +0044 % --------
    +0045 %     indexes = getIndexes(model, objects, type, returnLogical);
    +0046 
    +0047 p=parseRAVENargs(varargin, {'returnLogical', false});
    +0048 returnLogical=p.returnLogical;
    +0049 
    +0050 if ~islogical(objects) && ~isnumeric(objects)
    +0051     objects=convertCharArray(objects);
    +0052 end
    +0053 type=char(type);
    +0054 
    +0055 indexes=[];
    +0056 
    +0057 switch type
    +0058     case 'rxns'
    +0059         searchIn=model.rxns;
    +0060     case 'mets'
    +0061         searchIn=model.mets;
    +0062     case 'genes'
    +0063         searchIn=model.genes;
    +0064     case 'metnames'
    +0065         if returnLogical==true
    +0066             error('RAVEN:badInput', ['The combination of type ''metnames'' and ' ...
    +0067                 'returnLogical true is not supported, because each name can ' ...
    +0068                 'match several metabolites. Use type ''mets'' or ''metcomps'' ' ...
    +0069                 'for a logical vector.']);
    +0070         end
    +0071         searchIn=model.metNames;
    +0072     case 'metcomps'
    +0073         % If provided as metaboliteName[comp], then index search is quite
    +0074         % different from general approach, and therefore coded separately.
    +0075         mets=regexprep(objects,'(^.+)\[(.+)\]$','$1');
    +0076         comps=regexprep(objects,'(^.+)\[(.+)\]$','$2');
    +0077         indexes=zeros(1,numel(objects));
    +0078         for i=1:numel(objects)
    +0079             index=find(strcmp(mets(i),model.metNames));
    +0080             index=index(strcmp(comps(i),model.comps(model.metComps(index))));
    +0081             if isscalar(index)
    +0082                 indexes(i)=index;
    +0083             elseif isempty(index)
    +0084                 error(['Could not find object ''' objects{i} ''' in the model']);
    +0085             else
    +0086                 error(['''' objects{i} ''' is ambiguous: it matches the ' ...
    +0087                     'metabolites ' strjoin(model.mets(index),', ') ...
    +0088                     '. Use metabolite ids (type ''mets'') instead.']);
    +0089             end
    +0090         end
    +0091         indexes=indexes(:);
    +0092         if returnLogical==true
    +0093             tempIndexes=false(numel(model.mets),1);
    +0094             tempIndexes(indexes)=true;
    +0095             indexes=tempIndexes;
    +0096         end
    +0097         return %None of the remaining function needs to run if metcomps
    +0098     case 'ecrxns'
    +0099         if ~isfield(model,'ec')
    +0100             error('Type %s cannot be used if no model.ec structure is present.',type)
    +0101         end
    +0102         searchIn=model.ec.rxns;
    +0103     case 'ecenzymes'
    +0104         if ~isfield(model,'ec')
    +0105             error('Type %s cannot be used if no model.ec structure is present.',type)
    +0106         end
    +0107         searchIn=model.ec.enzymes;
    +0108     case 'ecgenes'
    +0109         if ~isfield(model,'ec')
    +0110             error('Type %s cannot be used if no model.ec structure is present.',type)
    +0111         end
    +0112         searchIn=model.ec.genes;
    +0113     otherwise
    +0114         if contains(objects,{'rxns','mets','metnames','metcomps','genes','ecgenes','ecenzymes','ecrxns'})
    +0115             error('The second and third parameter provided to run getIndexes are likely switched. Note that the second parameter is the object to find the index for, while the third parameter is the object type (''rxns'', ''mets'', ''metnames'', ''metcomps'', ''genes'', ''ecgenes'', ''ecenzymes'' or ''ecrxns'').')
    +0116         else
    +0117             error('Incorrect value of the ''type'' parameter. Allowed values are ''rxns'', ''mets'', ''metnames'', ''metcomps'', ''genes'', ''ecgenes'', ''ecenzymes'' or ''ecrxns''.');
    +0118        end
    +0119 end
    +0120 
    +0121 if iscell(objects)
    +0122     if strcmpi(type,'metnames')
    +0123         % metnames allows multiple matches per name (same metabolite in different
    +0124         % compartments), so return a cell array of index vectors.
    +0125         for i=1:numel(objects)
    +0126             indexes{i}=find(strcmp(objects(i),searchIn));
    +0127         end
    +0128     else
    +0129         % Build a hash map once (O(m)) for O(1) per-object lookup instead of
    +0130         % O(m) strcmp per object.
    +0131         searchMap = containers.Map(searchIn, 1:numel(searchIn));
    +0132         for i=1:numel(objects)
    +0133             if isKey(searchMap, objects{i})
    +0134                 indexes(i) = searchMap(objects{i});
    +0135             else
    +0136                 error(['Could not find object ''' objects{i} ''' in the model']);
    +0137             end
    +0138         end
    +0139     end
    +0140 else
    +0141     %Now it's either a logical mask or an array with indexes. We want indexes.
    +0142     if islogical(objects)
    +0143         indexes=find(objects);
    +0144     else
    +0145         indexes=objects;
    +0146     end
    +0147 end
    +0148 
    +0149 if returnLogical==true
    +0150     tempIndexes=false(numel(searchIn),1);
    +0151     tempIndexes(indexes)=true;
    +0152     indexes=tempIndexes;
    +0153 end
    +0154 
    +0155 indexes=indexes(:);
    +0156 if iscell(indexes) && length(indexes)==1
    +0157     indexes=indexes{1};
    +0158 end
    +0159 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/queries/getRxnsInComp.html b/docs/html/queries/getRxnsInComp.html similarity index 59% rename from doc/queries/getRxnsInComp.html rename to docs/html/queries/getRxnsInComp.html index 1bb1f51f..7b17daae 100644 --- a/doc/queries/getRxnsInComp.html +++ b/docs/html/queries/getRxnsInComp.html @@ -34,20 +34,21 @@

    DESCRIPTION ^SOURCE CODE ^% model : struct 0007 % a model structure. 0008 % comp : char -0009 % string with the compartment id. -0010 % -0011 % Name-Value Arguments -0012 % -------------------- -0013 % includePartial : logical -0014 % true if reactions with metabolites in several compartments (normally -0015 % transport reactions) should be included (default false). -0016 % -0017 % Returns -0018 % ------- -0019 % I : double -0020 % boolean vector of the reactions. -0021 % rxnNames : cell -0022 % the names of the reactions. -0023 % -0024 % Examples -0025 % -------- -0026 % [I, rxnNames] = getRxnsInComp(model, comp, includePartial); -0027 -0028 comp=char(comp); -0029 p=parseRAVENargs(varargin, {'includePartial',false}); -0030 includePartial=p.includePartial; -0031 -0032 J=find(ismember(upper(model.comps),upper(comp))); -0033 -0034 if numel(J)~=1 -0035 EM=['No unique match to compartment "' comp{1} '"']; -0036 error('RAVEN:badInput', '%s', EM); -0037 end -0038 -0039 K=model.metComps==J; %Get all metabolites in the compartment -0040 -0041 S=model.S~=0; -0042 -0043 %Find the reactions which involve any of the mets -0044 [~, I]=find(S(K,:)); -0045 I=unique(I); -0046 -0047 %Then remove the ones which also include metabolites in other comps -0048 if includePartial==false -0049 I=I(sum(S(:,I))==sum(S(K,I))); -0050 end -0051 rxnNames=model.rxnNames(I); -0052 end

    +0009 % the compartment id (case insensitive). An error is thrown if the id +0010 % does not match exactly one compartment. +0011 % +0012 % Name-Value Arguments +0013 % -------------------- +0014 % includePartial : logical +0015 % true if reactions with metabolites in several compartments (for +0016 % example transport reactions) are included (default false). +0017 % +0018 % Returns +0019 % ------- +0020 % I : double +0021 % indexes of the reactions in the compartment. +0022 % rxnNames : cell +0023 % the names of these reactions. +0024 % +0025 % Examples +0026 % -------- +0027 % [I, rxnNames] = getRxnsInComp(model, comp, includePartial); +0028 +0029 comp=char(comp); +0030 p=parseRAVENargs(varargin, {'includePartial',false}); +0031 includePartial=p.includePartial; +0032 +0033 J=find(ismember(upper(model.comps),upper(comp))); +0034 +0035 if numel(J)~=1 +0036 EM=['No unique match to compartment "' comp{1} '"']; +0037 error('RAVEN:badInput', '%s', EM); +0038 end +0039 +0040 K=model.metComps==J; %Get all metabolites in the compartment +0041 +0042 S=model.S~=0; +0043 +0044 %Find the reactions which involve any of the mets +0045 [~, I]=find(S(K,:)); +0046 I=unique(I); +0047 +0048 %Then remove the ones which also include metabolites in other comps +0049 if includePartial==false +0050 I=I(sum(S(:,I))==sum(S(K,I))); +0051 end +0052 rxnNames=model.rxnNames(I); +0053 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/queries/getTransportRxns.html b/docs/html/queries/getTransportRxns.html similarity index 78% rename from doc/queries/getTransportRxns.html rename to docs/html/queries/getTransportRxns.html index 2d619104..74422859 100644 --- a/doc/queries/getTransportRxns.html +++ b/docs/html/queries/getTransportRxns.html @@ -47,7 +47,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    @@ -83,28 +84,29 @@

    SOURCE CODE ^% Notes 0020 % ----- 0021 % Transport reactions are defined as reactions involving (at least) one -0022 % metabolite name in more than one compartment. -0023 -0024 transportRxns=false(numel(model.rxns),1); -0025 -0026 %An exchange reaction against an explicit boundary metabolite (as -0027 %closeModel creates) copies the real metabolite's own name onto its -0028 %boundary counterpart, so it has exactly the same "same name, different -0029 %compartment" shape as a genuine transport reaction. Exclude it, using -0030 %getExchangeRxns' own definition rather than re-deriving one here. -0031 [~,exchIdx]=getExchangeRxns(model,'all'); -0032 isExch=false(numel(model.rxns),1); -0033 isExch(exchIdx)=true; -0034 -0035 for i=1:numel(model.rxns) -0036 if isExch(i) -0037 continue -0038 end -0039 %Get the involved metabolites in each reaction -0040 mets=model.metNames(model.S(:,i)~=0); -0041 transportRxns(i)=numel(mets)~=numel(unique(mets)); -0042 end -0043 end

    +0022 % metabolite name in more than one compartment. Exchange reactions, as +0023 % identified by getExchangeRxns, are not transport reactions. +0024 +0025 transportRxns=false(numel(model.rxns),1); +0026 +0027 %An exchange reaction against an explicit boundary metabolite (as +0028 %closeModel creates) copies the real metabolite's own name onto its +0029 %boundary counterpart, so it has exactly the same "same name, different +0030 %compartment" shape as a genuine transport reaction. Exclude it, using +0031 %getExchangeRxns' own definition rather than re-deriving one here. +0032 [~,exchIdx]=getExchangeRxns(model,'all'); +0033 isExch=false(numel(model.rxns),1); +0034 isExch(exchIdx)=true; +0035 +0036 for i=1:numel(model.rxns) +0037 if isExch(i) +0038 continue +0039 end +0040 %Get the involved metabolites in each reaction +0041 mets=model.metNames(model.S(:,i)~=0); +0042 transportRxns(i)=numel(mets)~=numel(unique(mets)); +0043 end +0044 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/queries/index.html b/docs/html/queries/index.html new file mode 100644 index 00000000..2bf32072 --- /dev/null +++ b/docs/html/queries/index.html @@ -0,0 +1,29 @@ + + + + Index for Directory queries + + + + + + + + + + +
    < Master indexIndex for queries >
    + +

    Index for queries

    + +

    Matlab files in this directory:

    + +
     buildEquationbuildEquation Construct single equation string for a given reaction.
     checkModelStructcheckModelStruct Check a model structure for errors and inconsistencies.
     constructEquationsconstructEquations Construct equation strings for reactions.
     constructSconstructS Construct a stoichiometric matrix from a cell array of equations.
     getAllRxnsFromGenesgetAllRxnsFromGenes Find all reactions annotated by the genes of a set.
     getElementalBalancegetElementalBalance Check whether the reactions of a model are balanced.
     getExchangeRxnsgetExchangeRxns Retrieve the exchange reactions from a model.
     getGenesFromGrRulesgetGenesFromGrRules Extract gene list and rxnGeneMat from grRules array.
     getIndexesgetIndexes Retrieve the indexes for a list of reactions or metabolites.
     getRxnsInCompgetRxnsInComp Get the reactions in a specified compartment.
     getTransportRxnsgetTransportRxns Retrieve the transport reactions from a model.
     modelSummarymodelSummary Print an overview of a RAVEN model and its flux state.
     parseFormulasparseFormulas Get the elemental composition from formulas.
     parseRxnEquparseRxnEqu Get all metabolite names from a cell array of equations.
     printFluxesprintFluxes Print reactions and fluxes to the screen or to a file.
     printModelStatsprintModelStats Print statistics about a model to the screen.
    + + + + +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/queries/modelSummary.html b/docs/html/queries/modelSummary.html new file mode 100644 index 00000000..0a6e0a0a --- /dev/null +++ b/docs/html/queries/modelSummary.html @@ -0,0 +1,232 @@ + + + + Description of modelSummary + + + + + + + + + +
    Home > queries > modelSummary.m
    + + + +

    modelSummary +

    + +

    PURPOSE ^

    +
    modelSummary Print an overview of a RAVEN model and its flux state.
    + +

    SYNOPSIS ^

    +
    function modelSummary(model, varargin)
    + +

    DESCRIPTION ^

    +
     modelSummary  Print an overview of a RAVEN model and its flux state.
    +
    + Prints the model dimensions and the objective function. When a flux vector
    + is supplied, it also prints the objective value and the net exchange
    + fluxes, partitioned into uptake and secretion.
    +
    + Parameters
    + ----------
    + model : struct
    +     RAVEN model structure.
    +
    + Name-Value Arguments
    + --------------------
    + fluxes : double
    +     flux vector with numel(model.rxns) elements. When provided, the
    +     objective value and the exchange-flux summary are printed
    +     (default []).
    + cutoff : double
    +     minimum absolute net exchange flux to include (default 1e-8).
    + nMax : double
    +     maximum number of exchange reactions displayed per direction, in
    +     descending order of absolute flux (default 20).
    +
    + Examples
    + --------
    +     modelSummary(model)
    +     modelSummary(model, 'fluxes', sol.x)
    +     modelSummary(model, 'fluxes', sol.x, 'nMax', 10)
    +
    + See also
    + --------
    + printModelStats : model statistics. It breaks the counts down per
    +     compartment, adds the number of unique EC codes, biochemically unique
    +     reactions and unique metabolite names, and optionally reports dead ends
    +     and elemental imbalances. It prints no fluxes or objective.
    + printFluxes : prints the fluxes of reactions with options for filtering
    +     and for the format of the output.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • getExchangeRxns getExchangeRxns Retrieve the exchange reactions from a model.
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function modelSummary(model, varargin)
    +0002 % modelSummary  Print an overview of a RAVEN model and its flux state.
    +0003 %
    +0004 % Prints the model dimensions and the objective function. When a flux vector
    +0005 % is supplied, it also prints the objective value and the net exchange
    +0006 % fluxes, partitioned into uptake and secretion.
    +0007 %
    +0008 % Parameters
    +0009 % ----------
    +0010 % model : struct
    +0011 %     RAVEN model structure.
    +0012 %
    +0013 % Name-Value Arguments
    +0014 % --------------------
    +0015 % fluxes : double
    +0016 %     flux vector with numel(model.rxns) elements. When provided, the
    +0017 %     objective value and the exchange-flux summary are printed
    +0018 %     (default []).
    +0019 % cutoff : double
    +0020 %     minimum absolute net exchange flux to include (default 1e-8).
    +0021 % nMax : double
    +0022 %     maximum number of exchange reactions displayed per direction, in
    +0023 %     descending order of absolute flux (default 20).
    +0024 %
    +0025 % Examples
    +0026 % --------
    +0027 %     modelSummary(model)
    +0028 %     modelSummary(model, 'fluxes', sol.x)
    +0029 %     modelSummary(model, 'fluxes', sol.x, 'nMax', 10)
    +0030 %
    +0031 % See also
    +0032 % --------
    +0033 % printModelStats : model statistics. It breaks the counts down per
    +0034 %     compartment, adds the number of unique EC codes, biochemically unique
    +0035 %     reactions and unique metabolite names, and optionally reports dead ends
    +0036 %     and elemental imbalances. It prints no fluxes or objective.
    +0037 % printFluxes : prints the fluxes of reactions with options for filtering
    +0038 %     and for the format of the output.
    +0039 
    +0040 p = parseRAVENargs(varargin, {'fluxes',[]; 'cutoff',1e-8; 'nMax',20});
    +0041 fluxes = p.fluxes;
    +0042 cutoff = p.cutoff;
    +0043 nMax   = p.nMax;
    +0044 
    +0045 nRxns  = numel(model.rxns);
    +0046 nMets  = numel(model.mets);
    +0047 nGenes = 0; if isfield(model,'genes'), nGenes = numel(model.genes); end
    +0048 nComps = 0; if isfield(model,'comps'), nComps = numel(model.comps); end
    +0049 
    +0050 W   = 62;
    +0051 bar = repmat('=', 1, W);
    +0052 sep = repmat('-', 1, W);
    +0053 
    +0054 fprintf('%s\n', bar);
    +0055 modelID = ''; if isfield(model,'id') && ~isempty(model.id), modelID = model.id; end
    +0056 if isfield(model,'name') && ~isempty(model.name)
    +0057     fprintf('  %s  (%s)\n', model.name, modelID);
    +0058 elseif ~isempty(modelID)
    +0059     fprintf('  %s\n', modelID);
    +0060 else
    +0061     fprintf('  (unnamed model)\n');
    +0062 end
    +0063 fprintf('%s\n', bar);
    +0064 fprintf('  %-15s %5d    %-15s %5d\n', 'Reactions:',  nRxns, 'Metabolites:',  nMets);
    +0065 fprintf('  %-15s %5d    %-15s %5d\n', 'Genes:',      nGenes,'Compartments:', nComps);
    +0066 fprintf('%s\n', sep);
    +0067 
    +0068 % Objective
    +0069 objIdx = find(model.c ~= 0);
    +0070 if isempty(objIdx)
    +0071     fprintf('  Objective: (none)\n');
    +0072 else
    +0073     fprintf('  Objective:\n');
    +0074     for i = 1:numel(objIdx)
    +0075         j = objIdx(i);
    +0076         rName = '';
    +0077         if isfield(model,'rxnNames') && j <= numel(model.rxnNames) && ~isempty(model.rxnNames{j})
    +0078             rName = [' (' model.rxnNames{j} ')'];
    +0079         end
    +0080         fprintf('    %+.4g \xD7 %s%s\n', model.c(j), model.rxns{j}, rName);
    +0081     end
    +0082 end
    +0083 
    +0084 if isempty(fluxes)
    +0085     fprintf('%s\n', bar);
    +0086     return
    +0087 end
    +0088 
    +0089 objVal = model.c' * fluxes;
    +0090 fprintf('  Objective value: %.6g\n', objVal);
    +0091 fprintf('%s\n', sep);
    +0092 
    +0093 % Classify exchange fluxes
    +0094 [~, exchIdx] = getExchangeRxns(model);
    +0095 
    +0096 upRows  = {};   % {metLabel, rxnID, absFlux}
    +0097 outRows = {};
    +0098 
    +0099 for i = 1:numel(exchIdx)
    +0100     f = fluxes(exchIdx(i));
    +0101     if abs(f) < cutoff, continue; end
    +0102     col = model.S(:, exchIdx(i));
    +0103     m = find(col, 1);           % exchange reactions have exactly one metabolite
    +0104     if isempty(m), continue; end
    +0105     netProd = full(col(m)) * f; % positive = metabolite secreted; full() avoids sparse scalar in cell
    +0106     mLabel  = metLabel_(model, m);
    +0107     rID     = model.rxns{exchIdx(i)};
    +0108     if netProd > 0
    +0109         outRows{end+1} = {mLabel, rID, netProd};   %#ok<AGROW>
    +0110     else
    +0111         upRows{end+1}  = {mLabel, rID, -netProd};  %#ok<AGROW>
    +0112     end
    +0113 end
    +0114 
    +0115 printSection_('  UPTAKE',    upRows,  nMax, W);
    +0116 printSection_('  SECRETION', outRows, nMax, W);
    +0117 fprintf('%s\n', bar);
    +0118 end
    +0119 
    +0120 %--------------------------------------------------------------------------
    +0121 function printSection_(title, rows, nMax, W)
    +0122     fprintf('\n%s\n', title);
    +0123     fprintf('  %-26s  %-20s  %10s\n', 'Metabolite', 'Reaction', 'Flux');
    +0124     fprintf('  %s\n', repmat('-', 1, W-2));
    +0125     if isempty(rows)
    +0126         fprintf('  (none)\n');
    +0127         return
    +0128     end
    +0129     vals = cellfun(@(r) r{3}, rows);
    +0130     [~, si] = sort(vals, 'descend');
    +0131     rows = rows(si);
    +0132     for i = 1:min(numel(rows), nMax)
    +0133         fprintf('  %-26s  %-20s  %10.4g\n', rows{i}{1}, rows{i}{2}, rows{i}{3});
    +0134     end
    +0135     if numel(rows) > nMax
    +0136         fprintf('  ... (%d more, use nMax to show all)\n', numel(rows) - nMax);
    +0137     end
    +0138 end
    +0139 
    +0140 function s = metLabel_(model, metIdx)
    +0141     if isfield(model,'metNames') && ~isempty(model.metNames{metIdx})
    +0142         s = model.metNames{metIdx};
    +0143     else
    +0144         s = model.mets{metIdx};
    +0145     end
    +0146     if numel(s) > 26, s = [s(1:23) '...']; end
    +0147 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/queries/parseFormulas.html b/docs/html/queries/parseFormulas.html similarity index 50% rename from doc/queries/parseFormulas.html rename to docs/html/queries/parseFormulas.html index cdc4ec9c..2c421212 100644 --- a/doc/queries/parseFormulas.html +++ b/docs/html/queries/parseFormulas.html @@ -37,15 +37,16 @@

    DESCRIPTION ^DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 </ul>
 This function is called by:
 <ul style= -
  • checkModelStruct checkModelStruct Perform a number of checks to ensure a model structure is ok.
  • getElementalBalance getElementalBalance Check whether the reactions of a model are balanced.
  • +
  • checkModelStruct checkModelStruct Check a model structure for errors and inconsistencies.
  • getElementalBalance getElementalBalance Check whether the reactions of a model are balanced.
  • @@ -98,261 +102,265 @@

    SOURCE CODE ^% Name-Value Arguments 0010 % -------------------- 0011 % noPolymers : logical -0012 % assume that all polymers consist of one element. Corresponds to -0013 % counting everything between (...)n as n being equal to one. Only one -0014 % set of parentheses is allowed. If this is false then polymers are -0015 % returned as "Could not parse formula" (default false). +0012 % treat polymers as a single unit: everything between (...)n is counted +0013 % with n equal to one. Only one set of parentheses is allowed. If this is +0014 % false, polymers get exitFlag -1, "Could not parse formula" +0015 % (default false). 0016 % isInchi : logical 0017 % true if the formulas are in the InChI format (default false). 0018 % ignoreRX : logical -0019 % ignore R-groups and bound protein. This can be useful since they are -0020 % often used only as intermediates (default false). -0021 % -0022 % Returns -0023 % ------- -0024 % elements : struct -0025 % struct with fields: -0026 % -0027 % - abbrevs : cell array with abbreviations for all used elements -0028 % - names : cell array with the names for all used elements -0029 % useMat : double -0030 % MxN matrix with the number of atoms for each formula (M) and each -0031 % element (N). -0032 % exitFlag : double -0033 % array with the exit flags: -0034 % -0035 % - 1 : successful parsing -0036 % - 0 : no formula found -0037 % - -1 : could not parse formula -0038 % MW : double -0039 % predicted molecular weight (g/mol). This is only returned for -0040 % formulas which can be successfully parsed, and its calculation -0041 % doesn't affect the exitFlag variable. NaN is returned if the weight -0042 % couldn't be calculated. -0043 % -0044 % Examples -0045 % -------- -0046 % [elements, useMat, exitFlag, MW] = ... -0047 % parseFormulas(formulas, noPolymers, isInchi, ignoreRX); -0048 -0049 p=parseRAVENargs(varargin, {'noPolymers',false; 'isInchi',false; 'ignoreRX',false}); -0050 noPolymers=p.noPolymers; -0051 isInchi=p.isInchi; -0052 ignoreRX=p.ignoreRX; -0053 -0054 elements.abbrevs={'C', 'N', 'O', 'S', 'P', 'H', 'He', 'Li', 'Be', 'B', 'F', 'Ne', 'Na', 'Mg', 'Al',... -0055 'Si', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni',... -0056 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc',... -0057 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce',... -0058 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta',... -0059 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra',... -0060 'Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr',... -0061 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'R', 'X'}'; -0062 elements.names={'carbon', 'nitrogen', 'oxygen', 'sulfur', 'phosphorus', 'hydrogen', 'helium', 'lithium', 'beryllium', 'boron',... -0063 'fluorine', 'neon', 'sodium', 'magnesium', 'aluminum,', 'silicon',... -0064 'chlorine', 'argon', 'potassium', 'calcium', 'scandium', 'titanium', 'vanadium',... -0065 'chromium', 'manganese', 'iron', 'cobalt', 'nickel', 'copper', 'zinc', 'gallium', 'germanium',... -0066 'arsenic', 'selenium', 'bromine', 'krypton', 'rubidium', 'strontium', 'yttrium', 'zirconium',... -0067 'niobium', 'molybdenum', 'technetium', 'ruthenium', 'rhodium', 'palladium', 'silver', 'cadmium',... -0068 'indium', 'tin', 'antimony', 'tellurium', 'iodine', 'xenon', 'cesium', 'barium', 'lanthanum',... -0069 'cerium', 'praseodymium', 'neodymium', 'promethium', 'samarium', 'europium', 'gadolinium',... -0070 'terbium', 'dysprosium', 'holmium', 'erbium', 'thulium', 'ytterbium', 'lutetium', 'hafnium',... -0071 'tantalum', 'tungsten', 'rhenium', 'osmium', 'iridium', 'platinum', 'gold', 'mercury',... -0072 'thallium', 'lead', 'bismuth', 'polonium', 'astatine', 'radon', 'francium', 'radium',... -0073 'actinium', 'thorium', 'protactinium', 'uranium', 'neptunium', 'plutonium', 'americium',... -0074 'curium', 'berkelium', 'californium', 'einsteinium', 'fermium', 'mendelevium', 'nobelium',... -0075 'lawrencium', 'rutherfordium', 'dubnium', 'seaborgium', 'bohrium', 'hassium', 'meitnerium',... -0076 'darmstadtium', 'roentgenium', 'copernicium', 'generic group', 'bound protein'}'; -0077 -0078 EWs=[12.0107 14.0067 15.9994 32.065 30.973762 1.00794 4.002602 6.941 9.012182 10.811 18.9984032 ... -0079 20.1797 22.98976928 24.305 26.9815386 28.0855 35.453 39.948 39.0983 40.078 44.955912 47.867 50.9415 ... -0080 51.9961 54.938045 55.845 58.933195 58.6934 63.546 65.39 69.723 72.64 74.9216 78.96 79.904 83.798 ... -0081 85.4678 87.62 88.90585 91.224 92.906 95.94 97.9072 101.07 102.905 106.42 107.8682 112.411 114.818 ... -0082 118.71 121.76 127.6 126.904 131.293 132.9054519 137.327 138.90547 140.116 140.90765 144.242 144.9127 ... -0083 150.36 151.964 157.25 158.92535 162.5 164.93 167.259 168.93421 173.04 174.967 178.49 180.94788 183.84 ... -0084 186.207 190.23 192.217 195.084 196.966569 200.59 204.3833 207.2 208.9804 208.9824 209.9871 222.0176 ... -0085 223.0197 226.0254 227.0277 232.03806 231.03588 238.02891 237.0482 244.0642 243.0614 247.0704 247.0703 ... -0086 251.0796 252.083 257.0951 258.0984 259.101 262.1097 261.1088 262 266 264 277 268 271 272 nan nan nan]'; -0087 -0088 %Set the EWs of these groups to 0 -0089 if ignoreRX==true -0090 EWs(end-1:end)=0; -0091 end -0092 -0093 useMat=zeros(numel(formulas),numel(elements.abbrevs)); -0094 -0095 exitFlag=zeros(numel(formulas),1); +0019 % set the atomic weight of R-groups and bound protein to 0, which allows +0020 % MW to be calculated for formulas that contain them. The atoms are still +0021 % counted in useMat (default false). +0022 % +0023 % Returns +0024 % ------- +0025 % elements : struct +0026 % struct with fields: +0027 % +0028 % - abbrevs : cell array with abbreviations for all used elements +0029 % - names : cell array with the names for all used elements +0030 % +0031 % Elements that occur in none of the formulas are not included. +0032 % useMat : double +0033 % MxN matrix with the number of atoms for each formula (M) and each +0034 % element (N). +0035 % exitFlag : double +0036 % array with the exit flags: +0037 % +0038 % - 1 : successful parsing +0039 % - 0 : no formula found +0040 % - -1 : could not parse formula +0041 % MW : double +0042 % predicted molecular weight (g/mol). This is only calculated when +0043 % requested, and only for formulas which can be successfully parsed; +0044 % its calculation does not affect the exitFlag variable. NaN is returned +0045 % if the weight cannot be calculated, for example for formulas that +0046 % contain R-groups or bound protein while ignoreRX is false. +0047 % +0048 % Examples +0049 % -------- +0050 % [elements, useMat, exitFlag, MW] = ... +0051 % parseFormulas(formulas, noPolymers, isInchi, ignoreRX); +0052 +0053 p=parseRAVENargs(varargin, {'noPolymers',false; 'isInchi',false; 'ignoreRX',false}); +0054 noPolymers=p.noPolymers; +0055 isInchi=p.isInchi; +0056 ignoreRX=p.ignoreRX; +0057 +0058 elements.abbrevs={'C', 'N', 'O', 'S', 'P', 'H', 'He', 'Li', 'Be', 'B', 'F', 'Ne', 'Na', 'Mg', 'Al',... +0059 'Si', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni',... +0060 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc',... +0061 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce',... +0062 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta',... +0063 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra',... +0064 'Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr',... +0065 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'R', 'X'}'; +0066 elements.names={'carbon', 'nitrogen', 'oxygen', 'sulfur', 'phosphorus', 'hydrogen', 'helium', 'lithium', 'beryllium', 'boron',... +0067 'fluorine', 'neon', 'sodium', 'magnesium', 'aluminum,', 'silicon',... +0068 'chlorine', 'argon', 'potassium', 'calcium', 'scandium', 'titanium', 'vanadium',... +0069 'chromium', 'manganese', 'iron', 'cobalt', 'nickel', 'copper', 'zinc', 'gallium', 'germanium',... +0070 'arsenic', 'selenium', 'bromine', 'krypton', 'rubidium', 'strontium', 'yttrium', 'zirconium',... +0071 'niobium', 'molybdenum', 'technetium', 'ruthenium', 'rhodium', 'palladium', 'silver', 'cadmium',... +0072 'indium', 'tin', 'antimony', 'tellurium', 'iodine', 'xenon', 'cesium', 'barium', 'lanthanum',... +0073 'cerium', 'praseodymium', 'neodymium', 'promethium', 'samarium', 'europium', 'gadolinium',... +0074 'terbium', 'dysprosium', 'holmium', 'erbium', 'thulium', 'ytterbium', 'lutetium', 'hafnium',... +0075 'tantalum', 'tungsten', 'rhenium', 'osmium', 'iridium', 'platinum', 'gold', 'mercury',... +0076 'thallium', 'lead', 'bismuth', 'polonium', 'astatine', 'radon', 'francium', 'radium',... +0077 'actinium', 'thorium', 'protactinium', 'uranium', 'neptunium', 'plutonium', 'americium',... +0078 'curium', 'berkelium', 'californium', 'einsteinium', 'fermium', 'mendelevium', 'nobelium',... +0079 'lawrencium', 'rutherfordium', 'dubnium', 'seaborgium', 'bohrium', 'hassium', 'meitnerium',... +0080 'darmstadtium', 'roentgenium', 'copernicium', 'generic group', 'bound protein'}'; +0081 +0082 EWs=[12.0107 14.0067 15.9994 32.065 30.973762 1.00794 4.002602 6.941 9.012182 10.811 18.9984032 ... +0083 20.1797 22.98976928 24.305 26.9815386 28.0855 35.453 39.948 39.0983 40.078 44.955912 47.867 50.9415 ... +0084 51.9961 54.938045 55.845 58.933195 58.6934 63.546 65.39 69.723 72.64 74.9216 78.96 79.904 83.798 ... +0085 85.4678 87.62 88.90585 91.224 92.906 95.94 97.9072 101.07 102.905 106.42 107.8682 112.411 114.818 ... +0086 118.71 121.76 127.6 126.904 131.293 132.9054519 137.327 138.90547 140.116 140.90765 144.242 144.9127 ... +0087 150.36 151.964 157.25 158.92535 162.5 164.93 167.259 168.93421 173.04 174.967 178.49 180.94788 183.84 ... +0088 186.207 190.23 192.217 195.084 196.966569 200.59 204.3833 207.2 208.9804 208.9824 209.9871 222.0176 ... +0089 223.0197 226.0254 227.0277 232.03806 231.03588 238.02891 237.0482 244.0642 243.0614 247.0704 247.0703 ... +0090 251.0796 252.083 257.0951 258.0984 259.101 262.1097 261.1088 262 266 264 277 268 271 272 nan nan nan]'; +0091 +0092 %Set the EWs of these groups to 0 +0093 if ignoreRX==true +0094 EWs(end-1:end)=0; +0095 end 0096 -0097 %"H+", if parsed from InChI code, would have the composition "p+1". Change -0098 %to fit with how the rest of the compounds are written -0099 formulas=strrep(formulas,'p+1','H+'); +0097 useMat=zeros(numel(formulas),numel(elements.abbrevs)); +0098 +0099 exitFlag=zeros(numel(formulas),1); 0100 -0101 %Ignore charge to make the parsing easier -0102 formulas=strrep(formulas,'+',''); -0103 formulas=strrep(formulas,'-',''); +0101 %"H+", if parsed from InChI code, would have the composition "p+1". Change +0102 %to fit with how the rest of the compounds are written +0103 formulas=strrep(formulas,'p+1','H+'); 0104 -0105 %Loop through each formula -0106 for i=1:numel(formulas) -0107 if ~isempty(formulas{i}) -0108 sucess=false; %True once at least one element has been read -0109 failed=false; %True if any part of the formula could not be read -0110 formula=formulas{i}; -0111 -0112 %If it's an InChI code. The composition is found between the first -0113 %and the second "/" For some simple molecules such as salts only -0114 %the first "/" is present -0115 if isInchi==true -0116 S=regexp(formula,'/','split'); -0117 if numel(S)>=2 -0118 formula=S{2}; -0119 else -0120 formula=''; -0121 end -0122 end -0123 %Only look at what's between the parantheses (polymers are not -0124 %supported in InChI) -0125 if isInchi==false -0126 LP=strfind(formula,'('); -0127 RP=strfind(formula,')n'); -0128 % if numel(LP)>1 || numel(RP)>1 -0129 % exitFlag(i)=-1; continue; -0130 % end if numel(LP)>1 || numel(RP)>1 -0131 % exitFlag(i)=-1; continue; -0132 % end -0133 if numel(LP)==1 && numel(RP)==1 -0134 %This means that the polymer should be regarded as a -0135 %monomer -0136 if noPolymers==true -0137 %This means that there are one set of parantheses -0138 formula=strrep(formula,'(',''); -0139 formula=strrep(formula,')n',''); -0140 else -0141 %This means that polymers should be ignored -0142 exitFlag(i)=-1; -0143 continue; -0144 end -0145 else -0146 if ~isempty(LP) || ~isempty(RP) -0147 exitFlag(i)=-1; -0148 continue; -0149 end -0150 end -0151 end -0152 -0153 %Get the indexes of the numeric (or ".") characters -0154 nonNumeric=false(numel(formula),1); -0155 nonNumeric(regexp(formula,'[^0-9.]'))=true; +0105 %Ignore charge to make the parsing easier +0106 formulas=strrep(formulas,'+',''); +0107 formulas=strrep(formulas,'-',''); +0108 +0109 %Loop through each formula +0110 for i=1:numel(formulas) +0111 if ~isempty(formulas{i}) +0112 sucess=false; %True once at least one element has been read +0113 failed=false; %True if any part of the formula could not be read +0114 formula=formulas{i}; +0115 +0116 %If it's an InChI code. The composition is found between the first +0117 %and the second "/" For some simple molecules such as salts only +0118 %the first "/" is present +0119 if isInchi==true +0120 S=regexp(formula,'/','split'); +0121 if numel(S)>=2 +0122 formula=S{2}; +0123 else +0124 formula=''; +0125 end +0126 end +0127 %Only look at what's between the parantheses (polymers are not +0128 %supported in InChI) +0129 if isInchi==false +0130 LP=strfind(formula,'('); +0131 RP=strfind(formula,')n'); +0132 % if numel(LP)>1 || numel(RP)>1 +0133 % exitFlag(i)=-1; continue; +0134 % end if numel(LP)>1 || numel(RP)>1 +0135 % exitFlag(i)=-1; continue; +0136 % end +0137 if numel(LP)==1 && numel(RP)==1 +0138 %This means that the polymer should be regarded as a +0139 %monomer +0140 if noPolymers==true +0141 %This means that there are one set of parantheses +0142 formula=strrep(formula,'(',''); +0143 formula=strrep(formula,')n',''); +0144 else +0145 %This means that polymers should be ignored +0146 exitFlag(i)=-1; +0147 continue; +0148 end +0149 else +0150 if ~isempty(LP) || ~isempty(RP) +0151 exitFlag(i)=-1; +0152 continue; +0153 end +0154 end +0155 end 0156 -0157 %Get the indexes of the upper characters (since each element starts -0158 %with an upper character) -0159 upperI=isstrprop(formula,'upper'); -0160 upperX=find(upperI); -0161 -0162 for j=1:numel(upperX) -0163 %The first case is when it's the last character. Then the -0164 %coefficient must be 1 -0165 isLast=false; -0166 if upperX(j)==numel(formula) -0167 coeff=1; -0168 element=formula(upperX(j)); -0169 isLast=true; -0170 end -0171 -0172 if isLast==false -0173 %The second case is when the following character is a -0174 %character -0175 if nonNumeric(upperX(j)+1) -0176 %Is it a new element? -0177 if upperI(upperX(j)+1) -0178 %New element, that means that the coefficient was 1 -0179 %and that the element was only one character -0180 coeff=1; -0181 element=formula(upperX(j)); -0182 else -0183 %This means that it's an element with two -0184 %characters -0185 if j==numel(upperX) -0186 if upperX(j)<numel(formula)-1 -0187 coeff=str2double(formula(upperX(j)+2:end)); -0188 else -0189 coeff=1; -0190 end -0191 else -0192 %Check if there is a number or a new element -0193 %after it -0194 if nonNumeric(upperX(j)+2)==true -0195 coeff=1; -0196 else -0197 coeff=str2double(formula(upperX(j)+2:upperX(j+1)-1)); -0198 end -0199 end -0200 element=formula(upperX(j):upperX(j)+1); -0201 end -0202 else -0203 %Then it is a numeral -0204 if j==numel(upperX) -0205 coeff=str2double(formula(upperX(j)+1:end)); -0206 else -0207 coeff=str2double(formula(upperX(j)+1:upperX(j+1)-1)); -0208 end -0209 element=formula(upperX(j)); -0210 end -0211 end -0212 -0213 %Find the element -0214 I=strcmp(element,elements.abbrevs); -0215 if any(I) -0216 if ~isnan(coeff) -0217 useMat(i,I)=useMat(i,I)+coeff; -0218 sucess=true; -0219 else -0220 failed=true; -0221 break; -0222 end -0223 else -0224 failed=true; -0225 break; -0226 end -0227 end -0228 %A formula that stopped part way through is not parsed, even if the -0229 %elements read before that point were recognised: reporting it as -0230 %parsed would hand callers a composition that is missing everything -0231 %after the first unreadable element -0232 if sucess==false || failed==true -0233 useMat(i,:)=0; %Reset for this formula -0234 exitFlag(i)=-1; -0235 else -0236 exitFlag(i)=1; -0237 end -0238 end -0239 end -0240 -0241 %Remove the elements which are never used. any(...,1) is needed because a -0242 %single formula makes useMat a row vector, for which any() would collapse -0243 %the whole row to one value instead of testing each element -0244 I=~any(useMat,1); -0245 useMat(:,I)=[]; -0246 elements.abbrevs(I)=[]; -0247 elements.names(I)=[]; -0248 EWs(I)=[]; -0249 -0250 %Calcluate the weight contribution of each element in each formula. Note -0251 %that this will give NaNs for all formulas if R or X groups are in EWs, -0252 %since 0*NaN is still NaN. Therefore only use elements with known mass -0253 if nargout>3 -0254 P=bsxfun(@times,useMat(:,~isnan(EWs)),EWs(~isnan(EWs)).'); -0255 MW=sum(P,2); -0256 -0257 %Then remove the calculations for the formulas that use an element of -0258 %unknown mass. This has to be a per-formula test: find() on the -0259 %submatrix returns linear indices into it, which are not row indices of -0260 %MW once more than one unknown-mass element is in play (R and X together -0261 %is the common case), so it blanked the wrong formulas and could grow MW -0262 %past the number of formulas -0263 MW(any(useMat(:,isnan(EWs))~=0,2))=nan; -0264 MW(exitFlag~=1)=nan; -0265 end -0266 end

    +0157 %Get the indexes of the numeric (or ".") characters +0158 nonNumeric=false(numel(formula),1); +0159 nonNumeric(regexp(formula,'[^0-9.]'))=true; +0160 +0161 %Get the indexes of the upper characters (since each element starts +0162 %with an upper character) +0163 upperI=isstrprop(formula,'upper'); +0164 upperX=find(upperI); +0165 +0166 for j=1:numel(upperX) +0167 %The first case is when it's the last character. Then the +0168 %coefficient must be 1 +0169 isLast=false; +0170 if upperX(j)==numel(formula) +0171 coeff=1; +0172 element=formula(upperX(j)); +0173 isLast=true; +0174 end +0175 +0176 if isLast==false +0177 %The second case is when the following character is a +0178 %character +0179 if nonNumeric(upperX(j)+1) +0180 %Is it a new element? +0181 if upperI(upperX(j)+1) +0182 %New element, that means that the coefficient was 1 +0183 %and that the element was only one character +0184 coeff=1; +0185 element=formula(upperX(j)); +0186 else +0187 %This means that it's an element with two +0188 %characters +0189 if j==numel(upperX) +0190 if upperX(j)<numel(formula)-1 +0191 coeff=str2double(formula(upperX(j)+2:end)); +0192 else +0193 coeff=1; +0194 end +0195 else +0196 %Check if there is a number or a new element +0197 %after it +0198 if nonNumeric(upperX(j)+2)==true +0199 coeff=1; +0200 else +0201 coeff=str2double(formula(upperX(j)+2:upperX(j+1)-1)); +0202 end +0203 end +0204 element=formula(upperX(j):upperX(j)+1); +0205 end +0206 else +0207 %Then it is a numeral +0208 if j==numel(upperX) +0209 coeff=str2double(formula(upperX(j)+1:end)); +0210 else +0211 coeff=str2double(formula(upperX(j)+1:upperX(j+1)-1)); +0212 end +0213 element=formula(upperX(j)); +0214 end +0215 end +0216 +0217 %Find the element +0218 I=strcmp(element,elements.abbrevs); +0219 if any(I) +0220 if ~isnan(coeff) +0221 useMat(i,I)=useMat(i,I)+coeff; +0222 sucess=true; +0223 else +0224 failed=true; +0225 break; +0226 end +0227 else +0228 failed=true; +0229 break; +0230 end +0231 end +0232 %A formula that stopped part way through is not parsed, even if the +0233 %elements read before that point were recognised: reporting it as +0234 %parsed would hand callers a composition that is missing everything +0235 %after the first unreadable element +0236 if sucess==false || failed==true +0237 useMat(i,:)=0; %Reset for this formula +0238 exitFlag(i)=-1; +0239 else +0240 exitFlag(i)=1; +0241 end +0242 end +0243 end +0244 +0245 %Remove the elements which are never used. any(...,1) is needed because a +0246 %single formula makes useMat a row vector, for which any() would collapse +0247 %the whole row to one value instead of testing each element +0248 I=~any(useMat,1); +0249 useMat(:,I)=[]; +0250 elements.abbrevs(I)=[]; +0251 elements.names(I)=[]; +0252 EWs(I)=[]; +0253 +0254 %Calcluate the weight contribution of each element in each formula. Note +0255 %that this will give NaNs for all formulas if R or X groups are in EWs, +0256 %since 0*NaN is still NaN. Therefore only use elements with known mass +0257 if nargout>3 +0258 P=bsxfun(@times,useMat(:,~isnan(EWs)),EWs(~isnan(EWs)).'); +0259 MW=sum(P,2); +0260 +0261 %Then remove the calculations for the formulas that use an element of +0262 %unknown mass. This has to be a per-formula test: find() on the +0263 %submatrix returns linear indices into it, which are not row indices of +0264 %MW once more than one unknown-mass element is in play (R and X together +0265 %is the common case), so it blanked the wrong formulas and could grow MW +0266 %past the number of formulas +0267 MW(any(useMat(:,isnan(EWs))~=0,2))=nan; +0268 MW(exitFlag~=1)=nan; +0269 end +0270 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/queries/parseRxnEqu.html b/docs/html/queries/parseRxnEqu.html similarity index 56% rename from doc/queries/parseRxnEqu.html rename to docs/html/queries/parseRxnEqu.html index a4068037..e654348e 100644 --- a/doc/queries/parseRxnEqu.html +++ b/docs/html/queries/parseRxnEqu.html @@ -37,7 +37,7 @@

    DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    @@ -76,7 +77,7 @@

    SOURCE CODE ^% Returns 0010 % ------- 0011 % metabolites : cell -0012 % a cell array with the involved metabolites. +0012 % a unique, sorted cell array with the involved metabolites. 0013 % 0014 % Examples 0015 % -------- @@ -84,71 +85,72 @@

    SOURCE CODE ^% 0018 % Notes 0019 % ----- -0020 % The equations should be written like: +0020 % The equations are written like: 0021 % -0022 % 1 A + 3 B (=> or <=>) 5C + 2 D +0022 % 1 A + 3 B (=> or <=>) 5 C + 2 D 0023 % -0024 % If the equation is expressed as for example '... + (n-1) starch' then -0025 % '(n-1) starch' will be interpreted as one metabolite. -0026 -0027 if ~iscell(equations) -0028 equations={equations}; -0029 end -0030 -0031 metabolites={}; -0032 -0033 %Replace the the direction arrows and plus signs with a weird character -0034 %that will be used for parsing -0035 equations=strrep(equations,' <=> ', '€'); -0036 equations=strrep(equations,' => ', '€'); -0037 equations=strrep(equations,' + ', '€'); -0038 equations=strtrim(equations); -0039 -0040 for i=1:numel(equations) -0041 %Split each equation in possible metabolites -0042 candidates=regexp(equations{i},'€','split'); -0043 -0044 %If the splitting character is at the end (if exchange rxns), then an -0045 %empty string will exist together with the real ones. Remove it -0046 candidates(cellfun(@isempty,candidates))=[]; -0047 -0048 %Now remove the potential coefficient before each metabolite. -0049 %Appended one cell at a time instead of [metabolites;x], which -0050 %recopies the whole (growing) array on every metabolite across every -0051 %equation - quadratic in the total metabolite-occurrence count. -0052 for j=1:numel(candidates) -0053 %If the metabolite has a coefficient it will look as 'number name' -0054 space=strfind(candidates{j},' '); -0055 -0056 if isempty(space) -0057 %Add the metabolite -0058 metabolites{end+1,1}=candidates{j}; %#ok<AGROW> -0059 else -0060 potNumber=candidates{j}(1:space(1)); -0061 %I use str2double here which can't deal with fractions (1/3 glc -0062 %and so on). I do this because I don't want to risk calling -0063 %functions -0064 [~,isNumber]=str2num(potNumber); -0065 -0066 if isNumber==1 -0067 %Remove the coefficient -0068 metName=candidates{j}(space(1)+1:end); -0069 metabolites{end+1,1}=metName; %#ok<AGROW> -0070 else -0071 %The metabolite name contained spaces -0072 metabolites{end+1,1}=candidates{j}; %#ok<AGROW> -0073 end -0074 end -0075 end -0076 end -0077 -0078 metabolites=strtrim(metabolites); -0079 -0080 %Upper/lower case is treated as different names. This should be checked for -0081 %later since it's bad modelling practice -0082 metabolites=unique(metabolites); -0083 -0084 end

    +0024 % with a space around each arrow and plus sign and between a coefficient and +0025 % its metabolite name. If the equation is expressed as for example +0026 % '... + (n-1) starch', '(n-1) starch' is interpreted as one metabolite. +0027 +0028 if ~iscell(equations) +0029 equations={equations}; +0030 end +0031 +0032 metabolites={}; +0033 +0034 %Replace the the direction arrows and plus signs with a weird character +0035 %that will be used for parsing +0036 equations=strrep(equations,' <=> ', '€'); +0037 equations=strrep(equations,' => ', '€'); +0038 equations=strrep(equations,' + ', '€'); +0039 equations=strtrim(equations); +0040 +0041 for i=1:numel(equations) +0042 %Split each equation in possible metabolites +0043 candidates=regexp(equations{i},'€','split'); +0044 +0045 %If the splitting character is at the end (if exchange rxns), then an +0046 %empty string will exist together with the real ones. Remove it +0047 candidates(cellfun(@isempty,candidates))=[]; +0048 +0049 %Now remove the potential coefficient before each metabolite. +0050 %Appended one cell at a time instead of [metabolites;x], which +0051 %recopies the whole (growing) array on every metabolite across every +0052 %equation - quadratic in the total metabolite-occurrence count. +0053 for j=1:numel(candidates) +0054 %If the metabolite has a coefficient it will look as 'number name' +0055 space=strfind(candidates{j},' '); +0056 +0057 if isempty(space) +0058 %Add the metabolite +0059 metabolites{end+1,1}=candidates{j}; %#ok<AGROW> +0060 else +0061 potNumber=candidates{j}(1:space(1)); +0062 %I use str2double here which can't deal with fractions (1/3 glc +0063 %and so on). I do this because I don't want to risk calling +0064 %functions +0065 [~,isNumber]=str2num(potNumber); +0066 +0067 if isNumber==1 +0068 %Remove the coefficient +0069 metName=candidates{j}(space(1)+1:end); +0070 metabolites{end+1,1}=metName; %#ok<AGROW> +0071 else +0072 %The metabolite name contained spaces +0073 metabolites{end+1,1}=candidates{j}; %#ok<AGROW> +0074 end +0075 end +0076 end +0077 end +0078 +0079 metabolites=strtrim(metabolites); +0080 +0081 %Upper/lower case is treated as different names. This should be checked for +0082 %later since it's bad modelling practice +0083 metabolites=unique(metabolites); +0084 +0085 end


    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/queries/printFluxes.html b/docs/html/queries/printFluxes.html new file mode 100644 index 00000000..2764163d --- /dev/null +++ b/docs/html/queries/printFluxes.html @@ -0,0 +1,305 @@ + + + + Description of printFluxes + + + + + + + + + +
    Home > queries > printFluxes.m
    + + + +

    printFluxes +

    + +

    PURPOSE ^

    +
    printFluxes Print reactions and fluxes to the screen or to a file.
    + +

    SYNOPSIS ^

    +
    function printFluxes(model, fluxes, varargin)
    + +

    DESCRIPTION ^

    +
     printFluxes  Print reactions and fluxes to the screen or to a file.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + fluxes : double
    +     a vector with fluxes, or a matrix with one row per reaction and one
    +     column per flux distribution.
    +
    + Name-Value Arguments
    + --------------------
    + onlyExchange : logical
    +     only print exchange fluxes (default true).
    + cutOffFlux : double
    +     only print fluxes with absolute values above or equal to this value
    +     (default 10^-8).
    + outputFile : char
    +     a file to save the print-out to (default is output to the command
    +     window).
    + outputString : char
    +     a string that specifies the output of each reaction (default
    +     "%rxnID\t(%rxnName):\t%flux\n").
    + metaboliteList : cell
    +     cell array of metabolite names (case insensitive). Only reactions
    +     involving any of these metabolites are printed.
    +
    + Notes
    + -----
    + The following codes are available for user-defined output strings:
    +
    + - %rxnID : reaction ID
    + - %rxnName : reaction name
    + - %lower : lower bound
    + - %upper : upper bound
    + - %obj : objective coefficient
    + - %eqn : equation
    + - %flux : flux
    + - %element : equation using the metabolite formulas in place of metabolite
    +   names
    + - %unbalanced : "(*)" if the reaction is unbalanced and "(-)" if it could
    +   not be parsed
    + - %lumped : equation where the elemental compositions of the left-hand and
    +   right-hand sides are lumped
    +
    + Examples
    + --------
    +     printFluxes(model, fluxes, 'onlyExchange', false, 'cutOffFlux', 10^-6, ...
    +                 'outputFile', 'fluxes.txt', 'metaboliteList', {'glucose'});
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function printFluxes(model, fluxes, varargin)
    +0002 % printFluxes  Print reactions and fluxes to the screen or to a file.
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % model : struct
    +0007 %     a model structure.
    +0008 % fluxes : double
    +0009 %     a vector with fluxes, or a matrix with one row per reaction and one
    +0010 %     column per flux distribution.
    +0011 %
    +0012 % Name-Value Arguments
    +0013 % --------------------
    +0014 % onlyExchange : logical
    +0015 %     only print exchange fluxes (default true).
    +0016 % cutOffFlux : double
    +0017 %     only print fluxes with absolute values above or equal to this value
    +0018 %     (default 10^-8).
    +0019 % outputFile : char
    +0020 %     a file to save the print-out to (default is output to the command
    +0021 %     window).
    +0022 % outputString : char
    +0023 %     a string that specifies the output of each reaction (default
    +0024 %     "%rxnID\t(%rxnName):\t%flux\n").
    +0025 % metaboliteList : cell
    +0026 %     cell array of metabolite names (case insensitive). Only reactions
    +0027 %     involving any of these metabolites are printed.
    +0028 %
    +0029 % Notes
    +0030 % -----
    +0031 % The following codes are available for user-defined output strings:
    +0032 %
    +0033 % - %rxnID : reaction ID
    +0034 % - %rxnName : reaction name
    +0035 % - %lower : lower bound
    +0036 % - %upper : upper bound
    +0037 % - %obj : objective coefficient
    +0038 % - %eqn : equation
    +0039 % - %flux : flux
    +0040 % - %element : equation using the metabolite formulas in place of metabolite
    +0041 %   names
    +0042 % - %unbalanced : "(*)" if the reaction is unbalanced and "(-)" if it could
    +0043 %   not be parsed
    +0044 % - %lumped : equation where the elemental compositions of the left-hand and
    +0045 %   right-hand sides are lumped
    +0046 %
    +0047 % Examples
    +0048 % --------
    +0049 %     printFluxes(model, fluxes, 'onlyExchange', false, 'cutOffFlux', 10^-6, ...
    +0050 %                 'outputFile', 'fluxes.txt', 'metaboliteList', {'glucose'});
    +0051 
    +0052 p=parseRAVENargs(varargin, {'onlyExchange',true; 'cutOffFlux',10^-8; 'outputFile',[]; 'outputString',[]; 'metaboliteList',{}});
    +0053 onlyExchange=p.onlyExchange;
    +0054 cutOffFlux=p.cutOffFlux;
    +0055 if isempty(cutOffFlux)
    +0056     cutOffFlux=10^-8;
    +0057 end
    +0058 outputFile=p.outputFile;
    +0059 if ~isempty(outputFile)
    +0060     outputFile=char(outputFile);
    +0061     fid=fopen(outputFile,'w');
    +0062 else
    +0063     fid=1;
    +0064 end
    +0065 outputString=p.outputString;
    +0066 if isempty(outputString)
    +0067     outputString='%rxnID\t(%rxnName):\t%flux\n';
    +0068 else
    +0069     outputString=char(outputString);
    +0070 end
    +0071 metaboliteList=p.metaboliteList;
    +0072 if ~isempty(metaboliteList)
    +0073     metaboliteList=convertCharArray(metaboliteList);
    +0074 end
    +0075 if isempty(fluxes)
    +0076     EM='Empty vector of fluxes, solveLP possibly returned infeasible';
    +0077     error('RAVEN:badInput', '%s', EM);
    +0078 elseif size(fluxes,1)~=numel(model.rxns)
    +0079     EM='The number of fluxes and the number of reactions must be the same';
    +0080     error('RAVEN:badInput', '%s', EM);
    +0081 end
    +0082 
    +0083 %Only keep reactions involving the defined metabolites
    +0084 if ~isempty(metaboliteList)
    +0085     I=ismember(upper(model.metNames),upper(metaboliteList));
    +0086     [~, K]=find(model.S(I,:));
    +0087     
    +0088     %Delete all other reactions
    +0089     toDelete=true(numel(model.rxns),1);
    +0090     toDelete(K)=false;
    +0091     model=removeReactions(model,toDelete);
    +0092     fluxes(toDelete,:)=[];
    +0093 end
    +0094 
    +0095 if onlyExchange==true
    +0096     fprintf(fid,'EXCHANGE FLUXES:\n');
    +0097 else
    +0098     fprintf(fid,'FLUXES:\n');
    +0099 end
    +0100 
    +0101 %Remove reactions which are below the cut off
    +0102 toDelete=abs(fluxes)<cutOffFlux;
    +0103 toDelete=all(toDelete,2);
    +0104 model=removeReactions(model,toDelete,true,true);
    +0105 fluxes(toDelete,:)=[];
    +0106 
    +0107 if any(strfind(outputString,'%eqn'))
    +0108     %Construct the equations
    +0109     eqn=constructEquations(model);
    +0110 else
    +0111     eqn=cell(numel(model.rxns),1);
    +0112     eqn(:)={''};
    +0113 end
    +0114 if any(strfind(outputString,'%element'))
    +0115     %For printing equations using the composition
    +0116     cModel=model;
    +0117     cModel.metNames=cModel.metFormulas;
    +0118     cModel.metNames(cellfun(@isempty,cModel.metNames))={'?'};
    +0119     element=constructEquations(cModel);
    +0120 else
    +0121     element=cell(numel(model.rxns),1);
    +0122     element(:)={''};
    +0123 end
    +0124 
    +0125 if any(strfind(outputString,'%unbalanced')) || any(strfind(outputString,'%lumped'))
    +0126     balanceStructure=getElementalBalance(model);
    +0127 end
    +0128 
    +0129 unbalanced=cell(numel(model.rxns),1);
    +0130 unbalanced(:)={''};
    +0131 if any(strfind(outputString,'%unbalanced'))
    +0132     unbalanced(balanceStructure.balanceStatus==0)={'(*)'};
    +0133     unbalanced(balanceStructure.balanceStatus<0)={'(-)'};
    +0134 end
    +0135 
    +0136 lumped=cell(numel(model.rxns),1);
    +0137 lumped(:)={''};
    +0138 if any(strfind(outputString,'%lumped'))
    +0139     for i=1:numel(model.rxns)
    +0140         leftGroup='';
    +0141         rightGroup='';
    +0142         for j=1:numel(balanceStructure.elements.names)
    +0143             I=balanceStructure.leftComp(i,j);
    +0144             if I~=0
    +0145                 if I==1
    +0146                     leftGroup=[leftGroup balanceStructure.elements.abbrevs{j}];
    +0147                 else
    +0148                     leftGroup=[leftGroup balanceStructure.elements.abbrevs{j} num2str(I)];
    +0149                 end
    +0150             end
    +0151             I=balanceStructure.rightComp(i,j);
    +0152             if I~=0
    +0153                 if I==1
    +0154                     rightGroup=[rightGroup balanceStructure.elements.abbrevs{j}];
    +0155                 else
    +0156                     rightGroup=[rightGroup balanceStructure.elements.abbrevs{j} num2str(I)];
    +0157                 end
    +0158             end
    +0159         end
    +0160         if model.rev(i)
    +0161             lumped{i}=[leftGroup ' <=> ' rightGroup];
    +0162         else
    +0163             lumped{i}=[leftGroup ' => ' rightGroup];
    +0164         end
    +0165     end
    +0166 end
    +0167 
    +0168 %Use getExchangeRxns' own definition of "exchange reaction" rather than a
    +0169 %local, incomplete re-implementation: a model that still carries explicit
    +0170 %boundary metabolites represents an exchange as realMet <=> realMet[b] --
    +0171 %one reactant AND one product -- which "no reactants or no products" alone
    +0172 %does not recognise, silently printing nothing for such a model.
    +0173 isExchRxn=false(numel(model.rxns),1);
    +0174 if onlyExchange==true
    +0175     [~,exchIdx]=getExchangeRxns(model,'all');
    +0176     isExchRxn(exchIdx)=true;
    +0177 end
    +0178 
    +0179 for i=1:numel(model.rxns)
    +0180     %Only print if it is an exchange reaction or if all reactions should be
    +0181     %printed.
    +0182     if (onlyExchange==false || isExchRxn(i))
    +0183         printString=outputString;
    +0184         
    +0185         %Produce the final string
    +0186         printString=strrep(printString,'%rxnID',model.rxns{i});
    +0187         printString=strrep(printString,'%eqn',eqn{i});
    +0188         printString=strrep(printString,'%rxnName',model.rxnNames{i});
    +0189         printString=strrep(printString,'%lower',num2str(model.lb(i)));
    +0190         printString=strrep(printString,'%upper',num2str(model.ub(i)));
    +0191         printString=strrep(printString,'%obj',num2str(model.c(i)));
    +0192         printString=strrep(printString,'%flux',num2str(fluxes(i,:)));
    +0193         printString=strrep(printString,'%element',element{i});
    +0194         printString=strrep(printString,'%unbalanced',unbalanced{i});
    +0195         printString=strrep(printString,'%lumped',lumped{i});
    +0196         %Substituted model text (rxnID, eqn, rxnName, etc.) may itself
    +0197         %contain "%", which fprintf would otherwise reinterpret as a new
    +0198         %format directive; print as literal data instead, after manually
    +0199         %expanding the template's own \t and \n escapes.
    +0200         printString=strrep(printString,'\t',sprintf('\t'));
    +0201         printString=strrep(printString,'\n',sprintf('\n'));
    +0202         fprintf(fid,'%s',printString);
    +0203     end
    +0204 end
    +0205 
    +0206 if fid~=1
    +0207     fprintf('File successfully saved.\n');
    +0208     fclose(fid);
    +0209 end
    +0210 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/queries/printModelStats.html b/docs/html/queries/printModelStats.html new file mode 100644 index 00000000..ab8f7425 --- /dev/null +++ b/docs/html/queries/printModelStats.html @@ -0,0 +1,262 @@ + + + + Description of printModelStats + + + + + + + + + +
    Home > queries > printModelStats.m
    + + + +

    printModelStats +

    + +

    PURPOSE ^

    +
    printModelStats Print statistics about a model to the screen.
    + +

    SYNOPSIS ^

    +
    function printModelStats(model, varargin)
    + +

    DESCRIPTION ^

    +
     printModelStats  Print statistics about a model to the screen.
    +
    + Prints the numbers of genes, reactions and metabolites in total and per
    + compartment, the number of unique EC codes (if the eccodes field exists),
    + the number of biochemically unique reactions and the number of unique
    + metabolite names.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + printModelIssues : logical
    +     true if information about unused metabolites, empty reactions,
    +     dead-end reactions and metabolites, and elemental balancing is printed
    +     (default false).
    + printDetails : logical
    +     true if the individual metabolites and reactions of each issue are
    +     listed. Used only if printModelIssues is true (default true).
    +
    + Examples
    + --------
    +     printModelStats(model, printModelIssues, printDetails);
    +
    + See also
    + --------
    + modelSummary : prints the model dimensions without the per-compartment
    +     breakdown, the objective function and, given a flux vector, the
    +     objective value and the net uptake and secretion.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function printModelStats(model, varargin)
    +0002 % printModelStats  Print statistics about a model to the screen.
    +0003 %
    +0004 % Prints the numbers of genes, reactions and metabolites in total and per
    +0005 % compartment, the number of unique EC codes (if the eccodes field exists),
    +0006 % the number of biochemically unique reactions and the number of unique
    +0007 % metabolite names.
    +0008 %
    +0009 % Parameters
    +0010 % ----------
    +0011 % model : struct
    +0012 %     a model structure.
    +0013 %
    +0014 % Name-Value Arguments
    +0015 % --------------------
    +0016 % printModelIssues : logical
    +0017 %     true if information about unused metabolites, empty reactions,
    +0018 %     dead-end reactions and metabolites, and elemental balancing is printed
    +0019 %     (default false).
    +0020 % printDetails : logical
    +0021 %     true if the individual metabolites and reactions of each issue are
    +0022 %     listed. Used only if printModelIssues is true (default true).
    +0023 %
    +0024 % Examples
    +0025 % --------
    +0026 %     printModelStats(model, printModelIssues, printDetails);
    +0027 %
    +0028 % See also
    +0029 % --------
    +0030 % modelSummary : prints the model dimensions without the per-compartment
    +0031 %     breakdown, the objective function and, given a flux vector, the
    +0032 %     objective value and the net uptake and secretion.
    +0033 
    +0034 p=parseRAVENargs(varargin, {'printModelIssues',false; 'printDetails',true});
    +0035 printModelIssues=p.printModelIssues;
    +0036 printDetails=p.printDetails;
    +0037 
    +0038 %Escapes model text before it is spliced into an fprintf template: the
    +0039 %assembled string is used as a format string below, so a literal "%" in
    +0040 %e.g. a metabolite or compartment name would otherwise be misread as a
    +0041 %format directive and swallow the remainder of the line.
    +0042 esc=@(s) strrep(s,'%','%%');
    +0043 
    +0044 fprintf(['Network statistics for ' esc(model.id) ': ' esc(model.name) '\n']);
    +0045 
    +0046 %Get which reactions are present in each compartment
    +0047 rxnComps=sparse(numel(model.rxns),numel(model.comps));
    +0048 
    +0049 %For each compartment, find the metabolites that are present in that
    +0050 %compartment and then the reactions they are involved in
    +0051 for i=1:numel(model.comps)
    +0052     [~, I]=find(model.S(model.metComps==i,:));
    +0053     rxnComps(I,i)=1;
    +0054 end
    +0055 
    +0056 if isfield(model,'eccodes')
    +0057     fprintf(['EC-numbers\t\t\t' num2str(numel(unique(model.eccodes))) '\n']);
    +0058 end
    +0059 
    +0060 %Print information about genes
    +0061 if isfield(model,'genes')
    +0062     fprintf(['Genes*\t\t\t\t' num2str(numel(model.genes)) '\n']);
    +0063     %Find the genes in each compartment
    +0064     for i=1:numel(model.comps)
    +0065         [~, I]=find(model.rxnGeneMat(rxnComps(:,i)==1,:));
    +0066         fprintf(['\t' esc(model.compNames{i}) '\t' num2str(numel(unique(I))) '\n']);
    +0067     end
    +0068 end
    +0069 
    +0070 %Print information about reactions
    +0071 fprintf(['\nReactions*\t\t\t' num2str(numel(model.rxns)) '\n']);
    +0072 for i=1:numel(model.comps)
    +0073     fprintf(['\t' esc(model.compNames{i}) '\t' num2str(sum(rxnComps(:,i))) '\n']);
    +0074 end
    +0075 
    +0076 %Removes the effect of compartments and removes duplicate reactions
    +0077 temp=model;
    +0078 temp.comps(:)={'s'}; %Set all compartments to be the same
    +0079 equ=constructEquations(sortModel(temp,true,true),temp.rxns,false);
    +0080 
    +0081 fprintf(['Unique reactions**\t' num2str(numel(unique(equ))) '\n']);
    +0082 
    +0083 %Print information about metabolites
    +0084 fprintf(['\nMetabolites\t\t\t' num2str(numel(model.mets)) '\n']);
    +0085 for i=1:numel(model.comps)
    +0086     fprintf(['\t' esc(model.compNames{i}) '\t' num2str(sum(model.metComps==i)) '\n']);
    +0087 end
    +0088 
    +0089 fprintf(['Unique metabolites\t' num2str(numel(unique(model.metNames))) '\n']);
    +0090 
    +0091 fprintf('\n* Genes and reactions are counted for each compartment if any of the corresponding metabolites are in that compartment. The sum may therefore not add up to the total number.\n');
    +0092 fprintf('** Unique reactions are defined as being biochemically unique (no compartmentalization)\n');
    +0093 
    +0094 %Also print some potential problems if there are any
    +0095 if printModelIssues==true
    +0096     fprintf(['\nShort model quality summary for ' esc(model.id) ': ' esc(model.name) '\n']);
    +0097     
    +0098     %Check that all the metabolites are being used
    +0099     involvedMat=model.S;
    +0100     involvedMat(involvedMat~=0)=1;
    +0101     usedMets=sum(involvedMat,2);
    +0102     notPresent=find(usedMets==0);
    +0103     if ~isempty(notPresent)
    +0104         errorText=['Non-used metabolites\t' num2str(numel(notPresent)) '\n'];
    +0105         if printDetails==true
    +0106             for i=1:numel(notPresent)
    +0107                 errorText=[errorText '\t(' esc(model.mets{notPresent(i)}) ') ' esc(model.metNames{notPresent(i)}) '\n'];
    +0108             end
    +0109             errorText=[errorText '\n'];
    +0110         end
    +0111         fprintf(errorText);
    +0112     end
    +0113     
    +0114     %Check if there are empty reactions
    +0115     usedRxns=sum(involvedMat,1);
    +0116     notUsed=find(usedRxns==0);
    +0117     if ~isempty(notUsed)
    +0118         errorText=['Empty reactions\t' num2str(numel(notUsed)) '\n'];
    +0119         if printDetails==true
    +0120             for i=1:numel(notUsed)
    +0121                 errorText=[errorText '\t' esc(model.rxns{notUsed(i)}) '\n'];
    +0122             end
    +0123             errorText=[errorText '\n'];
    +0124         end
    +0125         fprintf(errorText);
    +0126     end
    +0127     
    +0128     %Check if there are dead-end reactions/metabolites
    +0129     [~, deletedReactions, deletedMetabolites]=simplifyModel(model,true,false,false,true);
    +0130     
    +0131     if ~isempty(deletedReactions)
    +0132         errorText=['Dead-end reactions\t' num2str(numel(deletedReactions)) '\n'];
    +0133         if printDetails==true
    +0134             for i=1:numel(deletedReactions)
    +0135                 errorText=[errorText '\t' esc(deletedReactions{i}) '\n'];
    +0136             end
    +0137             errorText=[errorText '\n'];
    +0138         end
    +0139         fprintf(errorText);
    +0140     end
    +0141     
    +0142     %Ignore non-used metabolites
    +0143     deletedMetabolites=setdiff(deletedMetabolites,model.mets(notPresent));
    +0144     %Must map to indexes in order to print names
    +0145     deletedMetabolites=find(ismember(model.mets,deletedMetabolites));
    +0146     if ~isempty(deletedMetabolites)
    +0147         errorText=['Dead-end metabolites\t' num2str(numel(deletedMetabolites)) '\n'];
    +0148         if printDetails==true
    +0149             for i=1:numel(deletedMetabolites)
    +0150                 errorText=[errorText '\t(' esc(model.mets{deletedMetabolites(i)}) ') ' esc(model.metNames{deletedMetabolites(i)}) '\n'];
    +0151             end
    +0152             errorText=[errorText '\n'];
    +0153         end
    +0154         fprintf(errorText);
    +0155     end
    +0156     
    +0157     balanceStructure=getElementalBalance(model);
    +0158     
    +0159     notParsed=find(balanceStructure.balanceStatus<0);
    +0160     notBalanced=find(balanceStructure.balanceStatus==0);
    +0161     
    +0162     if ~isempty(notParsed)
    +0163         errorText=['Reactions which could not be elementally balanced\t' num2str(numel(notParsed)) '\n'];
    +0164         if printDetails==true
    +0165             for i=1:numel(notParsed)
    +0166                 errorText=[errorText '\t' esc(model.rxns{notParsed(i)}) '\n'];
    +0167             end
    +0168             errorText=[errorText '\n'];
    +0169         end
    +0170         fprintf(errorText);
    +0171     end
    +0172     if ~isempty(notBalanced)
    +0173         errorText=['Reactions which are elementally unbalanced\t' num2str(numel(notBalanced)) '\n'];
    +0174         if printDetails==true
    +0175             names=strcat(balanceStructure.elements.names,{', '});
    +0176             for i=1:numel(notBalanced)
    +0177                 badOnes=sprintf('%s', names{abs(balanceStructure.leftComp(notBalanced(i),:)-balanceStructure.rightComp(notBalanced(i),:))>10^-7});
    +0178                 errorText=[errorText '\t' esc(model.rxns{notBalanced(i)}) '\t' esc(badOnes(1:end-2)) '\n'];
    +0179             end
    +0180             errorText=[errorText '\n'];
    +0181         end
    +0182         fprintf(errorText);
    +0183     end
    +0184 end
    +0185 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/reconstruction/guessComposition.html b/docs/html/reconstruction/guessComposition.html new file mode 100644 index 00000000..42345a82 --- /dev/null +++ b/docs/html/reconstruction/guessComposition.html @@ -0,0 +1,261 @@ + + + + Description of guessComposition + + + + + + + + + +
    Home > reconstruction > guessComposition.m
    + + + +

    guessComposition +

    + +

    PURPOSE ^

    +
    guessComposition Guess the composition of metabolites without one.
    + +

    SYNOPSIS ^

    +
    function [model, guessedFor, couldNotGuess]=guessComposition(model, varargin)
    + +

    DESCRIPTION ^

    +
     guessComposition  Guess the composition of metabolites without one.
    +
    + Derives the elemental composition of metabolites without a formula from the
    + reactions they participate in, and writes it to the metFormulas field.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + printResults : logical
    +     true if the predicted compositions are printed (default true).
    +
    + Returns
    + -------
    + model : struct
    +     a model structure with the predicted formulas added to metFormulas, for
    +     all metabolites that share the metabolite name.
    + guessedFor : cell
    +     metabolite names for which a composition could be guessed.
    + couldNotGuess : cell
    +     metabolite names, among those without a formula in the input model,
    +     for which no composition could be assigned.
    +
    + Examples
    + --------
    +     [model, guessedFor, couldNotGuess] = guessComposition(model, printResults);
    +
    + Notes
    + -----
    + A metabolite's composition can only be resolved from a reaction in which
    + every other participating metabolite already has a known composition;
    + the function repeats this until no further metabolites can be resolved.
    + It requires that the existing compositions and reactions are correct. If a
    + metabolite would need different compositions in different reactions, or a
    + reaction gives both positive and negative atom counts, the function issues
    + a warning and assigns no composition to that metabolite, so the warnings
    + can also be used to check a model for such inconsistencies. Completion
    + without warnings does not show that the pre-existing compositions are
    + correct.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [model, guessedFor, couldNotGuess]=guessComposition(model, varargin)
    +0002 % guessComposition  Guess the composition of metabolites without one.
    +0003 %
    +0004 % Derives the elemental composition of metabolites without a formula from the
    +0005 % reactions they participate in, and writes it to the metFormulas field.
    +0006 %
    +0007 % Parameters
    +0008 % ----------
    +0009 % model : struct
    +0010 %     a model structure.
    +0011 %
    +0012 % Name-Value Arguments
    +0013 % --------------------
    +0014 % printResults : logical
    +0015 %     true if the predicted compositions are printed (default true).
    +0016 %
    +0017 % Returns
    +0018 % -------
    +0019 % model : struct
    +0020 %     a model structure with the predicted formulas added to metFormulas, for
    +0021 %     all metabolites that share the metabolite name.
    +0022 % guessedFor : cell
    +0023 %     metabolite names for which a composition could be guessed.
    +0024 % couldNotGuess : cell
    +0025 %     metabolite names, among those without a formula in the input model,
    +0026 %     for which no composition could be assigned.
    +0027 %
    +0028 % Examples
    +0029 % --------
    +0030 %     [model, guessedFor, couldNotGuess] = guessComposition(model, printResults);
    +0031 %
    +0032 % Notes
    +0033 % -----
    +0034 % A metabolite's composition can only be resolved from a reaction in which
    +0035 % every other participating metabolite already has a known composition;
    +0036 % the function repeats this until no further metabolites can be resolved.
    +0037 % It requires that the existing compositions and reactions are correct. If a
    +0038 % metabolite would need different compositions in different reactions, or a
    +0039 % reaction gives both positive and negative atom counts, the function issues
    +0040 % a warning and assigns no composition to that metabolite, so the warnings
    +0041 % can also be used to check a model for such inconsistencies. Completion
    +0042 % without warnings does not show that the pre-existing compositions are
    +0043 % correct.
    +0044 
    +0045 p=parseRAVENargs(varargin, {'printResults',true});
    +0046 printResults=p.printResults;
    +0047 
    +0048 %The metabolites for which there is no elemental composition
    +0049 originalMissing=unique(model.metNames(cellfun(@isempty,model.metFormulas)));
    +0050 guessedFor={};
    +0051 
    +0052 %This is to keep track of if new metabolite compositions were predicted
    +0053 predicted=true;
    +0054 while predicted==true
    +0055     predicted=false;
    +0056     
    +0057     %Get the unique names (composition should be independent of
    +0058     %compartment) for the metabolites without composition
    +0059     metNames=unique(model.metNames(cellfun(@isempty,model.metFormulas)));
    +0060     
    +0061     %Parse the formulas in the model
    +0062     [elements, useMat, exitFlag]=parseFormulas(model.metFormulas, true,false);
    +0063     
    +0064     for i=1:numel(metNames)
    +0065         %Get the metabolites with this name. Not so neat, but this is a
    +0066         %fast function anyways
    +0067         mets=find(ismember(model.metNames,metNames(i)));
    +0068         
    +0069         currentComp=[];
    +0070         
    +0071         %Loop through the metabolites -1: Could not assign due to missing
    +0072         %info. -2: Could not assign due to contradiction 1: Composition
    +0073         %assigned
    +0074         metStatus=-1;
    +0075         for j=1:numel(mets)
    +0076             %Get the reactions that the metabolite participates in
    +0077             [~, I]=find(model.S(mets(j),:));
    +0078             if any(I)
    +0079                 for k=1:numel(I)
    +0080                     %Loop through the reactions and check if all other mets
    +0081                     %in them have known composition
    +0082                     eqn=model.S(:,I(k));
    +0083                     %The metabolite's own stoichiometric coefficient in
    +0084                     %this reaction, needed to recover its atom counts from
    +0085                     %the balance below
    +0086                     coeff=eqn(mets(j));
    +0087                     eqn(mets(j))=0;
    +0088                     if all(exitFlag(eqn~=0)==1)
    +0089                         %This means that all other mets had composition.
    +0090                         %Calculate the resulting composition for the
    +0091                         %unknown one
    +0092                         comp=useMat'*eqn;
    +0093 
    +0094                         %This can result in round off errors if there are
    +0095                         %stoichiometries with many decimals. Ignore values
    +0096                         %below 10^-12
    +0097                         comp(abs(comp)<10^-12)=0;
    +0098 
    +0099                         %Check if the composition consist of both negative
    +0100                         %and positive values. If so, throw an error
    +0101                         if all(comp<=0) || all(comp>=0)
    +0102                             %Normalize by the metabolite's own coefficient,
    +0103                             %so e.g. a coefficient of 2 halves the atom
    +0104                             %counts recovered from the balance
    +0105                             comp=abs(comp)/abs(coeff);
    +0106                             if isempty(currentComp)
    +0107                                 currentComp=comp;
    +0108                             end
    +0109                             %Also to deal with round off errors
    +0110                             if all(abs(currentComp-comp)<10^-10)
    +0111                                 metStatus=1;
    +0112                             else
    +0113                                 metStatus=-2;
    +0114                                 break;
    +0115                                 
    +0116                             end
    +0117                         else
    +0118                             metStatus=-2;
    +0119                             break;
    +0120                         end
    +0121                     end
    +0122                 end
    +0123                 %If there was contradictions in one compartment, then abort
    +0124                 %for all compartments
    +0125                 if metStatus==-2
    +0126                     break;
    +0127                 end
    +0128             else
    +0129                 %The metabolite does not participate, no composition can be
    +0130                 %calculated
    +0131                 metStatus=-1;
    +0132             end
    +0133         end
    +0134         %Check status of the metabolite
    +0135         switch metStatus
    +0136             case -2
    +0137                 EM=['Could not predict composition for "' metNames{i} '" due to inconsistencies'];
    +0138                 warning('RAVEN:warning', '%s', EM);
    +0139             case 1
    +0140                 %Calculate and add the composition
    +0141                 str=getCompString(elements,comp);
    +0142                 model.metFormulas(mets)={str};
    +0143                 if printResults==true
    +0144                     fprintf(['Predicted composition for "' metNames{i} '" to be ' str '\n']);
    +0145                 end
    +0146                 
    +0147                 %Keep track
    +0148                 guessedFor=[guessedFor;metNames(i)];
    +0149                 
    +0150                 predicted=true; %To loop again
    +0151         end
    +0152     end
    +0153 end
    +0154 
    +0155 couldNotGuess=setdiff(originalMissing,guessedFor);
    +0156 end
    +0157 
    +0158 %Helper function for getting the composition string
    +0159 function str=getCompString(elements,comp)
    +0160 str='';
    +0161 
    +0162 for i=1:numel(comp)
    +0163     if comp(i)~=0
    +0164         if comp(i)==1
    +0165             str=[str elements.abbrevs{i}];
    +0166         else
    +0167             str=[str elements.abbrevs{i} num2str(comp(i))];
    +0168         end
    +0169     end
    +0170 end
    +0171 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/reconstruction/homology/getBlast.html b/docs/html/reconstruction/homology/getBlast.html new file mode 100644 index 00000000..37f6b837 --- /dev/null +++ b/docs/html/reconstruction/homology/getBlast.html @@ -0,0 +1,316 @@ + + + + Description of getBlast + + + + + + + + + +
    Home > reconstruction > homology > getBlast.m
    + + + +

    getBlast +

    + +

    PURPOSE ^

    +
    getBlast Bidirectional BLAST between an organism and template organisms.
    + +

    SYNOPSIS ^

    +
    function [blastStructure,blastReport]=getBlast(organismID,fastaFile,modelIDs,refFastaFiles,varargin)
    + +

    DESCRIPTION ^

    +
     getBlast  Bidirectional BLAST between an organism and template organisms.
    +
    + Parameters
    + ----------
    + organismID : char
    +     the id of the organism of interest. This should also match with the
    +     id supplied to getModelFromHomology.
    + fastaFile : char
    +     a FASTA file with the protein sequences for the organism of interest.
    + modelIDs : cell
    +     a cell array of model ids. These must match the "model.id" fields in
    +     the "models" structure if the output is to be used with
    +     getModelFromHomology.
    + refFastaFiles : cell
    +     a cell array with the paths to the corresponding FASTA files.
    +
    + Name-Value Arguments
    + --------------------
    + developMode : logical
    +     true if blastReport is filled, as used in the unit test for BLAST+
    +     (default false).
    + hideVerbose : logical
    +     true if no status messages should be printed (default false).
    +
    + Returns
    + -------
    + blastStructure : struct
    +     structure array with one element per search direction and template
    +     organism (2 times the number of modelIDs), containing the bidirectional
    +     homology measurements (fromId, toId, fromGenes, toGenes, evalue,
    +     identity, aligLen, bitscore and ppos) for use by getModelFromHomology.
    + blastReport : struct
    +     structure containing MD5 hashes for FASTA database files and
    +     unparsed BLAST output data. The fields are empty if developMode is
    +     false.
    +
    + Notes
    + -----
    + This function calls BLAST+ to perform a bidirectional homology test
    + between the organism of interest and a set of other organisms using
    + standard settings. The only filtering this function applies is the removal
    + of hits with an E-value higher than 10e-5.
    +
    + Examples
    + --------
    +     [blastStructure,blastReport] = getBlast(organismID,fastaFile,...
    +        modelIDs,refFastaFiles,developMode,hideVerbose);
    +
    + See also
    + --------
    + getModelFromHomology, getDiamond, runSuppOrth
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [blastStructure,blastReport]=getBlast(organismID,fastaFile,...
    +0002     modelIDs,refFastaFiles,varargin)
    +0003 % getBlast  Bidirectional BLAST between an organism and template organisms.
    +0004 %
    +0005 % Parameters
    +0006 % ----------
    +0007 % organismID : char
    +0008 %     the id of the organism of interest. This should also match with the
    +0009 %     id supplied to getModelFromHomology.
    +0010 % fastaFile : char
    +0011 %     a FASTA file with the protein sequences for the organism of interest.
    +0012 % modelIDs : cell
    +0013 %     a cell array of model ids. These must match the "model.id" fields in
    +0014 %     the "models" structure if the output is to be used with
    +0015 %     getModelFromHomology.
    +0016 % refFastaFiles : cell
    +0017 %     a cell array with the paths to the corresponding FASTA files.
    +0018 %
    +0019 % Name-Value Arguments
    +0020 % --------------------
    +0021 % developMode : logical
    +0022 %     true if blastReport is filled, as used in the unit test for BLAST+
    +0023 %     (default false).
    +0024 % hideVerbose : logical
    +0025 %     true if no status messages should be printed (default false).
    +0026 %
    +0027 % Returns
    +0028 % -------
    +0029 % blastStructure : struct
    +0030 %     structure array with one element per search direction and template
    +0031 %     organism (2 times the number of modelIDs), containing the bidirectional
    +0032 %     homology measurements (fromId, toId, fromGenes, toGenes, evalue,
    +0033 %     identity, aligLen, bitscore and ppos) for use by getModelFromHomology.
    +0034 % blastReport : struct
    +0035 %     structure containing MD5 hashes for FASTA database files and
    +0036 %     unparsed BLAST output data. The fields are empty if developMode is
    +0037 %     false.
    +0038 %
    +0039 % Notes
    +0040 % -----
    +0041 % This function calls BLAST+ to perform a bidirectional homology test
    +0042 % between the organism of interest and a set of other organisms using
    +0043 % standard settings. The only filtering this function applies is the removal
    +0044 % of hits with an E-value higher than 10e-5.
    +0045 %
    +0046 % Examples
    +0047 % --------
    +0048 %     [blastStructure,blastReport] = getBlast(organismID,fastaFile,...
    +0049 %        modelIDs,refFastaFiles,developMode,hideVerbose);
    +0050 %
    +0051 % See also
    +0052 % --------
    +0053 % getModelFromHomology, getDiamond, runSuppOrth
    +0054 
    +0055 p=parseRAVENargs(varargin, {'developMode',false; 'hideVerbose',false});
    +0056 developMode=p.developMode;
    +0057 hideVerbose=p.hideVerbose;
    +0058 
    +0059 %Everything should be cell arrays
    +0060 organismID=convertCharArray(organismID);
    +0061 fastaFile=convertCharArray(fastaFile);
    +0062 modelIDs=convertCharArray(modelIDs);
    +0063 refFastaFiles=convertCharArray(refFastaFiles);
    +0064 
    +0065 %Create blank structures for results
    +0066 blastStructure=[];
    +0067 blastReport.dbHashes.phr={};
    +0068 blastReport.dbHashes.pot={};
    +0069 blastReport.dbHashes.psq={};
    +0070 blastReport.dbHashes.pto={};
    +0071 blastReport.blastTxtOutput={};
    +0072 
    +0073 %Get the directory for RAVEN Toolbox
    +0074 ravenPath=findRAVENroot();
    +0075 
    +0076 %Generate temporary names for BLAST databases and output files
    +0077 tmpDB=tempname;
    +0078 outFile=tempname;
    +0079 if ispc && contains(tmpDB,' ')
    +0080     warning(['MATLAB assigned ''%s'' as temporary file path, but it '...
    +0081              'contains a space character, which is not compatible with '...
    +0082              'BLAST. Instead, a temporary folder will be initiated at '...
    +0083              '''C:\\tempRAVEN\\'' which should manually be removed when '...
    +0084              'finished.'],tmpDB)
    +0085     tmpDB=tempname('C:\tempRAVEN');
    +0086     outFile=tempname('C:\tempRAVEN');
    +0087 end
    +0088 
    +0089 %Check for existence of files. If no full path is specified for a file,
    +0090 %assume that it is in the current folder
    +0091 if isrow(refFastaFiles)
    +0092     files=horzcat(fastaFile,refFastaFiles);
    +0093 else
    +0094     files=vertcat(fastaFile,refFastaFiles);
    +0095 end
    +0096 
    +0097 files=checkFileExistence(files,2); %Copy files to temp dir
    +0098 fastaFile = files(1);
    +0099 refFastaFiles = files(2:end);
    +0100 
    +0101 %Identify the operating system
    +0102 if ispc
    +0103     binEnd='.exe';
    +0104     setenv('BLASTDB_LMDB_MAP_SIZE','1000000');
    +0105 elseif ismac
    +0106     binEnd='.mac';
    +0107 elseif isunix
    +0108     binEnd='';
    +0109 else
    +0110     error('RAVEN:badInput', '%s', 'Unknown OS, exiting.')
    +0111     return
    +0112 end
    +0113 
    +0114 %Fetch the BLAST+ binaries on demand if they are not already present
    +0115 if ~exist(fullfile(ravenPath,'src','external','blast+',['blastp' binEnd]),'file')
    +0116     downloadRavenBinaries({'blast+'});
    +0117 end
    +0118 
    +0119 %Run BLAST multi-threaded to use all logical cores assigned to MATLAB
    +0120 cores = evalc('feature(''numcores'')');
    +0121 cores = strsplit(cores, 'MATLAB was assigned: ');
    +0122 cores = regexp(cores{2},'^\d*','match');
    +0123 cores = cores{1};
    +0124 
    +0125 %Create a database for the new organism and blast each of the refFastaFiles
    +0126 %against it
    +0127 [status, message]=system(['"' fullfile(ravenPath,'src','external','blast+',['makeblastdb' binEnd]) '" -in "' fastaFile{1} '" -out "' fullfile(tmpDB, 'tmpDB') '" -dbtype prot']);
    +0128 if developMode
    +0129     blastReport.dbHashes.phr{numel(blastReport.dbHashes.phr)+1}=filemd5(fullfile(tmpDB, 'tmpDB.phr'));
    +0130     blastReport.dbHashes.pot{numel(blastReport.dbHashes.pot)+1}=filemd5(fullfile(tmpDB, 'tmpDB.pot'));
    +0131     blastReport.dbHashes.psq{numel(blastReport.dbHashes.psq)+1}=filemd5(fullfile(tmpDB, 'tmpDB.psq'));
    +0132     blastReport.dbHashes.pto{numel(blastReport.dbHashes.pto)+1}=filemd5(fullfile(tmpDB, 'tmpDB.pto'));
    +0133 end
    +0134 if status~=0
    +0135     error('makeblastdb did not run successfully, error:\n%s',strip(message))
    +0136 end
    +0137 
    +0138 for i=1:numel(refFastaFiles)
    +0139     if ~hideVerbose
    +0140         fprintf(['BLASTing "' modelIDs{i} '" against "' organismID{1} '"..\n']);
    +0141     end
    +0142     [status, message]=system(['"' fullfile(ravenPath,'src','external','blast+',['blastp' binEnd]) '" -query "' refFastaFiles{i} '" -out "' outFile '_' num2str(i) '" -db "' fullfile(tmpDB, 'tmpDB') '" -evalue 10e-5 -outfmt "10 qseqid sseqid evalue pident length bitscore ppos" -num_threads "' cores '"']);
    +0143     if developMode
    +0144         blastReport.blastTxtOutput{numel(blastReport.blastTxtOutput)+1}=importdata([outFile '_' num2str(i)]);
    +0145     end
    +0146     if status~=0
    +0147         error('blastp did not run successfully, error:\n%s',strip(message))
    +0148     end
    +0149 end
    +0150 delete([tmpDB filesep 'tmpDB*']);
    +0151 
    +0152 %Then create a database for each of the reference organisms and blast the
    +0153 %new organism against them
    +0154 for i=1:numel(refFastaFiles)
    +0155     if ~hideVerbose
    +0156         fprintf(['BLASTing "' organismID{1} '" against "' modelIDs{i} '"..\n']);
    +0157     end
    +0158     [status, message]=system(['"' fullfile(ravenPath,'src','external','blast+',['makeblastdb' binEnd]) '" -in "' refFastaFiles{i} '" -out "' fullfile(tmpDB, 'tmpDB') '" -dbtype prot']);
    +0159     if status~=0
    +0160         error('makeblastdb did not run successfully, error:\n%s',strip(message))
    +0161     end
    +0162     [status, message]=system(['"' fullfile(ravenPath,'src','external','blast+',['blastp' binEnd]) '" -query "' fastaFile{1} '" -out "' outFile '_r' num2str(i) '" -db "' fullfile(tmpDB, 'tmpDB') '" -evalue 10e-5 -outfmt "10 qseqid sseqid evalue pident length bitscore ppos" -num_threads "' cores '"']);
    +0163     if developMode
    +0164         blastReport.dbHashes.phr{numel(blastReport.dbHashes.phr)+1}=filemd5(fullfile(tmpDB, 'tmpDB.phr'));
    +0165         blastReport.dbHashes.pot{numel(blastReport.dbHashes.pot)+1}=filemd5(fullfile(tmpDB, 'tmpDB.pot'));
    +0166         blastReport.dbHashes.psq{numel(blastReport.dbHashes.psq)+1}=filemd5(fullfile(tmpDB, 'tmpDB.psq'));
    +0167         blastReport.dbHashes.pto{numel(blastReport.dbHashes.pto)+1}=filemd5(fullfile(tmpDB, 'tmpDB.pto'));
    +0168         blastReport.blastTxtOutput{numel(blastReport.blastTxtOutput)+1}=importdata([outFile '_r' num2str(i)]);
    +0169     end    
    +0170     if status~=0
    +0171         error('blastp did not run successfully, error:\n%s',strip(message))
    +0172     end
    +0173     delete([tmpDB filesep 'tmpDB*']);
    +0174 end
    +0175 
    +0176 %Done with the BLAST, do the parsing of the text files
    +0177 for i=1:numel(refFastaFiles)*2
    +0178     tempStruct=[];
    +0179     if i<=numel(refFastaFiles)
    +0180         tempStruct.fromId=modelIDs{i};
    +0181         tempStruct.toId=organismID{1};
    +0182         A=readtable([outFile '_' num2str(i)],'Delimiter',',','Format','%s%s%f%f%f%f%f');
    +0183     else
    +0184         tempStruct.fromId=organismID{1};
    +0185         tempStruct.toId=modelIDs{i-numel(refFastaFiles)};
    +0186         A=readtable([outFile '_r' num2str(i-numel(refFastaFiles))],'Delimiter',',','Format','%s%s%f%f%f%f%f');
    +0187     end
    +0188     tempStruct.fromGenes=A{:,1};
    +0189     tempStruct.toGenes=A{:,2};
    +0190     tempStruct.evalue=table2array(A(:,3));
    +0191     tempStruct.identity=table2array(A(:,4));
    +0192     tempStruct.aligLen=table2array(A(:,5));
    +0193     tempStruct.bitscore=table2array(A(:,6));
    +0194     tempStruct.ppos=table2array(A(:,7));
    +0195     blastStructure=[blastStructure tempStruct];
    +0196 end
    +0197 
    +0198 %Remove the old tempfiles
    +0199 delete([outFile '*']);
    +0200 %Remove the temp fasta files
    +0201 delete(files{:})
    +0202 end
    +0203 
    +0204 function hash = filemd5(path)
    +0205 %Compute MD5 of a file via the Java MessageDigest API (no toolbox required).
    +0206 md  = java.security.MessageDigest.getInstance('MD5');
    +0207 fis = java.io.FileInputStream(java.io.File(path));
    +0208 buf = javaArray('byte', 65536);
    +0209 while true
    +0210     n = fis.read(buf);
    +0211     if n < 0; break; end
    +0212     md.update(buf, 0, n);
    +0213 end
    +0214 fis.close();
    +0215 bytes = typecast(md.digest(), 'uint8');
    +0216 hash  = lower(sprintf('%02x', bytes));
    +0217 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/reconstruction/homology/getDiamond.html b/docs/html/reconstruction/homology/getDiamond.html new file mode 100644 index 00000000..a4a0c58b --- /dev/null +++ b/docs/html/reconstruction/homology/getDiamond.html @@ -0,0 +1,282 @@ + + + + Description of getDiamond + + + + + + + + + +
    Home > reconstruction > homology > getDiamond.m
    + + + +

    getDiamond +

    + +

    PURPOSE ^

    +
    getDiamond Bidirectional BLAST with DIAMOND against template organisms.
    + +

    SYNOPSIS ^

    +
    function [blastStructure,diamondReport]=getDiamond(organismID,fastaFile,modelIDs,refFastaFiles,varargin)
    + +

    DESCRIPTION ^

    +
     getDiamond  Bidirectional BLAST with DIAMOND against template organisms.
    +
    + Parameters
    + ----------
    + organismID : char
    +     the id of the organism of interest. This should also match with the
    +     id supplied to getModelFromHomology.
    + fastaFile : char
    +     a FASTA file with the protein sequences for the organism of interest.
    + modelIDs : cell
    +     a cell array of model ids. These must match the "model.id" fields in
    +     the "models" structure if the output is to be used with
    +     getModelFromHomology.
    + refFastaFiles : cell
    +     a cell array with the paths to the corresponding FASTA files.
    +
    + Name-Value Arguments
    + --------------------
    + developMode : logical
    +     true if diamondReport is filled, as used in the unit test for DIAMOND
    +     (default false).
    + hideVerbose : logical
    +     true if no status messages should be printed (default false).
    +
    + Returns
    + -------
    + blastStructure : struct
    +     structure array with one element per search direction and template
    +     organism (2 times the number of modelIDs), containing the bidirectional
    +     homology measurements (fromId, toId, fromGenes, toGenes, evalue,
    +     identity, aligLen, bitscore and ppos) for use by getModelFromHomology.
    + diamondReport : struct
    +     structure containing MD5 hashes for FASTA database files and
    +     unparsed DIAMOND output data. The fields are empty if developMode is
    +     false.
    +
    + Notes
    + -----
    + This function calls DIAMOND to perform a bidirectional homology search
    + between the organism of interest and a set of other organisms using the
    + "--more-sensitive" setting from DIAMOND. getBlast() gives the most
    + sensitive results; getDiamond() is >15x faster. The blastStructure is in
    + the same format as that obtained from getBlast().
    +
    + Examples
    + --------
    +     [blastStructure,diamondReport] = getDiamond(organismID,fastaFile,...
    +        modelIDs,refFastaFiles,developMode,hideVerbose);
    +
    + See also
    + --------
    + getModelFromHomology, getBlast, runSuppOrth
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function [blastStructure,diamondReport]=getDiamond(organismID,fastaFile,...
    +0002     modelIDs,refFastaFiles,varargin)
    +0003 % getDiamond  Bidirectional BLAST with DIAMOND against template organisms.
    +0004 %
    +0005 % Parameters
    +0006 % ----------
    +0007 % organismID : char
    +0008 %     the id of the organism of interest. This should also match with the
    +0009 %     id supplied to getModelFromHomology.
    +0010 % fastaFile : char
    +0011 %     a FASTA file with the protein sequences for the organism of interest.
    +0012 % modelIDs : cell
    +0013 %     a cell array of model ids. These must match the "model.id" fields in
    +0014 %     the "models" structure if the output is to be used with
    +0015 %     getModelFromHomology.
    +0016 % refFastaFiles : cell
    +0017 %     a cell array with the paths to the corresponding FASTA files.
    +0018 %
    +0019 % Name-Value Arguments
    +0020 % --------------------
    +0021 % developMode : logical
    +0022 %     true if diamondReport is filled, as used in the unit test for DIAMOND
    +0023 %     (default false).
    +0024 % hideVerbose : logical
    +0025 %     true if no status messages should be printed (default false).
    +0026 %
    +0027 % Returns
    +0028 % -------
    +0029 % blastStructure : struct
    +0030 %     structure array with one element per search direction and template
    +0031 %     organism (2 times the number of modelIDs), containing the bidirectional
    +0032 %     homology measurements (fromId, toId, fromGenes, toGenes, evalue,
    +0033 %     identity, aligLen, bitscore and ppos) for use by getModelFromHomology.
    +0034 % diamondReport : struct
    +0035 %     structure containing MD5 hashes for FASTA database files and
    +0036 %     unparsed DIAMOND output data. The fields are empty if developMode is
    +0037 %     false.
    +0038 %
    +0039 % Notes
    +0040 % -----
    +0041 % This function calls DIAMOND to perform a bidirectional homology search
    +0042 % between the organism of interest and a set of other organisms using the
    +0043 % "--more-sensitive" setting from DIAMOND. getBlast() gives the most
    +0044 % sensitive results; getDiamond() is >15x faster. The blastStructure is in
    +0045 % the same format as that obtained from getBlast().
    +0046 %
    +0047 % Examples
    +0048 % --------
    +0049 %     [blastStructure,diamondReport] = getDiamond(organismID,fastaFile,...
    +0050 %        modelIDs,refFastaFiles,developMode,hideVerbose);
    +0051 %
    +0052 % See also
    +0053 % --------
    +0054 % getModelFromHomology, getBlast, runSuppOrth
    +0055 
    +0056 p=parseRAVENargs(varargin, {'developMode',false; 'hideVerbose',false});
    +0057 developMode=p.developMode;
    +0058 hideVerbose=p.hideVerbose;
    +0059 
    +0060 %Everything should be cell arrays
    +0061 organismID=convertCharArray(organismID);
    +0062 fastaFile=convertCharArray(fastaFile);
    +0063 modelIDs=convertCharArray(modelIDs);
    +0064 refFastaFiles=convertCharArray(refFastaFiles);
    +0065 
    +0066 %Create blank structures for results
    +0067 blastStructure=[];
    +0068 diamondReport.dbHashes={};
    +0069 diamondReport.diamondTxtOutput={};
    +0070 
    +0071 %Get the directory for RAVEN Toolbox.
    +0072 ravenPath=findRAVENroot();
    +0073 
    +0074 %Generate temporary names for DIAMOND databases and output files
    +0075 tmpDB=tempname;
    +0076 outFile=tempname;
    +0077 
    +0078 %Check for existence of files. If no full path is specified for a file,
    +0079 %assume that it is in the current folder
    +0080 if isrow(refFastaFiles)
    +0081     files=horzcat(fastaFile,refFastaFiles);
    +0082 else
    +0083     files=vertcat(fastaFile,refFastaFiles);
    +0084 end
    +0085 
    +0086 files=checkFileExistence(files,2); %Copy files to temp dir
    +0087 fastaFile = files(1);
    +0088 refFastaFiles = files(2:end);
    +0089 
    +0090 %Identify the operating system
    +0091 if ispc
    +0092     binEnd='.exe';
    +0093 elseif ismac
    +0094     binEnd='.mac';
    +0095 elseif isunix
    +0096     binEnd='';
    +0097 else
    +0098     error('RAVEN:badInput', '%s', 'Unknown OS, exiting.')
    +0099     return
    +0100 end
    +0101 
    +0102 %Fetch the DIAMOND binary on demand if it is not already present
    +0103 if ~exist(fullfile(ravenPath,'src','external','diamond',['diamond' binEnd]),'file')
    +0104     downloadRavenBinaries({'diamond'});
    +0105 end
    +0106 
    +0107 %Run DIAMOND multi-threaded to use all logical cores assigned to MATLAB.
    +0108 cores = evalc('feature(''numcores'')');
    +0109 cores = strsplit(cores, 'MATLAB was assigned: ');
    +0110 cores = regexp(cores{2},'^\d*','match');
    +0111 cores = cores{1};
    +0112 
    +0113 %Create a database for the new organism and blast each of the refFastaFiles
    +0114 %against it
    +0115 [status, message]=system(['"' fullfile(ravenPath,'src','external','diamond',['diamond' binEnd]) '" makedb --in "' fastaFile{1} '" --db "' fullfile(tmpDB) '"']);
    +0116 if developMode
    +0117     diamondReport.dbHashes{numel(diamondReport.dbHashes)+1} = char(regexp(message,'[a-f0-9]{32}','match'));
    +0118 end
    +0119 if status~=0
    +0120     error('DIAMOND makedb did not run successfully, error:\n%s',strip(message))
    +0121 end
    +0122 
    +0123 for i=1:numel(refFastaFiles)
    +0124     if ~hideVerbose
    +0125         fprintf(['Running DIAMOND blastp with "' modelIDs{i} '" against "' organismID{1} '"..\n']);
    +0126     end
    +0127     [status, message]=system(['"' fullfile(ravenPath,'src','external','diamond',['diamond' binEnd]) '" blastp --query "' refFastaFiles{i} '" --out "' outFile '_' num2str(i) '" --db "' fullfile(tmpDB) '" --more-sensitive --outfmt 6 qseqid sseqid evalue pident length bitscore ppos --threads ' cores ]);
    +0128     if developMode
    +0129         diamondReport.diamondTxtOutput{numel(diamondReport.diamondTxtOutput)+1}=importdata([outFile '_' num2str(i)]);
    +0130     end
    +0131     if status~=0
    +0132         error('DIAMOND blastp did not run successfully, error:\n%s',strip(message))
    +0133     end
    +0134 end
    +0135 delete([tmpDB filesep 'tmpDB*']);
    +0136 
    +0137 %Then create a database for each of the reference organisms and blast the
    +0138 %new organism against them
    +0139 for i=1:numel(refFastaFiles)
    +0140     if ~hideVerbose
    +0141         fprintf(['Running DIAMOND blastp with "' organismID{1} '" against "' modelIDs{i} '"..\n']);
    +0142     end
    +0143     [status, message1]=system(['"' fullfile(ravenPath,'src','external','diamond',['diamond' binEnd]) '" makedb --in "' refFastaFiles{i} '" --db "' fullfile(tmpDB) '"']);
    +0144     if status~=0
    +0145         error('DIAMOND makedb did not run successfully, error:\n%s',strip(message1))
    +0146     end
    +0147     [status, message]=system(['"' fullfile(ravenPath,'src','external','diamond',['diamond' binEnd]) '" blastp --query "' fastaFile{1} '" --out "' outFile '_r' num2str(i) '" --db "' fullfile(tmpDB) '" --more-sensitive --outfmt 6 qseqid sseqid evalue pident length bitscore ppos --threads ' cores]);
    +0148     if developMode
    +0149         diamondReport.dbHashes{numel(diamondReport.dbHashes)+1} = char(regexp(message1,'[a-f0-9]{32}','match'));
    +0150         diamondReport.diamondTxtOutput{numel(diamondReport.diamondTxtOutput)+1}=importdata([outFile '_r' num2str(i)]);
    +0151     end
    +0152     if status~=0
    +0153         error('DIAMOND blastp did not run successfully, error:\n%s',strip(message))
    +0154     end
    +0155     delete([tmpDB filesep 'tmpDB*']);
    +0156 end
    +0157 
    +0158 %Done with the DIAMOND blastp, do the parsing of the text files
    +0159 for i=1:numel(refFastaFiles)*2
    +0160     tempStruct=[];
    +0161     if i<=numel(refFastaFiles)
    +0162         tempStruct.fromId=modelIDs{i};
    +0163         tempStruct.toId=organismID{1};
    +0164         A=readtable([outFile '_' num2str(i)],'Delimiter','\t','Format','%s%s%f%f%f%f%f');
    +0165     else
    +0166         tempStruct.fromId=organismID{1};
    +0167         tempStruct.toId=modelIDs{i-numel(refFastaFiles)};
    +0168         A=readtable([outFile '_r' num2str(i-numel(refFastaFiles))],'Delimiter','\t','Format','%s%s%f%f%f%f%f');
    +0169     end
    +0170     tempStruct.fromGenes=A{:,1};
    +0171     tempStruct.toGenes=A{:,2};
    +0172     tempStruct.evalue=table2array(A(:,3));
    +0173     tempStruct.identity=table2array(A(:,4));
    +0174     tempStruct.aligLen=table2array(A(:,5));
    +0175     tempStruct.bitscore=table2array(A(:,6));
    +0176     tempStruct.ppos=table2array(A(:,7));
    +0177     blastStructure=[blastStructure tempStruct];
    +0178 end
    +0179 
    +0180 %Remove the old tempfiles
    +0181 delete([outFile '*']);
    +0182 %Remove the temp fasta files
    +0183 delete(files{:})
    +0184 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/reconstruction/homology/getModelFromHomology.html b/docs/html/reconstruction/homology/getModelFromHomology.html new file mode 100644 index 00000000..c7a40a77 --- /dev/null +++ b/docs/html/reconstruction/homology/getModelFromHomology.html @@ -0,0 +1,1028 @@ + + + + Description of getModelFromHomology + + + + + + + + + +
    Home > reconstruction > homology > getModelFromHomology.m
    + + + +

    getModelFromHomology +

    + +

    PURPOSE ^

    +
    getModelFromHomology Construct a new model from existing models and homology.
    + +

    SYNOPSIS ^

    +
    function [draftModel, hitGenes]=getModelFromHomology(models,blastStructure,getModelFor,varargin)
    + +

    DESCRIPTION ^

    +
     getModelFromHomology  Construct a new model from existing models and homology.
    +
    + Constructs a new model from a set of existing models and gene homology
    + information.
    +
    + Parameters
    + ----------
    + models : cell
    +     a cell array of model structures to build the model from. These
    +     models must be sorted by importance in decreasing order.
    + blastStructure : struct
    +     a blastStructure as produced by getBlast, getDiamond,
    +     hitsFromSuppOrth, runSuppOrth or makeFakeBlastStructure.
    + getModelFor : char
    +     a three-four letter abbreviation of the organism to build a model
    +     for. Must have BLASTP hits in both directions to the organisms in
    +     "models".
    +
    + Name-Value Arguments
    + --------------------
    + preferredOrder : cell
    +     the order in which reactions are added from the models, as a cell array
    +     of model IDs. If not supplied, reactions are included from all models,
    +     otherwise one gene results in reactions from one model only
    +     (default {}).
    + strictness : double
    +     integer that specifies which reactions are included (default 1):
    +
    +     - 1 : map new genes to old for all pairs which have BLASTP results
    +       that pass the filters (maxE, minLen, minIde) in both directions.
    +     - 2 : map new genes to old for all pairs which have BLASTP results
    +       that pass the filters in one direction, selected by mapNewGenesToOld.
    +     - 3 : apply the filters (maxE, minLen, minIde), retain only the best hit
    +       (highest bitscore, ties included) among the hits that passed them for
    +       each query gene in each direction separately, then map new genes to
    +       old for all pairs which have BLASTP results in both directions.
    + onlyGenesInModels : logical
    +     consider BLASTP results only for genes that exist in the models. This
    +     tends to import a larger fraction from the existing models but may
    +     give less reliable results. Changes the result only if strictness=3
    +     (default false).
    + maxE : double
    +     only look at genes with E-values < this value (default 10^-30). In a
    +     benchmark against KEGG and OMA orthology, with the minIde filter in
    +     place, values between 10^-4 and 10^-50 gave the same result.
    + minLen : double
    +     only look at genes with alignment length >= this value (default 100).
    +     Measured against KEGG and OMA orthology across four organisms: any
    +     value at or below 150 performs the same, while higher values discard
    +     real orthologs without reducing the number of wrong ones.
    + minIde : double
    +     only look at genes with identity >= this value (default 40 (%)).
    +     This filter determines the result. In the calibration, with a wrongly
    +     transferred reaction counted as worse than a missing one, 40 lies in the
    +     35 to 45 range that the KEGG and OMA references favour.
    + mapNewGenesToOld : logical
    +     determines how to match genes when strictness is 2: true maps the new
    +     genes to the old, false maps the old genes to the new (default true).
    + complexPolicy : char
    +     what happens to an "and"-linked subunit without an ortholog (case
    +     insensitive): "flag" keeps the reaction with the subunit renamed
    +     OLD_MODELID_geneName, for review; "keep" drops that subunit;
    +     "drop" drops the whole reaction (default "flag").
    + keepGeneFree : logical
    +     also transfer the template reactions that have no grRule (spontaneous,
    +     transport, exchange, artificial). By default they are dropped, because
    +     no gene supports them in the new organism. When the template is a
    +     curated reference whose gene-free reactions belong in every derived
    +     model, true transfers them. With several templates, a gene-free
    +     reaction comes in once per template (default false).
    + preserveNotes : logical
    +     keep rxnNotes and rxnConfidenceScores of each transferred reaction from
    +     the template. By default the notes say the reaction was included by
    +     getModelFromHomology and the confidence score is 2. The rxnReferences of
    +     the template reactions are always kept (default false).
    +
    + Returns
    + -------
    + draftModel : struct
    +     a model structure for the new organism.
    + hitGenes : struct
    +     structure with the fields oldGenes (the template genes that were
    +     mapped) and newGenes (the corresponding genes of the new organism).
    +
    + Examples
    + --------
    +     draftModel = getModelFromHomology(models, blastStructure, getModelFor);
    +
    + Notes
    + -----
    + The models in the "models" structure should name the metabolites in the
    + same manner, have their reversible reactions in the same direction (run
    + sortModel), and use the same compartment names. To avoid keeping
    + unnecessary old genes, the models should not have "or"-relations in their
    + grRules (use expandModel).
    +
    + The gene ids of the new organism in blastStructure become its gene ids in
    + the draft model, so they cannot contain whitespace, parentheses, "|" or
    + "&". FASTA headers from UniProt, such as "sp|P33284|HXK_KLULA", should be
    + reduced to the accession first.
    +
    + The resulting draft model contains only reactions associated with
    + orthologous genes, plus the gene-free ones with keepGeneFree. With
    + complexPolicy "flag", the old (original) genes involved in "and" relations
    + in grRules without any orthologs are included in the draft model as
    + OLD_MODELID_geneName. A gene that several template genes map to appears
    + once in a grRule. The draft does not keep the version, date or
    + descriptive annotation of the templates.
    +
    + "to" and "from" are relative to the new organism: "to" is a BLAST search from
    + a template organism to the new organism, "from" is a search from the new
    + organism to a template organism.
    +
    + The hits can also come from SuppOrth consensus orthologs (hitsFromSuppOrth,
    + runSuppOrth). Their e-value, identity and alignment length are
    + placeholders that pass the filters, so minSupport and the other filters of
    + hitsFromSuppOrth decide which pairs are used.
    +
    + See also
    + --------
    + getBlast, getDiamond, hitsFromSuppOrth, runSuppOrth, makeFakeBlastStructure
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [draftModel, hitGenes]=getModelFromHomology(models,blastStructure,...
    +0002     getModelFor,varargin)
    +0003 % getModelFromHomology  Construct a new model from existing models and homology.
    +0004 %
    +0005 % Constructs a new model from a set of existing models and gene homology
    +0006 % information.
    +0007 %
    +0008 % Parameters
    +0009 % ----------
    +0010 % models : cell
    +0011 %     a cell array of model structures to build the model from. These
    +0012 %     models must be sorted by importance in decreasing order.
    +0013 % blastStructure : struct
    +0014 %     a blastStructure as produced by getBlast, getDiamond,
    +0015 %     hitsFromSuppOrth, runSuppOrth or makeFakeBlastStructure.
    +0016 % getModelFor : char
    +0017 %     a three-four letter abbreviation of the organism to build a model
    +0018 %     for. Must have BLASTP hits in both directions to the organisms in
    +0019 %     "models".
    +0020 %
    +0021 % Name-Value Arguments
    +0022 % --------------------
    +0023 % preferredOrder : cell
    +0024 %     the order in which reactions are added from the models, as a cell array
    +0025 %     of model IDs. If not supplied, reactions are included from all models,
    +0026 %     otherwise one gene results in reactions from one model only
    +0027 %     (default {}).
    +0028 % strictness : double
    +0029 %     integer that specifies which reactions are included (default 1):
    +0030 %
    +0031 %     - 1 : map new genes to old for all pairs which have BLASTP results
    +0032 %       that pass the filters (maxE, minLen, minIde) in both directions.
    +0033 %     - 2 : map new genes to old for all pairs which have BLASTP results
    +0034 %       that pass the filters in one direction, selected by mapNewGenesToOld.
    +0035 %     - 3 : apply the filters (maxE, minLen, minIde), retain only the best hit
    +0036 %       (highest bitscore, ties included) among the hits that passed them for
    +0037 %       each query gene in each direction separately, then map new genes to
    +0038 %       old for all pairs which have BLASTP results in both directions.
    +0039 % onlyGenesInModels : logical
    +0040 %     consider BLASTP results only for genes that exist in the models. This
    +0041 %     tends to import a larger fraction from the existing models but may
    +0042 %     give less reliable results. Changes the result only if strictness=3
    +0043 %     (default false).
    +0044 % maxE : double
    +0045 %     only look at genes with E-values < this value (default 10^-30). In a
    +0046 %     benchmark against KEGG and OMA orthology, with the minIde filter in
    +0047 %     place, values between 10^-4 and 10^-50 gave the same result.
    +0048 % minLen : double
    +0049 %     only look at genes with alignment length >= this value (default 100).
    +0050 %     Measured against KEGG and OMA orthology across four organisms: any
    +0051 %     value at or below 150 performs the same, while higher values discard
    +0052 %     real orthologs without reducing the number of wrong ones.
    +0053 % minIde : double
    +0054 %     only look at genes with identity >= this value (default 40 (%)).
    +0055 %     This filter determines the result. In the calibration, with a wrongly
    +0056 %     transferred reaction counted as worse than a missing one, 40 lies in the
    +0057 %     35 to 45 range that the KEGG and OMA references favour.
    +0058 % mapNewGenesToOld : logical
    +0059 %     determines how to match genes when strictness is 2: true maps the new
    +0060 %     genes to the old, false maps the old genes to the new (default true).
    +0061 % complexPolicy : char
    +0062 %     what happens to an "and"-linked subunit without an ortholog (case
    +0063 %     insensitive): "flag" keeps the reaction with the subunit renamed
    +0064 %     OLD_MODELID_geneName, for review; "keep" drops that subunit;
    +0065 %     "drop" drops the whole reaction (default "flag").
    +0066 % keepGeneFree : logical
    +0067 %     also transfer the template reactions that have no grRule (spontaneous,
    +0068 %     transport, exchange, artificial). By default they are dropped, because
    +0069 %     no gene supports them in the new organism. When the template is a
    +0070 %     curated reference whose gene-free reactions belong in every derived
    +0071 %     model, true transfers them. With several templates, a gene-free
    +0072 %     reaction comes in once per template (default false).
    +0073 % preserveNotes : logical
    +0074 %     keep rxnNotes and rxnConfidenceScores of each transferred reaction from
    +0075 %     the template. By default the notes say the reaction was included by
    +0076 %     getModelFromHomology and the confidence score is 2. The rxnReferences of
    +0077 %     the template reactions are always kept (default false).
    +0078 %
    +0079 % Returns
    +0080 % -------
    +0081 % draftModel : struct
    +0082 %     a model structure for the new organism.
    +0083 % hitGenes : struct
    +0084 %     structure with the fields oldGenes (the template genes that were
    +0085 %     mapped) and newGenes (the corresponding genes of the new organism).
    +0086 %
    +0087 % Examples
    +0088 % --------
    +0089 %     draftModel = getModelFromHomology(models, blastStructure, getModelFor);
    +0090 %
    +0091 % Notes
    +0092 % -----
    +0093 % The models in the "models" structure should name the metabolites in the
    +0094 % same manner, have their reversible reactions in the same direction (run
    +0095 % sortModel), and use the same compartment names. To avoid keeping
    +0096 % unnecessary old genes, the models should not have "or"-relations in their
    +0097 % grRules (use expandModel).
    +0098 %
    +0099 % The gene ids of the new organism in blastStructure become its gene ids in
    +0100 % the draft model, so they cannot contain whitespace, parentheses, "|" or
    +0101 % "&". FASTA headers from UniProt, such as "sp|P33284|HXK_KLULA", should be
    +0102 % reduced to the accession first.
    +0103 %
    +0104 % The resulting draft model contains only reactions associated with
    +0105 % orthologous genes, plus the gene-free ones with keepGeneFree. With
    +0106 % complexPolicy "flag", the old (original) genes involved in "and" relations
    +0107 % in grRules without any orthologs are included in the draft model as
    +0108 % OLD_MODELID_geneName. A gene that several template genes map to appears
    +0109 % once in a grRule. The draft does not keep the version, date or
    +0110 % descriptive annotation of the templates.
    +0111 %
    +0112 % "to" and "from" are relative to the new organism: "to" is a BLAST search from
    +0113 % a template organism to the new organism, "from" is a search from the new
    +0114 % organism to a template organism.
    +0115 %
    +0116 % The hits can also come from SuppOrth consensus orthologs (hitsFromSuppOrth,
    +0117 % runSuppOrth). Their e-value, identity and alignment length are
    +0118 % placeholders that pass the filters, so minSupport and the other filters of
    +0119 % hitsFromSuppOrth decide which pairs are used.
    +0120 %
    +0121 % See also
    +0122 % --------
    +0123 % getBlast, getDiamond, hitsFromSuppOrth, runSuppOrth, makeFakeBlastStructure
    +0124 
    +0125 hitGenes.oldGenes = [];  % collect the old genes from the template model (organism)
    +0126 hitGenes.newGenes = [];  % collect the new genes of the draft model (target organism)
    +0127 
    +0128 getModelFor=char(getModelFor);
    +0129 
    +0130 p=parseRAVENargs(varargin, {'preferredOrder',[]; 'strictness',1; ...
    +0131     'onlyGenesInModels',false; 'maxE',10^-30; 'minLen',100; 'minIde',40; ...
    +0132     'mapNewGenesToOld',true; 'complexPolicy','flag'; 'keepGeneFree',false; ...
    +0133     'preserveNotes',false});
    +0134 complexPolicy=lower(char(p.complexPolicy));
    +0135 if ~ismember(complexPolicy,{'flag','keep','drop'})
    +0136     error('complexPolicy must be "flag", "keep" or "drop", not "%s"',complexPolicy);
    +0137 end
    +0138 keepGeneFree=p.keepGeneFree;
    +0139 preserveNotes=p.preserveNotes;
    +0140 preferredOrder=p.preferredOrder;
    +0141 if isempty(preferredOrder)
    +0142     preferredOrder=[];
    +0143 else
    +0144     preferredOrder=convertCharArray(preferredOrder);
    +0145     [row,col]=size(preferredOrder);
    +0146     if col>row
    +0147         preferredOrder=transpose(preferredOrder);
    +0148     end
    +0149 end
    +0150 switch p.strictness
    +0151     case 2
    +0152         bidirectional=false; bestHitsOnly=false;
    +0153     case 3
    +0154         bidirectional=true;  bestHitsOnly=true;
    +0155     otherwise
    +0156         bidirectional=true;  bestHitsOnly=false;
    +0157 end
    +0158 onlyGenesInModels=p.onlyGenesInModels;
    +0159 maxE=p.maxE;
    +0160 minLen=p.minLen;
    +0161 minIde=p.minIde;
    +0162 mapNewGenesToOld=p.mapNewGenesToOld;
    +0163 
    +0164 if isfield(models,'S')
    +0165     models={models};
    +0166 end
    +0167     
    +0168 %Check that all the information is in the blast structure
    +0169 modelNames=cell(numel(models),1);
    +0170 for i=1:numel(models)
    +0171     modelNames{i}=models{i}.id;
    +0172     %Gene short names, geneMiriams and proteins are often different
    +0173     %between species, safer not to include them
    +0174     if isfield(models{i},'geneShortNames')
    +0175         models{i}=rmfield(models{i},'geneShortNames');
    +0176     end
    +0177     if isfield(models{i},'geneMiriams')
    +0178         models{i}=rmfield(models{i},'geneMiriams');
    +0179     end
    +0180     if isfield(models{i},'proteins')
    +0181         models{i}=rmfield(models{i},'proteins');
    +0182     end
    +0183     %The geneFrom field also loses meaning if the genes are replaced by
    +0184     %orthologs
    +0185     if isfield(models{i},'geneFrom')
    +0186         models{i}=rmfield(models{i},'geneFrom');
    +0187     end
    +0188 end
    +0189 
    +0190 %Check that genes do not begin with ( or end with ), as this makes problematic grRules
    +0191 for i=1:numel(blastStructure)
    +0192     problemGenes = startsWith(blastStructure(i).fromGenes,'(') | endsWith(blastStructure(i).fromGenes,')');
    +0193     if any(problemGenes)
    +0194         error(['One or multiple gene identifiers from ' blastStructure(i).fromId ...
    +0195                ' starts with ''('' and/or ends with '')'', which is not allowed'])
    +0196     end
    +0197 end
    +0198 
    +0199 %Assume for now that all information is there and that it is correct. This
    +0200 %is important to fix since no further checks are being made!
    +0201 
    +0202 %Check whether provided fasta files use the same gene identifiers as
    +0203 %provided template models
    +0204 for i=1:numel(blastStructure)
    +0205     if ~strcmp(blastStructure(i).fromId,getModelFor)
    +0206         j=strcmpi(blastStructure(i).fromId,modelNames);
    +0207         if j==0
    +0208             error(['While the blastStructure contains sequences from '...
    +0209                 'organismID "%s" (as\nprovided in getBlast), none of '...
    +0210                 'template models have this id (as model.id)'],...
    +0211                 string(blastStructure(i).fromId));
    +0212         end
    +0213         k=sum(ismember(blastStructure(i).fromGenes,models{j}.genes));
    +0214         if k<(numel(models{j}.genes)*0.05)
    +0215             error(['Less than 5%% of the genes in the template model '...
    +0216                 'with model.id "%s"\ncan be found in the blastStructure. '...
    +0217                 'Ensure that the protein FASTA\nused in getBlast and '...
    +0218                 'the template model used in getModelFromHomology\nuse '...
    +0219                 'the same style of gene identifiers'],models{j}.id)
    +0220         end
    +0221     end
    +0222 end
    +0223 
    +0224 %Standardize grRules of template models
    +0225 for i=1:length(models)
    +0226     fprintf('\nStandardizing grRules of template model with ID "%s" ...',models{i}.id);
    +0227     [models{i}.grRules,models{i}.rxnGeneMat]=standardizeGrRules(models{i},false);
    +0228 end
    +0229 fprintf(' done\n');
    +0230 
    +0231 %Remove all gene matches that are below the cutoffs
    +0232 for i=1:numel(blastStructure)
    +0233     indexes=blastStructure(i).evalue<maxE & blastStructure(i).aligLen>=minLen & blastStructure(i).identity>=minIde; %Do it in this direction to lose NaNs
    +0234     blastStructure(i).fromGenes(~indexes)=[];
    +0235     blastStructure(i).toGenes(~indexes)=[];
    +0236     blastStructure(i).evalue(~indexes)=[];
    +0237     blastStructure(i).identity(~indexes)=[];
    +0238     blastStructure(i).aligLen(~indexes)=[];
    +0239     blastStructure(i).bitscore(~indexes)=[];
    +0240     blastStructure(i).ppos(~indexes)=[];
    +0241 end
    +0242 
    +0243 %Remove all reactions from the models that have no genes encoding for them,
    +0244 %unless keepGeneFree. Also remove all genes that encode for no reactions.
    +0245 %There should not be any but there might be mistakes
    +0246 geneFreeRxns=cell(numel(models),1);
    +0247 geneFreeRxns(:)={{}};
    +0248 for i=1:numel(models)
    +0249     [hasGenes, ~]=find(models{i}.rxnGeneMat);
    +0250     hasNoGenes=1:numel(models{i}.rxns);
    +0251     hasNoGenes(hasGenes)=[];
    +0252     if keepGeneFree
    +0253         geneFreeRxns{i}=models{i}.rxns(hasNoGenes);
    +0254     else
    +0255         models{i}=removeReactions(models{i},hasNoGenes,true,true);
    +0256     end
    +0257 end
    +0258 
    +0259 %Create a structure that contains all genes used in the blasts in any
    +0260 %direction for each of the models in "models" and for the new organism. The
    +0261 %first cell is for the new organism and then according to the preferred
    +0262 %order. If no such order is supplied, then according to the order in
    +0263 %"models"
    +0264 allGenes=cell(numel(models)+1,1);
    +0265 if isempty(preferredOrder)
    +0266     useOrder=modelNames;
    +0267 else
    +0268     useOrder=preferredOrder;
    +0269 end
    +0270 
    +0271 %Get the corresponding indexes for those models in the "models" structure
    +0272 useOrderIndexes=zeros(numel(models),1);
    +0273 for i=1:numel(models)
    +0274     [~, index]=ismember(models{i}.id,useOrder);
    +0275     useOrderIndexes(index)=i;
    +0276 end
    +0277 
    +0278 %Remove all genes from the blast structure that have no genes in the models
    +0279 if onlyGenesInModels==true
    +0280     modelGenes={};
    +0281     for i=1:numel(models)
    +0282         modelGenes=[modelGenes;models{i}.genes];
    +0283     end
    +0284     for i=1:numel(blastStructure)
    +0285         %Check to see if it should match the toId or fromId
    +0286         if strcmpi(blastStructure(i).fromId,getModelFor)
    +0287             I=ismember(blastStructure(i).toGenes,modelGenes);
    +0288         else
    +0289             I=ismember(blastStructure(i).fromGenes,modelGenes);
    +0290         end
    +0291         blastStructure(i).fromGenes(~I)=[];
    +0292         blastStructure(i).toGenes(~I)=[];
    +0293         blastStructure(i).evalue(~I)=[];
    +0294         blastStructure(i).identity(~I)=[];
    +0295         blastStructure(i).aligLen(~I)=[];
    +0296         blastStructure(i).bitscore(~I)=[];
    +0297         blastStructure(i).ppos(~I)=[];
    +0298         
    +0299         %Check that no matching in blastStructure is empty. This happens if
    +0300         %no genes in the models are present in the corresponding sheet
    +0301         if isempty(blastStructure(i).fromGenes)
    +0302             EM=['No genes in matching from ' blastStructure(i).fromId ' to ' blastStructure(i).toId ' are present in the corresponding model'];
    +0303             error('RAVEN:badInput', '%s', EM);
    +0304         end
    +0305     end
    +0306 end
    +0307 
    +0308 %If only best orthologs are to be used then all other measurements are
    +0309 %deleted from the blastStructure. All code after this stays the same. This
    +0310 %means that preferred order can still matter. The best ortholog is selected
    +0311 %by bitscore, which unlike E-value does not shift when the search database
    +0312 %changes size.
    +0313 if bestHitsOnly
    +0314     for i=1:numel(blastStructure)
    +0315         keep=false(numel(blastStructure(i).toGenes),1);
    +0316         [allFromGenes, ~, I]=unique(blastStructure(i).fromGenes);
    +0317 
    +0318         %It would be nice to get rid of this loop
    +0319         for j=1:numel(allFromGenes)
    +0320             allMatches=find(I==j);
    +0321             bestMatches=allMatches(blastStructure(i).bitscore(allMatches)==max(blastStructure(i).bitscore(allMatches)));
    +0322 
    +0323             %Keep the best matches
    +0324             keep(bestMatches)=true;
    +0325         end
    +0326 
    +0327         %Delete all matches that were not best matches
    +0328         blastStructure(i).fromGenes(~keep)=[];
    +0329         blastStructure(i).toGenes(~keep)=[];
    +0330         blastStructure(i).evalue(~keep)=[];
    +0331         blastStructure(i).identity(~keep)=[];
    +0332         blastStructure(i).aligLen(~keep)=[];
    +0333         blastStructure(i).bitscore(~keep)=[];
    +0334         blastStructure(i).ppos(~keep)=[];
    +0335     end
    +0336 end
    +0337 
    +0338 useOrder=[{getModelFor};useOrder];
    +0339 
    +0340 for i=1:numel(blastStructure)
    +0341     [~, toIndex]=ismember(blastStructure(i).toId,useOrder);
    +0342     [~, fromIndex]=ismember(blastStructure(i).fromId,useOrder);
    +0343     
    +0344     %Add all genes to the corresponding list in allGenes
    +0345     allGenes{toIndex}=[allGenes{toIndex};blastStructure(i).toGenes];
    +0346     allGenes{fromIndex}=[allGenes{fromIndex};blastStructure(i).fromGenes];
    +0347 end
    +0348 
    +0349 %Keep only the unique gene names
    +0350 maxOtherGeneNr=0; %Determines the dimension of the connectivity matrixes
    +0351 for i=1:numel(allGenes)
    +0352     allGenes{i}=unique(allGenes{i});
    +0353     if i>1
    +0354         if numel(allGenes{i})>maxOtherGeneNr
    +0355             maxOtherGeneNr=numel(allGenes{i});
    +0356         end
    +0357     end
    +0358 end
    +0359 
    +0360 %Generate a cell array of matrixes that describes how the genes in the new
    +0361 %organism map to the models. Each cell matches to the corresponding model
    +0362 %in useOrder (starting at 2 of course). First dimension is gene in new
    +0363 %organism, second which gene it is in the other organism. The second matrix
    +0364 %describes how they map back.
    +0365 
    +0366 %As it is now, a significant match is indicated by a 1. This could be
    +0367 %expanded to contain some kind of significance level. The first dimension
    +0368 %is still the genes in the new model.
    +0369 
    +0370 allTo=cell(numel(useOrder)-1,1);
    +0371 allFrom=cell(numel(useOrder)-1,1);
    +0372 
    +0373 for i=1:numel(useOrder)-1
    +0374     allTo{i}=sparse(numel(allGenes{1}),numel(allGenes{i+1}));
    +0375     allFrom{i}=sparse(numel(allGenes{1}),numel(allGenes{i+1}));
    +0376 end
    +0377 
    +0378 %Fill the matches to other species
    +0379 for i=1:numel(blastStructure)
    +0380     if strcmp(blastStructure(i).toId,getModelFor)
    +0381         %This was "to" the new organism. They should all match so no checks
    +0382         %are being made
    +0383         [~, a]=ismember(blastStructure(i).toGenes,allGenes{1});
    +0384         [~, fromModel]=ismember(blastStructure(i).fromId,useOrder);
    +0385         [~, b]=ismember(blastStructure(i).fromGenes,allGenes{fromModel});
    +0386         idx = sub2ind(size(allTo{fromModel-1}), a, b);
    +0387         allTo{fromModel-1}(idx)=1;
    +0388     else
    +0389         %This was "from" the new organism
    +0390         [~, a]=ismember(blastStructure(i).fromGenes,allGenes{1});
    +0391         [~, toModel]=ismember(blastStructure(i).toId,useOrder);
    +0392         [~, b]=ismember(blastStructure(i).toGenes,allGenes{toModel});
    +0393         idx = sub2ind(size(allFrom{toModel-1}), a, b);
    +0394         allFrom{toModel-1}(idx)=1;
    +0395     end
    +0396 end
    +0397 
    +0398 %Now we have all the gene matches in a convenient way. For all the genes in
    +0399 %the new organism get the genes that should be included from other
    +0400 %organisms. If all genes should be included this simply means keep the
    +0401 %allFrom matrix as it is. If only orthologs which could be mapped in both
    +0402 %BLAST directions are to be included then only those elements are kept.
    +0403 
    +0404 finalMappings=cell(numel(useOrder)-1,1);
    +0405 if bidirectional
    +0406     for j=1:numel(allFrom)
    +0407         finalMappings{j}=allTo{j}~=0 & allFrom{j}~=0;
    +0408     end
    +0409 else
    +0410     if mapNewGenesToOld==true
    +0411         finalMappings=allFrom;
    +0412     else
    +0413         finalMappings=allTo;
    +0414     end
    +0415 end
    +0416 
    +0417 %Remove all genes from the mapping that are not in the models. This does not
    +0418 %do much if only genes in the models were used for the original mapping.
    +0419 %Also simplify the finalMapping and allGenes structures so that they only
    +0420 %contain mappings that exist
    +0421 usedNewGenes=false(numel(allGenes{1}),1);
    +0422 
    +0423 for i=1:numel(allGenes)-1
    +0424     %First remove mappings for those genes that are not in the model
    +0425     if onlyGenesInModels==false
    +0426         a=ismember(allGenes{i+1},models{useOrderIndexes(i)}.genes);
    +0427         finalMappings{i}(:,~a)=false;
    +0428     end
    +0429     
    +0430     %Then remove unused ones and simplify
    +0431     [a, b]=find(finalMappings{i});
    +0432     usedGenes=false(numel(allGenes{i+1}),1);
    +0433     usedNewGenes(a)=true;
    +0434     usedGenes(b)=true;
    +0435     finalMappings{i}=finalMappings{i}(:,usedGenes);
    +0436     allGenes{i+1}=allGenes{i+1}(usedGenes);
    +0437 end
    +0438 
    +0439 %Remove all new genes that have not been mapped to anything
    +0440 allGenes{1}=allGenes{1}(usedNewGenes);
    +0441 for i=1:numel(finalMappings)
    +0442     finalMappings{i}=finalMappings{i}(usedNewGenes,:);
    +0443 end
    +0444 
    +0445 %Gene ids end up in grRules, which are split at whitespace and parentheses,
    +0446 %and which COBRA also splits at "|" and "&"
    +0447 unusable=allGenes{1}(~cellfun(@isempty,regexp(allGenes{1},'[\s()|&]','once')));
    +0448 if ~isempty(unusable)
    +0449     error('RAVEN:badInput',['%d gene ids of "%s" cannot be written into a grRule, '...
    +0450         'because they contain whitespace, parentheses, "|" or "&": %s. Map them '...
    +0451         'to gene ids first, or use the accession alone for UniProt headers such '...
    +0452         'as "sp|P33284|HXK_KLULA".'],numel(unusable),getModelFor,...
    +0453         strjoin(unusable(1:min(3,end))',', '));
    +0454 end
    +0455 
    +0456 %Now is it time to choose which reactions should be included from which
    +0457 %models. If there is a preferred order specified then each gene can only
    +0458 %result in reactions from one model, otherwise they should all be included
    +0459 
    +0460 %Start by simplifying the models by removing genes/reactions that are not
    +0461 %used. This is where it gets weird with complexes, especially "or"
    +0462 %complexes. In this step only reactions which are encoded by one single
    +0463 %gene, or where all genes should be deleted, are deleted. The info on the
    +0464 %full complex is still present in the grRules
    +0465 
    +0466 for i=1:numel(models)
    +0467     a=ismember(models{useOrderIndexes(i)}.genes,allGenes{i+1});
    +0468     
    +0469     %Remove reactions that are not associated to any of the genes in
    +0470     %allGenes, thereby also keeping complexes where only for one of the
    +0471     %genes was matched
    +0472     [rxnsToKeep,~] = find(models{useOrderIndexes(i)}.rxnGeneMat(:,a));
    +0473     rxnsToRemove = repmat(1,numel(models{useOrderIndexes(i)}.rxns),1);
    +0474     rxnsToRemove(rxnsToKeep) = 0;
    +0475     rxnsToRemove(ismember(models{useOrderIndexes(i)}.rxns,geneFreeRxns{useOrderIndexes(i)})) = 0;
    +0476     rxnsToRemove = find(rxnsToRemove);
    +0477     models{useOrderIndexes(i)}=removeReactions(models{useOrderIndexes(i)},rxnsToRemove,true,true,true);
    +0478 end
    +0479 
    +0480 %Since mergeModels function will be used in the end, the models are
    +0481 %simplified further by deleting genes/reactions in the order specified by
    +0482 %preferredOrder. This means that the last model will only contain reactions
    +0483 %for genes that mapped only to that model
    +0484 
    +0485 allUsedGenes=false(numel(allGenes{1}),1);
    +0486 
    +0487 if ~isempty(preferredOrder) && numel(models)>1
    +0488     [usedGenes, ~]=find(finalMappings{1}); %All that are used in the first model in preferredOrder
    +0489     allUsedGenes(usedGenes)=true;
    +0490     for i=2:numel(finalMappings)
    +0491         [usedGenes, ~]=find(finalMappings{i});
    +0492         usedGenes=unique(usedGenes);
    +0493         a=ismember(usedGenes,find(allUsedGenes));
    +0494         
    +0495         [~, genesToDelete]=find(finalMappings{i}(usedGenes(a),:)); %IMPORTANT! IS it really correct to remove all genes that map?
    +0496         genesToDelete=unique(genesToDelete); %Maybe not needed, but for clarity if nothing else
    +0497         
    +0498         %Remove all the genes that were already found and add the other
    +0499         %ones to allUsedGenes
    +0500         if ~isempty(genesToDelete)
    +0501             models{useOrderIndexes(i)}=removeGenes(models{useOrderIndexes(i)},allGenes{i+1}(genesToDelete),true,true,false);
    +0502         end
    +0503         allUsedGenes(usedGenes)=true;
    +0504         
    +0505         %Remove the deleted genes from finalMappings and allGenes.
    +0506         finalMappings{i}(:,genesToDelete)=[];
    +0507         allGenes{i+1}(genesToDelete)=[];
    +0508     end
    +0509 end
    +0510 
    +0511 %Now loop through the models and update the gene associations. Genes not
    +0512 %belonging to the new organism will be renamed as "OLD_MODELID_gene"
    +0513 for i=1:numel(models)
    +0514     %Find all the new genes that should be used for this model
    +0515     [newGenes, oldGenes]=find(finalMappings{i});
    +0516     
    +0517     %Create a new gene list with the genes from the new organism and those
    +0518     %genes that could not be removed
    +0519     replaceableGenes=allGenes{i+1}(unique(oldGenes));
    +0520     nonReplaceableGenes=setdiff(models{useOrderIndexes(i)}.genes,replaceableGenes);
    +0521     fullGeneList=[allGenes{1}(unique(newGenes));nonReplaceableGenes];
    +0522     
    +0523     %Just to save some indexing later. This is the LAST index of
    +0524     %replaceable ones
    +0525     nonRepStartIndex=numel(unique(newGenes));
    +0526     
    +0527     %Construct a new rxnGeneMat
    +0528     newRxnGeneMat=sparse(numel(models{useOrderIndexes(i)}.rxns),numel(fullGeneList));
    +0529     
    +0530     %Now update the rxnGeneMat. This is a little tricky and could
    +0531     %probably be done in a more efficient way, but I just loop through the
    +0532     %reactions and add them one by one
    +0533     for j=1:numel(models{useOrderIndexes(i)}.rxns)
    +0534         %Get the old genes encoding for this reaction
    +0535         [~, oldGeneIds]=find(models{useOrderIndexes(i)}.rxnGeneMat(j,:));
    +0536 
    +0537         %Tokenize the grRule once; all gene substitutions operate on the
    +0538         %token list so that no gene ID can partially match another.
    +0539         tokens=gprTokenize(models{useOrderIndexes(i)}.grRules{j});
    +0540 
    +0541         %Update the matrix for each gene. This includes replacing one gene
    +0542         %with several new ones if there were several matches
    +0543         for k=1:numel(oldGeneIds)
    +0544             %Match the gene to one in the gene list. This is done as a text
    +0545             %match. Could probably be done better, but the indexing is
    +0546             %complex
    +0547 
    +0548             geneName=models{useOrderIndexes(i)}.genes(oldGeneIds(k));
    +0549 
    +0550             %First search in the mappable genes
    +0551             mapIndex=find(ismember(allGenes{i+1},geneName));
    +0552 
    +0553             if ~isempty(mapIndex)
    +0554                 % add the old genes
    +0555                 hitGenes.oldGenes = [hitGenes.oldGenes, {geneName}];
    +0556 
    +0557                 %Get the new genes for that gene
    +0558                 a=find(finalMappings{i}(:,mapIndex));
    +0559 
    +0560                 %Find the positions of these genes in the final gene list
    +0561                 [~, b]=ismember(allGenes{1}(a),fullGeneList);
    +0562 
    +0563                 %Update the matrix
    +0564                 newRxnGeneMat(j,b)=1;
    +0565 
    +0566                 %Build repString for hitGenes (display only)
    +0567                 repString=fullGeneList{b(1)};
    +0568                 if numel(b)>1
    +0569                     for l=2:numel(b)
    +0570                         repString=[repString ' or ' fullGeneList{b(l)}]; %#ok<AGROW>
    +0571                     end
    +0572                     repString=['(' repString ')'];
    +0573                 end
    +0574                 hitGenes.newGenes = [hitGenes.newGenes, {repString}];
    +0575 
    +0576                 %Replace the old gene token with the new gene(s)
    +0577                 tokens=gprReplaceToken(tokens,geneName{1},fullGeneList(b));
    +0578             else
    +0579                 %Then search in the non-replaceable genes. There could only
    +0580                 %be one match here
    +0581                 index=find(ismember(nonReplaceableGenes,geneName));
    +0582 
    +0583                 %Update the matrix
    +0584                 newRxnGeneMat(j,nonRepStartIndex+index)=1;
    +0585 
    +0586                 oldName=strcat('OLD_',models{useOrderIndexes(i)}.id,'_',geneName{1});
    +0587                 tokens=gprReplaceToken(tokens,geneName{1},{oldName});
    +0588             end
    +0589         end
    +0590 
    +0591         models{useOrderIndexes(i)}.grRules{j}=gprBuildRule(tokens);
    +0592     end
    +0593     
    +0594     %Add the new list of genes
    +0595     models{useOrderIndexes(i)}.rxnGeneMat=newRxnGeneMat;
    +0596     if ~isempty(nonReplaceableGenes)
    +0597         models{useOrderIndexes(i)}.genes=[allGenes{1}(unique(newGenes));strcat('OLD_',models{useOrderIndexes(i)}.id,'_',nonReplaceableGenes)];
    +0598     else
    +0599         models{useOrderIndexes(i)}.genes=allGenes{1}(unique(newGenes));
    +0600     end
    +0601     if isfield(models{useOrderIndexes(i)},'geneComps')
    +0602         geneComps=models{useOrderIndexes(i)}.geneComps(1);
    +0603         models{useOrderIndexes(i)}.geneComps=zeros(numel(models{useOrderIndexes(i)}.genes),1);
    +0604         %Assume that all genes are in the same compartment, and this
    +0605         %compartment is specified for the first gene
    +0606         models{useOrderIndexes(i)}.geneComps(:)=geneComps;
    +0607     end
    +0608 end
    +0609 
    +0610 %Now merge the models. All information should be correct except for "or"
    +0611 %complexes
    +0612 draftModel=mergeModels(models,'metNames');
    +0613 
    +0614 %Remove OLD_ genes that appear only in OR branches (they were added for
    +0615 %AND-complex genes with no orthologs; in OR branches they are redundant).
    +0616 %Uses token-level removal to avoid partial-match hazards.
    +0617 for oldI=1:numel(draftModel.grRules)
    +0618     if ~contains(draftModel.grRules{oldI},'OLD_')
    +0619         continue
    +0620     end
    +0621     tkns=gprTokenize(draftModel.grRules{oldI});
    +0622     again=true;
    +0623     while again
    +0624         again=false;
    +0625         for kk=1:numel(tkns)
    +0626             if startsWith(tkns{kk},'OLD_')
    +0627                 if kk>1 && strcmp(tkns{kk-1},'or')
    +0628                     tkns(kk-1:kk)=[];
    +0629                     again=true; break
    +0630                 elseif kk<numel(tkns) && strcmp(tkns{kk+1},'or')
    +0631                     tkns(kk:kk+1)=[];
    +0632                     again=true; break
    +0633                 end
    +0634             end
    +0635         end
    +0636     end
    +0637     draftModel.grRules{oldI}=gprBuildRule(tkns);
    +0638 end
    +0639 
    +0640 %Apply the complex policy to the OLD_ genes left in "and" relations, and
    +0641 %write each grRule without repeated genes or redundant brackets
    +0642 toRemove=false(numel(draftModel.rxns),1);
    +0643 for j=1:numel(draftModel.grRules)
    +0644     rule=draftModel.grRules{j};
    +0645     if isempty(rule)
    +0646         continue
    +0647     end
    +0648     if contains(rule,'OLD_') && strcmp(complexPolicy,'drop')
    +0649         toRemove(j)=true;
    +0650     elseif contains(rule,'OLD_') && strcmp(complexPolicy,'keep')
    +0651         draftModel.grRules{j}=gprSimplify(rule,'OLD_');
    +0652         toRemove(j)=isempty(draftModel.grRules{j});
    +0653     else
    +0654         draftModel.grRules{j}=gprSimplify(rule,'');
    +0655     end
    +0656 end
    +0657 if any(toRemove)
    +0658     draftModel=removeReactions(draftModel,draftModel.rxns(toRemove),true,true,true);
    +0659 end
    +0660 
    +0661 %Change name of the resulting model
    +0662 draftModel.id=getModelFor;
    +0663 name='Generated by getModelFromHomology using ';
    +0664 for i=1:numel(models)
    +0665     if i<numel(models)
    +0666         name=[name models{i}.id ', '];
    +0667     else
    +0668         name=[name models{i}.id];
    +0669     end
    +0670 end
    +0671 draftModel.name=name;
    +0672 %The draft is a new model: the templates' version, date and description are
    +0673 %not its own
    +0674 draftModel=rmfield(draftModel,intersect({'version','date','description'},fieldnames(draftModel)));
    +0675 if isfield(draftModel,'annotation')
    +0676     descriptive={'taxonomy','sourceUrl','note','givenName','familyName','authors','email','organization'};
    +0677     draftModel.annotation=rmfield(draftModel.annotation,intersect(descriptive,fieldnames(draftModel.annotation)));
    +0678 end
    +0679 if ~preserveNotes || ~isfield(draftModel,'rxnNotes')
    +0680     draftModel.rxnNotes=cell(length(draftModel.rxns),1);
    +0681     draftModel.rxnNotes(:)={'Included by getModelFromHomology'};
    +0682 end
    +0683 if ~preserveNotes || ~isfield(draftModel,'rxnConfidenceScores')
    +0684     draftModel.rxnConfidenceScores=NaN(length(draftModel.rxns),1);
    +0685     draftModel.rxnConfidenceScores(:)=2;
    +0686 end
    +0687 draftModel=deleteUnusedGenes(draftModel,0);
    +0688 %Standardize grRules and notify if problematic grRules are found
    +0689 [draftModel.grRules,draftModel.rxnGeneMat]=standardizeGrRules(draftModel,false);
    +0690 draftModel=deleteUnusedGenes(draftModel,false);
    +0691 end
    +0692 
    +0693 
    +0694 function tokens=gprTokenize(rule)
    +0695 % Split a GPR rule into tokens: gene IDs, "and", "or", "(", ")".
    +0696 % Brackets are separated from adjacent text by whitespace before splitting.
    +0697 if isempty(rule)
    +0698     tokens={};
    +0699     return
    +0700 end
    +0701 r=regexprep(rule,'([\(\)])',' $1 ');
    +0702 r=regexprep(r,'\s+',' ');
    +0703 tokens=strsplit(strtrim(r),' ');
    +0704 tokens=tokens(~cellfun(@isempty,tokens));
    +0705 end
    +0706 
    +0707 
    +0708 function rule=gprBuildRule(tokens)
    +0709 % Reconstruct a GPR rule string from a token list.
    +0710 if isempty(tokens)
    +0711     rule='';
    +0712     return
    +0713 end
    +0714 rule=strjoin(tokens,' ');
    +0715 rule=strrep(rule,'( ','(');
    +0716 rule=strrep(rule,' )',')');
    +0717 end
    +0718 
    +0719 
    +0720 function tokens=gprReplaceToken(tokens,oldGene,newGenes)
    +0721 % Replace each occurrence of token oldGene with the gene(s) in newGenes.
    +0722 % newGenes is a cell array; multiple genes are wrapped in parentheses and
    +0723 % joined with " or ".
    +0724 k=1;
    +0725 while k<=numel(tokens)
    +0726     if strcmp(tokens{k},oldGene)
    +0727         if numel(newGenes)==1
    +0728             tokens{k}=newGenes{1};
    +0729             k=k+1;
    +0730         else
    +0731             seq={'('};
    +0732             for g=1:numel(newGenes)
    +0733                 seq{end+1}=newGenes{g}; %#ok<AGROW>
    +0734                 if g<numel(newGenes)
    +0735                     seq{end+1}='or'; %#ok<AGROW>
    +0736                 end
    +0737             end
    +0738             seq{end+1}=')';
    +0739             tokens=[tokens(1:k-1),seq,tokens(k+1:end)];
    +0740             k=k+numel(seq);
    +0741         end
    +0742     else
    +0743         k=k+1;
    +0744     end
    +0745 end
    +0746 end
    +0747 
    +0748 
    +0749 function rule=gprSimplify(rule,removePrefix)
    +0750 % Rewrite a grRule as a tree: genes starting with removePrefix (if any) are
    +0751 % dropped from their group, groups of the same operator are flattened,
    +0752 % repeated genes or groups are removed, and brackets appear only around a
    +0753 % group of the other operator. A rule with no gene left becomes ''.
    +0754 tokens=gprTokenize(rule);
    +0755 if isempty(tokens)
    +0756     rule='';
    +0757     return
    +0758 end
    +0759 [node,~]=gprParseOr(tokens,1);
    +0760 node=gprPrune(node,removePrefix);
    +0761 if isempty(node)
    +0762     rule='';
    +0763 else
    +0764     rule=gprRender(node,'');
    +0765 end
    +0766 end
    +0767 
    +0768 
    +0769 function [node,pos]=gprParseOr(tokens,pos)
    +0770 [node,pos]=gprParseAnd(tokens,pos);
    +0771 kids={node};
    +0772 while pos<=numel(tokens) && strcmpi(tokens{pos},'or')
    +0773     [node,pos]=gprParseAnd(tokens,pos+1);
    +0774     kids{end+1}=node; %#ok<AGROW>
    +0775 end
    +0776 if numel(kids)>1
    +0777     node=struct('op','or','gene','','kids',{kids});
    +0778 end
    +0779 end
    +0780 
    +0781 
    +0782 function [node,pos]=gprParseAnd(tokens,pos)
    +0783 [node,pos]=gprParseAtom(tokens,pos);
    +0784 kids={node};
    +0785 while pos<=numel(tokens) && strcmpi(tokens{pos},'and')
    +0786     [node,pos]=gprParseAtom(tokens,pos+1);
    +0787     kids{end+1}=node; %#ok<AGROW>
    +0788 end
    +0789 if numel(kids)>1
    +0790     node=struct('op','and','gene','','kids',{kids});
    +0791 end
    +0792 end
    +0793 
    +0794 
    +0795 function [node,pos]=gprParseAtom(tokens,pos)
    +0796 if pos>numel(tokens)
    +0797     error('Malformed grRule: it ends with an operator');
    +0798 end
    +0799 if strcmp(tokens{pos},'(')
    +0800     [node,pos]=gprParseOr(tokens,pos+1);
    +0801     if pos>numel(tokens) || ~strcmp(tokens{pos},')')
    +0802         error('Malformed grRule: unbalanced brackets');
    +0803     end
    +0804     pos=pos+1;
    +0805 else
    +0806     node=struct('op','gene','gene',tokens{pos},'kids',{{}});
    +0807     pos=pos+1;
    +0808 end
    +0809 end
    +0810 
    +0811 
    +0812 function node=gprPrune(node,removePrefix)
    +0813 if strcmp(node.op,'gene')
    +0814     if ~isempty(removePrefix) && startsWith(node.gene,removePrefix)
    +0815         node=[];
    +0816     end
    +0817     return
    +0818 end
    +0819 kids={};
    +0820 seen={};
    +0821 for k=1:numel(node.kids)
    +0822     kid=gprPrune(node.kids{k},removePrefix);
    +0823     if isempty(kid)
    +0824         continue
    +0825     end
    +0826     if strcmp(kid.op,node.op)
    +0827         parts=kid.kids;
    +0828     else
    +0829         parts={kid};
    +0830     end
    +0831     for m=1:numel(parts)
    +0832         key=gprRender(parts{m},node.op);
    +0833         if ~ismember(key,seen)
    +0834             seen{end+1}=key; %#ok<AGROW>
    +0835             kids{end+1}=parts{m}; %#ok<AGROW>
    +0836         end
    +0837     end
    +0838 end
    +0839 if isempty(kids)
    +0840     node=[];
    +0841 elseif numel(kids)==1
    +0842     node=kids{1};
    +0843 else
    +0844     node.kids=kids;
    +0845 end
    +0846 end
    +0847 
    +0848 
    +0849 function rule=gprRender(node,parentOp)
    +0850 if strcmp(node.op,'gene')
    +0851     rule=node.gene;
    +0852     return
    +0853 end
    +0854 parts=cellfun(@(k) gprRender(k,node.op),node.kids,'UniformOutput',false);
    +0855 rule=strjoin(parts,[' ' node.op ' ']);
    +0856 if ~isempty(parentOp) && ~strcmp(parentOp,node.op)
    +0857     rule=['(' rule ')'];
    +0858 end
    +0859 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/reconstruction/homology/hitsFromSuppOrth.html b/docs/html/reconstruction/homology/hitsFromSuppOrth.html new file mode 100644 index 00000000..64cff190 --- /dev/null +++ b/docs/html/reconstruction/homology/hitsFromSuppOrth.html @@ -0,0 +1,466 @@ + + + + Description of hitsFromSuppOrth + + + + + + + + + +
    Home > reconstruction > homology > hitsFromSuppOrth.m
    + + + +

    hitsFromSuppOrth +

    + +

    PURPOSE ^

    +
    hitsFromSuppOrth Read SuppOrth consensus orthologs into a blastStructure.
    + +

    SYNOPSIS ^

    +
    function [blastStructure,suppOrthInfo]=hitsFromSuppOrth(source,organismID,varargin)
    + +

    DESCRIPTION ^

    +
     hitsFromSuppOrth  Read SuppOrth consensus orthologs into a blastStructure.
    +
    + SuppOrth (https://github.com/fma91/SuppOrth) runs several ortholog
    + predictors (OrthoFinder, SonicParanoid2, Broccoli, and optionally an
    + imported OMA run) on a set of proteomes and writes suppOrth.tsv: one row
    + per cross-species protein pair, with the number of predictors that
    + support it. This function turns that table into a blastStructure for
    + getModelFromHomology.
    +
    + Parameters
    + ----------
    + source : char or table
    +     path to suppOrth.tsv, or a MATLAB table with its columns. Protein
    +     ids are read as text, so ids such as "NA" or "0123" are kept.
    + organismID : char
    +     SuppOrth species label of the organism to build a model for: the
    +     FASTA file name without extension, or the --labels value. It becomes
    +     fromId and toId unless speciesToModel maps it to another id.
    +
    + Name-Value Arguments
    + --------------------
    + speciesToModel : cell or containers.Map
    +     SuppOrth species label to model id, as an N-by-2 cell array or a
    +     containers.Map. A label mapped to '' is left out. When given, every
    +     label in the table must be included. When empty, labels are used as
    +     model ids (default []).
    + minSupport : double
    +     minimum number of predictors that found the pair (Lv_support).
    +     SuppOrth has already filtered the table with its own --supportLv and
    +     --identity_filter; this filter is applied in addition (default 1).
    + requireTools : cell
    +     predictors that must all have found the pair, by column name
    +     ('orthofinder', 'sonicparanoid', 'broccoli', 'oma') (default {}).
    + minIdentity : double
    +     minimum SuppOrth identity. A pair without an alignment hit fails this
    +     filter (default [], no filter).
    + maxEvalue : double
    +     maximum SuppOrth e-value. A pair without an alignment hit fails this
    +     filter (default [], no filter).
    + proteinToGene : cell, containers.Map or char
    +     protein id to model gene id, as an N-by-2 cell array, a
    +     containers.Map, a JSON file with one object of strings, or a
    +     two-column tab-separated file (lines starting with # are skipped).
    +     Proteins missing from the map keep their id, and their number is
    +     reported in a warning. A gene pair takes the highest support over its
    +     protein pairs, the union of their predictors, the highest identity
    +     and bitscore, and the lowest e-value; these values can come from
    +     different protein pairs (default [], no mapping).
    +
    + Returns
    + -------
    + blastStructure : struct
    +     one element per search direction between organismID and each other
    +     species, as from getBlast or getDiamond. Every pair is written in
    +     both directions, so strictness 1 and 2 of getModelFromHomology give
    +     the same result. evalue, identity, aligLen and ppos are placeholders
    +     (0, 100, 1000, 100) that pass the filters of getModelFromHomology;
    +     bitscore is SuppOrth's value (0 when it has none), which strictness 3
    +     ranks by.
    + suppOrthInfo : table
    +     one row per gene pair, with modelId, newGene, templateGene, support,
    +     supportedBy, identity, evalue and bitscore (SuppOrth's values).
    +
    + Notes
    + -----
    + Only pairs between organismID and another species are kept. Species that
    + are not templates in getModelFromHomology have to be left out with
    + speciesToModel, because getModelFromHomology requires every id in the
    + blastStructure to be the new organism or a template.
    +
    + Examples
    + --------
    +     blastStructure = hitsFromSuppOrth('out/suppOrth.tsv','klac',...
    +        'minSupport',2,'proteinToGene','klac_genes.tsv');
    +
    + See also
    + --------
    + runSuppOrth, getModelFromHomology, getDiamond
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    • runSuppOrth runSuppOrth Run SuppOrth on an organism and template proteomes.
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [blastStructure,suppOrthInfo]=hitsFromSuppOrth(source,organismID,varargin)
    +0002 % hitsFromSuppOrth  Read SuppOrth consensus orthologs into a blastStructure.
    +0003 %
    +0004 % SuppOrth (https://github.com/fma91/SuppOrth) runs several ortholog
    +0005 % predictors (OrthoFinder, SonicParanoid2, Broccoli, and optionally an
    +0006 % imported OMA run) on a set of proteomes and writes suppOrth.tsv: one row
    +0007 % per cross-species protein pair, with the number of predictors that
    +0008 % support it. This function turns that table into a blastStructure for
    +0009 % getModelFromHomology.
    +0010 %
    +0011 % Parameters
    +0012 % ----------
    +0013 % source : char or table
    +0014 %     path to suppOrth.tsv, or a MATLAB table with its columns. Protein
    +0015 %     ids are read as text, so ids such as "NA" or "0123" are kept.
    +0016 % organismID : char
    +0017 %     SuppOrth species label of the organism to build a model for: the
    +0018 %     FASTA file name without extension, or the --labels value. It becomes
    +0019 %     fromId and toId unless speciesToModel maps it to another id.
    +0020 %
    +0021 % Name-Value Arguments
    +0022 % --------------------
    +0023 % speciesToModel : cell or containers.Map
    +0024 %     SuppOrth species label to model id, as an N-by-2 cell array or a
    +0025 %     containers.Map. A label mapped to '' is left out. When given, every
    +0026 %     label in the table must be included. When empty, labels are used as
    +0027 %     model ids (default []).
    +0028 % minSupport : double
    +0029 %     minimum number of predictors that found the pair (Lv_support).
    +0030 %     SuppOrth has already filtered the table with its own --supportLv and
    +0031 %     --identity_filter; this filter is applied in addition (default 1).
    +0032 % requireTools : cell
    +0033 %     predictors that must all have found the pair, by column name
    +0034 %     ('orthofinder', 'sonicparanoid', 'broccoli', 'oma') (default {}).
    +0035 % minIdentity : double
    +0036 %     minimum SuppOrth identity. A pair without an alignment hit fails this
    +0037 %     filter (default [], no filter).
    +0038 % maxEvalue : double
    +0039 %     maximum SuppOrth e-value. A pair without an alignment hit fails this
    +0040 %     filter (default [], no filter).
    +0041 % proteinToGene : cell, containers.Map or char
    +0042 %     protein id to model gene id, as an N-by-2 cell array, a
    +0043 %     containers.Map, a JSON file with one object of strings, or a
    +0044 %     two-column tab-separated file (lines starting with # are skipped).
    +0045 %     Proteins missing from the map keep their id, and their number is
    +0046 %     reported in a warning. A gene pair takes the highest support over its
    +0047 %     protein pairs, the union of their predictors, the highest identity
    +0048 %     and bitscore, and the lowest e-value; these values can come from
    +0049 %     different protein pairs (default [], no mapping).
    +0050 %
    +0051 % Returns
    +0052 % -------
    +0053 % blastStructure : struct
    +0054 %     one element per search direction between organismID and each other
    +0055 %     species, as from getBlast or getDiamond. Every pair is written in
    +0056 %     both directions, so strictness 1 and 2 of getModelFromHomology give
    +0057 %     the same result. evalue, identity, aligLen and ppos are placeholders
    +0058 %     (0, 100, 1000, 100) that pass the filters of getModelFromHomology;
    +0059 %     bitscore is SuppOrth's value (0 when it has none), which strictness 3
    +0060 %     ranks by.
    +0061 % suppOrthInfo : table
    +0062 %     one row per gene pair, with modelId, newGene, templateGene, support,
    +0063 %     supportedBy, identity, evalue and bitscore (SuppOrth's values).
    +0064 %
    +0065 % Notes
    +0066 % -----
    +0067 % Only pairs between organismID and another species are kept. Species that
    +0068 % are not templates in getModelFromHomology have to be left out with
    +0069 % speciesToModel, because getModelFromHomology requires every id in the
    +0070 % blastStructure to be the new organism or a template.
    +0071 %
    +0072 % Examples
    +0073 % --------
    +0074 %     blastStructure = hitsFromSuppOrth('out/suppOrth.tsv','klac',...
    +0075 %        'minSupport',2,'proteinToGene','klac_genes.tsv');
    +0076 %
    +0077 % See also
    +0078 % --------
    +0079 % runSuppOrth, getModelFromHomology, getDiamond
    +0080 
    +0081 p=parseRAVENargs(varargin, {'speciesToModel',[]; 'minSupport',1; ...
    +0082     'requireTools',{}; 'minIdentity',[]; 'maxEvalue',[]; 'proteinToGene',[]});
    +0083 organismID=char(organismID);
    +0084 requireTools=cellstr(p.requireTools);
    +0085 if isempty(p.requireTools)
    +0086     requireTools={};
    +0087 end
    +0088 
    +0089 T=readTable(source);
    +0090 columns=T.Properties.VariableNames;
    +0091 
    +0092 labels=unique([T.Species1;T.Species2]);
    +0093 if ~ismember(organismID,labels)
    +0094     error('RAVEN:badInput','organismID "%s" is not a species in the table: %s',...
    +0095         organismID,strjoin(labels,', '));
    +0096 end
    +0097 if isempty(p.speciesToModel)
    +0098     idOf=containers.Map(labels,labels);
    +0099 else
    +0100     idOf=toMap(p.speciesToModel);
    +0101     unmapped=labels(~isKey(idOf,labels));
    +0102     if ~isempty(unmapped)
    +0103         error('RAVEN:badInput',['speciesToModel has no entry for %s; map each '...
    +0104             'label to a model id, or to '''' to leave it out.'],strjoin(unmapped,', '));
    +0105     end
    +0106     if isempty(idOf(organismID))
    +0107         error('RAVEN:badInput','speciesToModel maps organismID "%s" to ''''.',organismID);
    +0108     end
    +0109 end
    +0110 
    +0111 %The per-predictor 0/1 columns are those between SupportedBy and Identity
    +0112 alignment={'Identity','e_val','Bitscore'};
    +0113 start=find(strcmp(columns,'SupportedBy'))+1;
    +0114 stop=find(strcmp(columns,'Identity'))-1;
    +0115 if isempty(stop)
    +0116     stop=numel(columns);
    +0117 end
    +0118 toolColumns=setdiff(columns(start:stop),alignment,'stable');
    +0119 unknown=setdiff(requireTools,toolColumns,'stable');
    +0120 if ~isempty(unknown)
    +0121     error('RAVEN:badInput','requireTools %s not in the table; its predictors are %s',...
    +0122         strjoin(unknown,', '),strjoin(toolColumns,', '));
    +0123 end
    +0124 
    +0125 keep=T.Lv_support>=p.minSupport;
    +0126 for i=1:numel(requireTools)
    +0127     keep=keep & toNumber(T.(requireTools{i}))==1;
    +0128 end
    +0129 if ~isempty(p.minIdentity)
    +0130     keep=keep & T.Identity>=p.minIdentity; %NaN fails
    +0131 end
    +0132 if ~isempty(p.maxEvalue)
    +0133     keep=keep & T.e_val<=p.maxEvalue; %NaN fails
    +0134 end
    +0135 T=T(keep,:);
    +0136 
    +0137 %Orient every pair as (organism protein, template species, template protein)
    +0138 first=strcmp(T.Species1,organismID) & ~strcmp(T.Species2,organismID);
    +0139 second=strcmp(T.Species2,organismID) & ~strcmp(T.Species1,organismID);
    +0140 newProt=[T.Protein1(first);T.Protein2(second)];
    +0141 label=[T.Species2(first);T.Species1(second)];
    +0142 template=[T.Protein2(first);T.Protein1(second)];
    +0143 support=[T.Lv_support(first);T.Lv_support(second)];
    +0144 supportedBy=[T.SupportedBy(first);T.SupportedBy(second)];
    +0145 identity=[T.Identity(first);T.Identity(second)];
    +0146 evalue=[T.e_val(first);T.e_val(second)];
    +0147 bitscore=[T.Bitscore(first);T.Bitscore(second)];
    +0148 modelId=values(idOf,label);
    +0149 modelId=modelId(:);
    +0150 I=~cellfun(@isempty,modelId);
    +0151 [newProt,template,modelId,support,supportedBy,identity,evalue,bitscore]=...
    +0152     deal(newProt(I),template(I),modelId(I),support(I),supportedBy(I),...
    +0153     identity(I),evalue(I),bitscore(I));
    +0154 
    +0155 if ~isempty(p.proteinToGene)
    +0156     geneOf=toMap(p.proteinToGene);
    +0157     proteins=unique([newProt;template]);
    +0158     nMissing=sum(~isKey(geneOf,proteins));
    +0159     if nMissing>0
    +0160         warning('RAVEN:hitsFromSuppOrth:unmappedProteins',['%d of %d proteins are not '...
    +0161             'in proteinToGene and keep their protein id.'],nMissing,numel(proteins));
    +0162     end
    +0163     newProt=mapIds(geneOf,newProt);
    +0164     template=mapIds(geneOf,template);
    +0165 end
    +0166 
    +0167 if isempty(newProt)
    +0168     error('RAVEN:badInput',['No SuppOrth pairs between "%s" and a template species '...
    +0169         'remain after filtering.'],organismID);
    +0170 end
    +0171 
    +0172 %One row per gene pair, sorted by model, new gene and template gene
    +0173 key=strcat(modelId,char(31),newProt,char(31),template);
    +0174 [key,firstRow,group]=unique(key);
    +0175 info=table();
    +0176 info.modelId=modelId(firstRow);
    +0177 info.newGene=newProt(firstRow);
    +0178 info.templateGene=template(firstRow);
    +0179 info.support=accumarray(group,support,[],@max);
    +0180 rows=accumarray(group,(1:numel(group))',[],@(r) {r});
    +0181 info.supportedBy=cellfun(@(r) joinLabels(supportedBy(r)),rows,'UniformOutput',false);
    +0182 info.identity=accumarray(group,identity,[],@max);
    +0183 info.evalue=accumarray(group,evalue,[],@min);
    +0184 info.bitscore=accumarray(group,bitscore,[],@max);
    +0185 suppOrthInfo=info;
    +0186 
    +0187 newId=idOf(organismID);
    +0188 score=info.bitscore;
    +0189 score(isnan(score))=0;
    +0190 models=unique(info.modelId);
    +0191 blastStructure=[];
    +0192 for direction=1:2
    +0193     for i=1:numel(models)
    +0194         r=strcmp(info.modelId,models{i});
    +0195         element=[];
    +0196         if direction==1
    +0197             element.fromId=models{i};
    +0198             element.toId=newId;
    +0199             element.fromGenes=info.templateGene(r);
    +0200             element.toGenes=info.newGene(r);
    +0201         else
    +0202             element.fromId=newId;
    +0203             element.toId=models{i};
    +0204             element.fromGenes=info.newGene(r);
    +0205             element.toGenes=info.templateGene(r);
    +0206         end
    +0207         n=sum(r);
    +0208         element.evalue=zeros(n,1);
    +0209         element.identity=100*ones(n,1);
    +0210         element.aligLen=1000*ones(n,1);
    +0211         element.bitscore=score(r);
    +0212         element.ppos=100*ones(n,1);
    +0213         blastStructure=[blastStructure element]; %#ok<AGROW>
    +0214     end
    +0215 end
    +0216 end
    +0217 
    +0218 function T=readTable(source)
    +0219 required={'Species1','Species2','Protein1','Protein2','Lv_support','SupportedBy'};
    +0220 text=[required(1:4),{'SupportedBy'}];
    +0221 if istable(source)
    +0222     T=source;
    +0223 else
    +0224     source=char(source);
    +0225     if ~isfile(source)
    +0226         error('RAVEN:badInput','SuppOrth table "%s" does not exist.',source);
    +0227     end
    +0228     lines=splitlines(fileread(source));
    +0229     lines=lines(~cellfun(@isempty,lines));
    +0230     header=strsplit(lines{1},'\t','CollapseDelimiters',false);
    +0231     fields=regexp(lines(2:end),'\t','split');
    +0232     width=cellfun(@numel,fields);
    +0233     bad=find(width~=numel(header),1);
    +0234     if ~isempty(bad)
    +0235         error('RAVEN:badInput','%s: line %d has %d fields, the header has %d.',...
    +0236             source,bad+1,width(bad),numel(header));
    +0237     end
    +0238     fields=vertcat(fields{:});
    +0239     if isempty(fields)
    +0240         fields=cell(0,numel(header));
    +0241     end
    +0242     T=cell2table(fields,'VariableNames',header);
    +0243 end
    +0244 missing=setdiff(required,T.Properties.VariableNames,'stable');
    +0245 if ~isempty(missing)
    +0246     error('RAVEN:badInput',['Not a suppOrth.tsv table (SuppOrth 0.4): missing columns %s. '...
    +0247         'suppOrth.genes.tsv is not supported; use proteinToGene instead.'],strjoin(missing,', '));
    +0248 end
    +0249 %Protein ids such as "NA" or "0123" stay text
    +0250 for i=1:numel(text)
    +0251     T.(text{i})=cellstr(string(T.(text{i})));
    +0252 end
    +0253 for c={'Identity','e_val','Bitscore'}
    +0254     if ismember(c{1},T.Properties.VariableNames)
    +0255         T.(c{1})=toNumber(T.(c{1}));
    +0256     else
    +0257         T.(c{1})=nan(height(T),1);
    +0258     end
    +0259 end
    +0260 T.Lv_support=toNumber(T.Lv_support);
    +0261 end
    +0262 
    +0263 function x=toNumber(x)
    +0264 %Read numbers exactly; empty and non-numeric fields become NaN
    +0265 if ~isnumeric(x)
    +0266     x=str2double(x);
    +0267 end
    +0268 x=double(x(:));
    +0269 end
    +0270 
    +0271 function m=toMap(x)
    +0272 if isa(x,'containers.Map')
    +0273     m=x;
    +0274 elseif iscell(x)
    +0275     if size(x,2)~=2
    +0276         error('RAVEN:badInput','A cell array map needs two columns.');
    +0277     end
    +0278     m=containers.Map(cellstr(string(x(:,1))),cellstr(string(x(:,2))));
    +0279 elseif ischar(x) || isstring(x)
    +0280     m=readMapFile(char(x));
    +0281 else
    +0282     error('RAVEN:badInput','A map must be a two-column cell array, a containers.Map or a file.');
    +0283 end
    +0284 end
    +0285 
    +0286 function m=readMapFile(file)
    +0287 [~,~,ext]=fileparts(file);
    +0288 if any(strcmpi(ext,{'.pkl','.pickle'}))
    +0289     error('RAVEN:badInput',['%s: pickle maps are not read; use a cell array, '...
    +0290         'a JSON file or a two-column tab-separated file.'],file);
    +0291 end
    +0292 if ~isfile(file)
    +0293     error('RAVEN:badInput','Map file "%s" does not exist.',file);
    +0294 end
    +0295 text=fileread(file);
    +0296 if strcmpi(ext,'.json')
    +0297     %Keys are protein ids, which jsondecode would turn into field names
    +0298     pairs=regexp(text,'"((?:[^"\\]|\\.)*)"\s*:\s*"((?:[^"\\]|\\.)*)"','tokens');
    +0299     pairs=vertcat(pairs{:});
    +0300     if isempty(pairs)
    +0301         m=containers.Map('KeyType','char','ValueType','char');
    +0302         return
    +0303     end
    +0304     pairs=regexprep(pairs,'\\(["\\/])','$1');
    +0305     m=containers.Map(pairs(:,1),pairs(:,2));
    +0306     return
    +0307 end
    +0308 lines=splitlines(text);
    +0309 lines=lines(~cellfun(@isempty,strtrim(lines)) & ~startsWith(lines,'#'));
    +0310 fields=regexp(lines,'\t','split');
    +0311 short=find(cellfun(@numel,fields)<2,1);
    +0312 if ~isempty(short)
    +0313     error('RAVEN:badInput','%s: expected two tab-separated columns, got "%s"',file,lines{short});
    +0314 end
    +0315 keys=strtrim(cellfun(@(f) f{1},fields,'UniformOutput',false));
    +0316 vals=strtrim(cellfun(@(f) f{2},fields,'UniformOutput',false));
    +0317 if isempty(keys)
    +0318     m=containers.Map('KeyType','char','ValueType','char');
    +0319 else
    +0320     m=containers.Map(keys,vals);
    +0321 end
    +0322 end
    +0323 
    +0324 function ids=mapIds(m,ids)
    +0325 known=isKey(m,ids);
    +0326 if any(known)
    +0327     ids(known)=values(m,ids(known));
    +0328 end
    +0329 end
    +0330 
    +0331 function joined=joinLabels(labels)
    +0332 %Union of comma-separated predictor labels, in SuppOrth's F, S, B, O order
    +0333 found=strtrim(split(strjoin(labels,','),','));
    +0334 found=unique(found(~cellfun(@isempty,found)));
    +0335 order={'F','S','B','O'};
    +0336 [isKnown,rank]=ismember(found,order);
    +0337 known=found(isKnown);
    +0338 [~,o]=sort(rank(isKnown));
    +0339 joined=strjoin([known(o);sort(found(~isKnown))],',');
    +0340 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/reconstruction/homology/index.html b/docs/html/reconstruction/homology/index.html similarity index 75% rename from doc/reconstruction/homology/index.html rename to docs/html/reconstruction/homology/index.html index c8786ec7..5c4a5b2b 100644 --- a/doc/reconstruction/homology/index.html +++ b/docs/html/reconstruction/homology/index.html @@ -19,7 +19,7 @@

    Index for reconstruction/homology

    Matlab files in this directory:

    -
     getBlastgetBlast Bidirectional BLAST between an organism and template organisms.
     getDiamondgetDiamond Bidirectional BLAST with DIAMOND against template organisms.
     getModelFromHomologygetModelFromHomology Construct a new model from existing models and homology.
     makeFakeBlastStructuremakeFakeBlastStructure Make a fake blastStructure from an ortholog list.
    + getBlastgetBlast Bidirectional BLAST between an organism and template organisms.  getDiamondgetDiamond Bidirectional BLAST with DIAMOND against template organisms.  getModelFromHomologygetModelFromHomology Construct a new model from existing models and homology.  hitsFromSuppOrthhitsFromSuppOrth Read SuppOrth consensus orthologs into a blastStructure.  makeFakeBlastStructuremakeFakeBlastStructure Make a fake blastStructure from an ortholog list.  runSuppOrthrunSuppOrth Run SuppOrth on an organism and template proteomes. diff --git a/doc/reconstruction/homology/makeFakeBlastStructure.html b/docs/html/reconstruction/homology/makeFakeBlastStructure.html similarity index 70% rename from doc/reconstruction/homology/makeFakeBlastStructure.html rename to docs/html/reconstruction/homology/makeFakeBlastStructure.html index de9f71a7..04b56599 100644 --- a/doc/reconstruction/homology/makeFakeBlastStructure.html +++ b/docs/html/reconstruction/homology/makeFakeBlastStructure.html @@ -29,12 +29,11 @@

    SYNOPSIS ^DESCRIPTION ^

     makeFakeBlastStructure  Make a fake blastStructure from an ortholog list.
     
    - This is a structure that would normally be generated by getBlast. It
    - allows to feed a predefined list of orthologs to getModelFromHomology
    - while retaining the further use of that function. For this function to
    - work, it is crucial that the orthologList is a cell array where the first
    - column contains the genes from the source organism, and the second column
    - contains the genes from the target organism.
    + The structure has the format that getBlast generates. It feeds a
    + predefined list of orthologs to getModelFromHomology. The orthologList
    + is a cell array with at least 10 rows and two columns: the first column
    + contains the genes from the source organism and the second column the
    + genes from the target organism.
     
      Parameters
      ----------
    @@ -52,9 +51,10 @@ 

    DESCRIPTION ^DESCRIPTION ^getModelFromHomology, getBlast

    + getModelFromHomology, getBlast, hitsFromSuppOrth

    CROSS-REFERENCE INFORMATION ^

    @@ -80,32 +80,32 @@

    SOURCE CODE ^
    0001 function blastStructure=makeFakeBlastStructure(orthologList,sourceModelID,getModelFor)
     0002 % makeFakeBlastStructure  Make a fake blastStructure from an ortholog list.
     0003 %
    -0004 % This is a structure that would normally be generated by getBlast. It
    -0005 % allows to feed a predefined list of orthologs to getModelFromHomology
    -0006 % while retaining the further use of that function. For this function to
    -0007 % work, it is crucial that the orthologList is a cell array where the first
    -0008 % column contains the genes from the source organism, and the second column
    -0009 % contains the genes from the target organism.
    -0010 %
    -0011 % Parameters
    -0012 % ----------
    -0013 % orthologList : cell
    -0014 %     cell array of orthologous genes, where the first column contains the
    -0015 %     genes from the source organism, while the second column contains the
    -0016 %     genes from the target organism.
    -0017 % sourceModelID : char
    -0018 %     ID of the model that will be used as template, that contains the
    -0019 %     genes in the first column of orthologList.
    -0020 % getModelFor : char
    -0021 %     the name of the organism to build a model for, identical to the
    -0022 %     getModelFor parameter in the getModelFromHomology function.
    -0023 %
    -0024 % Returns
    -0025 % -------
    -0026 % blastStructure : struct
    -0027 %     a fake blastStructure, where the evalue, identity and aligLen are
    -0028 %     set at extreme values, such that all orthologous pairs will pass the
    -0029 %     filter when running getModelFromHomology.
    +0004 % The structure has the format that getBlast generates. It feeds a
    +0005 % predefined list of orthologs to getModelFromHomology. The orthologList
    +0006 % is a cell array with at least 10 rows and two columns: the first column
    +0007 % contains the genes from the source organism and the second column the
    +0008 % genes from the target organism.
    +0009 %
    +0010 % Parameters
    +0011 % ----------
    +0012 % orthologList : cell
    +0013 %     cell array of orthologous genes, where the first column contains the
    +0014 %     genes from the source organism, while the second column contains the
    +0015 %     genes from the target organism.
    +0016 % sourceModelID : char
    +0017 %     ID of the model that will be used as template, that contains the
    +0018 %     genes in the first column of orthologList.
    +0019 % getModelFor : char
    +0020 %     the name of the organism to build a model for, identical to the
    +0021 %     getModelFor parameter in the getModelFromHomology function.
    +0022 %
    +0023 % Returns
    +0024 % -------
    +0025 % blastStructure : struct
    +0026 %     a fake blastStructure with one element per direction. For every pair,
    +0027 %     evalue is 0, identity is 100, aligLen is 1000, bitscore is 100 and
    +0028 %     ppos is 100. All orthologous pairs pass the filters of
    +0029 %     getModelFromHomology.
     0030 %
     0031 % Examples
     0032 % --------
    @@ -113,7 +113,7 @@ 

    SOURCE CODE ^% 0035 % See also 0036 % -------- -0037 % getModelFromHomology, getBlast +0037 % getModelFromHomology, getBlast, hitsFromSuppOrth 0038 0039 if nargin<3 0040 error('All three parameters should be set'); diff --git a/docs/html/reconstruction/homology/runSuppOrth.html b/docs/html/reconstruction/homology/runSuppOrth.html new file mode 100644 index 00000000..63c57d32 --- /dev/null +++ b/docs/html/reconstruction/homology/runSuppOrth.html @@ -0,0 +1,401 @@ + + + + Description of runSuppOrth + + + + + + + + + +
    Home > reconstruction > homology > runSuppOrth.m
    + + + +

    runSuppOrth +

    + +

    PURPOSE ^

    +
    runSuppOrth Run SuppOrth on an organism and template proteomes.
    + +

    SYNOPSIS ^

    +
    function [blastStructure,suppOrthInfo]=runSuppOrth(organismID,fastaFile,modelIDs,refFastaFiles,outDir,varargin)
    + +

    DESCRIPTION ^

    +
     runSuppOrth  Run SuppOrth on an organism and template proteomes.
    +
    + Runs the suppOrth command (https://github.com/fma91/SuppOrth) and reads
    + its consensus orthologs with hitsFromSuppOrth.
    +
    + Parameters
    + ----------
    + organismID : char
    +     the id of the organism of interest, as in getModelFromHomology.
    + fastaFile : char
    +     a FASTA file (optionally gzipped) with the proteins of the organism.
    + modelIDs : cell
    +     the template model ids, as "model.id" in getModelFromHomology.
    + refFastaFiles : cell
    +     the proteome of each template, in the order of modelIDs, with FASTA
    +     ids equal to the gene ids of that model (or mapped to them with
    +     proteinToGene).
    + outDir : char
    +     folder for SuppOrth's output.
    +
    + Name-Value Arguments
    + --------------------
    + tools : char
    +     comma-separated predictors, or 'all' for orthofinder, sonicparanoid
    +     and broccoli (default 'all').
    + supportLv : double
    +     SuppOrth's --supportLv: a pair is kept if at least this many
    +     predictors found it, or if its identity is above identityFilter
    +     (default 3, or the number of tools when fewer are selected).
    + identityFilter : double
    +     SuppOrth's --identity_filter (default 80).
    + alignment : char
    +     SuppOrth's --alignment (default 'diamond').
    + threads : double
    +     number of threads (default: the cores assigned to MATLAB).
    + extraArgs : cell
    +     further arguments for "suppOrth run", for example {'--resume'} or
    +     {'--oma','path/to/Output'} (default {}).
    + suppOrth : char
    +     the suppOrth command. If empty, the RAVEN_SUPPORTH environment
    +     variable is used, and then suppOrth on the PATH (default '').
    +
    + The name-value arguments of hitsFromSuppOrth (minSupport, requireTools,
    + minIdentity, maxEvalue, proteinToGene, speciesToModel) are passed on.
    +
    + Returns
    + -------
    + blastStructure : struct
    +     as from hitsFromSuppOrth, for getModelFromHomology.
    + suppOrthInfo : table
    +     SuppOrth's values per gene pair, as from hitsFromSuppOrth.
    +
    + Notes
    + -----
    + SuppOrth requires protein ids to be unique over all proteomes. When two
    + FASTA files share an id (for example two templates of the same species),
    + every id is given a "<label>__" prefix in copies written to
    + outDir/prefixed_inputs, and the prefix is removed from the returned
    + hits. outDir/suppOrth.tsv then contains the prefixed ids. SuppOrth's
    + console output is written to outDir/run_supporth.log.
    +
    + SuppOrth's predictors run on Linux and macOS only. On Windows, run
    + SuppOrth under WSL or on a Linux host, and read suppOrth.tsv with
    + hitsFromSuppOrth.
    +
    + Examples
    + --------
    +     blastStructure = runSuppOrth('klac','klac.fasta',{'yeastGEM'},...
    +        {'yeast.fasta'},'supporth_out','proteinToGene','klac_genes.tsv');
    +     draftModel = getModelFromHomology(yeastGEM,blastStructure,'klac');
    +
    + See also
    + --------
    + hitsFromSuppOrth, getModelFromHomology, getDiamond
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • hitsFromSuppOrth hitsFromSuppOrth Read SuppOrth consensus orthologs into a blastStructure.
    +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [blastStructure,suppOrthInfo]=runSuppOrth(organismID,fastaFile,modelIDs,refFastaFiles,outDir,varargin)
    +0002 % runSuppOrth  Run SuppOrth on an organism and template proteomes.
    +0003 %
    +0004 % Runs the suppOrth command (https://github.com/fma91/SuppOrth) and reads
    +0005 % its consensus orthologs with hitsFromSuppOrth.
    +0006 %
    +0007 % Parameters
    +0008 % ----------
    +0009 % organismID : char
    +0010 %     the id of the organism of interest, as in getModelFromHomology.
    +0011 % fastaFile : char
    +0012 %     a FASTA file (optionally gzipped) with the proteins of the organism.
    +0013 % modelIDs : cell
    +0014 %     the template model ids, as "model.id" in getModelFromHomology.
    +0015 % refFastaFiles : cell
    +0016 %     the proteome of each template, in the order of modelIDs, with FASTA
    +0017 %     ids equal to the gene ids of that model (or mapped to them with
    +0018 %     proteinToGene).
    +0019 % outDir : char
    +0020 %     folder for SuppOrth's output.
    +0021 %
    +0022 % Name-Value Arguments
    +0023 % --------------------
    +0024 % tools : char
    +0025 %     comma-separated predictors, or 'all' for orthofinder, sonicparanoid
    +0026 %     and broccoli (default 'all').
    +0027 % supportLv : double
    +0028 %     SuppOrth's --supportLv: a pair is kept if at least this many
    +0029 %     predictors found it, or if its identity is above identityFilter
    +0030 %     (default 3, or the number of tools when fewer are selected).
    +0031 % identityFilter : double
    +0032 %     SuppOrth's --identity_filter (default 80).
    +0033 % alignment : char
    +0034 %     SuppOrth's --alignment (default 'diamond').
    +0035 % threads : double
    +0036 %     number of threads (default: the cores assigned to MATLAB).
    +0037 % extraArgs : cell
    +0038 %     further arguments for "suppOrth run", for example {'--resume'} or
    +0039 %     {'--oma','path/to/Output'} (default {}).
    +0040 % suppOrth : char
    +0041 %     the suppOrth command. If empty, the RAVEN_SUPPORTH environment
    +0042 %     variable is used, and then suppOrth on the PATH (default '').
    +0043 %
    +0044 % The name-value arguments of hitsFromSuppOrth (minSupport, requireTools,
    +0045 % minIdentity, maxEvalue, proteinToGene, speciesToModel) are passed on.
    +0046 %
    +0047 % Returns
    +0048 % -------
    +0049 % blastStructure : struct
    +0050 %     as from hitsFromSuppOrth, for getModelFromHomology.
    +0051 % suppOrthInfo : table
    +0052 %     SuppOrth's values per gene pair, as from hitsFromSuppOrth.
    +0053 %
    +0054 % Notes
    +0055 % -----
    +0056 % SuppOrth requires protein ids to be unique over all proteomes. When two
    +0057 % FASTA files share an id (for example two templates of the same species),
    +0058 % every id is given a "<label>__" prefix in copies written to
    +0059 % outDir/prefixed_inputs, and the prefix is removed from the returned
    +0060 % hits. outDir/suppOrth.tsv then contains the prefixed ids. SuppOrth's
    +0061 % console output is written to outDir/run_supporth.log.
    +0062 %
    +0063 % SuppOrth's predictors run on Linux and macOS only. On Windows, run
    +0064 % SuppOrth under WSL or on a Linux host, and read suppOrth.tsv with
    +0065 % hitsFromSuppOrth.
    +0066 %
    +0067 % Examples
    +0068 % --------
    +0069 %     blastStructure = runSuppOrth('klac','klac.fasta',{'yeastGEM'},...
    +0070 %        {'yeast.fasta'},'supporth_out','proteinToGene','klac_genes.tsv');
    +0071 %     draftModel = getModelFromHomology(yeastGEM,blastStructure,'klac');
    +0072 %
    +0073 % See also
    +0074 % --------
    +0075 % hitsFromSuppOrth, getModelFromHomology, getDiamond
    +0076 
    +0077 readerArgs={'speciesToModel',[]; 'minSupport',1; 'requireTools',{}; ...
    +0078     'minIdentity',[]; 'maxEvalue',[]; 'proteinToGene',[]};
    +0079 p=parseRAVENargs(varargin, [{'tools','all'; 'supportLv',[]; 'identityFilter',80; ...
    +0080     'alignment','diamond'; 'threads',[]; 'extraArgs',{}; 'suppOrth',''}; readerArgs]);
    +0081 
    +0082 if ispc
    +0083     error('RAVEN:badInput',['SuppOrth''s predictors run on Linux and macOS only. Run '...
    +0084         'SuppOrth under WSL or on a Linux host, and read suppOrth.tsv with hitsFromSuppOrth.']);
    +0085 end
    +0086 organismID=char(organismID);
    +0087 modelIDs=cellstr(modelIDs);
    +0088 refFastaFiles=cellstr(refFastaFiles);
    +0089 if numel(modelIDs)~=numel(refFastaFiles)
    +0090     error('RAVEN:badInput','modelIDs and refFastaFiles must have the same length.');
    +0091 end
    +0092 if isempty(modelIDs)
    +0093     error('RAVEN:badInput','At least one template is needed.');
    +0094 end
    +0095 labels=[{organismID};modelIDs(:)];
    +0096 for i=1:numel(labels)
    +0097     l=labels{i};
    +0098     if isempty(l) || any(strcmp(l,{'.','..'})) || ~strcmp(l,strtrim(l)) || any(ismember(l,',/\'))
    +0099         error('RAVEN:badInput',['"%s" cannot be a SuppOrth species label: it must be '...
    +0100             'non-empty, not "." or "..", have no leading or trailing space, and contain '...
    +0101             'no ",", "/" or "\\".'],l);
    +0102     end
    +0103 end
    +0104 if numel(unique(labels))~=numel(labels)
    +0105     error('RAVEN:badInput','organismID and modelIDs must be distinct, got %s',strjoin(labels,', '));
    +0106 end
    +0107 
    +0108 tools=strtrim(strsplit(char(p.tools),','));
    +0109 tools=tools(~cellfun(@isempty,tools));
    +0110 if isequal(tools,{'all'})
    +0111     tools={'orthofinder','sonicparanoid','broccoli'};
    +0112 end
    +0113 supportLv=p.supportLv;
    +0114 if isempty(supportLv)
    +0115     supportLv=min(3,numel(tools));
    +0116 end
    +0117 if supportLv<1 || supportLv>4
    +0118     error('RAVEN:badInput','supportLv must be between 1 and 4, got %d',supportLv);
    +0119 end
    +0120 extraArgs=cellstr(p.extraArgs);
    +0121 if isempty(p.extraArgs)
    +0122     extraArgs={};
    +0123 end
    +0124 if supportLv>numel(tools) && ~any(strcmp(extraArgs,'--oma'))
    +0125     warning('RAVEN:runSuppOrth:supportLv',['supportLv=%d is more than the %d predictor(s) '...
    +0126         'selected; SuppOrth then keeps only pairs above identityFilter (%g %%).'],...
    +0127         supportLv,numel(tools),p.identityFilter);
    +0128 end
    +0129 threads=p.threads;
    +0130 if isempty(threads)
    +0131     cores=evalc('feature(''numcores'')');
    +0132     cores=strsplit(cores,'MATLAB was assigned: ');
    +0133     threads=str2double(regexp(cores{2},'^\d*','match','once'));
    +0134 end
    +0135 
    +0136 exe=resolveSuppOrth(char(p.suppOrth));
    +0137 fastas=[{char(fastaFile)};refFastaFiles(:)];
    +0138 for i=1:numel(fastas)
    +0139     if ~isfile(fastas{i})
    +0140         error('RAVEN:badInput','FASTA file "%s" does not exist.',fastas{i});
    +0141     end
    +0142 end
    +0143 if ~isfolder(outDir)
    +0144     mkdir(outDir);
    +0145 end
    +0146 
    +0147 %SuppOrth requires protein ids to be unique over all proteomes. Templates of
    +0148 %the same species often share gene ids, so in that case every id gets a
    +0149 %"<label>__" prefix in a copy of its FASTA, and the prefix is removed below.
    +0150 ids=cell(numel(fastas),1);
    +0151 for i=1:numel(fastas)
    +0152     ids{i}=fastaIds(fastas{i});
    +0153     ids{i}=unique(ids{i});
    +0154 end
    +0155 allIds=vertcat(ids{:});
    +0156 shared=numel(unique(allIds))<numel(allIds);
    +0157 if shared
    +0158     inputs=fullfile(outDir,'prefixed_inputs');
    +0159     if ~isfolder(inputs)
    +0160         mkdir(inputs);
    +0161     end
    +0162     for i=1:numel(fastas)
    +0163         dest=fullfile(inputs,[labels{i} '.faa']);
    +0164         writePrefixed(fastas{i},dest,[labels{i} '__']);
    +0165         fastas{i}=dest;
    +0166     end
    +0167 end
    +0168 
    +0169 args=[{exe,'run'},fastas',{'--labels',strjoin(labels,','),'-o',char(outDir),...
    +0170     '--tools',strjoin(tools,','),'--supportLv',num2str(supportLv),...
    +0171     '--identity_filter',num2str(p.identityFilter),'--alignment',char(p.alignment),...
    +0172     '-t',num2str(threads)},extraArgs(:)'];
    +0173 cmd=strjoin(cellfun(@shellQuote,args,'UniformOutput',false),' ');
    +0174 %MATLAB's library path can break the predictors' compiled dependencies
    +0175 [status,output]=system(['env -u LD_LIBRARY_PATH -u DYLD_LIBRARY_PATH ' cmd ' 2>&1']);
    +0176 logFile=fullfile(outDir,'run_supporth.log');
    +0177 fid=fopen(logFile,'w');
    +0178 fprintf(fid,'%s\n\n%s\n',strjoin(args,' '),strtrim(output));
    +0179 fclose(fid);
    +0180 if status~=0
    +0181     lines=splitlines(strtrim(output));
    +0182     %SuppOrth reports a failed stage before the consensus summary, which can
    +0183     %push it out of the last lines
    +0184     head=lines(1:max(0,end-20));
    +0185     failed=head(contains(head,'FAILED') | startsWith(head,'error'));
    +0186     tail=[failed;lines(max(1,end-19):end)];
    +0187     error('RAVEN:runSuppOrth:failed',['suppOrth exited with code %d; its output in %s '...
    +0188         'is incomplete and was not read. Full output in %s.\n%s'],status,char(outDir),...
    +0189         logFile,strjoin(tail,newline));
    +0190 end
    +0191 
    +0192 source=fullfile(outDir,'suppOrth.tsv');
    +0193 if shared
    +0194     source=stripPrefixes(source);
    +0195 end
    +0196 readerValues=cellfun(@(n) p.(n),readerArgs(:,1),'UniformOutput',false);
    +0197 reader=[readerArgs(:,1),readerValues]';
    +0198 [blastStructure,suppOrthInfo]=hitsFromSuppOrth(source,organismID,reader{:});
    +0199 end
    +0200 
    +0201 function exe=resolveSuppOrth(exe)
    +0202 if ~isempty(exe)
    +0203     return
    +0204 end
    +0205 exe=getenv('RAVEN_SUPPORTH');
    +0206 if ~isempty(exe)
    +0207     return
    +0208 end
    +0209 for name={'suppOrth','supporth'}
    +0210     [status,found]=system(['command -v ' name{1}]);
    +0211     if status==0 && ~isempty(strtrim(found))
    +0212         exe=strtrim(found);
    +0213         return
    +0214     end
    +0215 end
    +0216 error('RAVEN:runSuppOrth:notFound',['Could not find the SuppOrth command suppOrth. '...
    +0217     'Install it with pip install ''git+https://github.com/fma91/SuppOrth.git@stand_alone'' followed '...
    +0218     'by suppOrth install, then put suppOrth on the PATH, set RAVEN_SUPPORTH, or pass '...
    +0219     'the suppOrth argument.']);
    +0220 end
    +0221 
    +0222 function lines=readFastaLines(file)
    +0223 [~,~,ext]=fileparts(file);
    +0224 if strcmpi(ext,'.gz')
    +0225     tmp=tempname;
    +0226     unzipped=gunzip(file,tmp);
    +0227     text=fileread(unzipped{1});
    +0228     rmdir(tmp,'s');
    +0229 else
    +0230     text=fileread(file);
    +0231 end
    +0232 lines=splitlines(text);
    +0233 end
    +0234 
    +0235 function ids=fastaIds(file)
    +0236 %Sequence ids as SuppOrth reads them: the first word of each header line
    +0237 lines=readFastaLines(file);
    +0238 headers=lines(startsWith(lines,'>'));
    +0239 words=regexp(headers,'^>\s*(\S+)','tokens','once');
    +0240 words=words(~cellfun(@isempty,words));
    +0241 ids=cellfun(@(w) w{1},words,'UniformOutput',false);
    +0242 end
    +0243 
    +0244 function writePrefixed(src,dest,prefix)
    +0245 lines=readFastaLines(src);
    +0246 isHeader=startsWith(lines,'>');
    +0247 lines(isHeader)=strcat('>',prefix,extractAfter(lines(isHeader),1));
    +0248 if ~isempty(lines) && isempty(lines{end})
    +0249     lines(end)=[];
    +0250 end
    +0251 fid=fopen(dest,'w');
    +0252 fprintf(fid,'%s\n',lines{:});
    +0253 fclose(fid);
    +0254 end
    +0255 
    +0256 function T=stripPrefixes(file)
    +0257 %Remove the "<label>__" prefix that runSuppOrth added to protein ids
    +0258 lines=splitlines(fileread(file));
    +0259 lines=lines(~cellfun(@isempty,lines));
    +0260 header=strsplit(lines{1},'\t','CollapseDelimiters',false);
    +0261 fields=regexp(lines(2:end),'\t','split');
    +0262 fields=vertcat(fields{:});
    +0263 if isempty(fields)
    +0264     fields=cell(0,numel(header));
    +0265 end
    +0266 T=cell2table(fields,'VariableNames',header);
    +0267 for pair={{'Species1','Protein1'},{'Species2','Protein2'}}
    +0268     s=T.(pair{1}{1});
    +0269     prot=T.(pair{1}{2});
    +0270     prefix=strcat(s,'__');
    +0271     has=cellfun(@startsWith,prot,prefix);
    +0272     prot(has)=cellfun(@(x,pre) x(numel(pre)+1:end),prot(has),prefix(has),'UniformOutput',false);
    +0273     T.(pair{1}{2})=prot;
    +0274 end
    +0275 end
    +0276 
    +0277 function q=shellQuote(s)
    +0278 q=['''' strrep(char(s),'''','''\''''') ''''];
    +0279 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/reconstruction/index.html b/docs/html/reconstruction/index.html similarity index 100% rename from doc/reconstruction/index.html rename to docs/html/reconstruction/index.html diff --git a/doc/reconstruction/kegg/buildGlobalGPR.html b/docs/html/reconstruction/kegg/buildGlobalGPR.html similarity index 78% rename from doc/reconstruction/kegg/buildGlobalGPR.html rename to docs/html/reconstruction/kegg/buildGlobalGPR.html index ad5ee699..d26ceb5e 100644 --- a/doc/reconstruction/kegg/buildGlobalGPR.html +++ b/docs/html/reconstruction/kegg/buildGlobalGPR.html @@ -4,7 +4,7 @@ Description of buildGlobalGPR - + @@ -21,19 +21,19 @@

    buildGlobalGPR

    PURPOSE ^

    -
    buildGlobalGPR Join every KEGG organism's genes onto the reference
    +
    buildGlobalGPR Join the genes of all KEGG organisms to reference reactions by KO id.

    SYNOPSIS ^

    function [genes,rxnGeneMat]=buildGlobalGPR(rxns,koReaction,organismGeneKO)

    DESCRIPTION ^

    -
     buildGlobalGPR  Join every KEGG organism's genes onto the reference
    - reactions through their shared KO (KEGG Orthology) ids.
    +
     buildGlobalGPR  Join the genes of all KEGG organisms to reference reactions by KO id.
     
    - Builds two sparse gene-KO / KO-reaction incidence matrices sharing one
    - KO axis, and multiplies them, rather than expanding the join row by
    - row --- organismGeneKO can carry millions of rows (every gene of every
    - KEGG organism), so a per-row loop is not viable.
    + Joins every KEGG organism's genes onto the reference reactions through their
    + shared KO (KEGG Orthology) ids. The function builds two sparse incidence
    + matrices (gene to KO and KO to reaction) that share one KO axis, and
    + multiplies them. The join is not expanded row by row, because organismGeneKO
    + can contain millions of rows (every gene of every KEGG organism).
     
      Parameters
      ----------
    @@ -50,7 +50,7 @@ 

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 
 <h2><a name=SOURCE CODE ^

    0001 function [genes,rxnGeneMat]=buildGlobalGPR(rxns,koReaction,organismGeneKO)
    -0002 % buildGlobalGPR  Join every KEGG organism's genes onto the reference
    -0003 % reactions through their shared KO (KEGG Orthology) ids.
    -0004 %
    -0005 % Builds two sparse gene-KO / KO-reaction incidence matrices sharing one
    -0006 % KO axis, and multiplies them, rather than expanding the join row by
    -0007 % row --- organismGeneKO can carry millions of rows (every gene of every
    -0008 % KEGG organism), so a per-row loop is not viable.
    +0002 % buildGlobalGPR  Join the genes of all KEGG organisms to reference reactions by KO id.
    +0003 %
    +0004 % Joins every KEGG organism's genes onto the reference reactions through their
    +0005 % shared KO (KEGG Orthology) ids. The function builds two sparse incidence
    +0006 % matrices (gene to KO and KO to reaction) that share one KO axis, and
    +0007 % multiplies them. The join is not expanded row by row, because organismGeneKO
    +0008 % can contain millions of rows (every gene of every KEGG organism).
     0009 %
     0010 % Parameters
     0011 % ----------
    @@ -90,7 +90,7 @@ 

    SOURCE CODE ^% genes : cell array 0023 % sorted unique 'organism:gene' identifiers. 0024 % rxnGeneMat : sparse double -0025 % numel(rxns) x numel(genes); rxnGeneMat(i,j)=1 when gene j shares at +0025 % numel(rxns) by numel(genes); rxnGeneMat(i,j)=1 when gene j shares at 0026 % least one KO with reaction i. 0027 0028 %Map each ko_reaction row onto the reference model's own reaction order; diff --git a/doc/reconstruction/kegg/buildGlobalKEGGModel.html b/docs/html/reconstruction/kegg/buildGlobalKEGGModel.html similarity index 83% rename from doc/reconstruction/kegg/buildGlobalKEGGModel.html rename to docs/html/reconstruction/kegg/buildGlobalKEGGModel.html index 450013ff..6437c0fc 100644 --- a/doc/reconstruction/kegg/buildGlobalKEGGModel.html +++ b/docs/html/reconstruction/kegg/buildGlobalKEGGModel.html @@ -29,11 +29,11 @@

    SYNOPSIS ^DESCRIPTION ^

     buildGlobalKEGGModel  Download and assemble the global KEGG model.
     
    - Fetches the raven-data <kegg version>_core.tar.gz bundle (the
    - gene-free reference model plus the ko_reaction / organism_gene_ko /
    - rxn_flags relational tables), then joins every organism's
    - genes onto the reference reactions through their shared KO ids. The
    - KEGG version is read from keggDataVersion, not hardcoded here.
    + Fetches the toolbox-assets <kegg version>_core.tar.gz bundle (the
    + gene-free reference model plus the ko_reaction, organism_gene_ko and
    + rxn_flags relational tables), then joins every organism's genes onto
    + the reference reactions through their shared KO ids. The KEGG version
    + is read from keggDataVersion.
     
      Parameters
      ----------
    @@ -43,9 +43,9 @@ 

    DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls:
      -
    • buildGlobalGPR buildGlobalGPR Join every KEGG organism's genes onto the reference
    • keggDataVersion keggDataVersion The raven-data release tag for KEGG artefacts.
    • readKEGGTable readKEGGTable Read a gzipped KEGG relational table into a table with
    +
  • buildGlobalGPR buildGlobalGPR Join the genes of all KEGG organisms to reference reactions by KO id.
  • keggDataVersion keggDataVersion The toolbox-assets release tag for KEGG artefacts.
  • readKEGGTable readKEGGTable Read a gzipped KEGG relational table into a table of cellstr columns.
  • This function is called by: @@ -71,11 +71,11 @@

    SOURCE CODE ^
    0001 function [model,KOModel,isSpontaneous,isUndefinedStoich,isIncomplete,isGeneral]=buildGlobalKEGGModel(ravenPath)
     0002 % buildGlobalKEGGModel  Download and assemble the global KEGG model.
     0003 %
    -0004 % Fetches the raven-data <kegg version>_core.tar.gz bundle (the
    -0005 % gene-free reference model plus the ko_reaction / organism_gene_ko /
    -0006 % rxn_flags relational tables), then joins every organism's
    -0007 % genes onto the reference reactions through their shared KO ids. The
    -0008 % KEGG version is read from keggDataVersion, not hardcoded here.
    +0004 % Fetches the toolbox-assets <kegg version>_core.tar.gz bundle (the
    +0005 % gene-free reference model plus the ko_reaction, organism_gene_ko and
    +0006 % rxn_flags relational tables), then joins every organism's genes onto
    +0007 % the reference reactions through their shared KO ids. The KEGG version
    +0008 % is read from keggDataVersion.
     0009 %
     0010 % Parameters
     0011 % ----------
    @@ -85,9 +85,9 @@ 

    SOURCE CODE ^% Returns 0016 % ------- 0017 % model : struct -0018 % the full global KEGG model (all reactions/metabolites, genes and -0019 % rxnGeneMat spanning every KEGG organism). Callers narrow this down -0020 % to one organism (see getKEGGModelForOrganism). +0018 % the full global KEGG model (all reactions and metabolites, genes and +0019 % rxnGeneMat spanning every KEGG organism). getKEGGModelForOrganism +0020 % narrows it to one organism. 0021 % KOModel : struct 0022 % a minimal model struct whose rxns are the KO ids linked to at 0023 % least one reaction (getKEGGModelForOrganism's HMM-search path uses @@ -96,8 +96,8 @@

    SOURCE CODE ^% reaction ids carrying the corresponding rxn_flags quality flag. 0027 0028 kver=keggDataVersion(); -0029 keggDir=fullfile(ravenPath,'reconstruction','kegg'); -0030 archive=fetchRavenDataAsset(keggDir,kver,[kver '_core.tar.gz']); +0029 keggDir=fullfile(ravenPath,'src','reconstruction','kegg'); +0030 archive=fetchToolboxAsset(keggDir,kver,[kver '_core.tar.gz']); 0031 0032 coreDir=fullfile(keggDir,[kver '_core']); 0033 if ~isfolder(coreDir) diff --git a/docs/html/reconstruction/kegg/getKEGGModelForOrganism.html b/docs/html/reconstruction/kegg/getKEGGModelForOrganism.html new file mode 100644 index 00000000..ee40d40a --- /dev/null +++ b/docs/html/reconstruction/kegg/getKEGGModelForOrganism.html @@ -0,0 +1,710 @@ + + + + Description of getKEGGModelForOrganism + + + + + + + + + +
    Home > reconstruction > kegg > getKEGGModelForOrganism.m
    + + + +

    getKEGGModelForOrganism +

    + +

    PURPOSE ^

    +
    getKEGGModelForOrganism Reconstruct a model from KEGG protein homology.
    + +

    SYNOPSIS ^

    +
    function model=getKEGGModelForOrganism(organismID,varargin)
    + +

    DESCRIPTION ^

    +
     getKEGGModelForOrganism  Reconstruct a model from KEGG protein homology.
    +
    + Reconstructs a genome-scale metabolic model based on protein homology to
    + the orthologies in KEGG. If the target species is not available in KEGG,
    + the function stops with an error; give a closely related species as
    + organismID. Without a fastaFile, the
    + protein homology search is skipped (see the fastaFile parameter).
    +
    + Parameters
    + ----------
    + organismID : char
    +     three or four letter abbreviation of the organism (as used in KEGG).
    +     If the organism is not available, a closely related species can be
    +     given. The abbreviation determines the genes that are kept when
    +     fastaFile is empty. "eukaryotes" or "prokaryotes" gives a model for
    +     the whole domain. Only applicable if fastaFile is empty, that is when
    +     no homology search is performed.
    +
    + Name-Value Arguments
    + --------------------
    + fastaFile : char
    +     a FASTA file that contains the protein sequences of the organism for
    +     which to reconstruct a model. If no FASTA file is supplied then a
    +     model is reconstructed based only on the organism abbreviation. In this
    +     case, cutOff, minScoreRatioKO, minScoreRatioG and dataDir are not
    +     used (default []).
    + dataDir : char
    +     directory of the HMM library, named kegg118_prokaryotes or
    +     kegg118_eukaryotes. The name gives the KEGG version and whether the
    +     HMMs were trained on pro- or eukaryotic sequences, and matches the
    +     published HMM library it is paired with. The prebuilt concatenated KO
    +     HMM library (dataDir.hmm) is downloaded from the corresponding
    +     toolbox-assets release if it is not already present. Required when
    +     fastaFile is supplied (default []).
    + outDir : char
    +     directory that is created if it does not exist when fastaFile is
    +     supplied. The function writes no result files to it (default is the
    +     temporary directory, where all *.out files are deleted before the
    +     reconstruction).
    + keepSpontaneous : logical
    +     include reactions labelled as "spontaneous" (default true).
    + keepUndefinedStoich : logical
    +     include reactions in the form n A <=> n+1 A. These are treated as
    +     two separate metabolites (default true).
    + keepIncomplete : logical
    +     include reactions which have been labelled as "incomplete",
    +     "erroneous" or "unclear" (default true).
    + keepGeneral : logical
    +     include reactions which have been labelled as "general reaction".
    +     These are reactions of the form "an aldehyde <=> an alcohol". Not all
    +     such reactions have this label, so the function cannot remove all of
    +     them (default false).
    + cutOff : double
    +     maximum full-sequence E-value from HMMER for assigning a gene to a KO
    +     (default 10^-30).
    + minScoreRatioKO : double
    +     a gene is removed from a KO if log(score)/log(best score in KO) is
    +     below this value, where score is the E-value of the gene. This
    +     removes genes from KOs with many genes where some have a lower
    +     E-value (default 0.3, lower is less strict).
    + minScoreRatioG : double
    +     a gene is only assigned to KOs for which log(score)/log(best score)
    +     is at least this value, where best score is the lowest E-value of
    +     that gene over its KOs after the minScoreRatioKO step. This removes the
    +     assignment of a gene to KOs with a much higher E-value than its best KO
    +     (default 0.9, lower is less strict).
    + maxPhylDist : double
    +     only used when fastaFile is empty and organismID is "eukaryotes" or
    +     "prokaryotes". -1 keeps genes from organisms in the same domain
    +     (Prokaryota, Eukaryota) as H. sapiens ("hsa") or E. coli ("eco"),
    +     respectively. Any other value, including Inf, does not restrict the
    +     domain, and the value is not compared with the distances calculated
    +     in getPhylDist (default Inf).
    + globalModel : struct
    +     structure containing both model and KOModel structures as generated
    +     by getModelFromKEGG. When empty, the global model is loaded with
    +     getModelFromKEGG. Providing globalModel avoids reloading it when
    +     getKEGGModelForOrganism is run several times, for example for
    +     different strains. Example:
    +     [globalModel.model,globalModel.KOModel] = getModelFromKEGG (default
    +     []).
    +
    + Returns
    + -------
    + model : struct
    +     the reconstructed model.
    +
    + Notes
    + -----
    + The reconstruction works in one of two modes:
    +
    +   1. From KEGG annotations (no fastaFile supplied). The reactions
    +      associated with organismID in the local KEGG database are kept;
    +      maxPhylDist applies to organismID "eukaryotes" and "prokaryotes".
    +   2. From protein homology (fastaFile supplied). The query proteome is
    +      searched, in a single hmmsearch, against a prebuilt KEGG-version- and
    +      domain-specific concatenated KO HMM library (for example
    +      kegg118_eukaryotes), downloaded from the corresponding toolbox-assets
    +      release if not already present in dataDir. Hits are filtered by cutOff,
    +      minScoreRatioKO and minScoreRatioG into a KO-gene matrix, from which the
    +      model is built (spontaneous reactions without genes are kept if
    +      keepSpontaneous is true).
    +
    + The global KEGG model (reaction-KO relationships) is either supplied via
    + globalModel or loaded with getModelFromKEGG.
    +
    + Examples
    + --------
    +     model=getKEGGModelForOrganism(organismID,fastaFile,dataDir,...
    +        outDir,keepSpontaneous,keepUndefinedStoich,keepIncomplete,...
    +        keepGeneral,cutOff,minScoreRatioKO,minScoreRatioG,maxPhylDist);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • getModelFromKEGG getModelFromKEGG Load the global KEGG model.
    • getPhylDist getPhylDist Load the pre-built KEGG phylogenetic distance matrix.
    • keggDataVersion keggDataVersion The toolbox-assets release tag for KEGG artefacts.
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function model=getKEGGModelForOrganism(organismID,varargin)
    +0002 % getKEGGModelForOrganism  Reconstruct a model from KEGG protein homology.
    +0003 %
    +0004 % Reconstructs a genome-scale metabolic model based on protein homology to
    +0005 % the orthologies in KEGG. If the target species is not available in KEGG,
    +0006 % the function stops with an error; give a closely related species as
    +0007 % organismID. Without a fastaFile, the
    +0008 % protein homology search is skipped (see the fastaFile parameter).
    +0009 %
    +0010 % Parameters
    +0011 % ----------
    +0012 % organismID : char
    +0013 %     three or four letter abbreviation of the organism (as used in KEGG).
    +0014 %     If the organism is not available, a closely related species can be
    +0015 %     given. The abbreviation determines the genes that are kept when
    +0016 %     fastaFile is empty. "eukaryotes" or "prokaryotes" gives a model for
    +0017 %     the whole domain. Only applicable if fastaFile is empty, that is when
    +0018 %     no homology search is performed.
    +0019 %
    +0020 % Name-Value Arguments
    +0021 % --------------------
    +0022 % fastaFile : char
    +0023 %     a FASTA file that contains the protein sequences of the organism for
    +0024 %     which to reconstruct a model. If no FASTA file is supplied then a
    +0025 %     model is reconstructed based only on the organism abbreviation. In this
    +0026 %     case, cutOff, minScoreRatioKO, minScoreRatioG and dataDir are not
    +0027 %     used (default []).
    +0028 % dataDir : char
    +0029 %     directory of the HMM library, named kegg118_prokaryotes or
    +0030 %     kegg118_eukaryotes. The name gives the KEGG version and whether the
    +0031 %     HMMs were trained on pro- or eukaryotic sequences, and matches the
    +0032 %     published HMM library it is paired with. The prebuilt concatenated KO
    +0033 %     HMM library (dataDir.hmm) is downloaded from the corresponding
    +0034 %     toolbox-assets release if it is not already present. Required when
    +0035 %     fastaFile is supplied (default []).
    +0036 % outDir : char
    +0037 %     directory that is created if it does not exist when fastaFile is
    +0038 %     supplied. The function writes no result files to it (default is the
    +0039 %     temporary directory, where all *.out files are deleted before the
    +0040 %     reconstruction).
    +0041 % keepSpontaneous : logical
    +0042 %     include reactions labelled as "spontaneous" (default true).
    +0043 % keepUndefinedStoich : logical
    +0044 %     include reactions in the form n A <=> n+1 A. These are treated as
    +0045 %     two separate metabolites (default true).
    +0046 % keepIncomplete : logical
    +0047 %     include reactions which have been labelled as "incomplete",
    +0048 %     "erroneous" or "unclear" (default true).
    +0049 % keepGeneral : logical
    +0050 %     include reactions which have been labelled as "general reaction".
    +0051 %     These are reactions of the form "an aldehyde <=> an alcohol". Not all
    +0052 %     such reactions have this label, so the function cannot remove all of
    +0053 %     them (default false).
    +0054 % cutOff : double
    +0055 %     maximum full-sequence E-value from HMMER for assigning a gene to a KO
    +0056 %     (default 10^-30).
    +0057 % minScoreRatioKO : double
    +0058 %     a gene is removed from a KO if log(score)/log(best score in KO) is
    +0059 %     below this value, where score is the E-value of the gene. This
    +0060 %     removes genes from KOs with many genes where some have a lower
    +0061 %     E-value (default 0.3, lower is less strict).
    +0062 % minScoreRatioG : double
    +0063 %     a gene is only assigned to KOs for which log(score)/log(best score)
    +0064 %     is at least this value, where best score is the lowest E-value of
    +0065 %     that gene over its KOs after the minScoreRatioKO step. This removes the
    +0066 %     assignment of a gene to KOs with a much higher E-value than its best KO
    +0067 %     (default 0.9, lower is less strict).
    +0068 % maxPhylDist : double
    +0069 %     only used when fastaFile is empty and organismID is "eukaryotes" or
    +0070 %     "prokaryotes". -1 keeps genes from organisms in the same domain
    +0071 %     (Prokaryota, Eukaryota) as H. sapiens ("hsa") or E. coli ("eco"),
    +0072 %     respectively. Any other value, including Inf, does not restrict the
    +0073 %     domain, and the value is not compared with the distances calculated
    +0074 %     in getPhylDist (default Inf).
    +0075 % globalModel : struct
    +0076 %     structure containing both model and KOModel structures as generated
    +0077 %     by getModelFromKEGG. When empty, the global model is loaded with
    +0078 %     getModelFromKEGG. Providing globalModel avoids reloading it when
    +0079 %     getKEGGModelForOrganism is run several times, for example for
    +0080 %     different strains. Example:
    +0081 %     [globalModel.model,globalModel.KOModel] = getModelFromKEGG (default
    +0082 %     []).
    +0083 %
    +0084 % Returns
    +0085 % -------
    +0086 % model : struct
    +0087 %     the reconstructed model.
    +0088 %
    +0089 % Notes
    +0090 % -----
    +0091 % The reconstruction works in one of two modes:
    +0092 %
    +0093 %   1. From KEGG annotations (no fastaFile supplied). The reactions
    +0094 %      associated with organismID in the local KEGG database are kept;
    +0095 %      maxPhylDist applies to organismID "eukaryotes" and "prokaryotes".
    +0096 %   2. From protein homology (fastaFile supplied). The query proteome is
    +0097 %      searched, in a single hmmsearch, against a prebuilt KEGG-version- and
    +0098 %      domain-specific concatenated KO HMM library (for example
    +0099 %      kegg118_eukaryotes), downloaded from the corresponding toolbox-assets
    +0100 %      release if not already present in dataDir. Hits are filtered by cutOff,
    +0101 %      minScoreRatioKO and minScoreRatioG into a KO-gene matrix, from which the
    +0102 %      model is built (spontaneous reactions without genes are kept if
    +0103 %      keepSpontaneous is true).
    +0104 %
    +0105 % The global KEGG model (reaction-KO relationships) is either supplied via
    +0106 % globalModel or loaded with getModelFromKEGG.
    +0107 %
    +0108 % Examples
    +0109 % --------
    +0110 %     model=getKEGGModelForOrganism(organismID,fastaFile,dataDir,...
    +0111 %        outDir,keepSpontaneous,keepUndefinedStoich,keepIncomplete,...
    +0112 %        keepGeneral,cutOff,minScoreRatioKO,minScoreRatioG,maxPhylDist);
    +0113 
    +0114 p=parseRAVENargs(varargin, {'fastaFile',[]; 'dataDir',[]; 'outDir',[]; ...
    +0115     'keepSpontaneous',true; 'keepUndefinedStoich',true; ...
    +0116     'keepIncomplete',true; 'keepGeneral',false; 'cutOff',10^-30; ...
    +0117     'minScoreRatioKO',0.3; 'minScoreRatioG',0.9; 'maxPhylDist',inf; ...
    +0118     'globalModel',[]});
    +0119 fastaFile=p.fastaFile;
    +0120 if isempty(fastaFile)
    +0121     fastaFile=[];
    +0122 else
    +0123     fastaFile=char(fastaFile);
    +0124 end
    +0125 dataDir=p.dataDir;
    +0126 if isempty(dataDir)
    +0127     dataDir=[];
    +0128 else
    +0129     dataDir=char(dataDir);
    +0130 end
    +0131 outDir=p.outDir;
    +0132 if isempty(outDir)
    +0133     outDir=tempdir;
    +0134     %Delete all *.out files if any exist
    +0135     delete(fullfile(outDir,'*.out'));
    +0136 else
    +0137     outDir=char(outDir);
    +0138 end
    +0139 keepSpontaneous=p.keepSpontaneous;
    +0140 if isempty(keepSpontaneous)
    +0141     keepSpontaneous=true;
    +0142 end
    +0143 keepUndefinedStoich=p.keepUndefinedStoich;
    +0144 if isempty(keepUndefinedStoich)
    +0145     keepUndefinedStoich=true;
    +0146 end
    +0147 keepIncomplete=p.keepIncomplete;
    +0148 if isempty(keepIncomplete)
    +0149     keepIncomplete=true;
    +0150 end
    +0151 keepGeneral=p.keepGeneral;
    +0152 if isempty(keepGeneral)
    +0153     keepGeneral=false;
    +0154 end
    +0155 cutOff=p.cutOff;
    +0156 if isempty(cutOff)
    +0157     cutOff=10^-30;
    +0158 end
    +0159 minScoreRatioKO=p.minScoreRatioKO;
    +0160 if isempty(minScoreRatioKO)
    +0161     minScoreRatioKO=0.3;
    +0162 end
    +0163 minScoreRatioG=p.minScoreRatioG;
    +0164 if isempty(minScoreRatioG)
    +0165     minScoreRatioG=0.9;
    +0166 end
    +0167 maxPhylDist=p.maxPhylDist;
    +0168 if isempty(maxPhylDist)
    +0169     maxPhylDist=inf;
    +0170     %Include all sequences for each reaction
    +0171 end
    +0172 globalModel=p.globalModel;
    +0173 
    +0174 if isempty(fastaFile)
    +0175     fprintf(['\n*** The model reconstruction from KEGG based on the annotation available for KEGG Species <strong>' organismID '</strong> ***\n\n']);
    +0176 else
    +0177     fprintf('\n*** The model reconstruction from KEGG based on the protein homology search against KEGG Orthology specific HMMs ***\n\n');
    +0178     %Check if query fasta exists
    +0179     fastaFile=checkFileExistence(fastaFile,2); %Copy file to temp dir
    +0180 end
    +0181 
    +0182 %Run the external binaries multi-threaded to use all logical cores assigned
    +0183 %to MATLAB
    +0184 cores = evalc('feature(''numcores'')');
    +0185 cores = strsplit(cores, 'MATLAB was assigned: ');
    +0186 cores = regexp(cores{2},'^\d*','match');
    +0187 cores = cores{1};
    +0188 
    +0189 %Get the directory for RAVEN Toolbox.
    +0190 ravenPath=findRAVENroot();
    +0191 
    +0192 libraryFile='';
    +0193 
    +0194 %Checking if dataDir is consistent. It must point to a RAVEN-provided
    +0195 %pre-trained HMM set, compatible with the current RAVEN version. The
    +0196 %reconstruction uses the concatenated KO HMM library (a single
    +0197 %gzip-compressed flatfile, queried in one hmmsearch); if it is not already
    +0198 %present it is downloaded from the corresponding toolbox-assets release
    +0199 %(https://github.com/CompMetEng/toolbox-assets).
    +0200 %Only needed for the protein-homology path (fastaFile supplied): the
    +0201 %annotation-only path never touches libraryFile, so skip the (100+ MB)
    +0202 %download/extraction entirely when there is no FASTA file to search.
    +0203 if ~isempty(dataDir) && ~isempty(fastaFile)
    +0204     kver=keggDataVersion();
    +0205     hmmOptions={[kver '_eukaryotes'],[kver '_prokaryotes']};
    +0206     if ~endsWith(dataDir,hmmOptions)
    +0207         error(['Pre-trained HMMs set is not recognised. dataDir must match one of: ' strjoin(hmmOptions,' or ')])
    +0208     end
    +0209     %dataDir points to a RAVEN-provided set. Its name matches the
    +0210     %published HMM library asset, so it doubles as the download filename.
    +0211     hmmName=hmmOptions{endsWith(dataDir,hmmOptions)};
    +0212     libraryFile=[dataDir '.hmm'];
    +0213     if isfile(libraryFile)
    +0214         fprintf(['NOTE: Found <strong>' libraryFile '</strong> HMM library, it will therefore be used during reconstruction\n']);
    +0215     elseif isfile([libraryFile '.gz'])
    +0216         fprintf('Extracting the HMM library file... ');
    +0217         gunzip([libraryFile '.gz']);
    +0218         fprintf('COMPLETE\n');
    +0219     else
    +0220         fetchToolboxAsset(fileparts(libraryFile),kver,[hmmName '.hmm.gz']);
    +0221         fprintf('Extracting the HMM library file... ');
    +0222         gunzip([libraryFile '.gz']);
    +0223         fprintf('COMPLETE\n');
    +0224     end
    +0225     %Check that the HMM library is available
    +0226     if ~isfile(libraryFile)
    +0227         error(['The HMM library seems improperly extracted and not found at ',strrep(libraryFile,'\','/'),'. Please remove the corresponding .gz file and rerun getKEGGModelForOrganism']);
    +0228     end
    +0229 end
    +0230 
    +0231 %Check if the fasta-file contains "/" or "\". If not then it is probably just
    +0232 %a file name. Expand to full path.
    +0233 if any(fastaFile)
    +0234     if ~any(strfind(fastaFile,'\')) && ~any(strfind(fastaFile,'/'))
    +0235         fastaFile=which(fastaFile);
    +0236     end
    +0237     if ~isfolder(outDir)
    +0238         mkdir(outDir);
    +0239     end
    +0240 end
    +0241 
    +0242 %Load the global KEGG model. Can be provided as input to skip loading
    +0243 %when calling getKEGGModelForOrganism multiple times for different strains.
    +0244 if ~isempty(globalModel)
    +0245     model=globalModel.model;
    +0246     KOModel=globalModel.KOModel;
    +0247 else
    +0248     [model, KOModel]=getModelFromKEGG(keepSpontaneous,keepUndefinedStoich,keepIncomplete,keepGeneral);
    +0249 end
    +0250 model.id=organismID;
    +0251 model.c=zeros(numel(model.rxns),1);
    +0252 
    +0253 %If no FASTA file is supplied, then just remove all genes which are not for
    +0254 %the given organism ID
    +0255 if isempty(fastaFile)
    +0256     %Check if organismID can be found in KEGG species list or is
    +0257     %set to "eukaryotes" or "prokaryotes"
    +0258     phylDistsFull=getPhylDist(false);
    +0259     if ~ismember(organismID,[phylDistsFull.ids 'eukaryotes' 'prokaryotes'])
    +0260         error('Provided organismID is incorrect. Only species abbreviations from KEGG Species List or "eukaryotes"/"prokaryotes" are allowed.');
    +0261     end
    +0262 
    +0263     fprintf(['Pruning the model from <strong>non-' organismID '</strong> genes... ']);
    +0264     if ismember(organismID,{'eukaryotes','prokaryotes'})
    +0265         phylDists=getPhylDist(maxPhylDist==-1);
    +0266         if strcmp(organismID,'eukaryotes')
    +0267             proxyid='hsa';
    +0268             %Use H. sapiens here
    +0269         else
    +0270             proxyid='eco';
    +0271             %Use E. coli here
    +0272         end
    +0273         [~, phylDistId]=ismember(proxyid,phylDists.ids);
    +0274         idsToKeep=phylDists.ids(~isinf(phylDists.distMat(phylDistId,:)));
    +0275         taxIDs=cellfun(@(x) x{1},cellfun(@(x) strsplit(x,':'),model.genes,'UniformOutput',false),'UniformOutput',false);
    +0276         I=ismember(upper(taxIDs),upper(idsToKeep));
    +0277     else
    +0278         %KEGG organism IDs may have three or four letters
    +0279         organismID=strcat(organismID,':');
    +0280         %Add colon for accurate matching
    +0281         if length(organismID)==4
    +0282             I=cellfun(@(x) strcmpi(x(1:4),organismID),model.genes);
    +0283         elseif length(organismID)==5
    +0284             I=cellfun(@(x) strcmpi(x(1:5),organismID),model.genes);
    +0285         end
    +0286     end
    +0287     %Remove those genes
    +0288     model.genes=model.genes(I);
    +0289     model.rxnGeneMat=model.rxnGeneMat(:,I);
    +0290     fprintf('COMPLETE\n');
    +0291 end
    +0292 
    +0293 %First remove all reactions without genes
    +0294 if keepSpontaneous==true
    +0295     fprintf('Removing non-spontaneous reactions without GPR rules... ');
    +0296     load(fullfile(ravenPath,'src','reconstruction','kegg','keggModel.mat'),'isSpontaneous');
    +0297     I=~any(model.rxnGeneMat,2)&~ismember(model.rxns,isSpontaneous);
    +0298     spontRxnsWithGenes=model.rxns(any(model.rxnGeneMat,2)&~ismember(model.rxns,isSpontaneous));
    +0299 else
    +0300     fprintf('Removing reactions without GPR rules... ');
    +0301     I=~any(model.rxnGeneMat,2);
    +0302 end
    +0303 model=removeReactions(model,I,true);
    +0304 fprintf('COMPLETE\n');
    +0305 
    +0306 %Clean gene names
    +0307 fprintf('Fixing gene names in the model... ');
    +0308 %Get rid of the prefix organism id
    +0309 model.genes=regexprep(model.genes,'^\w+?:','');
    +0310 fprintf('COMPLETE\n');
    +0311 
    +0312 %If no FASTA file is supplied, then we are done here
    +0313 if isempty(fastaFile)
    +0314     %Create grRules
    +0315     fprintf('Constructing GPR associations and annotations for the model... ');
    +0316     model.grRules=cell(numel(model.rxns),1);
    +0317     model.grRules(:)={''};
    +0318     %Add the gene associations as "or"
    +0319     for i=1:numel(model.rxns)
    +0320         %Find the involved genes
    +0321         I=find(model.rxnGeneMat(i,:));
    +0322         if any(I)
    +0323             model.grRules{i}=['(' model.genes{I(1)}];
    +0324             for j=2:numel(I)
    +0325                 model.grRules{i}=[model.grRules{i} ' or ' model.genes{I(j)}];
    +0326             end
    +0327             model.grRules{i}=[model.grRules{i} ')'];
    +0328         end
    +0329     end
    +0330     %Fix grRules and reconstruct rxnGeneMat
    +0331     [grRules,rxnGeneMat] = standardizeGrRules(model); %Give detailed output
    +0332     model.grRules = grRules;
    +0333     model.rxnGeneMat = rxnGeneMat;
    +0334     %Add geneMiriams, assuming that it follows the syntax
    +0335     %kegg.genes/organismID:geneName
    +0336     model.geneMiriams='';
    +0337     for i=1:numel(model.genes)
    +0338         model.geneMiriams{i,1}.name{1,1}='kegg.genes';
    +0339         model.geneMiriams{i,1}.value{1,1}=strcat(lower(organismID),model.genes{i,1});
    +0340     end
    +0341     %Add the description to the reactions. The reference model has no
    +0342     %reaction notes, so readYAMLmodel leaves the field out
    +0343     if ~isfield(model,'rxnNotes')
    +0344         model.rxnNotes=repmat({''},numel(model.rxns),1);
    +0345     end
    +0346     for i=1:numel(model.rxns)
    +0347         if ~isempty(model.rxnNotes{i})
    +0348             model.rxnNotes(i)=strcat('Included by getKEGGModelForOrganism (without HMMs).',model.rxnNotes(i));
    +0349             model.rxnNotes(i)=strrep(model.rxnNotes(i),'.','. ');
    +0350         else
    +0351             model.rxnNotes(i)={'Included by getKEGGModelForOrganism (without HMMs)'};
    +0352         end
    +0353     end
    +0354     fprintf('COMPLETE\n\n');
    +0355     fprintf('*** Model reconstruction complete ***\n');
    +0356     return;
    +0357 end
    +0358 
    +0359 %Query the whole proteome against the concatenated KO HMM library in a
    +0360 %single hmmsearch. With the profile library as the query and the
    +0361 %proteome as the target sequence database, per-hit E-values stay
    +0362 %comparable across KOs (same search direction, same effective database
    +0363 %size), and no per-organism phylogenetic-distance subsampling is needed
    +0364 %because the library sequence set is already fixed.
    +0365 if ispc
    +0366     binEnd='.exe';
    +0367 elseif ismac
    +0368     binEnd='.mac';
    +0369 else
    +0370     binEnd='';
    +0371 end
    +0372 %Fetch the HMMER binary on demand if it is not already present
    +0373 if ~exist(fullfile(ravenPath,'src','external','hmmer',['hmmsearch' binEnd]),'file')
    +0374     downloadRavenBinaries({'hmmer'});
    +0375 end
    +0376 tblFile=[tempname '.tblout'];
    +0377 fprintf('Querying the user-specified FASTA file against the KEGG Orthology specific HMMs... ');
    +0378 [status, output]=system(['"' fullfile(ravenPath,'src','external','hmmer',['hmmsearch' binEnd]) '" --cpu "' num2str(cores) '" --tblout "' tblFile '" "' libraryFile '" "' fastaFile '"']);
    +0379 if status~=0
    +0380     EM=['Error when querying the concatenated HMM library:\n' output];
    +0381     error('RAVEN:badInput', '%s', EM);
    +0382 end
    +0383 fprintf('COMPLETE\n');
    +0384 
    +0385 fprintf('Parsing the HMM search results... ');
    +0386 koGeneMat=zeros(numel(KOModel.rxns),3000); %Make room for 3000 genes
    +0387 genes=cell(3000,1);
    +0388 %Map each KO id to its row in koGeneMat
    +0389 koTable=java.util.Hashtable;
    +0390 for i=1:numel(KOModel.rxns)
    +0391     koTable.put(KOModel.rxns{i},i);
    +0392 end
    +0393 %Store the column index for each gene in a hash list
    +0394 hTable=java.util.Hashtable;
    +0395 geneCounter=0;
    +0396 fid=fopen(tblFile,'r');
    +0397 while 1
    +0398     tline=fgetl(fid);
    +0399     if ~ischar(tline)
    +0400         break;
    +0401     end
    +0402     %Skip the comment/header lines that hmmsearch --tblout writes
    +0403     if isempty(tline) || tline(1)=='#'
    +0404         continue;
    +0405     end
    +0406     elements=regexp(strtrim(tline),'\s+','split');
    +0407     if numel(elements)<5
    +0408         continue;
    +0409     end
    +0410     %With the profile library as the query, the target name (col 1) is
    +0411     %the proteome gene, the query name (col 3) is the KO, and the
    +0412     %full-sequence E-value is in col 5
    +0413     gene=elements{1};
    +0414     ko=elements{3};
    +0415     score=str2double(elements{5});
    +0416     koIdx=koTable.get(ko);
    +0417     if isempty(koIdx) || isnan(score) || score>cutOff
    +0418         continue;
    +0419     end
    +0420     %If the score is exactly 0, change it to a very small value to avoid
    +0421     %NaN
    +0422     if score==0
    +0423         score=10^-250;
    +0424     end
    +0425     I=hTable.get(gene);
    +0426     if any(I)
    +0427         %Keep the best (smallest) E-value for this gene-KO pair
    +0428         if koGeneMat(koIdx,I)==0 || score<koGeneMat(koIdx,I)
    +0429             koGeneMat(koIdx,I)=score;
    +0430         end
    +0431     else
    +0432         geneCounter=geneCounter+1;
    +0433         hTable.put(gene,geneCounter);
    +0434         genes{geneCounter}=gene;
    +0435         koGeneMat(koIdx,geneCounter)=score;
    +0436     end
    +0437 end
    +0438 fclose(fid);
    +0439 delete(tblFile);
    +0440 fprintf('COMPLETE\n');
    +0441 
    +0442 fprintf('Removing gene, KEGG Orthology associations below minScoreRatioKO, minScoreRatioG... ');
    +0443 koGeneMat=koGeneMat(:,1:geneCounter);
    +0444 
    +0445 %Remove the genes for each KO that are below minScoreRatioKO.
    +0446 for i=1:size(koGeneMat,1)
    +0447     J=find(koGeneMat(i,:));
    +0448     if any(J)
    +0449         koGeneMat(i,J(log(koGeneMat(i,J))/log(min(koGeneMat(i,J)))<minScoreRatioKO))=0;
    +0450     end
    +0451 end
    +0452 
    +0453 %Remove the KOs for each gene that are below minScoreRatioG
    +0454 for i=1:size(koGeneMat,2)
    +0455     J=find(koGeneMat(:,i));
    +0456     if any(J)
    +0457         koGeneMat(J(log(koGeneMat(J,i))/log(min(koGeneMat(J,i)))<minScoreRatioG),i)=0;
    +0458     end
    +0459 end
    +0460 fprintf('COMPLETE\n');
    +0461 
    +0462 fprintf('Adding gene annotations to the model... ');
    +0463 %Create the new model
    +0464 model.genes=genes(1:geneCounter);
    +0465 model.grRules=cell(numel(model.rxns),1);
    +0466 model.grRules(:)={''};
    +0467 model.rxnGeneMat=sparse(numel(model.rxns),numel(model.genes));
    +0468 
    +0469 %Loop through the reactions and add the corresponding genes
    +0470 for i=1:numel(model.rxns)
    +0471     if isstruct(model.rxnMiriams{i})
    +0472         %Get all KOs
    +0473         I=find(strcmpi(model.rxnMiriams{i}.name,'kegg.orthology'));
    +0474         KOs=model.rxnMiriams{i}.value(I);
    +0475         %Find the KOs and the corresponding genes
    +0476         J=ismember(KOModel.rxns,KOs);
    +0477         [~, K]=find(koGeneMat(J,:));
    +0478 
    +0479         if any(K)
    +0480             model.rxnGeneMat(i,K)=1;
    +0481             %Also delete KOs for which no genes were found. If no genes at
    +0482             %all were matched to the reaction it will be deleted later
    +0483             L=sum(koGeneMat(J,:),2)==0;
    +0484             model.rxnMiriams{i}.value(I(L))=[];
    +0485             model.rxnMiriams{i}.name(I(L))=[];
    +0486         end
    +0487     end
    +0488 end
    +0489 fprintf('COMPLETE\n');
    +0490 
    +0491 %Find and delete all reactions without genes. This also removes genes that
    +0492 %are not used (which could happen because minScoreRatioG and
    +0493 %minScoreRatioKO). If keepSpontaneous==true, the spontaneous reactions
    +0494 %without genes are kept in the model. Spontaneous reactions with original
    +0495 %gene associations are treated in the same way, like the rest of the
    +0496 %reactions - if gene associations were removed during HMM search, such
    +0497 %reactions are deleted from the model
    +0498 if keepSpontaneous==true
    +0499     %Not the most comprise way to delete reactions without genes, but this
    +0500     %makes the code easier to understand. Firstly the non-spontaneous
    +0501     %reactions without genes are removed. After that, the second deletion
    +0502     %step removes spontaneous reactions, which had gene associations before
    +0503     %HMM search, but no longer have after it
    +0504     fprintf('Removing non-spontaneous reactions which after HMM search no longer have GPR rules... ');
    +0505     I=~any(model.rxnGeneMat,2)&~ismember(model.rxns,isSpontaneous);
    +0506     model=removeReactions(model,I,true,true);
    +0507     I=~any(model.rxnGeneMat,2)&ismember(model.rxns,spontRxnsWithGenes);
    +0508     model=removeReactions(model,I,true,true);
    +0509 else
    +0510     %Just simply check for any new reactions without genes and remove
    +0511     %it
    +0512     fprintf('Removing reactions which after HMM search no longer have GPR rules... ');
    +0513     I=~any(model.rxnGeneMat,2);
    +0514     model=removeReactions(model,I,true,true);
    +0515 end
    +0516 fprintf('COMPLETE\n');
    +0517 
    +0518 fprintf('Constructing GPR rules and finalizing the model... ');
    +0519 %Add the gene associations as "or"
    +0520 for i=1:numel(model.rxns)
    +0521     %Find the involved genes
    +0522     I=find(model.rxnGeneMat(i,:));
    +0523     if any(I)
    +0524         model.grRules{i}=['(' model.genes{I(1)}];
    +0525         for j=2:numel(I)
    +0526             model.grRules{i}=[model.grRules{i} ' or ' model.genes{I(j)}];
    +0527         end
    +0528         model.grRules{i}=[model.grRules{i} ')'];
    +0529     end
    +0530 end
    +0531 
    +0532 %Fix grRules and reconstruct rxnGeneMat
    +0533 [grRules,rxnGeneMat] = standardizeGrRules(model,false); %Give detailed output
    +0534 model.grRules = grRules;
    +0535 model.rxnGeneMat = rxnGeneMat;
    +0536 
    +0537 %Add the description to the reactions. The reference model has no reaction
    +0538 %notes, so readYAMLmodel leaves the field out
    +0539 if ~isfield(model,'rxnNotes')
    +0540     model.rxnNotes=repmat({''},numel(model.rxns),1);
    +0541 end
    +0542 for i=1:numel(model.rxns)
    +0543     if ~isempty(model.rxnNotes{i})
    +0544         model.rxnNotes(i)=strcat('Included by getKEGGModelForOrganism (using HMMs).',model.rxnNotes(i));
    +0545         model.rxnNotes(i)=strrep(model.rxnNotes(i),'.','. ');
    +0546     else
    +0547         model.rxnNotes(i)={'Included by getKEGGModelForOrganism (using HMMs)'};
    +0548     end
    +0549 end
    +0550 %Remove the temp fasta file
    +0551 delete(fastaFile)
    +0552 fprintf('COMPLETE\n\n*** Model reconstruction complete ***\n');
    +0553 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/reconstruction/kegg/getModelFromKEGG.html b/docs/html/reconstruction/kegg/getModelFromKEGG.html similarity index 50% rename from doc/reconstruction/kegg/getModelFromKEGG.html rename to docs/html/reconstruction/kegg/getModelFromKEGG.html index c4b8d064..51ae7361 100644 --- a/doc/reconstruction/kegg/getModelFromKEGG.html +++ b/docs/html/reconstruction/kegg/getModelFromKEGG.html @@ -29,19 +29,18 @@

    SYNOPSIS ^DESCRIPTION ^

     getModelFromKEGG  Load the global KEGG model.
     
    - Loads the global KEGG reaction/gene model from keggModel.mat. On first
    - use --- when no keggModel.mat is present yet --- the underlying
    - artefacts (the gene-free reference model plus the KO/reaction/organism-
    - gene relational tables, published as a raven-data release) are
    - downloaded and assembled instead, and the result is cached to
    - keggModel.mat so later calls load instantly. The first build can take
    - a while (the organism-gene table covers every KEGG organism) and needs
    - a few hundred MB of disk.
    + Loads the global KEGG reaction and gene model from keggModel.mat. When
    + keggModel.mat is not present, the underlying artefacts (the gene-free
    + reference model plus the KO, reaction and organism-gene relational
    + tables, published as a toolbox-assets release) are downloaded and
    + assembled, and the result is saved to keggModel.mat for later calls. The
    + first build takes longer because the organism-gene table covers every
    + KEGG organism, and needs a few hundred MB of disk.
     
      Name-Value Arguments
      --------------------
      keepSpontaneous : logical
    -     include reactions labeled as "spontaneous" (default true).
    +     include reactions labelled as "spontaneous" (default true).
      keepUndefinedStoich : logical
          include reactions in the form n A <=> n+1 A (default true).
      keepIncomplete : logical
    @@ -53,16 +52,18 @@ 

    DESCRIPTION ^SOURCE CODE ^
    0001 function [model,KOModel]=getModelFromKEGG(varargin)
     0002 % getModelFromKEGG  Load the global KEGG model.
     0003 %
    -0004 % Loads the global KEGG reaction/gene model from keggModel.mat. On first
    -0005 % use --- when no keggModel.mat is present yet --- the underlying
    -0006 % artefacts (the gene-free reference model plus the KO/reaction/organism-
    -0007 % gene relational tables, published as a raven-data release) are
    -0008 % downloaded and assembled instead, and the result is cached to
    -0009 % keggModel.mat so later calls load instantly. The first build can take
    -0010 % a while (the organism-gene table covers every KEGG organism) and needs
    -0011 % a few hundred MB of disk.
    -0012 %
    -0013 % Name-Value Arguments
    -0014 % --------------------
    -0015 % keepSpontaneous : logical
    -0016 %     include reactions labeled as "spontaneous" (default true).
    -0017 % keepUndefinedStoich : logical
    -0018 %     include reactions in the form n A <=> n+1 A (default true).
    -0019 % keepIncomplete : logical
    -0020 %     include reactions labelled as "incomplete", "erroneous" or "unclear"
    -0021 %     (default true).
    -0022 % keepGeneral : logical
    -0023 %     include reactions labelled as "general reaction" (default false).
    -0024 %
    -0025 % Returns
    -0026 % -------
    -0027 % model : struct
    -0028 %     a model structure with all reactions and the metabolites used in them.
    -0029 % KOModel : struct
    -0030 %     a model structure representing KEGG Orthology ids and their associated
    -0031 %     genes. KO ids are stored as reactions.
    -0032 %
    -0033 % Notes
    -0034 % -----
    -0035 % The model output can be used as a template for fillGaps. In that case,
    -0036 % remove the genes and rxnGeneMat fields before parsing:
    -0037 %     model = rmfield(model, 'genes'), etc.
    -0038 %
    -0039 % See also
    -0040 % --------
    -0041 % getKEGGModelForOrganism, getPhylDist, fillGaps
    -0042 
    -0043 ravenPath=findRAVENroot();
    -0044 
    -0045 p=parseRAVENargs(varargin, {'keepSpontaneous',true; 'keepUndefinedStoich',true; ...
    -0046     'keepIncomplete',true; 'keepGeneral',false});
    -0047 keepSpontaneous=p.keepSpontaneous;
    -0048 keepUndefinedStoich=p.keepUndefinedStoich;
    -0049 keepIncomplete=p.keepIncomplete;
    -0050 keepGeneral=p.keepGeneral;
    -0051 
    -0052 modelFile=fullfile(ravenPath,'reconstruction','kegg','keggModel.mat');
    -0053 if isfile(modelFile)
    -0054     fprintf(['Importing the global KEGG model from ' strrep(modelFile,'\','/') '... ']);
    -0055     load(modelFile,'model','KOModel','isSpontaneous','isUndefinedStoich','isIncomplete','isGeneral');
    -0056     fprintf('COMPLETE\n');
    -0057 else
    -0058     [model,KOModel,isSpontaneous,isUndefinedStoich,isIncomplete,isGeneral]=buildGlobalKEGGModel(ravenPath);
    -0059     fprintf(['Saving the global KEGG model to ' strrep(modelFile,'\','/') ' for future use... ']);
    -0060     save(modelFile,'model','KOModel','isSpontaneous','isUndefinedStoich','isIncomplete','isGeneral','-v7.3');
    -0061     fprintf('COMPLETE\n');
    -0062 end
    -0063 
    -0064 if keepSpontaneous==false
    -0065     model=removeReactions(model,intersect(isSpontaneous,model.rxns),true,true);
    -0066 end
    -0067 if keepUndefinedStoich==false
    -0068     model=removeReactions(model,intersect(isUndefinedStoich,model.rxns),true,true);
    -0069 end
    -0070 if keepIncomplete==false
    -0071     model=removeReactions(model,intersect(isIncomplete,model.rxns),true,true);
    -0072 end
    -0073 if keepGeneral==false
    -0074     model=removeReactions(model,intersect(isGeneral,model.rxns),true,true);
    -0075 end
    -0076 end

    +0004 % Loads the global KEGG reaction and gene model from keggModel.mat. When +0005 % keggModel.mat is not present, the underlying artefacts (the gene-free +0006 % reference model plus the KO, reaction and organism-gene relational +0007 % tables, published as a toolbox-assets release) are downloaded and +0008 % assembled, and the result is saved to keggModel.mat for later calls. The +0009 % first build takes longer because the organism-gene table covers every +0010 % KEGG organism, and needs a few hundred MB of disk. +0011 % +0012 % Name-Value Arguments +0013 % -------------------- +0014 % keepSpontaneous : logical +0015 % include reactions labelled as "spontaneous" (default true). +0016 % keepUndefinedStoich : logical +0017 % include reactions in the form n A <=> n+1 A (default true). +0018 % keepIncomplete : logical +0019 % include reactions labelled as "incomplete", "erroneous" or "unclear" +0020 % (default true). +0021 % keepGeneral : logical +0022 % include reactions labelled as "general reaction" (default false). +0023 % +0024 % Returns +0025 % ------- +0026 % model : struct +0027 % a model structure with the reactions that the keep* options retain (by +0028 % default all except those labelled as "general reaction") and the +0029 % metabolites used in them. +0030 % KOModel : struct +0031 % a model structure representing KEGG Orthology ids and their associated +0032 % genes. KO ids are stored as reactions. +0033 % +0034 % Notes +0035 % ----- +0036 % The model output can be used as a template for fillGaps after the genes +0037 % and rxnGeneMat fields are removed: +0038 % model = rmfield(model, 'genes') and model = rmfield(model, 'rxnGeneMat'). +0039 % +0040 % See also +0041 % -------- +0042 % getKEGGModelForOrganism, getPhylDist, fillGaps +0043 +0044 ravenPath=findRAVENroot(); +0045 +0046 p=parseRAVENargs(varargin, {'keepSpontaneous',true; 'keepUndefinedStoich',true; ... +0047 'keepIncomplete',true; 'keepGeneral',false}); +0048 keepSpontaneous=p.keepSpontaneous; +0049 keepUndefinedStoich=p.keepUndefinedStoich; +0050 keepIncomplete=p.keepIncomplete; +0051 keepGeneral=p.keepGeneral; +0052 +0053 modelFile=fullfile(ravenPath,'src','reconstruction','kegg','keggModel.mat'); +0054 if isfile(modelFile) +0055 fprintf(['Importing the global KEGG model from ' strrep(modelFile,'\','/') '... ']); +0056 load(modelFile,'model','KOModel','isSpontaneous','isUndefinedStoich','isIncomplete','isGeneral'); +0057 fprintf('COMPLETE\n'); +0058 else +0059 [model,KOModel,isSpontaneous,isUndefinedStoich,isIncomplete,isGeneral]=buildGlobalKEGGModel(ravenPath); +0060 fprintf(['Saving the global KEGG model to ' strrep(modelFile,'\','/') ' for future use... ']); +0061 save(modelFile,'model','KOModel','isSpontaneous','isUndefinedStoich','isIncomplete','isGeneral','-v7.3'); +0062 fprintf('COMPLETE\n'); +0063 end +0064 +0065 if keepSpontaneous==false +0066 model=removeReactions(model,intersect(isSpontaneous,model.rxns),true,true); +0067 end +0068 if keepUndefinedStoich==false +0069 model=removeReactions(model,intersect(isUndefinedStoich,model.rxns),true,true); +0070 end +0071 if keepIncomplete==false +0072 model=removeReactions(model,intersect(isIncomplete,model.rxns),true,true); +0073 end +0074 if keepGeneral==false +0075 model=removeReactions(model,intersect(isGeneral,model.rxns),true,true); +0076 end +0077 end


    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/reconstruction/kegg/getPhylDist.html b/docs/html/reconstruction/kegg/getPhylDist.html similarity index 64% rename from doc/reconstruction/kegg/getPhylDist.html rename to docs/html/reconstruction/kegg/getPhylDist.html index ffb9de19..131c3e80 100644 --- a/doc/reconstruction/kegg/getPhylDist.html +++ b/docs/html/reconstruction/kegg/getPhylDist.html @@ -30,7 +30,8 @@

    DESCRIPTION ^
     getPhylDist  Load the pre-built KEGG phylogenetic distance matrix.
     
      Loads the pre-built phylogenetic distance matrix from keggPhylDist.mat.
    - The artefact is distributed as a raven-data release asset.
    + The matrix ships with RAVEN and is derived from the KEGG taxonomy, which
    + is a toolbox-assets release asset.
     
      Name-Value Arguments
      --------------------
    @@ -68,47 +69,48 @@ 

    SOURCE CODE ^% getPhylDist Load the pre-built KEGG phylogenetic distance matrix. 0003 % 0004 % Loads the pre-built phylogenetic distance matrix from keggPhylDist.mat. -0005 % The artefact is distributed as a raven-data release asset. -0006 % -0007 % Name-Value Arguments -0008 % -------------------- -0009 % onlyInKingdom : logical -0010 % if true, returns a distance matrix with Inf for cross-domain pairs -0011 % (Prokaryota vs Eukaryota) (default false). -0012 % -0013 % Returns -0014 % ------- -0015 % phylDistStruct : struct -0016 % structure with fields: -0017 % -0018 % - ids : cell array of KEGG organism abbreviations. -0019 % - distMat : pairwise distance matrix (number of tree-nodes apart). -0020 % -0021 % Notes -0022 % ----- -0023 % Distance is based on the number of nodes two organisms are apart in the -0024 % KEGG taxonomy tree. -0025 -0026 p=parseRAVENargs(varargin, {'onlyInKingdom',false}); -0027 onlyInKingdom=p.onlyInKingdom; -0028 -0029 ravenPath=findRAVENroot(); -0030 distFile=fullfile(ravenPath,'reconstruction','kegg','keggPhylDist.mat'); -0031 if ~exist(distFile,'file') -0032 error('getPhylDist:noData', ... -0033 ['keggPhylDist.mat not found at ' strrep(distFile,'\','/') '.\n' ... -0034 'It ships with RAVEN, so a missing copy means an incomplete checkout. ' ... -0035 'Restore it from the RAVEN repository, or generate it with the ' ... -0036 'raven-toolbox Python package.']); -0037 end -0038 fprintf(['Importing the KEGG phylogenetic distance matrix from ' strrep(distFile,'\','/') '... ']); -0039 load(distFile); -0040 fprintf('COMPLETE\n'); -0041 -0042 if onlyInKingdom==true -0043 phylDistStruct=phylDistStructOnlyInKingdom; -0044 end -0045 end

    +0005 % The matrix ships with RAVEN and is derived from the KEGG taxonomy, which +0006 % is a toolbox-assets release asset. +0007 % +0008 % Name-Value Arguments +0009 % -------------------- +0010 % onlyInKingdom : logical +0011 % if true, returns a distance matrix with Inf for cross-domain pairs +0012 % (Prokaryota vs Eukaryota) (default false). +0013 % +0014 % Returns +0015 % ------- +0016 % phylDistStruct : struct +0017 % structure with fields: +0018 % +0019 % - ids : cell array of KEGG organism abbreviations. +0020 % - distMat : pairwise distance matrix (number of tree-nodes apart). +0021 % +0022 % Notes +0023 % ----- +0024 % Distance is based on the number of nodes two organisms are apart in the +0025 % KEGG taxonomy tree. +0026 +0027 p=parseRAVENargs(varargin, {'onlyInKingdom',false}); +0028 onlyInKingdom=p.onlyInKingdom; +0029 +0030 ravenPath=findRAVENroot(); +0031 distFile=fullfile(ravenPath,'src','reconstruction','kegg','keggPhylDist.mat'); +0032 if ~exist(distFile,'file') +0033 error('getPhylDist:noData', ... +0034 ['keggPhylDist.mat not found at ' strrep(distFile,'\','/') '.\n' ... +0035 'It ships with RAVEN, so a missing copy means an incomplete checkout. ' ... +0036 'Restore it from the RAVEN repository, or generate it with the ' ... +0037 'raven-toolbox Python package.']); +0038 end +0039 fprintf(['Importing the KEGG phylogenetic distance matrix from ' strrep(distFile,'\','/') '... ']); +0040 load(distFile); +0041 fprintf('COMPLETE\n'); +0042 +0043 if onlyInKingdom==true +0044 phylDistStruct=phylDistStructOnlyInKingdom; +0045 end +0046 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/reconstruction/kegg/index.html b/docs/html/reconstruction/kegg/index.html similarity index 55% rename from doc/reconstruction/kegg/index.html rename to docs/html/reconstruction/kegg/index.html index 86fec3d3..a2264cb3 100644 --- a/doc/reconstruction/kegg/index.html +++ b/docs/html/reconstruction/kegg/index.html @@ -19,7 +19,7 @@

    Index for reconstruction/kegg

    Matlab files in this directory:

    -
     buildGlobalGPRbuildGlobalGPR Join every KEGG organism's genes onto the reference
     buildGlobalKEGGModelbuildGlobalKEGGModel Download and assemble the global KEGG model.
     getKEGGModelForOrganismgetKEGGModelForOrganism Reconstruct a model from KEGG protein homology.
     getModelFromKEGGgetModelFromKEGG Load the global KEGG model.
     getPhylDistgetPhylDist Load the pre-built KEGG phylogenetic distance matrix.
     keggDataVersionkeggDataVersion The raven-data release tag for KEGG artefacts.
     readKEGGTablereadKEGGTable Read a gzipped KEGG relational table into a table with
    + buildGlobalGPRbuildGlobalGPR Join the genes of all KEGG organisms to reference reactions by KO id.  buildGlobalKEGGModelbuildGlobalKEGGModel Download and assemble the global KEGG model.  getKEGGModelForOrganismgetKEGGModelForOrganism Reconstruct a model from KEGG protein homology.  getModelFromKEGGgetModelFromKEGG Load the global KEGG model.  getPhylDistgetPhylDist Load the pre-built KEGG phylogenetic distance matrix.  keggDataVersionkeggDataVersion The toolbox-assets release tag for KEGG artefacts.  readKEGGTablereadKEGGTable Read a gzipped KEGG relational table into a table of cellstr columns.

    Other Matlab-specific files in this directory:

      diff --git a/doc/reconstruction/kegg/keggDataVersion.html b/docs/html/reconstruction/kegg/keggDataVersion.html similarity index 60% rename from doc/reconstruction/kegg/keggDataVersion.html rename to docs/html/reconstruction/kegg/keggDataVersion.html index f2a044e1..9e0651de 100644 --- a/doc/reconstruction/kegg/keggDataVersion.html +++ b/docs/html/reconstruction/kegg/keggDataVersion.html @@ -4,7 +4,7 @@ Description of keggDataVersion - + @@ -21,24 +21,23 @@

      keggDataVersion

      PURPOSE ^

      -
      keggDataVersion The raven-data release tag for KEGG artefacts.
      +
      keggDataVersion The toolbox-assets release tag for KEGG artefacts.

      SYNOPSIS ^

      function version=keggDataVersion()

      DESCRIPTION ^

      -
       keggDataVersion  The raven-data release tag for KEGG artefacts.
      +
       keggDataVersion  The toolbox-assets release tag for KEGG artefacts.
       
      - Reads reconstruction/kegg/KEGG_VERSION.md --- the single place this
      - version is recorded --- so every function that downloads a KEGG
      - artefact (getModelFromKEGG, getKEGGModelForOrganism) stays in sync
      - when raven-data publishes a new KEGG release. The version is the
      - file's first line.
      + Reads the first line of src/reconstruction/kegg/KEGG_VERSION.md. The file is
      + the single place where the version is recorded, so that every function that
      + downloads a KEGG artefact (getModelFromKEGG, getKEGGModelForOrganism) uses
      + the same release.
       
        Returns
        -------
        version : char
      -     the raven-data release tag, e.g. 'kegg118'.
      + the toolbox-assets release tag, for example 'kegg118'.

      CROSS-REFERENCE INFORMATION ^

      @@ -54,28 +53,27 @@

      CROSS-REFERENCE INFORMATION ^
 
 <h2><a name=SOURCE CODE ^

      0001 function version=keggDataVersion()
      -0002 % keggDataVersion  The raven-data release tag for KEGG artefacts.
      +0002 % keggDataVersion  The toolbox-assets release tag for KEGG artefacts.
       0003 %
      -0004 % Reads reconstruction/kegg/KEGG_VERSION.md --- the single place this
      -0005 % version is recorded --- so every function that downloads a KEGG
      -0006 % artefact (getModelFromKEGG, getKEGGModelForOrganism) stays in sync
      -0007 % when raven-data publishes a new KEGG release. The version is the
      -0008 % file's first line.
      -0009 %
      -0010 % Returns
      -0011 % -------
      -0012 % version : char
      -0013 %     the raven-data release tag, e.g. 'kegg118'.
      -0014 
      -0015 ravenPath=findRAVENroot();
      -0016 versionFile=fullfile(ravenPath,'reconstruction','kegg','KEGG_VERSION.md');
      -0017 fid=fopen(versionFile,'r');
      -0018 if fid==-1
      -0019     error('keggDataVersion:fileNotFound','Cannot read %s.',strrep(versionFile,'\','/'))
      -0020 end
      -0021 version=strtrim(fgetl(fid));
      -0022 fclose(fid);
      -0023 end
      +0004 % Reads the first line of src/reconstruction/kegg/KEGG_VERSION.md. The file is +0005 % the single place where the version is recorded, so that every function that +0006 % downloads a KEGG artefact (getModelFromKEGG, getKEGGModelForOrganism) uses +0007 % the same release. +0008 % +0009 % Returns +0010 % ------- +0011 % version : char +0012 % the toolbox-assets release tag, for example 'kegg118'. +0013 +0014 ravenPath=findRAVENroot(); +0015 versionFile=fullfile(ravenPath,'src','reconstruction','kegg','KEGG_VERSION.md'); +0016 fid=fopen(versionFile,'r'); +0017 if fid==-1 +0018 error('keggDataVersion:fileNotFound','Cannot read %s.',strrep(versionFile,'\','/')) +0019 end +0020 version=strtrim(fgetl(fid)); +0021 fclose(fid); +0022 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/reconstruction/kegg/readKEGGTable.html b/docs/html/reconstruction/kegg/readKEGGTable.html similarity index 82% rename from doc/reconstruction/kegg/readKEGGTable.html rename to docs/html/reconstruction/kegg/readKEGGTable.html index ce9bc14d..c377249a 100644 --- a/doc/reconstruction/kegg/readKEGGTable.html +++ b/docs/html/reconstruction/kegg/readKEGGTable.html @@ -4,7 +4,7 @@ Description of readKEGGTable - + @@ -21,21 +21,21 @@

    readKEGGTable

    PURPOSE ^

    -
    readKEGGTable Read a gzipped KEGG relational table into a table with
    +
    readKEGGTable Read a gzipped KEGG relational table into a table of cellstr columns.

    SYNOPSIS ^

    function tbl=readKEGGTable(gzFile)

    DESCRIPTION ^

    -
     readKEGGTable  Read a gzipped KEGG relational table into a table with
    - every column forced to char cellstr (matching RAVEN's own
    - cell-array-of-char convention, regardless of the caller's default
    - text-import type).
    +
     readKEGGTable  Read a gzipped KEGG relational table into a table of cellstr columns.
    +
    + Every column is a cellstr (RAVEN's cell-array-of-char convention), whatever the
    + default text-import type of the caller.
     
      Parameters
      ----------
      gzFile : char
    -     path to the gzipped TSV file, e.g. '.../kegg118_ko_reaction.tsv.gz'.
    +     path to the gzipped TSV file, for example '.../kegg118_ko_reaction.tsv.gz'.
     
      Returns
      -------
    @@ -56,15 +56,15 @@ 

    CROSS-REFERENCE INFORMATION ^
 
 <h2><a name=SOURCE CODE ^

    0001 function tbl=readKEGGTable(gzFile)
    -0002 % readKEGGTable  Read a gzipped KEGG relational table into a table with
    -0003 % every column forced to char cellstr (matching RAVEN's own
    -0004 % cell-array-of-char convention, regardless of the caller's default
    -0005 % text-import type).
    +0002 % readKEGGTable  Read a gzipped KEGG relational table into a table of cellstr columns.
    +0003 %
    +0004 % Every column is a cellstr (RAVEN's cell-array-of-char convention), whatever the
    +0005 % default text-import type of the caller.
     0006 %
     0007 % Parameters
     0008 % ----------
     0009 % gzFile : char
    -0010 %     path to the gzipped TSV file, e.g. '.../kegg118_ko_reaction.tsv.gz'.
    +0010 %     path to the gzipped TSV file, for example '.../kegg118_ko_reaction.tsv.gz'.
     0011 %
     0012 % Returns
     0013 % -------
    diff --git a/doc/right.png b/docs/html/right.png
    similarity index 100%
    rename from doc/right.png
    rename to docs/html/right.png
    diff --git a/doc/sgi.png b/docs/html/sgi.png
    similarity index 100%
    rename from doc/sgi.png
    rename to docs/html/sgi.png
    diff --git a/doc/simulinkicon.gif b/docs/html/simulinkicon.gif
    similarity index 100%
    rename from doc/simulinkicon.gif
    rename to docs/html/simulinkicon.gif
    diff --git a/doc/solaris.png b/docs/html/solaris.png
    similarity index 100%
    rename from doc/solaris.png
    rename to docs/html/solaris.png
    diff --git a/doc/solver/checkSolution.html b/docs/html/solver/checkSolution.html
    similarity index 73%
    rename from doc/solver/checkSolution.html
    rename to docs/html/solver/checkSolution.html
    index 1bf7d3e3..088e67b8 100644
    --- a/doc/solver/checkSolution.html
    +++ b/docs/html/solver/checkSolution.html
    @@ -4,7 +4,7 @@
     
       Description of checkSolution
       
    -  
    +  
       
       
       
    @@ -21,18 +21,18 @@ 

    checkSolution

    PURPOSE ^

    -
    checkSolution Check if a Mosek solution is feasible and optimal.
    +
    checkSolution Check if a solver result is feasible and optimal.

    SYNOPSIS ^

    function [isFeasible, isOptimal]=checkSolution(res)

    DESCRIPTION ^

    -
     checkSolution  Check if a Mosek solution is feasible and optimal.
    +
     checkSolution  Check if a solver result is feasible and optimal.
     
      Parameters
      ----------
      res : struct
    -     the output structure from mosekopt.
    +     the output structure from optimizeProb.
     
      Returns
      -------
    @@ -43,11 +43,7 @@ 

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    @@ -63,12 +59,12 @@

    CROSS-REFERENCE INFORMATION ^
 
 <h2><a name=SOURCE CODE ^

    +0033 isFeasible=false; +0034 end +0035 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/solver/getMinNrFluxes.html b/docs/html/solver/getMinNrFluxes.html new file mode 100644 index 00000000..45848c56 --- /dev/null +++ b/docs/html/solver/getMinNrFluxes.html @@ -0,0 +1,677 @@ + + + + Description of getMinNrFluxes + + + + + + + + + +
    Home > solver > getMinNrFluxes.m
    + + + +

    getMinNrFluxes +

    + +

    PURPOSE ^

    +
    getMinNrFluxes Find the minimal set of fluxes that satisfy the model.
    + +

    SYNOPSIS ^

    +
    function [x,I,exitFlag]=getMinNrFluxes(model, varargin)
    + +

    DESCRIPTION ^

    +
     getMinNrFluxes  Find the minimal set of fluxes that satisfy the model.
    +
    + Uses mixed integer linear programming to find the minimal set of fluxes
    + that satisfy the model.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + toMinimize : cell or logical or double
    +     a cell array of reaction IDs, a logical vector with the same number
    +     of elements as reactions in the model, or a vector of indexes for the
    +     reactions that should be minimised (default model.rxns).
    + params : struct
    +     solver parameters passed to optimizeProb. With formulation
    +     'reversible', intTol (10^-9), TimeLimit (300) and Seed (26) are set
    +     when absent (default empty struct).
    + scores : double
    +     vector of weights for the reactions. A reaction with a negative
    +     score adds the negated score to the minimised objective when it
    +     carries flux. Scores of 0 or above are not supported and are
    +     changed to max(scores(scores<0)), or to 0 if scores has no negative
    +     entry at all. Must have the same dimension as toMinimize
    +     (find(toMinimize) if it is a logical vector) (default -1 for all
    +     reactions).
    + formulation : char
    +     which MILP formulation to build (default 'irrev'):
    +
    +     - 'irrev' : convert the model to irreversible format and use one
    +       binary per irreversible reaction, so a reversible reaction that is
    +       minimised needs two binaries. The magnitude of the fluxes is
    +       estimated with an LP first, which also provides a start solution.
    +     - 'reversible' : keep the model reversible and use one binary per
    +       reaction, with a larger constraint matrix. The formulation has
    +       fewer binaries but needs a solver that handles MILPs well. No LP
    +       estimate of the flux magnitudes is made, and a fixed flux of 1000 is
    +       assumed.
    + verbose : logical
    +     if true, the MILP progression will be shown (default false).
    + resolveTies : logical
    +     if true, select one optimum among the degenerate optima of the MILP
    +     instead of relying on Seed: the primary (scores) objective is held
    +     at its optimum, the count of "on" reactions is minimised, and among
    +     the sparsest solutions the summed reaction-id rank is minimised
    +     (lowest ids preferred). This takes two extra MILP solves. A phase
    +     that is not proven optimal within TimeLimit gives a warning, and the
    +     solution of the last phase is used when it is feasible. Only
    +     implemented for formulation='reversible'; combined with 'irrev' it
    +     gives an error. Matches resolve_ties in raven-toolbox
    +     (raven-gecko-parity#104) (default false).
    +
    + Returns
    + -------
    + x : double
    +     the corresponding fluxes for the full model.
    + I : double
    +     the indexes of the reactions in toMinimize that were used in the
    +     solution.
    + exitFlag : double
    +     exit status:
    +
    +     - 1 : optimal solution found
    +     - -1 : no solution found, either because the problem is infeasible or
    +       because the solver reached its time limit before finding one
    +     - -2 : a solution was found but is not proven optimal, because the
    +       solver stopped before reaching optimality. It is returned and may
    +       use more fluxes than the minimum
    +
    + Examples
    + --------
    +     [x, I, exitFlag] = getMinNrFluxes(model, toMinimize, params, scores);
    +
    + Notes
    + -----
    + The binary of a reaction is linked to its flux through a large flux
    + value. With formulation 'irrev' the value is the larger of 1000 and 5
    + times the largest flux of an LP solution; with 'reversible' it is 1000
    + (mmol/gDW/h). A flux above this value is cut off by the binary constraint
    + of the reaction, so the MILP can become infeasible.
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • checkSolution checkSolution Check if a solver result is feasible and optimal.
    • optimizeProb optimizeProb Optimise an LP or MILP formulated in COBRA terms.
    • solveLP solveLP Solve a linear programming problem.
    +This function is called by: +
      +
    • solveLP solveLP Solve a linear programming problem.
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [x,I,exitFlag]=getMinNrFluxes(model, varargin)
    +0002 % getMinNrFluxes  Find the minimal set of fluxes that satisfy the model.
    +0003 %
    +0004 % Uses mixed integer linear programming to find the minimal set of fluxes
    +0005 % that satisfy the model.
    +0006 %
    +0007 % Parameters
    +0008 % ----------
    +0009 % model : struct
    +0010 %     a model structure.
    +0011 %
    +0012 % Name-Value Arguments
    +0013 % --------------------
    +0014 % toMinimize : cell or logical or double
    +0015 %     a cell array of reaction IDs, a logical vector with the same number
    +0016 %     of elements as reactions in the model, or a vector of indexes for the
    +0017 %     reactions that should be minimised (default model.rxns).
    +0018 % params : struct
    +0019 %     solver parameters passed to optimizeProb. With formulation
    +0020 %     'reversible', intTol (10^-9), TimeLimit (300) and Seed (26) are set
    +0021 %     when absent (default empty struct).
    +0022 % scores : double
    +0023 %     vector of weights for the reactions. A reaction with a negative
    +0024 %     score adds the negated score to the minimised objective when it
    +0025 %     carries flux. Scores of 0 or above are not supported and are
    +0026 %     changed to max(scores(scores<0)), or to 0 if scores has no negative
    +0027 %     entry at all. Must have the same dimension as toMinimize
    +0028 %     (find(toMinimize) if it is a logical vector) (default -1 for all
    +0029 %     reactions).
    +0030 % formulation : char
    +0031 %     which MILP formulation to build (default 'irrev'):
    +0032 %
    +0033 %     - 'irrev' : convert the model to irreversible format and use one
    +0034 %       binary per irreversible reaction, so a reversible reaction that is
    +0035 %       minimised needs two binaries. The magnitude of the fluxes is
    +0036 %       estimated with an LP first, which also provides a start solution.
    +0037 %     - 'reversible' : keep the model reversible and use one binary per
    +0038 %       reaction, with a larger constraint matrix. The formulation has
    +0039 %       fewer binaries but needs a solver that handles MILPs well. No LP
    +0040 %       estimate of the flux magnitudes is made, and a fixed flux of 1000 is
    +0041 %       assumed.
    +0042 % verbose : logical
    +0043 %     if true, the MILP progression will be shown (default false).
    +0044 % resolveTies : logical
    +0045 %     if true, select one optimum among the degenerate optima of the MILP
    +0046 %     instead of relying on Seed: the primary (scores) objective is held
    +0047 %     at its optimum, the count of "on" reactions is minimised, and among
    +0048 %     the sparsest solutions the summed reaction-id rank is minimised
    +0049 %     (lowest ids preferred). This takes two extra MILP solves. A phase
    +0050 %     that is not proven optimal within TimeLimit gives a warning, and the
    +0051 %     solution of the last phase is used when it is feasible. Only
    +0052 %     implemented for formulation='reversible'; combined with 'irrev' it
    +0053 %     gives an error. Matches resolve_ties in raven-toolbox
    +0054 %     (raven-gecko-parity#104) (default false).
    +0055 %
    +0056 % Returns
    +0057 % -------
    +0058 % x : double
    +0059 %     the corresponding fluxes for the full model.
    +0060 % I : double
    +0061 %     the indexes of the reactions in toMinimize that were used in the
    +0062 %     solution.
    +0063 % exitFlag : double
    +0064 %     exit status:
    +0065 %
    +0066 %     - 1 : optimal solution found
    +0067 %     - -1 : no solution found, either because the problem is infeasible or
    +0068 %       because the solver reached its time limit before finding one
    +0069 %     - -2 : a solution was found but is not proven optimal, because the
    +0070 %       solver stopped before reaching optimality. It is returned and may
    +0071 %       use more fluxes than the minimum
    +0072 %
    +0073 % Examples
    +0074 % --------
    +0075 %     [x, I, exitFlag] = getMinNrFluxes(model, toMinimize, params, scores);
    +0076 %
    +0077 % Notes
    +0078 % -----
    +0079 % The binary of a reaction is linked to its flux through a large flux
    +0080 % value. With formulation 'irrev' the value is the larger of 1000 and 5
    +0081 % times the largest flux of an LP solution; with 'reversible' it is 1000
    +0082 % (mmol/gDW/h). A flux above this value is cut off by the binary constraint
    +0083 % of the reaction, so the MILP can become infeasible.
    +0084 
    +0085 p=parseRAVENargs(varargin, {'toMinimize',[]; 'params',[]; 'scores',[]; ...
    +0086     'formulation','irrev'; 'verbose',false; 'resolveTies',false});
    +0087 toMinimize=p.toMinimize;
    +0088 params=p.params;
    +0089 scores=p.scores;
    +0090 formulation=p.formulation;
    +0091 verbose=p.verbose;
    +0092 resolveTies=p.resolveTies;
    +0093 if isempty(toMinimize)
    +0094     toMinimize=model.rxns;
    +0095 elseif ~islogical(toMinimize) && ~isnumeric(toMinimize)
    +0096     toMinimize=convertCharArray(toMinimize);
    +0097 else
    +0098     toMinimize=model.rxns(toMinimize);
    +0099 end
    +0100 
    +0101 if isempty(formulation)
    +0102     formulation='irrev';
    +0103 end
    +0104 formulation=lower(char(formulation));
    +0105 if ~ismember(formulation,{'irrev','reversible'})
    +0106     EM='Valid options for formulation are "irrev" or "reversible"';
    +0107     error('RAVEN:badInput', '%s', EM);
    +0108 end
    +0109 
    +0110 %For passing parameters to the solver
    +0111 if isempty(params)
    +0112     params=struct();
    +0113 end
    +0114 
    +0115 if isempty(scores)
    +0116     %It says that the default is -1, but that is to fit with other code
    +0117     scores=ones(numel(toMinimize),1)*1;
    +0118 else
    +0119     if numel(scores)~=numel(toMinimize)
    +0120         EM='The number of scores must be the same as the number of reactions to minimize';
    +0121         error('RAVEN:badInput', '%s', EM);
    +0122     end
    +0123 
    +0124     %Change positive scores to have a small negative weight. This is a
    +0125     %temporary solution. If no score is negative there is nothing to clamp
    +0126     %to (max([]) errors here rather than filling in a value, since the
    +0127     %right-hand side is a computed empty array, not the literal [] MATLAB
    +0128     %treats as a delete), so clamp to 0 instead: every reaction is then
    +0129     %equally (un)weighted.
    +0130     negScores=scores<0;
    +0131     if any(negScores)
    +0132         scores(~negScores)=max(scores(negScores));
    +0133     else
    +0134         scores(~negScores)=0;
    +0135     end
    +0136 
    +0137     %It says that the default is -1, but that is to fit with other code
    +0138     scores=scores*-1;
    +0139 end
    +0140 
    +0141 if strcmp(formulation,'reversible')
    +0142     [x,I,exitFlag]=minNrFluxesReversible(model,toMinimize,scores,params,verbose,resolveTies);
    +0143 else
    +0144     if resolveTies
    +0145         EM='resolveTies is only implemented for formulation=''reversible''';
    +0146         error('RAVEN:badInput', '%s', EM);
    +0147     end
    +0148     [x,I,exitFlag]=minNrFluxesIrrev(model,toMinimize,scores,params,verbose);
    +0149 end
    +0150 end
    +0151 
    +0152 function [x,I,exitFlag]=minNrFluxesIrrev(model,toMinimize,scores,params,verbose)
    +0153 %One binary per irreversible reaction. Reversible reactions to minimize are
    +0154 %split by convertToIrrev and therefore carry two binaries each.
    +0155 
    +0156 exitFlag=1;
    +0157 
    +0158 %Check if the model is in irreversible format
    +0159 if any(model.rev)
    +0160     %Convert the model to irreversible format
    +0161     irrevModel=convertToIrrev(model);
    +0162 
    +0163     %Find the indexes for the reactions in toMinimize
    +0164     [indexes, I]=ismember(strrep(irrevModel.rxns,'_REV',''),toMinimize);
    +0165 else
    +0166     irrevModel=model;
    +0167 
    +0168     %Find the indexes for the reactions in toMinimize
    +0169     [indexes, I]=ismember(irrevModel.rxns,toMinimize);
    +0170 end
    +0171 
    +0172 indexes=find(indexes);
    +0173 %Adjust scores to fit with reversible
    +0174 scores=scores(I(indexes));
    +0175 
    +0176 %Add binary constraints in the following manner: -  Add one unique
    +0177 %"metabolite" for each integer reaction as a substrate.
    +0178 %   These metabolites can have net production
    +0179 %-  Add reactions for the production of each of those metabolites. The
    +0180 %   amount produced in one reaction unit must be larger than the largest
    +0181 %   possible flux in the model (but not too large to avoid bad scaling)
    +0182 
    +0183 %Calculate a solution to the problem without any constraints. This is to
    +0184 %get an estimate about the magnitude of fluxes in the model and to get a
    +0185 %feasible start solution.
    +0186 sol=solveLP(irrevModel,1);
    +0187 
    +0188 %Return an empty solution if the non-constrained problem could not be solved
    +0189 if isempty(sol.x)
    +0190     x=[];
    +0191     I=[];
    +0192     exitFlag=-1;
    +0193     return;
    +0194 end
    +0195 
    +0196 %Take the maximal times 5 to have a safe margin. If it is smaller than 1000,
    +0197 %then use 1000 instead.
    +0198 maxFlux=max(max(sol.x)*5,1000);
    +0199 
    +0200 intArray=speye(numel(irrevModel.rxns))*-1;
    +0201 intArray=intArray(indexes,:);
    +0202 prob.a=[irrevModel.S;intArray];
    +0203 a=[sparse(numel(irrevModel.mets),numel(indexes));speye(numel(indexes))*maxFlux];
    +0204 prob.a=[prob.a a];
    +0205 prob.ints.sub=numel(irrevModel.rxns)+1:numel(irrevModel.rxns)+numel(indexes);
    +0206 
    +0207 prob.c=[zeros(numel(irrevModel.rxns),1);scores(:);zeros(size(prob.a,1),1)]; %Minimize the number of fluxes
    +0208 prob.A=[prob.a -speye(size(prob.a,1))];
    +0209 prob.blc=[irrevModel.b(:,1);zeros(numel(indexes),1)];
    +0210 if size(irrevModel.b,2)==2
    +0211     prob.buc=[irrevModel.b(:,2);inf(numel(indexes),1)];
    +0212 else
    +0213     prob.buc=[irrevModel.b(:,1);inf(numel(indexes),1)];
    +0214 end
    +0215 prob.blx=[irrevModel.lb;zeros(numel(indexes),1)];
    +0216 prob.bux=[irrevModel.ub;ones(numel(indexes),1)];
    +0217 prob.lb = [prob.blx; prob.blc];
    +0218 prob.ub = [prob.bux; prob.buc];
    +0219 prob.osense=1;
    +0220 prob.csense=repmat('E', 1, size(prob.a,1),1);
    +0221 prob.b=zeros(size(prob.a,1), 1);
    +0222 
    +0223 %Use the output from the linear solution as starting point. Only the values
    +0224 %for the integer variables will be used, but all are supplied.
    +0225 prob.sol.int.xx=zeros(numel(prob.c),1);
    +0226 prob.sol.int.xx(prob.ints.sub(sol.x(indexes)>10^-12))=1;
    +0227 prob.x0=[];
    +0228 prob.vartype=repmat('C', size(prob.A,2), 1);
    +0229 prob.vartype(prob.ints.sub) = 'I'; % with .lb = 0 and .ub = 1, they are binary
    +0230 % integers (glpk in octave only allows "continuous" or "", not "binary")
    +0231 prob=rmfield(prob,{'blx','bux','blc','buc'});
    +0232 
    +0233 % Optimize the problem
    +0234 res = optimizeProb(prob,params,verbose);
    +0235 [isFeasible, isOptimal]=checkSolution(res);
    +0236 
    +0237 if ~isFeasible
    +0238     x=[];
    +0239     I=[];
    +0240     exitFlag=-1;
    +0241     if res.hitTimeLimit
    +0242         %Nothing is returned either way, but the caller reports -1 as "no
    +0243         %feasible solution exists", which is not what happened here.
    +0244         EM='Time limit reached before finding a solution. Try increasing the TimeLimit parameter.';
    +0245         warning('RAVEN:warning', '%s', EM);
    +0246     end
    +0247     return;
    +0248 end
    +0249 if res.hitTimeLimit || ~isOptimal
    +0250     %A feasible but suboptimal solution, i.e. the solver stopped before
    +0251     %reaching optimality. The solution is still returned, but must not be
    +0252     %reported as optimal: it may use more fluxes than the minimum.
    +0253     %Not every solver reports this in its status, gurobi for one presents a
    +0254     %MILP solution found before the limit as optimal, so res.hitTimeLimit is
    +0255     %consulted as well.
    +0256     exitFlag=-2;
    +0257 end
    +0258 
    +0259 xx=res.full(1:numel(irrevModel.rxns));
    +0260 I=res.full(numel(xx)+1:end);
    +0261 
    +0262 %Map back to original model from irrevModel
    +0263 x=xx(1:numel(model.rxns));
    +0264 if numel(irrevModel.rxns)>numel(model.rxns)
    +0265     x(model.rev~=0)=x(model.rev~=0)-xx(numel(model.rxns)+1:end);
    +0266 end
    +0267 
    +0268 I=ismember(toMinimize,strrep(irrevModel.rxns(indexes(I>10^-12)),'_REV',''));
    +0269 end
    +0270 
    +0271 function [x,I,exitFlag]=minNrFluxesReversible(model,toMinimize,scores,params,verbose,resolveTies)
    +0272 %One binary per reaction, on the reversible model. Trades a larger
    +0273 %constraint matrix for half the binaries on reversible reactions.
    +0274 
    +0275 exitFlag=1;
    +0276 
    +0277 %The trick to make this possible using a reversible model is that for
    +0278 %reversible reactions, we do the following. Set the flux Vi == Vipos - Vineg.
    +0279 %What happens then is that if the flux is positive, Vipos will have a nonzero value,
    +0280 %and when the flux is negative, Vineg will have a positive value. In addition
    +0281 %we can add an arbitrary constant to both Vipos and Vineg. For example, if the
    +0282 %flux Vi is -1, Vineg can be 4 and Vipos 3. This is however not a problem,
    +0283 %because we can be sure that Vineg + Vipos >= abs(Vi). Since we are interested
    +0284 %in forcing the ints to be on when there is a flux, it does not matter if we overestimate
    +0285 %the flux! So, we can simply constrain the boolean Yi to Yi*Maxflux >= Vineg + Vipos.
    +0286 
    +0287 %The matrix then becomes as this:
    +0288 %         S       p n int b     var
    +0289 %         SSSSSSSS        -
    +0290 %         SSSSSSSS         -
    +0291 %         SSSSSSSS          -
    +0292 %         SSSSSSSS           -
    +0293 %         SSSSSSSS            -
    +0294 %         SSSSSSSS             -
    +0295 %         -       1 -
    +0296 %               -  1 -
    +0297 %           -          M         -
    +0298 %             -         M         -
    +0299 %                 - - M         -
    +0300 %                  - -   M         -
    +0301 % An example with 8 rxns and 6 metabolites. - means -1, M max flux, and S is the S matrix.
    +0302 % 4 rxns are to be minimized(1,3,5,7) and 1,7 are reversible. The p and n
    +0303 % are the Vipos and Vineg variables (2 rxns of each). The ints are the Yi for
    +0304 % the variables that are to be minimized (the rest of the rxns does not have any).
    +0305 % The mets here are the constraints, so right under the S matrix, you have
    +0306 % Vi == Vipos - Vineg for the reactions 1 and 7 while the two next rows represent
    +0307 % the non-reversible rxns 3 and 5, where we simply say that yi*M >= Vi. The last
    +0308 % 2 rows are the reactions yi*M >= Vipos + Vineg. To the right, we first have a
    +0309 % -I matrix for setting the b constraints, and under that we have rxns that are
    +0310 % just variables (rxns) between 0 and Inf to complete the constraints mentioned above.
    +0311 %Ex: yi*M >= Vipos + Vineg is impl. as yi*M - Vipos - Vineg - var == 0, 0 <= var <= Inf.
    +0312 %
    +0313 %All rows should be equal to zero, so we do not set the b vector in the problem
    +0314 %The reactions should be constrained as follows
    +0315 %S - as given in model.lb and model.ub
    +0316 %pos and neg - between 0 and inf
    +0317 %ints - between 0 and 1
    +0318 %b - as stated in the model.b vector - if this has one column, that is lb and ub (fixed value), if two columns, that is lb and ub
    +0319 %var - between zero and inf
    +0320 
    +0321 [minLog, I]=ismember(model.rxns,toMinimize);
    +0322 indexes=find(minLog);
    +0323 revIndexes = find(minLog & (model.rev == 1));
    +0324 irrevIndexes = find(minLog & (model.rev == 0));
    +0325 revIndexesInInd = find(model.rev(indexes) == 1);
    +0326 irrevIndexesInInd = find(model.rev(indexes) == 0);
    +0327 
    +0328 %The int columns are ordered as indexes, i.e. by position in the model,
    +0329 %while the scores arrive ordered as toMinimize
    +0330 scores=scores(I(indexes));
    +0331 
    +0332 %Add binary constraints in the following manner: -  Add one unique
    +0333 %"metabolite" for each integer reaction as a substrate.
    +0334 %   These metabolites can have net production
    +0335 %-  Add reactions for the production of each of those metabolites. The
    +0336 %   amount produced in one reaction unit must be larger than the largest
    +0337 %   possible flux in the model (but not too large to avoid bad scaling)
    +0338 
    +0339 maxFlux=1000;
    +0340 %we build the total matrix as blocks: [S pos neg int b var]
    +0341 
    +0342 %s block
    +0343 intArray=speye(numel(model.rxns))*-1;
    +0344 intArrayRev=intArray(revIndexes,:);
    +0345 intArrayIrrev = intArray(irrevIndexes,:);
    +0346 sBlock=[model.S;intArrayRev;intArrayIrrev;sparse(numel(revIndexes), numel(model.rxns))]; %the S matrix and what is below
    +0347 
    +0348 %pos/neg blocks
    +0349 revposorneg1 = sparse(numel(model.mets), numel(revIndexes));
    +0350 revpos2 = speye(numel(revIndexes));
    +0351 revneg2 = -revpos2;
    +0352 revposneg3 = sparse(numel(irrevIndexes), numel(revIndexes));
    +0353 revposneg4 = revneg2;
    +0354 posBlock = [revposorneg1;revpos2;revposneg3;revposneg4];
    +0355 negBlock = [revposorneg1;revneg2;revposneg3;revposneg4];
    +0356 
    +0357 %int block
    +0358 int1 = sparse(numel(model.mets), numel(indexes));
    +0359 int2 = sparse(numel(revIndexes), numel(indexes));
    +0360 tmpEye = speye(numel(indexes))*maxFlux;
    +0361 int3 = tmpEye(irrevIndexesInInd,:);%we here select only the irrev indexes from the indexes
    +0362 int4 = tmpEye(revIndexesInInd,:);
    +0363 intBlock = [int1;int2;int3;int4];
    +0364 
    +0365 %b block
    +0366 b1 = speye(numel(model.mets))*-1;
    +0367 b2 = sparse(numel(indexes) + numel(revIndexes), numel(model.mets));
    +0368 bBlock = [b1;b2];
    +0369 
    +0370 %var block
    +0371 var1 = sparse(numel(model.mets), numel(indexes));
    +0372 var2 = sparse(numel(revIndexes), numel(indexes));
    +0373 tmpEye = speye(numel(indexes))*-1;
    +0374 var3 = tmpEye(irrevIndexesInInd,:);
    +0375 var4 = tmpEye(revIndexesInInd,:);
    +0376 varBlock = [var1;var2;var3;var4];
    +0377 
    +0378 prob.A = [sBlock posBlock negBlock intBlock bBlock varBlock];
    +0379 prob.a = prob.A;%I think this is needed as well
    +0380 
    +0381 prob.c=[zeros(numel(model.rxns),1);zeros(numel(revIndexes)*2,1); scores(:);zeros(numel(model.mets) + numel(indexes),1)]; %Minimize the sum of reaction scores for reactions that are on
    +0382 
    +0383 %ub and lb, text copied from above
    +0384 %S - as given in model.lb and model.ub
    +0385 %pos and neg - between 0 and inf
    +0386 %ints - between 0 and 1
    +0387 %b - as stated in the model.b vector - if this has one column, that is lb and ub (fixed value), if two columns, that is lb and ub
    +0388 %var - between zero and inf
    +0389 
    +0390 if size(model.b,2)==2
    +0391     bub = model.b(:,2);
    +0392 else
    +0393     bub = model.b(:,1);
    +0394 end
    +0395 
    +0396 prob.lb = [model.lb;zeros(numel(revIndexes)*2,1);zeros(numel(indexes),1);model.b(:,1);zeros(numel(indexes),1)];
    +0397 prob.ub = [model.ub;inf(numel(revIndexes)*2,1);ones(numel(indexes),1);bub;inf(numel(indexes),1)];
    +0398 
    +0399 prob.b=zeros(size(prob.a,1), 1);
    +0400 
    +0401 intsIndexes = find(prob.c ~= 0);
    +0402 prob.x0=[];
    +0403 prob.vartype=repmat('C', 1, size(prob.A,2));
    +0404 prob.vartype(intsIndexes) = 'B';
    +0405 prob.csense = repmat('E', 1, size(prob.A,1));
    +0406 prob.osense=1; %minimize the objective
    +0407 
    +0408 %Defaults that suit this formulation, without discarding what the caller set
    +0409 if ~isfield(params,'intTol')
    +0410     params.intTol = 10^-9;
    +0411 end
    +0412 if ~isfield(params,'TimeLimit')
    +0413     params.TimeLimit = 300;
    +0414 end
    +0415 if ~isfield(params,'Seed')
    +0416     %Although the solver reports "optimal solution found", different seeds
    +0417     %can return solutions with noticeably different objective values, so a
    +0418     %fixed one is used to keep results reproducible
    +0419     params.Seed = 26;
    +0420 end
    +0421 
    +0422 % Optimize the problem
    +0423 res = optimizeProb(prob,params,verbose);
    +0424 [isFeasible, isOptimal]=checkSolution(res);
    +0425 
    +0426 if ~isFeasible
    +0427     x=[];
    +0428     I=[];
    +0429     exitFlag=-1;
    +0430     if res.hitTimeLimit
    +0431         %Nothing is returned either way, but the caller reports -1 as "no
    +0432         %feasible solution exists", which is not what happened here.
    +0433         EM='Time limit reached before finding a solution. Try increasing the TimeLimit parameter.';
    +0434         warning('RAVEN:warning', '%s', EM);
    +0435     end
    +0436     return;
    +0437 end
    +0438 if res.hitTimeLimit || ~isOptimal
    +0439     %A feasible but suboptimal solution, i.e. the solver stopped before
    +0440     %reaching optimality. The solution is still returned, but must not be
    +0441     %reported as optimal: it may use more fluxes than the minimum.
    +0442     %Not every solver reports this in its status, gurobi for one presents a
    +0443     %MILP solution found before the limit as optimal, so res.hitTimeLimit is
    +0444     %consulted as well.
    +0445     exitFlag=-2;
    +0446 end
    +0447 
    +0448 if resolveTies
    +0449     %This MILP is highly degenerate: many reaction subsets reach the same
    +0450     %score optimum, and the solver returns an arbitrary one -- reproducible
    +0451     %for a fixed solver build + Seed, but fragile to a solver version bump
    +0452     %or an unrelated change upstream of the MILP (see raven-gecko-parity#104,
    +0453     %where this was demonstrated on genome-scale Human-GEM: the same Seed,
    +0454     %same problem, but a different MILP construction path flipped a tied
    +0455     %pair of reactions). This runs a lexicographic phase 2, holding the
    +0456     %score objective at its optimum with a floor constraint: first minimise
    +0457     %the count of "on" reactions (the sparsest optimum), then, among the
    +0458     %sparsest, minimise their summed reaction-id rank (prefer lower ids).
    +0459     %Ports raven-toolbox's _resolve_ties_fill (raven-toolbox#114).
    +0460     %
    +0461     %intCols identifies the int/binary variable columns directly from the
    +0462     %block layout (prob.c = [zeros(rxns); zeros(2*revIndexes); scores(:);
    +0463     %zeros(mets+indexes)]) rather than via find(prob.c~=0): a zero-scored
    +0464     %reaction would otherwise silently drop out of the column count and
    +0465     %misalign every later index into "indexes".
    +0466     intCols = numel(model.rxns) + 2*numel(revIndexes) + (1:numel(indexes));
    +0467     primaryObj = full(prob.c(intCols)' * res.full(intCols));
    +0468     tol = max(abs(primaryObj)*1e-7, 1e-7);
    +0469 
    +0470     floorRow = sparse(1,size(prob.A,2));
    +0471     floorRow(intCols) = prob.c(intCols)';
    +0472     tieProb = prob;
    +0473     tieProb.A = [prob.A; floorRow];
    +0474     tieProb.a = tieProb.A;
    +0475     tieProb.b = [prob.b; primaryObj+tol];
    +0476     tieProb.csense = [prob.csense 'L'];
    +0477 
    +0478     %An integer objective needs only a cheap absolute-gap proof, not a tiny
    +0479     %relative one -- RAVEN's own MIPGap default (1e-12) can be slow to prove
    +0480     %on a genome-scale problem where a gap below 1 already proves optimality
    +0481     %exactly (mirrors raven-toolbox's own MIPGap=0/MIPGapAbs=0.4 for the same
    +0482     %two phases).
    +0483     tieParams = params;
    +0484     tieParams.MIPGap = 0;
    +0485     tieParams.MIPGapAbs = 0.4;
    +0486 
    +0487     unproven = {};
    +0488 
    +0489     %Phase 2a: fewest "on" reactions among the score-optimal solutions.
    +0490     countObj = zeros(size(prob.c));
    +0491     countObj(intCols) = 1;
    +0492     tieProb.c = countObj;
    +0493     res2a = optimizeProb(tieProb,tieParams,verbose);
    +0494     [isFeasible2a, isOptimal2a] = checkSolution(res2a);
    +0495     if ~isOptimal2a
    +0496         unproven{end+1} = 'phase2a-parsimony';
    +0497     end
    +0498     if isFeasible2a
    +0499         kmin = full(countObj'*res2a.full);
    +0500 
    +0501         %Phase 2b: among the sparsest, prefer lower reaction ids -- a
    +0502         %deterministic tie-break independent of solver/seed.
    +0503         countRow = sparse(1,size(tieProb.A,2));
    +0504         countRow(intCols) = 1;
    +0505         tieProb2b = tieProb;
    +0506         tieProb2b.A = [tieProb.A; countRow];
    +0507         tieProb2b.a = tieProb2b.A;
    +0508         tieProb2b.b = [tieProb.b; kmin+0.5];
    +0509         tieProb2b.csense = [tieProb.csense 'L'];
    +0510 
    +0511         [~, sortOrder] = sort(model.rxns(indexes));
    +0512         ranks = zeros(numel(indexes),1);
    +0513         ranks(sortOrder) = (1:numel(indexes))';
    +0514         idObj = zeros(size(prob.c));
    +0515         idObj(intCols) = ranks;
    +0516         tieProb2b.c = idObj;
    +0517         res2b = optimizeProb(tieProb2b,tieParams,verbose);
    +0518         [isFeasible2b, isOptimal2b] = checkSolution(res2b);
    +0519         if ~isOptimal2b
    +0520             unproven{end+1} = 'phase2b-idrank';
    +0521         end
    +0522         if isFeasible2b
    +0523             %Adopt the tie-resolved solution; exitFlag still reflects
    +0524             %whether the *primary* (scores) objective was proven optimal,
    +0525             %same as without resolveTies.
    +0526             res = res2b;
    +0527         end
    +0528     end
    +0529     if ~isempty(unproven)
    +0530         EM = ['getMinNrFluxes tie resolution did not converge (' strjoin(unproven,', ') '): the selection among equally score-optimal solutions is itself an unproven incumbent, so resolveTies has reduced but not removed the run-to-run spread. Raise params.TimeLimit for a proven tie-break.'];
    +0531         warning('RAVEN:warning', '%s', EM);
    +0532     end
    +0533 end
    +0534 
    +0535 x=res.full(1:numel(model.rxns));%the fluxes
    +0536 
    +0537 ints = res.full(intsIndexes);
    +0538 %The margin for integers in gurobi is 10^-5; use 10^-3 to have some margin.
    +0539 I=ismember(toMinimize, model.rxns(indexes(ints > 10^-3)));
    +0540 
    +0541 sel = (ints > 10^-12) & (ints < 0.5);
    +0542 if sum(sel) > 0
    +0543     %This may indicate that there is a problem with the tolerances in the solver
    +0544     disp(['getMinNrFluxes: Some variables meant to be boolean in the MILP have intermediate values. Num vars: ' num2str(sum(sel))])
    +0545 end
    +0546 
    +0547 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/solver/index.html b/docs/html/solver/index.html similarity index 58% rename from doc/solver/index.html rename to docs/html/solver/index.html index b1b79505..5046e977 100644 --- a/doc/solver/index.html +++ b/docs/html/solver/index.html @@ -19,7 +19,7 @@

    Index for solver

    Matlab files in this directory:

    -
     checkSolutioncheckSolution Check if a Mosek solution is feasible and optimal.
     getMinNrFluxesgetMinNrFluxes Find the minimal set of fluxes that satisfy the model.
     optimizeProboptimizeProb Optimize an LP or MILP formulated in COBRA terms.
     setRavenSolversetRavenSolver Set the solver for RAVEN.
     solveLPsolveLP Solve a linear programming problem.
     splitProbForConditioningsplitProbForConditioning Improve LP conditioning by splitting wide columns.
    + checkSolutioncheckSolution Check if a solver result is feasible and optimal.  getMinNrFluxesgetMinNrFluxes Find the minimal set of fluxes that satisfy the model.  optimizeProboptimizeProb Optimise an LP or MILP formulated in COBRA terms.  setRavenSolversetRavenSolver Set the solver for RAVEN.  solveLPsolveLP Solve a linear programming problem.  splitProbForConditioningsplitProbForConditioning Improve LP conditioning by splitting wide columns. diff --git a/docs/html/solver/optimizeProb.html b/docs/html/solver/optimizeProb.html new file mode 100644 index 00000000..6b95268f --- /dev/null +++ b/docs/html/solver/optimizeProb.html @@ -0,0 +1,527 @@ + + + + Description of optimizeProb + + + + + + + + + +
    Home > solver > optimizeProb.m
    + + + +

    optimizeProb +

    + +

    PURPOSE ^

    +
    optimizeProb Optimise an LP or MILP formulated in COBRA terms.
    + +

    SYNOPSIS ^

    +
    function res = optimizeProb(prob,varargin)
    + +

    DESCRIPTION ^

    +
     optimizeProb  Optimise an LP or MILP formulated in COBRA terms.
    +
    + Parameters
    + ----------
    + prob : struct
    +     COBRA-style problem struct of an LP or MILP to be optimised.
    +
    + Name-Value Arguments
    + --------------------
    + params : struct
    +     solver-specific parameters. In addition to solver parameters, the
    +     field "maxRatio" can be set to a finite number > 1 for numerical
    +     conditioning: before solving an LP, any column whose coefficients
    +     span more than maxRatio is split with auxiliary metabolites and
    +     variables, which preserves the feasible region. See
    +     splitProbForConditioning. The field is removed before the parameters
    +     are passed to the solver, and is ignored for MILPs.
    + verbose : logical
    +     if true, MILP progress is shown (default true).
    +
    + Returns
    + -------
    + res : struct
    +     the output structure from the selected solver RAVENSOLVER (COBRA
    +     style). In addition to the fields set by the solver, the logical field
    +     "hitTimeLimit" is true if the solver stopped because it reached its
    +     time limit. The solution in res is then either absent or not proven
    +     optimal.
    +
    + Notes
    + -----
    + MILPs can be solved with GLPK, either RAVEN's bundled GLPK ('glpk') or
    + the COBRA Toolbox's GLPK interface ('cobra' with GLPK as its MILP solver).
    + The RAVEN:glpkMILP warning then states that GLPK works for small problems
    + and that Gurobi is much faster and more reliable for larger ones (such as
    + ftINIT or getMinNrFluxes on genome-scale models).
    +
    + See also
    + --------
    + splitProbForConditioning
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    • getMinNrFluxes getMinNrFluxes Find the minimal set of fluxes that satisfy the model.
    • solveLP solveLP Solve a linear programming problem.
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function res = optimizeProb(prob,varargin)
    +0002 % optimizeProb  Optimise an LP or MILP formulated in COBRA terms.
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % prob : struct
    +0007 %     COBRA-style problem struct of an LP or MILP to be optimised.
    +0008 %
    +0009 % Name-Value Arguments
    +0010 % --------------------
    +0011 % params : struct
    +0012 %     solver-specific parameters. In addition to solver parameters, the
    +0013 %     field "maxRatio" can be set to a finite number > 1 for numerical
    +0014 %     conditioning: before solving an LP, any column whose coefficients
    +0015 %     span more than maxRatio is split with auxiliary metabolites and
    +0016 %     variables, which preserves the feasible region. See
    +0017 %     splitProbForConditioning. The field is removed before the parameters
    +0018 %     are passed to the solver, and is ignored for MILPs.
    +0019 % verbose : logical
    +0020 %     if true, MILP progress is shown (default true).
    +0021 %
    +0022 % Returns
    +0023 % -------
    +0024 % res : struct
    +0025 %     the output structure from the selected solver RAVENSOLVER (COBRA
    +0026 %     style). In addition to the fields set by the solver, the logical field
    +0027 %     "hitTimeLimit" is true if the solver stopped because it reached its
    +0028 %     time limit. The solution in res is then either absent or not proven
    +0029 %     optimal.
    +0030 %
    +0031 % Notes
    +0032 % -----
    +0033 % MILPs can be solved with GLPK, either RAVEN's bundled GLPK ('glpk') or
    +0034 % the COBRA Toolbox's GLPK interface ('cobra' with GLPK as its MILP solver).
    +0035 % The RAVEN:glpkMILP warning then states that GLPK works for small problems
    +0036 % and that Gurobi is much faster and more reliable for larger ones (such as
    +0037 % ftINIT or getMinNrFluxes on genome-scale models).
    +0038 %
    +0039 % See also
    +0040 % --------
    +0041 % splitProbForConditioning
    +0042 
    +0043 p=parseRAVENargs(varargin, {'params',[]; 'verbose',true});
    +0044 params=p.params;
    +0045 if isempty(params)
    +0046     params=struct();
    +0047 end
    +0048 verbose=p.verbose;
    +0049 
    +0050 %Set as global variable for speed improvement if optimizeProb is run many times
    +0051 global RAVENSOLVER;
    +0052 %CBT_MILP_SOLVER is a COBRA Toolbox global, set by changeCobraSolver, and
    +0053 %has to be declared as such to be readable in the MILP check below
    +0054 global CBT_MILP_SOLVER;
    +0055 if isempty(RAVENSOLVER)
    +0056     if(~ispref('RAVEN','solver'))
    +0057         error('RAVEN:badInput', '%s', 'RAVEN solver not defined or unknown. Try using setRavenSolver(''solver'').');
    +0058     else
    +0059         RAVENSOLVER = getpref('RAVEN','solver');
    +0060     end
    +0061 end
    +0062 solver=RAVENSOLVER;
    +0063 if ~all(lower(prob.vartype) == 'c')
    +0064     milp=true;
    +0065     if strcmp(solver,'glpk') || (strcmp(solver,'cobra') && strcmp(CBT_MILP_SOLVER,'glpk'))
    +0066         warning('RAVEN:glpkMILP', ['Solving a MILP with GLPK. This works for small ' ...
    +0067             'problems, but for larger problems Gurobi is much faster and more reliable; ' ...
    +0068             'select it with setRavenSolver(''gurobi''). Silence this warning with ' ...
    +0069             'warning(''off'',''RAVEN:glpkMILP'').'])
    +0070     end
    +0071 else
    +0072     milp=false;
    +0073 end
    +0074 
    +0075 %% Define default parameters, which will then be used to make solver-
    +0076 % specific solverparams structures
    +0077 defaultparams.feasTol        = 1e-9;
    +0078 defaultparams.optTol         = 1e-9;
    +0079 defaultparams.objTol         = 1e-6;
    +0080 defaultparams.timeLimit      = 1000;
    +0081 %defaultparams.iterationLimit = 1000;
    +0082 defaultparams.intTol         = 1e-12;
    +0083 defaultparams.relMipGapTol   = 1e-12;
    +0084 defaultparams.absMipGapTol   = 1e-12;
    +0085 if milp
    +0086     defaultparams.MIPGap     = 1e-12;
    +0087     defaultparams.Seed       = 1;
    +0088 end
    +0089 
    +0090 %% Optionally improve numerical conditioning before solving. This is opt-in
    +0091 % through params.maxRatio and only applied to LPs. Columns whose coefficients
    +0092 % span more than maxRatio are split via auxiliary metabolites/variables, which
    +0093 % preserves the feasible region but reduces the spread within each column. The
    +0094 % added rows/columns are stripped from the result further down.
    +0095 condApplied = false;
    +0096 if isfield(params,'maxRatio')
    +0097     maxRatio = params.maxRatio;
    +0098     params   = rmfield(params,'maxRatio');
    +0099     if ~milp && isnumeric(maxRatio) && isscalar(maxRatio) && isfinite(maxRatio) && maxRatio>1
    +0100         [prob, condInfo] = splitProbForConditioning(prob, maxRatio);
    +0101         condApplied = condInfo.applied;
    +0102     end
    +0103 end
    +0104 
    +0105 res.obj=[];
    +0106 switch solver
    +0107     %% Use whatever solver is set by COBRA Toolbox changeCobraSolver
    +0108     case 'cobra'
    +0109         if milp
    +0110             cparams=struct('timeLimit',1e9,'printLevel',0,'intTol',1e-6,'relMipGapTol',1e-9);
    +0111             cparams=structUpdate(cparams,params);
    +0112             res=solveCobraMILP(prob,cparams);
    +0113         else
    +0114             res=solveCobraLP(prob);
    +0115         end
    +0116         %% Use Gurobi in a MATLAB environment
    +0117     case 'gurobi'
    +0118         if milp
    +0119             if verbose
    +0120                 solverparams.OutputFlag = 1;
    +0121             else
    +0122                 solverparams.OutputFlag = 0;
    +0123             end
    +0124             solverparams.IntFeasTol = 10^-9; %min val for gurobi
    +0125             solverparams.MIPGap = defaultparams.MIPGap;
    +0126             solverparams.Seed = defaultparams.Seed;
    +0127         else
    +0128             solverparams.OutputFlag = 0;
    +0129         end
    +0130         solverparams.DisplayInterval= 1; % Level of verbosity
    +0131         solverparams.TimeLimit      = defaultparams.timeLimit;
    +0132         solverparams.FeasibilityTol = defaultparams.feasTol;
    +0133         solverparams.OptimalityTol  = defaultparams.optTol;
    +0134         solverparams.Presolve       = 2;
    +0135         % getCurrentTask requires the Parallel Computing Toolbox; without it
    +0136         % the call itself errors, so guard it rather than requiring the
    +0137         % toolbox for every gurobi solve.
    +0138         if exist('getCurrentTask','file') && ~isempty(getCurrentTask) % If run in parallel, then one thread per gurobi
    +0139             solverparams.Threads=1;
    +0140         end
    +0141         solverparams = structUpdate(solverparams,params);
    +0142 
    +0143         % Restructering problem according to gurobi format
    +0144         if isfield(prob, 'csense')
    +0145             prob.sense = renameparams(prob.csense, {'L','G','E'}, {'<','>','='});
    +0146             prob = rmfield(prob, {'csense'});
    +0147         end
    +0148         % gurobi minimises unless told otherwise, so an absent osense means
    +0149         % minimisation; osense is also needed below to sign the duals
    +0150         osense = 1;
    +0151         if isfield(prob, 'osense')
    +0152             osense = prob.osense;
    +0153             if osense < 0
    +0154                 prob.modelsense = 'max';
    +0155             else
    +0156                 prob.modelsense = 'min';
    +0157             end
    +0158             prob = rmfield(prob, {'osense'});
    +0159         end
    +0160         [prob.obj, prob.rhs, prob.vtype] = deal(prob.c, prob.b, prob.vartype);
    +0161         prob = rmfield(prob, {'c','b','vartype'});
    +0162 
    +0163         resG = gurobi(prob,solverparams);
    +0164 
    +0165         % Name output fields the same as COBRA does. Every field below is
    +0166         % optional in the gurobi result: which ones are present depends on
    +0167         % the status and on the method gurobi chose (a barrier solve returns
    +0168         % no simplex basis, for instance). They are therefore read
    +0169         % individually rather than under one try/catch, which would have
    +0170         % reported a missing basis on an optimal solve as "infeasible".
    +0171         res.origStat = resG.status;
    +0172         if isfield(resG,'x')
    +0173             res.full = resG.x;
    +0174         end
    +0175         if isfield(resG,'objval')
    +0176             res.obj = resG.objval;
    +0177         end
    +0178         if isfield(resG,'pi') && isfield(resG,'rc')
    +0179             res.dual     = -resG.pi*osense;
    +0180             res.rcost    = -resG.rc*osense;
    +0181         end
    +0182         if milp && strcmp(resG.status, 'TIME_LIMIT') && isfield(resG,'x') && isfield(resG,'objval')
    +0183             % An incumbent was found before the time limit. It is usable but
    +0184             % not proven optimal, so it is reported as feasible rather than
    +0185             % optimal; callers that must not act on a suboptimal MILP
    +0186             % solution (getMinNrFluxes, solveLP) key on that distinction.
    +0187             resG.status = 'SUBOPTIMAL';
    +0188         end
    +0189         switch resG.status
    +0190             case 'OPTIMAL'
    +0191                 res.stat = 1;
    +0192             case {'UNBOUNDED','SUBOPTIMAL'}
    +0193                 res.stat = 2;
    +0194             otherwise
    +0195                 res.stat = 0;
    +0196         end
    +0197         if ~milp
    +0198             if isfield(resG,'vbasis')
    +0199                 res.vbasis = resG.vbasis;
    +0200             end
    +0201             if isfield(resG,'cbasis')
    +0202                 res.cbasis = resG.cbasis;
    +0203             end
    +0204         elseif isfield(resG,'mipgap')
    +0205             res.mipgap = resG.mipgap;
    +0206         end
    +0207         if res.stat>0 && ~isfield(res,'full')
    +0208             % A status that claims a solution without returning one
    +0209             res.stat = 0;
    +0210         end
    +0211         %% Use GLPK using RAVEN-provided binary
    +0212     case 'glpk'
    +0213         solverparams.scale   = 1; % Auto scaling
    +0214         %solverparams.tmlim   = defaultparams.timeLimit;
    +0215         solverparams.tolbnd  = defaultparams.feasTol;
    +0216         solverparams.toldj   = defaultparams.optTol;
    +0217         solverparams.tolint  = defaultparams.intTol;
    +0218         solverparams.tolobj  = defaultparams.objTol;
    +0219         solverparams.msglev  = 0; % Level of verbosity
    +0220         if milp
    +0221             solverparams.tmlim = defaultparams.timeLimit; % seconds
    +0222         end
    +0223         solverparams = structUpdate(solverparams,params);
    +0224 
    +0225         prob.csense = renameparams(prob.csense, {'L','G','E'}, {'U','L','S'});
    +0226 
    +0227         % Ensure that RAVEN glpk binary is used, return to original
    +0228         % directory afterwards
    +0229         [ravenDir,currDir]=findRAVENroot();
    +0230         cd(fullfile(ravenDir,'src','external','GLPKmex'))
    +0231         [xopt, fmin, errnum, extra] = glpk(prob.c, prob.A, prob.b, prob.lb, prob.ub, prob.csense, prob.vartype, prob.osense, solverparams);
    +0232         cd(currDir)
    +0233 
    +0234         switch errnum % 1 = undefined; 2 = feasible; 3 = infeasible; 4 = no feasible solution; 5 = optimal; 6 = no unbounded solution
    +0235             case 5
    +0236                 res.stat = 1; % Optimal
    +0237             case 2
    +0238                 res.stat = 2; % Feasible, but not optimal
    +0239             otherwise
    +0240                 res.stat = 0;
    +0241         end
    +0242         res.origStat = errnum;
    +0243         res.full     = xopt;
    +0244         res.obj      = fmin;
    +0245         res.dual     = -extra.lambda*prob.osense;
    +0246         res.rcost    = -extra.redcosts*prob.osense;
    +0247         if milp
    +0248             % glpk reports no gap; a proven optimum has none, anything else
    +0249             % is not accepted by callers that test the gap
    +0250             if res.stat == 1
    +0251                 res.mipgap = 0;
    +0252             else
    +0253                 res.mipgap = inf;
    +0254             end
    +0255         end
    +0256         %% Use scip
    +0257     case {'soplex','scip'} % Old 'soplex' option also allowed
    +0258         % scip takes a row range [rl,ru] rather than a right-hand side plus
    +0259         % a sense, so csense has to be translated into that range. Passing
    +0260         % [-b,b] is only the same problem when every row is an equality with
    +0261         % b = 0, which is the case for solveLP but for none of the MILPs
    +0262         % built elsewhere in RAVEN.
    +0263         rl = full(prob.b(:));
    +0264         ru = rl;
    +0265         csense = prob.csense(:);
    +0266         rl(csense=='L') = -inf;   % A*x <= b
    +0267         ru(csense=='G') = inf;    % A*x >= b
    +0268         [xopt,fval,exitflag,scipStats] = scip([], prob.c, prob.A, rl, ru, prob.lb, prob.ub, prob.vartype);
    +0269 
    +0270         %   [x,fval,exitflag,stats] = scip(H, f, A, rl, ru, lb, ub, xtype, sos, qc, nl, x0, opts)
    +0271         %
    +0272         %   Input arguments*:
    +0273         %       H - quadratic objective matrix (sparse, optional [NOT TRIL / TRIU])
    +0274         %       f - linear objective vector
    +0275         %       A - linear constraint matrix (sparse)
    +0276         %       rl - linear constraint lhs
    +0277         %       ru - linear constraint rhs
    +0278         %       lb - decision variable lower bounds
    +0279         %       ub - decision variable upper bounds
    +0280         %       xtype - string of variable integrality ("c" continuous, "i" integer, "b" binary)
    +0281         %       sos - SOS structure with fields type, index and weight (see below)
    +0282         %       qc - Quadratic Constraints structure with fields Q, l, qrl and qru (see below)
    +0283         %       nl - Nonlinear Objective and Constraints structure (see below)
    +0284         %       x0 - primal solution
    +0285         %       opts - solver options (see below)
    +0286         %
    +0287         %   Return arguments:
    +0288         %       x - solution vector
    +0289         %       fval - objective value at the solution
    +0290         %       exitflag - exit status (see below)
    +0291         %       stats - statistics structure
    +0292         %
    +0293         %   Option Fields (all optional, see also optiset for a list):
    +0294         %       solverOpts - specific SCIP options (list of pairs of parameter names and values)
    +0295         %       maxiter - maximum LP solver iterations
    +0296         %       maxnodes - maximum nodes to explore
    +0297         %       maxtime - maximum execution time [s]
    +0298         %       tolrfun - primal feasibility tolerance
    +0299         %       display - solver display level [0-5]
    +0300         %       probfile - write problem to given file
    +0301         %       presolvedfile - write presolved problem to file
    +0302         %
    +0303         %   Return Status, as returned by the MatlabSCIPInterface build that
    +0304         %   RAVEN provides and that setRavenSolver points at
    +0305         %   (github.com/scipopt/MatlabSCIPInterface). Verified against that
    +0306         %   binary with a bounded, an infeasible and an unbounded problem:
    +0307         %       1 - Problem Solved to Optimality
    +0308         %       2 - Problem is Infeasible
    +0309         %       3 - Problem is Unbounded
    +0310         %
    +0311         %   The OPTI Toolbox build of scip uses a different, longer set in
    +0312         %   which optimality is 11, infeasibility 12 and unboundedness 13,
    +0313         %   with the lower numbers meaning "a limit was reached". Those codes
    +0314         %   are accepted below as well, so either build reports optimality
    +0315         %   correctly; only OPTI's 1 ("User Interrupted") is ambiguous, and
    +0316         %   reading it as optimal is no worse than the previous mapping,
    +0317         %   which recognised no status from the shipped build at all.
    +0318         
    +0319         res.origStat = exitflag;
    +0320         res.full = xopt;
    +0321         res.obj  = fval;
    +0322 
    +0323         switch exitflag
    +0324             case {1, 11}                  % solved to optimality
    +0325                 res.stat = 1;
    +0326             case {3, 13}                  % unbounded
    +0327                 res.stat = 2;
    +0328             case {5, 6, 7, 8, 9, 10}      % OPTI: a limit was reached, so a
    +0329                 res.stat = 2;             % solution may exist but is not proven optimal
    +0330             otherwise                     % 2/12 infeasible, 0 unknown, 4, 14
    +0331                 res.stat = 0;
    +0332         end
    +0333 
    +0334         if milp
    +0335             % Callers such as ftINIT compare this against their own
    +0336             % params.MIPGap, so it has to be the quantity the gurobi branch
    +0337             % reports there: a relative gap. It is derived from the bounds
    +0338             % rather than read from scipStats.BBgap, whose units are not
    +0339             % documented. inf where no gap can be established, so that a
    +0340             % caller testing "gap small enough" does not accept the solution.
    +0341             if res.stat == 1
    +0342                 res.mipgap = 0;
    +0343             else
    +0344                 res.mipgap = inf;
    +0345                 primal = scipStats.PrimalBound;
    +0346                 if isfinite(primal) && abs(primal) > 1e-10
    +0347                     res.mipgap = abs(primal-scipStats.DualBound)/abs(primal);
    +0348                 end
    +0349             end
    +0350         end
    +0351     otherwise
    +0352         error('RAVEN solver not defined or unknown. Try using setRavenSolver(''solver'').');
    +0353 end
    +0354 %Flag a solve that ended on the time limit. res.stat cannot express this on
    +0355 %its own: a solver that times out with an incumbent solution in hand reports
    +0356 %the same status as one that finished, and a solver that times out with
    +0357 %nothing reports the same status as an infeasible problem. The raw status in
    +0358 %res.origStat does distinguish the two, but only in solver-specific terms,
    +0359 %so it is translated here where the solver is still known.
    +0360 res.hitTimeLimit=hitTimeLimit(solver,res,milp);
    +0361 if res.stat>0
    +0362     res.full=res.full(1:size(prob.a,2));
    +0363 end
    +0364 %Strip the auxiliary rows/columns that were added for conditioning, so that
    +0365 %the result matches the dimensions of the original problem. res.full is
    +0366 %already truncated to the original variables above (prob.a is left untouched).
    +0367 if condApplied
    +0368     if isfield(res,'dual') && numel(res.dual)>condInfo.nOrigRows
    +0369         res.dual=res.dual(1:condInfo.nOrigRows);
    +0370     end
    +0371     if isfield(res,'rcost') && numel(res.rcost)>condInfo.nOrigCols
    +0372         res.rcost=res.rcost(1:condInfo.nOrigCols);
    +0373     end
    +0374 end
    +0375 end
    +0376 
    +0377 function s_merged=structUpdate(s_old,s_new)
    +0378 %Remove overlapping fields from first struct;
    +0379 %Obtain all unique names of remaining fields;
    +0380 %Merge both structs
    +0381 s_merged = rmfield(s_old, intersect(fieldnames(s_old), fieldnames(s_new)));
    +0382 names = [fieldnames(s_merged); fieldnames(s_new)];
    +0383 s_merged = cell2struct([struct2cell(s_merged); struct2cell(s_new)], names, 1);
    +0384 end
    +0385 
    +0386 function timeLimit = hitTimeLimit(solver,res,milp)
    +0387 %Report whether the solver that produced res stopped on its time limit. Each
    +0388 %solver has its own status codes for this, and the "cobra" option hands the
    +0389 %status of the solver that COBRA dispatched to straight through, so that
    +0390 %solver is looked up as well.
    +0391 global CBT_LP_SOLVER CBT_MILP_SOLVER
    +0392 timeLimit=false;
    +0393 if ~isfield(res,'origStat')
    +0394     return;
    +0395 end
    +0396 if strcmp(solver,'cobra')
    +0397     if milp
    +0398         solver=CBT_MILP_SOLVER;
    +0399     else
    +0400         solver=CBT_LP_SOLVER;
    +0401     end
    +0402     if isempty(solver) || ~ischar(solver)
    +0403         return;
    +0404     end
    +0405 end
    +0406 origStat=res.origStat;
    +0407 isText=ischar(origStat) || isstring(origStat);
    +0408 isCode=isnumeric(origStat) && isscalar(origStat);
    +0409 switch lower(solver)
    +0410     case 'gurobi'
    +0411         timeLimit=isText && strcmp(origStat,'TIME_LIMIT');
    +0412     case 'mosek'
    +0413         timeLimit=isText && strcmp(origStat,'MSK_RES_TRM_MAX_TIME');
    +0414     case 'glpk'
    +0415         %109 from the simplex method, 209 from the interior point method
    +0416         timeLimit=isCode && ismember(origStat,[109 209]);
    +0417     case {'scip','soplex'}
    +0418         timeLimit=isCode && origStat==5;
    +0419     case {'ibm_cplex','tomlab_cplex','cplex_direct'}
    +0420         %11 for an LP, 107 and 108 for a MILP with and without an integer
    +0421         %solution found before the limit was reached
    +0422         timeLimit=isCode && ismember(origStat,[11 107 108]);
    +0423 end
    +0424 end
    +0425 
    +0426 function paramlist = renameparams(paramlist,old,new)
    +0427 if ~iscell(paramlist)
    +0428     wasNoCell = true;
    +0429     paramlist={paramlist};
    +0430 else
    +0431     wasNoCell = false;
    +0432 end
    +0433 for i=1:numel(old)
    +0434     paramlist = regexprep(paramlist,old{i},new{i});
    +0435 end
    +0436 if wasNoCell
    +0437     paramlist=paramlist{1};
    +0438 end
    +0439 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/solver/setRavenSolver.html b/docs/html/solver/setRavenSolver.html new file mode 100644 index 00000000..f4a1159e --- /dev/null +++ b/docs/html/solver/setRavenSolver.html @@ -0,0 +1,143 @@ + + + + Description of setRavenSolver + + + + + + + + + +
    Home > solver > setRavenSolver.m
    + + + +

    setRavenSolver +

    + +

    PURPOSE ^

    +
    setRavenSolver Set the solver for RAVEN.
    + +

    SYNOPSIS ^

    +
    function setRavenSolver(solver)
    + +

    DESCRIPTION ^

    +
     setRavenSolver  Set the solver for RAVEN.
    +
    + Sets the solver for RAVEN and optionally saves a default value to MATLAB
    + startup.
    +
    + Parameters
    + ----------
    + solver : char
    +     string with solver name. The following options are available:
    +
    +     - glpk : uses RAVEN-provided binaries. Solves LPs and MILPs; for
    +       MILPs the RAVEN:glpkMILP warning states that Gurobi is much faster
    +       and more reliable for larger problems.
    +     - gurobi : requires a working Gurobi installation.
    +     - scip : requires a SCIP MEX interface. RAVEN provides a
    +       precompiled binary for Windows only.
    +     - cobra : requires a working COBRA Toolbox installation. RAVEN
    +       parses the problem to COBRA and uses the solver that is set by
    +       changeCobraSolver. For MILPs, the RAVEN:glpkMILP warning is issued
    +       when COBRA's MILP solver is GLPK.
    +
    + Examples
    + --------
    +     setRavenSolver(solver);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function setRavenSolver(solver)
    +0002 % setRavenSolver  Set the solver for RAVEN.
    +0003 %
    +0004 % Sets the solver for RAVEN and optionally saves a default value to MATLAB
    +0005 % startup.
    +0006 %
    +0007 % Parameters
    +0008 % ----------
    +0009 % solver : char
    +0010 %     string with solver name. The following options are available:
    +0011 %
    +0012 %     - glpk : uses RAVEN-provided binaries. Solves LPs and MILPs; for
    +0013 %       MILPs the RAVEN:glpkMILP warning states that Gurobi is much faster
    +0014 %       and more reliable for larger problems.
    +0015 %     - gurobi : requires a working Gurobi installation.
    +0016 %     - scip : requires a SCIP MEX interface. RAVEN provides a
    +0017 %       precompiled binary for Windows only.
    +0018 %     - cobra : requires a working COBRA Toolbox installation. RAVEN
    +0019 %       parses the problem to COBRA and uses the solver that is set by
    +0020 %       changeCobraSolver. For MILPs, the RAVEN:glpkMILP warning is issued
    +0021 %       when COBRA's MILP solver is GLPK.
    +0022 %
    +0023 % Examples
    +0024 % --------
    +0025 %     setRavenSolver(solver);
    +0026 solver=char(solver);
    +0027 switch solver
    +0028     case 'cobra'
    +0029         global CBT_LP_SOLVER;
    +0030         if isempty(CBT_LP_SOLVER)
    +0031             error('COBRA toolbox is not initialized, run ''initCobraToolbox()''')
    +0032         else
    +0033             setpref('RAVEN','solver','cobra');
    +0034         end
    +0035     case 'mosek'
    +0036         error('MOSEK support has been discontinued since RAVEN 2.3.0.')
    +0037     case {'glpk','gurobi'}
    +0038         setpref('RAVEN','solver',solver)
    +0039     case 'soplex'
    +0040         error('The ''soplex'' solver is replaced by ''scip''.')
    +0041     case 'scip'
    +0042         if ~ispc
    +0043             try
    +0044                 scip; % The user might have installed SCIP manually
    +0045                 setpref('RAVEN','solver','scip')
    +0046             catch
    +0047                 error('SCIP not found. RAVEN only provides the precompiled SCIP MEX binary for Windows. Instructions on how to compile the SCIP MEX file are found at https://github.com/scipopt/MatlabSCIPInterface')
    +0048             end
    +0049         else
    +0050             ravenDir = findRAVENroot();
    +0051             if ~exist(fullfile(ravenDir,'src','external','scip','scip.mexw64'),'file')
    +0052                 try
    +0053                     disp('Downloading and installing RAVEN-provided SCIP MEX binary...')
    +0054                     websave(fullfile(ravenDir,'src','external','scip_mex_win.zip'),'https://github.com/SysBioChalmers/RAVEN/releases/download/v2.8.6/scip_mex_win.zip'); % Should be updated to release with SCIP functionality
    +0055                 catch
    +0056                     error('Unable to download SCIP MEX binary from the RAVEN GitHub page.')
    +0057                 end
    +0058                 unzip(fullfile(ravenDir,'src','external','scip_mex_win.zip'),fullfile(ravenDir,'src','external','scip'));
    +0059                 delete(fullfile(ravenDir,'src','external','scip_mex_win.zip'));
    +0060             end
    +0061             try
    +0062                 scip; % The pre-compiled MEX file might fail
    +0063                 setpref('RAVEN','solver','scip')
    +0064             catch
    +0065                 error('Unable to use the RAVEN-provided precompiled SCIP MEX binary. Instructions on how to compile the SCIP MEX file are found at https://github.com/scipopt/MatlabSCIPInterface')
    +0066             end
    +0067         end
    +0068     otherwise
    +0069         error('Invalid solver defined')
    +0070 end
    +0071 global RAVENSOLVER
    +0072 RAVENSOLVER = solver;
    +0073 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/solver/solveLP.html b/docs/html/solver/solveLP.html new file mode 100644 index 00000000..424a2120 --- /dev/null +++ b/docs/html/solver/solveLP.html @@ -0,0 +1,340 @@ + + + + Description of solveLP + + + + + + + + + +
    Home > solver > solveLP.m
    + + + +

    solveLP +

    + +

    PURPOSE ^

    +
    solveLP Solve a linear programming problem.
    + +

    SYNOPSIS ^

    +
    function [solution, hsSolOut]=solveLP(model,varargin)
    + +

    DESCRIPTION ^

    +
     solveLP  Solve a linear programming problem.
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    +
    + Name-Value Arguments
    + --------------------
    + minFlux : double
    +     determines if a second optimisation is performed to remove loops
    +     from the flux distribution (default 0):
    +
    +     - 0 : no second optimisation is performed
    +     - 1 : the sum of abs(fluxes) is minimised with an LP, which does not
    +       need the MILP that the minimisation of the number of fluxes requires
    +     - 2 : deprecated; gives an error
    +     - 3 : the number of fluxes is minimised with getMinNrFluxes, which
    +       solves a MILP and can take long. The result has the fewest active
    +       reactions, which can make the flux distribution easier to interpret
    + params : struct
    +     solver parameters, forwarded to optimizeProb. The field "maxRatio"
    +     (a number > 1) can be set for numerical conditioning of ill-scaled
    +     models: any reaction whose stoichiometric coefficients span more
    +     than maxRatio is split with auxiliary metabolites before solving,
    +     which preserves the feasible region (and thus the flux distribution
    +     of the original reactions). A common value is 1e6. No splitting is done
    +     when maxRatio is absent (default struct with relGap=0.4).
    + hsSol : struct
    +     hot-start solution for the LP solver, as hsSolOut from a previous
    +     call. It speeds up the solution when many similar optimisation
    +     problems are solved iteratively. A hsSol with stat<1 is ignored.
    +
    + Returns
    + -------
    + solution : struct
    +     LP solution with fields:
    +
    +     - f : objective value
    +     - x : primal (flux distribution)
    +     - stat : exit flag (1 = optimisation terminated successfully, 0 =
    +       solution is feasible but not necessarily optimal, -1 = no feasible
    +       solution found, -2 = solution found but flux minimisation failed)
    +     - msg : string describing the status of the optimisation
    +     - sPrice : shadow price (only reported if minFlux was 0)
    +     - rCost : reduced cost (only reported if minFlux was 0)
    + hsSolOut : struct
    +     solution to be used as hot-start solution (see the input parameters).
    +     Set only if minFlux is 0 or 1, and only when the solver returns a
    +     basis.
    +
    + Examples
    + --------
    +     [solution, hsSolOut] = solveLP(model, minFlux, params, hsSol);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • checkSolution checkSolution Check if a solver result is feasible and optimal.
    • getMinNrFluxes getMinNrFluxes Find the minimal set of fluxes that satisfy the model.
    • optimizeProb optimizeProb Optimise an LP or MILP formulated in COBRA terms.
    • solveLP solveLP Solve a linear programming problem.
    +This function is called by: +
      +
    • getMinNrFluxes getMinNrFluxes Find the minimal set of fluxes that satisfy the model.
    • solveLP solveLP Solve a linear programming problem.
    + + + + +

    SOURCE CODE ^

    +
    0001 function [solution, hsSolOut]=solveLP(model,varargin)
    +0002 % solveLP  Solve a linear programming problem.
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % model : struct
    +0007 %     a model structure.
    +0008 %
    +0009 % Name-Value Arguments
    +0010 % --------------------
    +0011 % minFlux : double
    +0012 %     determines if a second optimisation is performed to remove loops
    +0013 %     from the flux distribution (default 0):
    +0014 %
    +0015 %     - 0 : no second optimisation is performed
    +0016 %     - 1 : the sum of abs(fluxes) is minimised with an LP, which does not
    +0017 %       need the MILP that the minimisation of the number of fluxes requires
    +0018 %     - 2 : deprecated; gives an error
    +0019 %     - 3 : the number of fluxes is minimised with getMinNrFluxes, which
    +0020 %       solves a MILP and can take long. The result has the fewest active
    +0021 %       reactions, which can make the flux distribution easier to interpret
    +0022 % params : struct
    +0023 %     solver parameters, forwarded to optimizeProb. The field "maxRatio"
    +0024 %     (a number > 1) can be set for numerical conditioning of ill-scaled
    +0025 %     models: any reaction whose stoichiometric coefficients span more
    +0026 %     than maxRatio is split with auxiliary metabolites before solving,
    +0027 %     which preserves the feasible region (and thus the flux distribution
    +0028 %     of the original reactions). A common value is 1e6. No splitting is done
    +0029 %     when maxRatio is absent (default struct with relGap=0.4).
    +0030 % hsSol : struct
    +0031 %     hot-start solution for the LP solver, as hsSolOut from a previous
    +0032 %     call. It speeds up the solution when many similar optimisation
    +0033 %     problems are solved iteratively. A hsSol with stat<1 is ignored.
    +0034 %
    +0035 % Returns
    +0036 % -------
    +0037 % solution : struct
    +0038 %     LP solution with fields:
    +0039 %
    +0040 %     - f : objective value
    +0041 %     - x : primal (flux distribution)
    +0042 %     - stat : exit flag (1 = optimisation terminated successfully, 0 =
    +0043 %       solution is feasible but not necessarily optimal, -1 = no feasible
    +0044 %       solution found, -2 = solution found but flux minimisation failed)
    +0045 %     - msg : string describing the status of the optimisation
    +0046 %     - sPrice : shadow price (only reported if minFlux was 0)
    +0047 %     - rCost : reduced cost (only reported if minFlux was 0)
    +0048 % hsSolOut : struct
    +0049 %     solution to be used as hot-start solution (see the input parameters).
    +0050 %     Set only if minFlux is 0 or 1, and only when the solver returns a
    +0051 %     basis.
    +0052 %
    +0053 % Examples
    +0054 % --------
    +0055 %     [solution, hsSolOut] = solveLP(model, minFlux, params, hsSol);
    +0056 
    +0057 p=parseRAVENargs(varargin, {'minFlux',0; 'params',[]; 'hsSol',[]});
    +0058 minFlux=p.minFlux;
    +0059 params=p.params;
    +0060 if isempty(params)
    +0061     params.relGap=0.4;
    +0062 end
    +0063 hsSol=p.hsSol;
    +0064 
    +0065 %Default return values
    +0066 hsSolOut=[];
    +0067 solution.x=[];
    +0068 solution.f=[];
    +0069 solution.stat=-1;
    +0070 
    +0071 %Check for valid lb and ub
    +0072 if ~isnumeric([model.lb,model.ub])
    +0073     invalidBound = true(numel(model.lb),1);
    +0074 else
    +0075     invalidBound = false(numel(model.lb),1);
    +0076 end
    +0077 invalidBound = invalidBound | model.lb>model.ub;
    +0078 invalidBound = invalidBound | logical(sum(isnan([model.lb,model.ub]),2));
    +0079 if any(invalidBound)
    +0080     error(['Invalid set of bounds for reaction(s): ', strjoin(model.rxns(invalidBound),', '), '.'])
    +0081 end
    +0082 %Check for valid objective
    +0083 if ~isnumeric(model.c) || any(isnan(model.c))
    +0084     error('Invalid defintion of objective function in model.c.')
    +0085 end
    +0086 %Check for valid S-matrix
    +0087 if ~allfinite(model.S)
    +0088     invalidS = ~isfinite(model.S);
    +0089     error(['Invalid coefficients defined for reaction(s): ', strjoin(model.rxns(any(invalidS)),', '), '.'])
    +0090 end
    +0091 
    +0092 %Ignore the hot-start if the previous solution was not feasible
    +0093 if isfield(hsSol,'stat')
    +0094     if hsSol.stat<1
    +0095         hsSol=[];
    +0096     end
    +0097 end
    +0098 
    +0099 % Setup the linear problem for glpk
    +0100 prob        = [];
    +0101 prob.c      = [model.c*-1;zeros(size(model.S,1),1)];
    +0102 prob.b      = zeros(size(model.S,1), 1);
    +0103 prob.lb     = [model.lb; model.b(:,1)];
    +0104 prob.ub     = [model.ub; model.b(:,min(size(model.b,2),2))];
    +0105 prob.csense = repmat('E', 1, length(prob.b)); % Equality constraints
    +0106 prob.osense = 1; 
    +0107 prob.vartype = repmat('C', 1, length(prob.c)); % Fluxes are continuous values
    +0108 prob.A = [model.S -speye(size(model.S,1))];
    +0109 prob.a = model.S;
    +0110 
    +0111 %If hot-start should be used
    +0112 if ~isempty(hsSol)
    +0113     prob.vbasis=hsSol.vbasis;
    +0114     prob.cbasis=hsSol.cbasis;
    +0115 end
    +0116 
    +0117 % Parse the problem to the LP solver
    +0118 res = optimizeProb(prob,params);
    +0119 % Swap the sign of the obj-value
    +0120 res.obj = -res.obj;
    +0121 
    +0122 %Check if the problem was feasible and that the solution was optimal
    +0123 [isFeasible, isOptimal]=checkSolution(res);
    +0124 
    +0125 %If the problem was infeasible using hot-start it is often possible to
    +0126 %re-solve it without hot-start and get a feasible solution
    +0127 if ~isFeasible && ~isempty(hsSol)
    +0128     prob=rmfield(prob,{'vbasis','cbasis'});
    +0129     res=optimizeProb(prob,params);
    +0130     [isFeasible, isOptimal]=checkSolution(res);
    +0131 end
    +0132 
    +0133 %Return without solution if the problem was infeasible
    +0134 if ~isFeasible
    +0135     solution.msg='The problem is infeasible';
    +0136     return;
    +0137 end
    +0138 if ~isOptimal
    +0139     solution.msg='The problem is feasible, but not necessarily optimal';
    +0140     solution.stat=0;
    +0141 else
    +0142     %All is well
    +0143     solution.stat=1;
    +0144     solution.msg='Optimal solution found';
    +0145 end
    +0146 
    +0147 %Construct the output structure
    +0148 if isfield(res,'full')
    +0149     solution.x=res.full;
    +0150     if minFlux<=1
    +0151         if(isfield(res,'vbasis')) % gurobi uses vbasis and cbasis as hotstart
    +0152             hsSolOut.vbasis=res.vbasis;
    +0153             hsSolOut.cbasis=res.cbasis;
    +0154         end
    +0155     end
    +0156     solution.f=res.obj;
    +0157 if isfield(res,'dual')
    +0158     solution.sPrice=res.dual;
    +0159 else
    +0160     solution.sPrice=[];
    +0161 end
    +0162 if isfield(res,'rcost')
    +0163     solution.rCost=res.rcost(1:numel(model.rxns));
    +0164 else
    +0165     solution.rCost=[];
    +0166 end
    +0167 
    +0168 %If either the square, the number, or the sum of fluxes should be minimized
    +0169 %then the objective function value should be fixed before another
    +0170 %optimization. It is not correct to fix the reactions which participate in
    +0171 %the objective function to their values in solution.x, as there can be
    +0172 %multiple solutions with the same objective function value. In addition,
    +0173 %this approach could result in numerical issues when several fluxes are
    +0174 %fixed. Instead a new "fake metabolite" is added to the problem. This
    +0175 %metabolite is produced by each reaction with the stoichiometry that
    +0176 %reaction has in the objective function. The equality constraint of that
    +0177 %"fake metabolite" is then set to be at least as good as the objective
    +0178 %function value.
    +0179 if minFlux~=0
    +0180     model.S=[model.S;model.c'];
    +0181     model.mets=[model.mets;'TEMP'];
    +0182     
    +0183     %If the constraint on the objective function value is exact there is a
    +0184     %larger risk of numerical errors. However for the quadratic fitting
    +0185     %intervals are not allowed
    +0186     if minFlux~=2
    +0187         if size(model.b,2)==1
    +0188             model.b=[model.b model.b];
    +0189         end
    +0190         if solution.f>0
    +0191             model.b=[model.b;[solution.f*0.999999 inf]];
    +0192         else
    +0193             model.b=[model.b;[solution.f*1.000001 inf]];
    +0194         end
    +0195     else
    +0196         model.b=[model.b;ones(1,size(model.b,2))*solution.f];
    +0197     end
    +0198     
    +0199     switch minFlux
    +0200         %The sum of fluxes should be minimized
    +0201         case 1
    +0202             %Convert the model to the irreversible format
    +0203             revRxns=find(model.rev);
    +0204             if ~isempty(revRxns)
    +0205                 iModel=convertToIrrev(model);
    +0206             else
    +0207                 iModel=model;
    +0208             end
    +0209             %Minimize all fluxes
    +0210             iModel.c(:)=-1;
    +0211             sol=solveLP(iModel,0,params);
    +0212             
    +0213             %Map back to reversible fluxes
    +0214             if sol.stat>=0
    +0215                 solution.x=sol.x(1:numel(model.c));
    +0216                 solution.x(revRxns)=solution.x(revRxns)-sol.x(numel(model.c)+1:end);
    +0217             else
    +0218                 EM='Could not solve the problem of minimizing the sum of fluxes. Uses output from original problem';
    +0219                 warning('RAVEN:warning', '%s', EM);
    +0220                 solution.stat=-2;
    +0221             end
    +0222             
    +0223         case 2
    +0224             error('%Minimization of square of fluxes is deprecated')
    +0225             
    +0226             %The number of fluxes should be minimized
    +0227         case 3
    +0228             [qx,I]=getMinNrFluxes(model,model.rxns,params);
    +0229             qx(~I)=0;
    +0230             if any(I)
    +0231                 solution.x=qx;
    +0232             else
    +0233                 EM='Could not solve the problem of minimizing the number of fluxes. Uses output from linear program';
    +0234                 warning('RAVEN:warning', '%s', EM);
    +0235                 solution.stat=-2;
    +0236             end
    +0237     end
    +0238     solution=rmfield(solution,{'sPrice','rCost'});
    +0239 end
    +0240 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/solver/splitProbForConditioning.html b/docs/html/solver/splitProbForConditioning.html new file mode 100644 index 00000000..d0deaaa2 --- /dev/null +++ b/docs/html/solver/splitProbForConditioning.html @@ -0,0 +1,244 @@ + + + + Description of splitProbForConditioning + + + + + + + + + +
    Home > solver > splitProbForConditioning.m
    + + + +

    splitProbForConditioning +

    + +

    PURPOSE ^

    +
    splitProbForConditioning Improve LP conditioning by splitting wide columns.
    + +

    SYNOPSIS ^

    +
    function [prob, condInfo] = splitProbForConditioning(prob, maxRatio)
    + +

    DESCRIPTION ^

    +
     splitProbForConditioning  Improve LP conditioning by splitting wide columns.
    +
    + Reformulates an LP problem to improve its numerical conditioning by
    + splitting columns whose coefficients span more than a given ratio. In a
    + metabolic model a column corresponds to a reaction, and a wide spread of
    + stoichiometric coefficients within one reaction (for example a biomass reaction
    + with both large and small coefficients) cannot be removed by the
    + diagonal row and column scaling that LP solvers apply, and may lead to
    + unreliable solutions or spurious infeasibility.
    +
    + Splitting is done with auxiliary metabolites and variables that preserve
    + the feasible region of the original problem exactly, so the values of all
    + original variables are unchanged. A column is split when its ratio
    + exceeds maxRatio by more than 1 %. Columns with a single nonzero
    + coefficient are never split.
    +
    + optimizeProb calls this function on the COBRA-style problem struct of an
    + LP, immediately before the problem is passed to the solver. Auxiliary
    + rows and columns are appended after the original ones, so the
    + augmentation can be stripped from the solver output afterwards (see
    + condInfo).
    +
    + Parameters
    + ----------
    + prob : struct
    +     COBRA-style LP problem struct, with at least the fields A, b, c, lb,
    +     ub, csense and vartype.
    + maxRatio : double
    +     maximum allowed ratio between the largest and smallest absolute
    +     coefficient within any single column.
    +
    + Returns
    + -------
    + prob : struct
    +     the augmented problem struct. Identical to the input if no column
    +     needed splitting.
    + condInfo : struct
    +     struct describing the augmentation, with fields:
    +
    +     - applied : true if any column was split
    +     - nSplits : number of pairs of an auxiliary metabolite and a conversion
    +       variable added
    +     - nOrigRows : number of constraint rows before augmentation
    +     - nOrigCols : number of variables (columns) before augmentation
    +
    + Examples
    + --------
    +     [prob, condInfo] = splitProbForConditioning(prob, maxRatio);
    +
    + See also
    + --------
    + optimizeProb
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    • optimizeProb optimizeProb Optimise an LP or MILP formulated in COBRA terms.
    + + + + +

    SOURCE CODE ^

    +
    0001 function [prob, condInfo] = splitProbForConditioning(prob, maxRatio)
    +0002 % splitProbForConditioning  Improve LP conditioning by splitting wide columns.
    +0003 %
    +0004 % Reformulates an LP problem to improve its numerical conditioning by
    +0005 % splitting columns whose coefficients span more than a given ratio. In a
    +0006 % metabolic model a column corresponds to a reaction, and a wide spread of
    +0007 % stoichiometric coefficients within one reaction (for example a biomass reaction
    +0008 % with both large and small coefficients) cannot be removed by the
    +0009 % diagonal row and column scaling that LP solvers apply, and may lead to
    +0010 % unreliable solutions or spurious infeasibility.
    +0011 %
    +0012 % Splitting is done with auxiliary metabolites and variables that preserve
    +0013 % the feasible region of the original problem exactly, so the values of all
    +0014 % original variables are unchanged. A column is split when its ratio
    +0015 % exceeds maxRatio by more than 1 %. Columns with a single nonzero
    +0016 % coefficient are never split.
    +0017 %
    +0018 % optimizeProb calls this function on the COBRA-style problem struct of an
    +0019 % LP, immediately before the problem is passed to the solver. Auxiliary
    +0020 % rows and columns are appended after the original ones, so the
    +0021 % augmentation can be stripped from the solver output afterwards (see
    +0022 % condInfo).
    +0023 %
    +0024 % Parameters
    +0025 % ----------
    +0026 % prob : struct
    +0027 %     COBRA-style LP problem struct, with at least the fields A, b, c, lb,
    +0028 %     ub, csense and vartype.
    +0029 % maxRatio : double
    +0030 %     maximum allowed ratio between the largest and smallest absolute
    +0031 %     coefficient within any single column.
    +0032 %
    +0033 % Returns
    +0034 % -------
    +0035 % prob : struct
    +0036 %     the augmented problem struct. Identical to the input if no column
    +0037 %     needed splitting.
    +0038 % condInfo : struct
    +0039 %     struct describing the augmentation, with fields:
    +0040 %
    +0041 %     - applied : true if any column was split
    +0042 %     - nSplits : number of pairs of an auxiliary metabolite and a conversion
    +0043 %       variable added
    +0044 %     - nOrigRows : number of constraint rows before augmentation
    +0045 %     - nOrigCols : number of variables (columns) before augmentation
    +0046 %
    +0047 % Examples
    +0048 % --------
    +0049 %     [prob, condInfo] = splitProbForConditioning(prob, maxRatio);
    +0050 %
    +0051 % See also
    +0052 % --------
    +0053 % optimizeProb
    +0054 
    +0055 [nOrigRows, nOrigCols] = size(prob.A);
    +0056 condInfo.applied   = false;
    +0057 condInfo.nSplits   = 0;
    +0058 condInfo.nOrigRows = nOrigRows;
    +0059 condInfo.nOrigCols = nOrigCols;
    +0060 
    +0061 %Detection uses a small slack so that reactions that are only marginally over
    +0062 %the threshold are left alone. This also makes the transformation idempotent:
    +0063 %a freshly split column sits exactly at maxRatio and is not split again.
    +0064 detectRatio = maxRatio * 1.01;
    +0065 
    +0066 %Work on triplets to avoid repeated resizing of the sparse matrix
    +0067 [I, J, V] = find(prob.A);
    +0068 absV = abs(V);
    +0069 
    +0070 %Largest/smallest absolute coefficient and number of nonzeros per column
    +0071 cMax   = accumarray(J, absV, [nOrigCols 1], @max, 0);
    +0072 cMin   = accumarray(J, absV, [nOrigCols 1], @min, inf);
    +0073 nnzCol = accumarray(J, 1,    [nOrigCols 1], @sum, 0);
    +0074 
    +0075 %Single-coefficient columns (e.g. exchange reactions or slack variables) have
    +0076 %an undefined ratio and are never split
    +0077 badCols = find(nnzCol >= 2 & (cMax ./ cMin) > detectRatio);
    +0078 if isempty(badCols)
    +0079     return
    +0080 end
    +0081 
    +0082 %Flag the entries that are too small within their (bad) column. These are
    +0083 %removed from the original matrix and re-introduced via the auxiliary chain.
    +0084 smallThresh = cMax(J) / maxRatio;
    +0085 removeEntry = ismember(J, badCols) & (absV < smallThresh);
    +0086 
    +0087 %Keep all entries that are not removed
    +0088 keep = ~removeEntry;
    +0089 newI = I(keep);
    +0090 newJ = J(keep);
    +0091 newV = V(keep);
    +0092 
    +0093 %Auxiliary entries are accumulated here
    +0094 addI = zeros(0,1); addJ = zeros(0,1); addV = zeros(0,1);
    +0095 convLb = zeros(0,1); convUb = zeros(0,1);
    +0096 nStar = 0;   %auxiliary metabolite rows added so far
    +0097 nConv = 0;   %conversion variable columns added so far
    +0098 
    +0099 remIdx = find(removeEntry);
    +0100 for k = 1:numel(remIdx)
    +0101     e         = remIdx(k);
    +0102     metRow    = I(e);
    +0103     parentCol = J(e);
    +0104     coef      = V(e);
    +0105     curCmax   = cMax(parentCol);
    +0106     %Deliver the (signed) coefficient "coef" of metabolite "metRow" into
    +0107     %column "parentCol", splitting recursively until it is within range
    +0108     while true
    +0109         small = curCmax / maxRatio;
    +0110         if abs(coef) >= small
    +0111             %Coefficient is acceptable; place it directly
    +0112             addI(end+1,1) = metRow;    addJ(end+1,1) = parentCol; addV(end+1,1) = coef; %#ok<AGROW>
    +0113             break
    +0114         end
    +0115         %Otherwise introduce a star metabolite and a conversion variable
    +0116         beta    = sign(coef) * small;
    +0117         nStar   = nStar + 1; starRow = nOrigRows + nStar;
    +0118         nConv   = nConv + 1; convCol = nOrigCols + nConv;
    +0119         %The parent column now consumes the star metabolite (coefficient beta)
    +0120         addI(end+1,1) = starRow; addJ(end+1,1) = parentCol; addV(end+1,1) = beta; %#ok<AGROW>
    +0121         %The conversion variable produces one unit of the star metabolite
    +0122         addI(end+1,1) = starRow; addJ(end+1,1) = convCol;   addV(end+1,1) = 1;    %#ok<AGROW>
    +0123         %Conversion variables are determined by the equality and left unbounded
    +0124         convLb(end+1,1) = -inf; convUb(end+1,1) = inf; %#ok<AGROW>
    +0125         %Deliver the residual coefficient into the new conversion column. Its
    +0126         %reference scale is 1 (the production coefficient of the star met).
    +0127         coef      = -coef / beta;
    +0128         parentCol = convCol;
    +0129         curCmax   = 1;
    +0130     end
    +0131 end
    +0132 
    +0133 condInfo.applied = true;
    +0134 condInfo.nSplits = nStar;
    +0135 
    +0136 %Assemble the augmented matrix and extend the problem vectors. New rows are
    +0137 %appended at the bottom, new columns at the right.
    +0138 prob.A = sparse([newI; addI], [newJ; addJ], [newV; addV], ...
    +0139     nOrigRows + nStar, nOrigCols + nConv);
    +0140 prob.b       = [prob.b(:);       zeros(nStar,1)];
    +0141 prob.csense  = [prob.csense(:).', repmat('E',1,nStar)];
    +0142 prob.c       = [prob.c(:);       zeros(nConv,1)];
    +0143 prob.lb      = [prob.lb(:);      convLb];
    +0144 prob.ub      = [prob.ub(:);      convUb];
    +0145 prob.vartype = [prob.vartype(:).', repmat('C',1,nConv)];
    +0146 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/tasks/checkTasks.html b/docs/html/tasks/checkTasks.html new file mode 100644 index 00000000..0d66fc72 --- /dev/null +++ b/docs/html/tasks/checkTasks.html @@ -0,0 +1,451 @@ + + + + Description of checkTasks + + + + + + + + + +
    Home > tasks > checkTasks.m
    + + + +

    checkTasks +

    + +

    PURPOSE ^

    +
    checkTasks Perform a set of simulations as defined in a task file.
    + +

    SYNOPSIS ^

    +
    function [taskReport, essentialRxns, taskStructure, essentialFluxes]=checkTasks(model,inputFile,varargin)
    + +

    DESCRIPTION ^

    +
     checkTasks  Perform a set of simulations as defined in a task file.
    +
    + The function runs a set of tasks for a model. Each task is defined by
    + constraints on the model, and the task is successful if the problem is
    + feasible. Each row can contain one constraint on uptakes, one constraint on
    + outputs, one new equation, and one change of reaction bounds. If more bounds
    + are needed to define the task, then several rows can be used for each task.
    +
    + The task inputs and outputs are applied to the boundary metabolites of
    + the model. The model's own exchange reactions are not closed: they keep
    + their bounds, so in a model without boundary metabolites they stay open
    + during every task. To let the task define the whole boundary, run
    + checkTasks on closeModel(model).
    +
    + Parameters
    + ----------
    + model : struct
    +     a model structure.
    + inputFile : char
    +     a task list in Excel format. See the function parseTaskList for
    +     details (optional if taskStructure is supplied).
    +
    + Name-Value Arguments
    + --------------------
    + printOutput : logical
    +     true if the results of the test should be displayed (default true).
    + printOnlyFailed : logical
    +     true if only tasks that failed should be displayed (default false).
    + getEssential : logical
    +     true if the essential reactions should be calculated for all the
    +     tasks. This option is used by prepINITModel (ftINIT) and getINITModel
    +     (tINIT) (default false).
    + taskStructure : struct
    +     structure with the tasks, as from parseTaskList. If this is supplied
    +     then inputFile is ignored.
    + runParallel : logical
    +     true to evaluate tasks in parallel workers, as each task is
    +     independent of the others (see parallelWorkersRAVEN). In a worker,
    +     optimizeProb sets Gurobi to one thread, so the workers do not use
    +     more cores than assigned. With false the tasks are evaluated in
    +     series, which avoids the overhead of starting a pool for small task
    +     lists (default false).
    +
    + Returns
    + -------
    + taskReport : struct
    +     structure with the results, with fields:
    +
    +     - id : cell array with the id of the task
    +     - description : cell array with the description of the task
    +     - ok : boolean array with true if the task was successful
    + essentialRxns : logical
    +     MxN matrix with the essential reactions (M) for each task (N). An
    +     element is true if the corresponding reaction is essential in the
    +     corresponding task. Failed tasks and SHOULD FAIL tasks are ignored.
    +     This is used by the INIT algorithm (if tasks are supplied). If
    +     getEssential=false then essentialRxns=false(nRxns,nTasks).
    + taskStructure : struct
    +     structure with the tasks, as from parseTaskList.
    + essentialFluxes : double
    +     the fluxes of the model reactions in the solution of each task (NaN
    +     for a task without a solution), with the same dimensions as
    +     essentialRxns.
    +
    + Examples
    + --------
    +     [taskReport, essentialRxns, taskStructure] = checkTasks(model, inputFile, ...
    +         printOutput, printOnlyFailed, getEssential, taskStructure);
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: +
      +
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function [taskReport, essentialRxns, taskStructure, essentialFluxes]=checkTasks(model,inputFile,varargin)
    +0002 % checkTasks  Perform a set of simulations as defined in a task file.
    +0003 %
    +0004 % The function runs a set of tasks for a model. Each task is defined by
    +0005 % constraints on the model, and the task is successful if the problem is
    +0006 % feasible. Each row can contain one constraint on uptakes, one constraint on
    +0007 % outputs, one new equation, and one change of reaction bounds. If more bounds
    +0008 % are needed to define the task, then several rows can be used for each task.
    +0009 %
    +0010 % The task inputs and outputs are applied to the boundary metabolites of
    +0011 % the model. The model's own exchange reactions are not closed: they keep
    +0012 % their bounds, so in a model without boundary metabolites they stay open
    +0013 % during every task. To let the task define the whole boundary, run
    +0014 % checkTasks on closeModel(model).
    +0015 %
    +0016 % Parameters
    +0017 % ----------
    +0018 % model : struct
    +0019 %     a model structure.
    +0020 % inputFile : char
    +0021 %     a task list in Excel format. See the function parseTaskList for
    +0022 %     details (optional if taskStructure is supplied).
    +0023 %
    +0024 % Name-Value Arguments
    +0025 % --------------------
    +0026 % printOutput : logical
    +0027 %     true if the results of the test should be displayed (default true).
    +0028 % printOnlyFailed : logical
    +0029 %     true if only tasks that failed should be displayed (default false).
    +0030 % getEssential : logical
    +0031 %     true if the essential reactions should be calculated for all the
    +0032 %     tasks. This option is used by prepINITModel (ftINIT) and getINITModel
    +0033 %     (tINIT) (default false).
    +0034 % taskStructure : struct
    +0035 %     structure with the tasks, as from parseTaskList. If this is supplied
    +0036 %     then inputFile is ignored.
    +0037 % runParallel : logical
    +0038 %     true to evaluate tasks in parallel workers, as each task is
    +0039 %     independent of the others (see parallelWorkersRAVEN). In a worker,
    +0040 %     optimizeProb sets Gurobi to one thread, so the workers do not use
    +0041 %     more cores than assigned. With false the tasks are evaluated in
    +0042 %     series, which avoids the overhead of starting a pool for small task
    +0043 %     lists (default false).
    +0044 %
    +0045 % Returns
    +0046 % -------
    +0047 % taskReport : struct
    +0048 %     structure with the results, with fields:
    +0049 %
    +0050 %     - id : cell array with the id of the task
    +0051 %     - description : cell array with the description of the task
    +0052 %     - ok : boolean array with true if the task was successful
    +0053 % essentialRxns : logical
    +0054 %     MxN matrix with the essential reactions (M) for each task (N). An
    +0055 %     element is true if the corresponding reaction is essential in the
    +0056 %     corresponding task. Failed tasks and SHOULD FAIL tasks are ignored.
    +0057 %     This is used by the INIT algorithm (if tasks are supplied). If
    +0058 %     getEssential=false then essentialRxns=false(nRxns,nTasks).
    +0059 % taskStructure : struct
    +0060 %     structure with the tasks, as from parseTaskList.
    +0061 % essentialFluxes : double
    +0062 %     the fluxes of the model reactions in the solution of each task (NaN
    +0063 %     for a task without a solution), with the same dimensions as
    +0064 %     essentialRxns.
    +0065 %
    +0066 % Examples
    +0067 % --------
    +0068 %     [taskReport, essentialRxns, taskStructure] = checkTasks(model, inputFile, ...
    +0069 %         printOutput, printOnlyFailed, getEssential, taskStructure);
    +0070 
    +0071 p=parseRAVENargs(varargin, {'printOutput',true; 'printOnlyFailed',false; 'getEssential',false; 'taskStructure',[]; 'runParallel',false});
    +0072 printOutput=p.printOutput;
    +0073 printOnlyFailed=p.printOnlyFailed;
    +0074 getEssential=p.getEssential;
    +0075 taskStructure=p.taskStructure;
    +0076 runParallel=p.runParallel;
    +0077 
    +0078 %Prepare the input model a little
    +0079 model.b=zeros(numel(model.mets),2);
    +0080 
    +0081 modelMets=upper(strcat(model.metNames,'[',model.comps(model.metComps),']'));
    +0082 if ~isfield(model,'unconstrained')
    +0083     EM='Exchange metabolites should normally not be removed from the model when using checkTasks. Inputs and outputs are defined in the task file instead. Use importModel(file,false) to import a model with exchange metabolites remaining';
    +0084     warning('RAVEN:warning', '%s', EM);
    +0085 end
    +0086 
    +0087 %Parse the task file
    +0088 if isempty(taskStructure)
    +0089     taskStructure=parseTaskList(inputFile);
    +0090 end
    +0091 
    +0092 nTasks=numel(taskStructure);
    +0093 essentialRxns=false(numel(model.rxns),nTasks);
    +0094 essentialFluxes = NaN(numel(model.rxns),nTasks);
    +0095 
    +0096 %Each task is independent (tModel is rebuilt from model at the top of
    +0097 %every iteration, nothing carries over), so this loop can run in
    +0098 %parallel. ids/descriptions/ok are plain arrays sliced by i and only
    +0099 %assembled into taskReport once the loop is done, because parfor's
    +0100 %classifier rejects slicing a struct field that is itself indexed with
    +0101 %cell/array subscripts (taskReport.id{i,1}=... is not a supported sliced
    +0102 %form). runParallel=false (the default) makes this run exactly like the
    +0103 %previous for loop, in the client, serially.
    +0104 ids=cell(nTasks,1);
    +0105 descriptions=cell(nTasks,1);
    +0106 ok=false(nTasks,1);
    +0107 
    +0108 nW=parallelWorkersRAVEN(runParallel);
    +0109 parfor (i=1:nTasks, nW)
    +0110     tModel=model;
    +0111     ids{i}=taskStructure(i).id;
    +0112     descriptions{i}=taskStructure(i).description;
    +0113     %Set the inputs
    +0114     if ~isempty(taskStructure(i).inputs)
    +0115         [I, J]=ismember(upper(taskStructure(i).inputs),modelMets);
    +0116         J=J(I); %Only keep the ones with matches
    +0117         %Several model metabolites can share a name[comp]. Only the first
    +0118         %one is constrained, so tell the user when that happens
    +0119         dupMets=duplicateMetNames(taskStructure(i).inputs(I),modelMets);
    +0120         if ~isempty(dupMets)
    +0121             EM=['Several model metabolites match input(s) in "[' taskStructure(i).id '] ' taskStructure(i).description '": ' strjoin(dupMets,', ') '. Only the first match is constrained'];
    +0122             warning('RAVEN:warning', '%s', EM);
    +0123         end
    +0124         K=ismember(upper(taskStructure(i).inputs),'ALLMETS');
    +0125         L=~cellfun('isempty',strfind(upper(taskStructure(i).inputs),'ALLMETSIN'));
    +0126         %Check that all metabolites are either real metabolites or
    +0127         %ALLMETS/ALLMETSIN
    +0128         if ~all(I|K|L)
    +0129             fprintf(['ERROR: Could not find all inputs in "[' taskStructure(i).id '] ' taskStructure(i).description '"\n']);
    +0130             ok(i)=false;
    +0131             continue;
    +0132         end
    +0133         if numel(J)~=numel(unique(J))
    +0134             EM=['The constraints on some input(s) in "[' taskStructure(i).id '] ' taskStructure(i).description '" are defined more than one time'];
    +0135             error('RAVEN:badInput', '%s', EM);
    +0136         end
    +0137         %If all metabolites should be added
    +0138         if any(K)
    +0139             %Check if ALLMETS is the first metabolite. Otherwise print a
    +0140             %warning since it will write over any other constraints that
    +0141             %are set
    +0142             if K(1)==0
    +0143                 EM=['ALLMETS is used as an input in "[' taskStructure(i).id '] ' taskStructure(i).description '" but it it not the first metabolite in the list. Constraints defined for the metabolites before it will be over-written'];
    +0144                 warning('RAVEN:warning', '%s', EM);
    +0145             end
    +0146             %Use the first match of ALLMETS. There should only be one, but
    +0147             %still..
    +0148             tModel.b(:,1)=taskStructure(i).UBin(find(K,1))*-1;
    +0149         end
    +0150         %If metabolites in a specific compartment should be used
    +0151         if any(L)
    +0152             L=find(L);
    +0153             for j=1:numel(L)
    +0154                 %The compartment defined
    +0155                 compartment=upper(taskStructure(i).inputs{L(j)}(11:end-1));
    +0156                 %Check if it exists in the model
    +0157                 C=find(ismember(upper(model.comps),compartment));
    +0158                 if any(C)
    +0159                     %Match to metabolites
    +0160                     tModel.b(model.metComps==C,1)=taskStructure(i).UBin(L(j))*-1;
    +0161                 else
    +0162                     EM=['The compartment defined for ALLMETSIN in "[' taskStructure(i).id '] ' taskStructure(i).description '" does not exist'];
    +0163                     error('RAVEN:badInput', '%s', EM);
    +0164                 end
    +0165             end
    +0166         end
    +0167         %Then add the normal constraints
    +0168         if any(J)
    +0169             tModel.b(J,1)=taskStructure(i).UBin(I)*-1;
    +0170             tModel.b(J,2)=taskStructure(i).LBin(I)*-1;
    +0171         end
    +0172     end
    +0173     %Set the outputs
    +0174     if ~isempty(taskStructure(i).outputs)
    +0175         [I, J]=ismember(upper(taskStructure(i).outputs),modelMets);
    +0176         J=J(I); %Only keep the ones with matches
    +0177         %Several model metabolites can share a name[comp]. Only the first
    +0178         %one is constrained, so tell the user when that happens
    +0179         dupMets=duplicateMetNames(taskStructure(i).outputs(I),modelMets);
    +0180         if ~isempty(dupMets)
    +0181             EM=['Several model metabolites match output(s) in "[' taskStructure(i).id '] ' taskStructure(i).description '": ' strjoin(dupMets,', ') '. Only the first match is constrained'];
    +0182             warning('RAVEN:warning', '%s', EM);
    +0183         end
    +0184         K=ismember(upper(taskStructure(i).outputs),'ALLMETS');
    +0185         L=~cellfun('isempty',strfind(upper(taskStructure(i).outputs),'ALLMETSIN'));
    +0186         %Check that all metabolites are either real metabolites or
    +0187         %ALLMETS/ALLMETSIN
    +0188         if ~all(I|K|L)
    +0189             fprintf(['ERROR: Could not find all outputs in "[' taskStructure(i).id '] ' taskStructure(i).description '"\n']);
    +0190             ok(i)=false;
    +0191             continue;
    +0192         end
    +0193         if numel(J)~=numel(unique(J))
    +0194             EM=['The constraints on some output(s) in "[' taskStructure(i).id '] ' taskStructure(i).description '" are defined more than one time'];
    +0195             error('RAVEN:badInput', '%s', EM);
    +0196         end
    +0197         %If all metabolites should be added
    +0198         if any(K)
    +0199             %Check if ALLMETS is the first metabolite. Otherwise print a
    +0200             %warning since it will write over any other constraints that
    +0201             %are set
    +0202             if K(1)==0
    +0203                 EM=['ALLMETS is used as an output in "[' taskStructure(i).id '] ' taskStructure(i).description '" but it it not the first metabolite in the list. Constraints defined for the metabolites before it will be over-written'];
    +0204                 warning('RAVEN:warning', '%s', EM);
    +0205             end
    +0206             %Use the first match of ALLMETS. There should only be one, but
    +0207             %still..
    +0208             tModel.b(:,2)=taskStructure(i).UBout(find(K,1));
    +0209         end
    +0210         %If metabolites in a specific compartment should be used
    +0211         if any(L)
    +0212             L=find(L);
    +0213             for j=1:numel(L)
    +0214                 %The compartment defined
    +0215                 compartment=upper(taskStructure(i).outputs{L(j)}(11:end-1));
    +0216                 %Check if it exists in the model
    +0217                 C=find(ismember(upper(model.comps),compartment));
    +0218                 if any(C)
    +0219                     %Match to metabolites
    +0220                     tModel.b(model.metComps==C,2)=taskStructure(i).UBout(L(j));
    +0221                 else
    +0222                     EM=['The compartment defined for ALLMETSIN in "[' taskStructure(i).id '] ' taskStructure(i).description '" does not exist'];
    +0223                     error('RAVEN:badInput', '%s', EM);
    +0224                 end
    +0225             end
    +0226         end
    +0227         %Then add the normal constraints
    +0228         if any(J)
    +0229             %Verify that IN and OUT bounds are consistent. Cannot require
    +0230             %that a metabolite is simultaneously input AND output at some
    +0231             %nonzero flux.
    +0232             I = find(I);  % otherwise indexing becomes confusing
    +0233             nonzero_LBin = tModel.b(J,2) < 0;
    +0234             nonzero_LBout = taskStructure(i).LBout(I) > 0;
    +0235             if any(nonzero_LBin & nonzero_LBout)
    +0236                 EM=['The IN LB and OUT LB in "[' taskStructure(i).id '] ' taskStructure(i).description '" cannot be nonzero for the same metabolite'];
    +0237                 error('RAVEN:badInput', '%s', EM);
    +0238             end
    +0239             tModel.b(J(nonzero_LBout),1)=taskStructure(i).LBout(I(nonzero_LBout));
    +0240             tModel.b(J,2)=taskStructure(i).UBout(I);
    +0241         end
    +0242     end
    +0243     %Add new rxns
    +0244     if ~isempty(taskStructure(i).equations)
    +0245         % num2str on the whole column right-aligns every row to a common
    +0246         % width, embedding a leading space in "TEMPORARY_ 1" once any id
    +0247         % reaches two digits ("TEMPORARY_10"); format each one independently.
    +0248         rxnIds=arrayfun(@(x) sprintf('TEMPORARY_%d',x), ...
    +0249             (1:numel(taskStructure(i).equations))', 'UniformOutput', false);
    +0250         % Built as a single struct() call, not incremental rxn.field=...
    +0251         % assignments: parfor's variable classifier cannot establish that a
    +0252         % variable built up via several dot-indexed assignments is a
    +0253         % same-shape temporary in every iteration, and rejects it outright.
    +0254         rxn=struct('equations',{taskStructure(i).equations}, ...
    +0255             'lb',taskStructure(i).LBequ, 'ub',taskStructure(i).UBequ, ...
    +0256             'rxns',{rxnIds});
    +0257         %Allow for new metabolites to be added. This is because it should
    +0258         %be possible to add, say, a whole new pathway
    +0259         tModel=addRxns(tModel,rxn,3,[],true);
    +0260     end
    +0261     %Add changed bounds
    +0262     if ~isempty(taskStructure(i).changed)
    +0263         %NaN bounds mean "leave unchanged"
    +0264         lbSet=~isnan(taskStructure(i).LBrxn);
    +0265         ubSet=~isnan(taskStructure(i).UBrxn);
    +0266         tModel=setParam(tModel,'lb',taskStructure(i).changed(lbSet),taskStructure(i).LBrxn(lbSet));
    +0267         tModel=setParam(tModel,'ub',taskStructure(i).changed(ubSet),taskStructure(i).UBrxn(ubSet));
    +0268     end
    +0269     
    +0270     %Solve and print
    +0271     sol=solveLP(tModel);
    +0272     if ~isempty(sol.x)
    +0273         %assign the fluxes
    +0274         essentialFluxes(:,i) = sol.x(1:numel(model.rxns));
    +0275         
    +0276         if ~taskStructure(i).shouldFail
    +0277             ok(i)=true;
    +0278             if printOnlyFailed==false && printOutput==true
    +0279                 fprintf(['PASS: [' taskStructure(i).id '] ' taskStructure(i).description '\n']);
    +0280             end
    +0281             %Calculate the essential reactions
    +0282             if getEssential==true
    +0283                 [~, taskEssential]=getEssentialRxns(tModel);
    +0284                 %This is because there could be more reactions in tModel
    +0285                 %than in model. Built as a full local column and written
    +0286                 %with a plain ':' row index: parfor only accepts a sliced
    +0287                 %output write when every subscript besides the loop
    +0288                 %variable is ':', not a data-dependent index vector.
    +0289                 essentialCol=false(numel(model.rxns),1);
    +0290                 essentialCol(taskEssential(taskEssential<=numel(model.rxns)))=true;
    +0291                 essentialRxns(:,i)=essentialCol;
    +0292             end
    +0293         else
    +0294             ok(i)=false;
    +0295             if printOutput==true
    +0296                 fprintf(['PASS (should fail): [' taskStructure(i).id '] ' taskStructure(i).description '\n']);
    +0297             end
    +0298         end
    +0299     else
    +0300         if ~taskStructure(i).shouldFail
    +0301             ok(i)=false;
    +0302             if printOutput==true
    +0303                 fprintf(['FAIL: [' taskStructure(i).id '] ' taskStructure(i).description '\n']);
    +0304             end
    +0305         else
    +0306             ok(i)=true;
    +0307             if printOnlyFailed==false && printOutput==true
    +0308                 fprintf(['FAIL (should fail): [' taskStructure(i).id '] ' taskStructure(i).description '\n']);
    +0309             end
    +0310         end
    +0311     end
    +0312     if taskStructure(i).printFluxes && ~isempty(sol.x)
    +0313         sol=solveLP(tModel,1);
    +0314         if ~isempty(sol.x)
    +0315             printFluxes(tModel,sol.x,false,10^-6,[],'%rxnID (%eqn):%flux\n');
    +0316             fprintf('\n');
    +0317         end
    +0318     end
    +0319 end
    +0320 
    +0321 taskReport.id=ids;
    +0322 taskReport.description=descriptions;
    +0323 taskReport.ok=ok;
    +0324 
    +0325 end
    +0326 
    +0327 function dupMets = duplicateMetNames(names,modelMets)
    +0328 % Return the (upper-case) names that match more than one model metabolite.
    +0329 dupMets = unique(upper(names));
    +0330 isDup = false(size(dupMets));
    +0331 for k = 1:numel(dupMets)
    +0332     isDup(k) = sum(strcmp(modelMets,dupMets{k})) > 1;
    +0333 end
    +0334 dupMets = dupMets(isDup);
    +0335 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/tasks/index.html b/docs/html/tasks/index.html similarity index 100% rename from doc/tasks/index.html rename to docs/html/tasks/index.html diff --git a/docs/html/tasks/parseTaskList.html b/docs/html/tasks/parseTaskList.html new file mode 100644 index 00000000..bc0475f7 --- /dev/null +++ b/docs/html/tasks/parseTaskList.html @@ -0,0 +1,548 @@ + + + + Description of parseTaskList + + + + + + + + + +
    Home > tasks > parseTaskList.m
    + + + +

    parseTaskList +

    + +

    PURPOSE ^

    +
    parseTaskList Parse a task list file.
    + +

    SYNOPSIS ^

    +
    function taskStruct=parseTaskList(inputFile)
    + +

    DESCRIPTION ^

    +
     parseTaskList  Parse a task list file.
    +
    + Parameters
    + ----------
    + inputFile : char
    +     a task list in either Excel (*.xlsx, with a sheet named TASKS with
    +     all relevant content) or tab-delimited (*.txt) format. The binary
    +     *.xls format is not supported. The file may contain the following
    +     column headers. Rows that start with a non-empty cell are comments
    +     and are removed. The first row that starts with an empty cell is the
    +     header row, so the header row and the task rows must start with an
    +     empty cell:
    +
    +     - ID : the only required header (string or numeric). Ids may repeat,
    +       for example as category codes. A task starts at a row with a
    +       non-empty id and can span multiple rows; only the first row of a task
    +       should have an id
    +     - DESCRIPTION : description of the task
    +     - IN : allowed input(s) for the task. Metabolite names are written in
    +       the form "model.metName[model.comps]". Several inputs can be
    +       delimited by ";". If so, then the same bounds are used for all
    +       inputs. If that is not wanted, then use several rows for the task
    +     - IN LB : lower bound for the uptake of the metabolites in the row
    +       (optional, default 0)
    +     - IN UB : upper bound for the uptake of the metabolites in the row
    +       (optional, default 1000)
    +     - OUT : allowed output(s) for the task (see IN)
    +     - OUT LB : lower bound for the production of the metabolites in the
    +       row (optional, default 0)
    +     - OUT UB : upper bound for the production of the metabolites in the
    +       row (optional, default 1000)
    +     - EQU : equation to add. The equation is written in the form
    +       "0.4 A + 2 B <=> (or =>) C" and the metabolites in the
    +       form "model.metName[model.comps]" (optional)
    +     - EQU LB : lower bound for the equation (optional, default -1000 for
    +       reversible and 0 for irreversible)
    +     - EQU UB : upper bound for the equation (optional, default 1000)
    +     - CHANGED RXN : reaction ID for which to change the bounds for.
    +       Several IDs can be delimited by ";". If so, then the same bounds
    +       are used for all reactions. If that is not wanted, then use several
    +       rows for the task
    +     - CHANGED LB : lower bound for the reaction. If empty, the lower bound
    +       of the reaction is left as it is
    +     - CHANGED UB : upper bound for the reaction. If empty, the upper bound
    +       of the reaction is left as it is
    +     - SHOULD FAIL : true if the correct behaviour of the model is to not
    +       have a feasible solution given the constraints. Any non-empty value
    +       except 0, false and no (any case) counts as true (optional, default
    +       false)
    +     - PRINT FLUX : true if checkTasks should print the corresponding
    +       flux distribution for a task. Read in the same way as SHOULD FAIL
    +       (optional, default false)
    +     - COMMENTS : comments on the task, stored in the field comments
    +       (optional)
    +
    +     Headers are matched ignoring case and surrounding whitespace. In the
    +     ";"-delimited cells, items are trimmed and empty items are dropped.
    +
    + Returns
    + -------
    + taskStruct : struct
    +     array of structures with the following fields:
    +
    +     - id : the id of the task
    +     - description : the description of the task
    +     - shouldFail : true if the task should fail
    +     - printFluxes : true if the fluxes should be printed
    +     - comments : string with comments
    +     - inputs : cell array with input metabolites (in the form metName[comps])
    +     - LBin : array with lower bounds on inputs (default, 0)
    +     - UBin : array with upper bounds on inputs (default, 1000)
    +     - outputs : cell array with output metabolites (in the form metName[comps])
    +     - LBout : array with lower bounds on outputs (default, 0)
    +     - UBout : array with upper bounds on outputs (default, 1000)
    +     - equations : cell array with equations (with mets in the form metName[comps])
    +     - LBequ : array with lower bounds on equations (default, -1000 for
    +       reversible and 0 for irreversible)
    +     - UBequ : array with upper bounds on equations (default, 1000)
    +     - changed : cell array with reactions to change bounds for
    +     - LBrxn : array with lower bounds on changed reactions (NaN if empty,
    +       meaning the bound is left unchanged)
    +     - UBrxn : array with upper bounds on changed reactions (NaN if empty,
    +       meaning the bound is left unchanged)
    +
    + Examples
    + --------
    +     taskStruct = parseTaskList(inputFile);
    +
    + Notes
    + -----
    + The task list defines a set of tasks for a model to perform. A task is
    + defined by constraints on the model, and the task is successful if the
    + problem is feasible. Each row can contain one constraint on uptakes, one
    + constraint on outputs, one new equation, and one change of reaction bounds.
    + If more bounds are needed to define the task, then several rows can be used
    + for each task. To perform the task use checkTasks or fitTasks.
    +
    + The general metabolites "ALLMETS" and "ALLMETSIN[comps]" can be used as
    + inputs or outputs in the same manner as normal metabolites. They allow,
    + for example, excretion of all metabolites, to check whether the
    + synthesis of some metabolite or the degradation of some byproduct is
    + limiting. Only the upper bounds are used for these general metabolites,
    + so uptake or excretion can be allowed but not required. A lower bound
    + would overwrite the constraints of the individual metabolites.
    +
    + See also
    + --------
    + checkTasks, fitTasks
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    • checkTasks checkTasks Perform a set of simulations as defined in a task file.
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function taskStruct=parseTaskList(inputFile)
    +0002 % parseTaskList  Parse a task list file.
    +0003 %
    +0004 % Parameters
    +0005 % ----------
    +0006 % inputFile : char
    +0007 %     a task list in either Excel (*.xlsx, with a sheet named TASKS with
    +0008 %     all relevant content) or tab-delimited (*.txt) format. The binary
    +0009 %     *.xls format is not supported. The file may contain the following
    +0010 %     column headers. Rows that start with a non-empty cell are comments
    +0011 %     and are removed. The first row that starts with an empty cell is the
    +0012 %     header row, so the header row and the task rows must start with an
    +0013 %     empty cell:
    +0014 %
    +0015 %     - ID : the only required header (string or numeric). Ids may repeat,
    +0016 %       for example as category codes. A task starts at a row with a
    +0017 %       non-empty id and can span multiple rows; only the first row of a task
    +0018 %       should have an id
    +0019 %     - DESCRIPTION : description of the task
    +0020 %     - IN : allowed input(s) for the task. Metabolite names are written in
    +0021 %       the form "model.metName[model.comps]". Several inputs can be
    +0022 %       delimited by ";". If so, then the same bounds are used for all
    +0023 %       inputs. If that is not wanted, then use several rows for the task
    +0024 %     - IN LB : lower bound for the uptake of the metabolites in the row
    +0025 %       (optional, default 0)
    +0026 %     - IN UB : upper bound for the uptake of the metabolites in the row
    +0027 %       (optional, default 1000)
    +0028 %     - OUT : allowed output(s) for the task (see IN)
    +0029 %     - OUT LB : lower bound for the production of the metabolites in the
    +0030 %       row (optional, default 0)
    +0031 %     - OUT UB : upper bound for the production of the metabolites in the
    +0032 %       row (optional, default 1000)
    +0033 %     - EQU : equation to add. The equation is written in the form
    +0034 %       "0.4 A + 2 B <=> (or =>) C" and the metabolites in the
    +0035 %       form "model.metName[model.comps]" (optional)
    +0036 %     - EQU LB : lower bound for the equation (optional, default -1000 for
    +0037 %       reversible and 0 for irreversible)
    +0038 %     - EQU UB : upper bound for the equation (optional, default 1000)
    +0039 %     - CHANGED RXN : reaction ID for which to change the bounds for.
    +0040 %       Several IDs can be delimited by ";". If so, then the same bounds
    +0041 %       are used for all reactions. If that is not wanted, then use several
    +0042 %       rows for the task
    +0043 %     - CHANGED LB : lower bound for the reaction. If empty, the lower bound
    +0044 %       of the reaction is left as it is
    +0045 %     - CHANGED UB : upper bound for the reaction. If empty, the upper bound
    +0046 %       of the reaction is left as it is
    +0047 %     - SHOULD FAIL : true if the correct behaviour of the model is to not
    +0048 %       have a feasible solution given the constraints. Any non-empty value
    +0049 %       except 0, false and no (any case) counts as true (optional, default
    +0050 %       false)
    +0051 %     - PRINT FLUX : true if checkTasks should print the corresponding
    +0052 %       flux distribution for a task. Read in the same way as SHOULD FAIL
    +0053 %       (optional, default false)
    +0054 %     - COMMENTS : comments on the task, stored in the field comments
    +0055 %       (optional)
    +0056 %
    +0057 %     Headers are matched ignoring case and surrounding whitespace. In the
    +0058 %     ";"-delimited cells, items are trimmed and empty items are dropped.
    +0059 %
    +0060 % Returns
    +0061 % -------
    +0062 % taskStruct : struct
    +0063 %     array of structures with the following fields:
    +0064 %
    +0065 %     - id : the id of the task
    +0066 %     - description : the description of the task
    +0067 %     - shouldFail : true if the task should fail
    +0068 %     - printFluxes : true if the fluxes should be printed
    +0069 %     - comments : string with comments
    +0070 %     - inputs : cell array with input metabolites (in the form metName[comps])
    +0071 %     - LBin : array with lower bounds on inputs (default, 0)
    +0072 %     - UBin : array with upper bounds on inputs (default, 1000)
    +0073 %     - outputs : cell array with output metabolites (in the form metName[comps])
    +0074 %     - LBout : array with lower bounds on outputs (default, 0)
    +0075 %     - UBout : array with upper bounds on outputs (default, 1000)
    +0076 %     - equations : cell array with equations (with mets in the form metName[comps])
    +0077 %     - LBequ : array with lower bounds on equations (default, -1000 for
    +0078 %       reversible and 0 for irreversible)
    +0079 %     - UBequ : array with upper bounds on equations (default, 1000)
    +0080 %     - changed : cell array with reactions to change bounds for
    +0081 %     - LBrxn : array with lower bounds on changed reactions (NaN if empty,
    +0082 %       meaning the bound is left unchanged)
    +0083 %     - UBrxn : array with upper bounds on changed reactions (NaN if empty,
    +0084 %       meaning the bound is left unchanged)
    +0085 %
    +0086 % Examples
    +0087 % --------
    +0088 %     taskStruct = parseTaskList(inputFile);
    +0089 %
    +0090 % Notes
    +0091 % -----
    +0092 % The task list defines a set of tasks for a model to perform. A task is
    +0093 % defined by constraints on the model, and the task is successful if the
    +0094 % problem is feasible. Each row can contain one constraint on uptakes, one
    +0095 % constraint on outputs, one new equation, and one change of reaction bounds.
    +0096 % If more bounds are needed to define the task, then several rows can be used
    +0097 % for each task. To perform the task use checkTasks or fitTasks.
    +0098 %
    +0099 % The general metabolites "ALLMETS" and "ALLMETSIN[comps]" can be used as
    +0100 % inputs or outputs in the same manner as normal metabolites. They allow,
    +0101 % for example, excretion of all metabolites, to check whether the
    +0102 % synthesis of some metabolite or the degradation of some byproduct is
    +0103 % limiting. Only the upper bounds are used for these general metabolites,
    +0104 % so uptake or excretion can be allowed but not required. A lower bound
    +0105 % would overwrite the constraints of the individual metabolites.
    +0106 %
    +0107 % See also
    +0108 % --------
    +0109 % checkTasks, fitTasks
    +0110 
    +0111 if ~isfile(inputFile)
    +0112     error('Task list %s cannot be found',string(inputFile));
    +0113 end
    +0114 
    +0115 %Load the tasks file
    +0116 if strcmp(extractAfter(inputFile,strlength(inputFile) - 4), '.txt')
    +0117     %load from tab delimited text file
    +0118     fid = fopen(inputFile);
    +0119     %Need to read numeric columns as strings, this is converted further
    +0120     %down. If not, the titles would be lost.
    +0121     C = textscan(fid,'%q%q%q%q%q%q%q%q%q%q%q%q%q%q%q%q%q%q%q%q%*[^\n]', 'Delimiter', '\t');
    +0122     fclose(fid);
    +0123     raw = [C{:}];%unnest the cell array of cell arrays into a 2-dim cell array
    +0124 else
    +0125     if endsWith(lower(char(inputFile)),'.xls')
    +0126         error('RAVEN:badInput', '%s', [inputFile ': the old binary .xls format is not supported. Save the task list as .xlsx (sheet ''TASKS'') or as a tab-delimited .txt file.']);
    +0127     end
    +0128     %Read the TASKS sheet from an Excel file
    +0129     try
    +0130         raw=readcell(inputFile,'Sheet','TASKS');
    +0131     catch
    +0132         error('RAVEN:badInput', '%s', ['Could not load sheet "TASKS" from ' inputFile]);
    +0133     end
    +0134     %Normalise blank cells (which readcell returns as "missing") to [], as
    +0135     %expected by the downstream cleanSheet/parsing.
    +0136     raw(cellfun(@(x) all(ismissing(x)),raw))={[]};
    +0137 end
    +0138 
    +0139 %Rows without any content carry no information (a header must not be
    +0140 %mistaken for one of them)
    +0141 if iscell(raw)
    +0142     emptyRow=all(cellfun(@(x) isempty(x) || (ischar(x) && isempty(strtrim(x))),raw),2);
    +0143     raw=raw(~emptyRow,:);
    +0144 end
    +0145 
    +0146 %Leading rows that start with a non-empty cell are comments; the first row
    +0147 %with an empty first cell is the header row
    +0148 if iscell(raw)
    +0149     if isempty(raw)
    +0150         error('RAVEN:badInput', '%s', 'The TASKS sheet must have a column named ID');
    +0151     end
    +0152     firstBlank=cellfun(@(x) isempty(x) || (ischar(x) && isempty(strtrim(x))),raw(:,1));
    +0153     first=find(firstBlank,1);
    +0154     if isempty(first)
    +0155         error('RAVEN:badInput', '%s', 'The TASKS sheet must have a column named ID');
    +0156     end
    +0157     raw=raw(first:end,:);
    +0158 end
    +0159 
    +0160 %Remove all lines starting with "#" (or actually any character) and all
    +0161 %empty columns
    +0162 raw=cleanSheet(raw);
    +0163 
    +0164 %Captions
    +0165 columns={'ID';'DESCRIPTION';'IN';'IN LB';'IN UB';'OUT';'OUT LB';'OUT UB';'EQU';'EQU LB';'EQU UB';'CHANGED RXN';'CHANGED LB';'CHANGED UB';'SHOULD FAIL';'PRINT FLUX';'COMMENTS'};
    +0166 %Match the columns. Headers are matched ignoring case and surrounding
    +0167 %whitespace, so "id" and " IN LB " are accepted.
    +0168 header=raw(1,:);
    +0169 isText=cellfun(@ischar,header);
    +0170 header(isText)=upper(strtrim(header(isText)));
    +0171 [I, colI]=ismember(columns,header);
    +0172 
    +0173 %Only ID is required. Columns that are absent get index 0 from ismember, which
    +0174 %cannot be used to index raw, so point each of them at its own spare empty
    +0175 %column instead: every optional field then reads as empty and its default
    +0176 %applies.
    +0177 absent=find(colI==0);
    +0178 nOld=size(raw,2);
    +0179 raw(:,nOld+(1:numel(absent)))={[]};
    +0180 colI(absent)=nOld+(1:numel(absent));
    +0181 
    +0182 %Text cells (every cell of a text file, and numbers typed as text in Excel)
    +0183 %are converted to numbers; text that is not a number stays and is refused
    +0184 %below
    +0185 numericColumns = [0 0 0 1 1 0 1 1 0 1 1 0 1 1 0 0 0] == 1;
    +0186 cols = colI(numericColumns);
    +0187 numeric = cellfun(@isnumeric,raw(:,cols));
    +0188 %trick to avoid messing up the title row:
    +0189 numeric(1,:) = 1;
    +0190 for colind = 1:numel(cols)
    +0191     col = cols(colind);
    +0192     for r = find(~numeric(:,colind))'
    +0193         x = raw{r,col};
    +0194         if ischar(x) && isempty(strtrim(x))
    +0195             raw{r,col} = [];
    +0196         elseif ischar(x)
    +0197             %Only plain numbers are accepted; str2double alone would also read
    +0198             %'1,5' as 15, '1,000' as 1000 and '2i' as a complex number
    +0199             if ~isempty(regexpi(strtrim(x),'^([+-]?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?|[+-]?inf|nan)$','once'))
    +0200                 raw{r,col} = str2double(x);
    +0201             end
    +0202         end
    +0203     end
    +0204 end
    +0205 
    +0206 %Every bound that is given must be a number
    +0207 numericNames = columns([4 5 7 8 10 11 13 14]);
    +0208 numericCols = colI([4 5 7 8 10 11 13 14]);
    +0209 for k = 1:numel(numericCols)
    +0210     for r = 2:size(raw,1)
    +0211         x = raw{r,numericCols(k)};
    +0212         if ~isempty(x) && ~(isnumeric(x) && isscalar(x))
    +0213             error('RAVEN:badInput', 'The value in column %s of task list data row %d is not a number (rows are counted after the header, comment rows and empty rows are removed)', numericNames{k}, r-1);
    +0214         end
    +0215         %NaN means "leave unchanged" only in the bounds of changed reactions
    +0216         if k <= 6 && isnumeric(x) && isscalar(x) && isnan(x)
    +0217             error('RAVEN:badInput', 'The value in column %s of task list data row %d is NaN, which is only accepted in the bounds of changed reactions (rows are counted after the header, comment rows and empty rows are removed)', numericNames{k}, r-1);
    +0218         end
    +0219     end
    +0220 end
    +0221 
    +0222 %Check that the ID field is present
    +0223 if I(1)==0
    +0224     EM='The TASKS sheet must have a column named ID';
    +0225     error('RAVEN:badInput', '%s', EM);
    +0226 end
    +0227 
    +0228 %Rows before the first task id belong to no task
    +0229 firstId=find(~cellfun(@isempty,raw(2:end,colI(1))),1)+1;
    +0230 if isempty(firstId)
    +0231     firstId=size(raw,1)+1;
    +0232 end
    +0233 if firstId>2
    +0234     warning('RAVEN:warning', 'Skipping %d row(s) before the first task id', firstId-2);
    +0235     raw(2:firstId-1,:)=[];
    +0236 end
    +0237 if size(raw,1)<2
    +0238     taskStruct=[];
    +0239     return;
    +0240 end
    +0241 
    +0242 %Add default bounds where needed
    +0243 for i=[4 5 7 8]
    +0244     I=cellfun(@isempty,raw(:,colI(i)));
    +0245     if i==5 || i==8
    +0246         raw(I,colI(i))={1000};
    +0247     else
    +0248         raw(I,colI(i))={0};
    +0249     end
    +0250 end
    +0251 
    +0252 %Create an empty task structure
    +0253 eTask.id='';
    +0254 eTask.description='';
    +0255 eTask.shouldFail=false;
    +0256 eTask.printFluxes=false;
    +0257 eTask.comments='';
    +0258 eTask.inputs={};
    +0259 eTask.LBin=[];
    +0260 eTask.UBin=[];
    +0261 eTask.outputs={};
    +0262 eTask.LBout=[];
    +0263 eTask.UBout=[];
    +0264 eTask.equations={};
    +0265 eTask.LBequ=[];
    +0266 eTask.UBequ=[];
    +0267 eTask.changed={};
    +0268 eTask.LBrxn=[];
    +0269 eTask.UBrxn=[];
    +0270 
    +0271 %Main loop
    +0272 taskStruct=[];
    +0273 task=eTask;
    +0274 if isnumeric(raw{2,colI(1)})
    +0275     task.id=num2str(raw{2,colI(1)});
    +0276 else
    +0277     task.id=raw{2,colI(1)};
    +0278 end
    +0279 task.description=raw{2,colI(2)};
    +0280 if isTruthy(raw{2,colI(15)})
    +0281     task.shouldFail=true;
    +0282 end
    +0283 if isTruthy(raw{2,colI(16)})
    +0284     task.printFluxes=true;
    +0285 end
    +0286 if ~isempty(raw{2,colI(17)})
    +0287     task.comments=raw{2,colI(17)};
    +0288 end
    +0289 
    +0290 for i=2:size(raw,1)
    +0291     %Set the inputs
    +0292     if ischar(raw{i,colI(3)})
    +0293         inputs=splitList(raw{i,colI(3)});
    +0294         task.inputs=[task.inputs;inputs(:)];
    +0295         task.LBin=[task.LBin;ones(numel(inputs),1)*raw{i,colI(4)}];
    +0296         task.UBin=[task.UBin;ones(numel(inputs),1)*raw{i,colI(5)}];
    +0297     end
    +0298     %Set the outputs
    +0299     if ischar(raw{i,colI(6)})
    +0300         outputs=splitList(raw{i,colI(6)});
    +0301         task.outputs=[task.outputs;outputs(:)];
    +0302         task.LBout=[task.LBout;ones(numel(outputs),1)*raw{i,colI(7)}];
    +0303         task.UBout=[task.UBout;ones(numel(outputs),1)*raw{i,colI(8)}];
    +0304     end
    +0305     %Add new rxns
    +0306     if ischar(raw{i,colI(9)})
    +0307         task.equations=[task.equations;raw{i,colI(9)}];
    +0308         if ~isempty(raw{i,colI(10)})
    +0309             task.LBequ=[task.LBequ;raw{i,colI(10)}];
    +0310         else
    +0311             if any(strfind(raw{i,colI(9)},'<=>'))
    +0312                 task.LBequ=[task.LBequ;-1000];
    +0313             else
    +0314                 task.LBequ=[task.LBequ;0];
    +0315             end
    +0316         end
    +0317         if ~isempty(raw{i,colI(11)})
    +0318             task.UBequ=[task.UBequ;raw{i,colI(11)}];
    +0319         else
    +0320             task.UBequ=[task.UBequ;1000];
    +0321         end
    +0322     end
    +0323     %Add changed bounds
    +0324     if ischar(raw{i,colI(12)})
    +0325         changed=splitList(raw{i,colI(12)});
    +0326         task.changed=[task.changed;changed(:)];
    +0327         %An empty bound means "leave this bound as it is", stored as NaN
    +0328         task.LBrxn=[task.LBrxn;ones(numel(changed),1)*boundOrNaN(raw{i,colI(13)})];
    +0329         task.UBrxn=[task.UBrxn;ones(numel(changed),1)*boundOrNaN(raw{i,colI(14)})];
    +0330     end
    +0331     
    +0332     %Check if it should add more constraints
    +0333     if i<size(raw,1)
    +0334         if isempty(raw{i+1,colI(1)})
    +0335             continue;
    +0336         end
    +0337     end
    +0338     
    +0339     taskStruct=[taskStruct;task];
    +0340     task=eTask;
    +0341     if i<size(raw,1)
    +0342         if isnumeric(raw{i+1,colI(1)})
    +0343             task.id=num2str(raw{i+1,colI(1)});
    +0344         else
    +0345             task.id=raw{i+1,colI(1)};
    +0346         end
    +0347         task.description=raw{i+1,colI(2)};
    +0348         if isTruthy(raw{i+1,colI(15)})
    +0349             task.shouldFail=true;
    +0350         end
    +0351         if isTruthy(raw{i+1,colI(16)})
    +0352             task.printFluxes=true;
    +0353         end
    +0354         if ~isempty(raw{i+1,colI(17)})
    +0355             task.comments=raw{i+1,colI(17)};
    +0356         end
    +0357     end
    +0358 end
    +0359 
    +0360 %Should add more checks, such as unique IDs and missing headers
    +0361 
    +0362 end
    +0363 
    +0364 function tf = isTruthy(x)
    +0365 % A flag cell is true unless it is empty, 0, or one of the texts "0", "false"
    +0366 % and "no" (any case).
    +0367 if isempty(x)
    +0368     tf = false;
    +0369 elseif ischar(x) || isstring(x)
    +0370     tf = ~ismember(lower(strtrim(char(x))),{'','0','false','no'});
    +0371 elseif isnumeric(x) || islogical(x)
    +0372     tf = any(x(:) ~= 0);
    +0373 else
    +0374     tf = true;
    +0375 end
    +0376 end
    +0377 
    +0378 function items = splitList(str)
    +0379 % Split a ";"-delimited cell on ";", trim each item and drop empty ones, so
    +0380 % that "A; B;" gives {'A';'B'}.
    +0381 items = strtrim(regexp(str,';','split'));
    +0382 items = items(~cellfun(@isempty,items));
    +0383 end
    +0384 
    +0385 function b = boundOrNaN(x)
    +0386 % Bound for a changed reaction: NaN (leave unchanged) when the cell is empty.
    +0387 if isempty(x)
    +0388     b = NaN;
    +0389 else
    +0390     b = x;
    +0391 end
    +0392 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/up.png b/docs/html/up.png similarity index 100% rename from doc/up.png rename to docs/html/up.png diff --git a/doc/utils/convertCharArray.html b/docs/html/utils/convertCharArray.html similarity index 86% rename from doc/utils/convertCharArray.html rename to docs/html/utils/convertCharArray.html index 06ebe848..a60a2f5e 100644 --- a/doc/utils/convertCharArray.html +++ b/docs/html/utils/convertCharArray.html @@ -29,9 +29,9 @@

    SYNOPSIS ^DESCRIPTION ^

     convertCharArray  Ensure input is a cell array of character vectors.
     
    - Converts input to make sure it is a cell array of character vectors.
    - String arrays are transformed into character vectors. Output is always a
    - cell array, also if only one character vector is given as input.
    + Converts the input to a cell array of character vectors. String arrays
    + are transformed into character vectors. A single character vector gives
    + a cell array with one element. An empty input gives [].
     
      Parameters
      ----------
    @@ -41,7 +41,7 @@ 

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 </ul>
 This function is called by:
 <ul style= -
  • ravenList ravenList Format a header and item list for warning() or error().
  • +
  • ravenList ravenList Format a header and a list of items for warning() or error().
  • @@ -63,9 +63,9 @@

    SOURCE CODE ^
    0001 function inputConverted = convertCharArray(funcInput)
     0002 % convertCharArray  Ensure input is a cell array of character vectors.
     0003 %
    -0004 % Converts input to make sure it is a cell array of character vectors.
    -0005 % String arrays are transformed into character vectors. Output is always a
    -0006 % cell array, also if only one character vector is given as input.
    +0004 % Converts the input to a cell array of character vectors. String arrays
    +0005 % are transformed into character vectors. A single character vector gives
    +0006 % a cell array with one element. An empty input gives [].
     0007 %
     0008 % Parameters
     0009 % ----------
    @@ -75,7 +75,7 @@ 

    SOURCE CODE ^% Returns 0014 % ------- 0015 % inputConverted : cell -0016 % cell array of character vectors. +0016 % cell array of character vectors, or [] if funcInput is empty. 0017 % 0018 % Examples 0019 % -------- diff --git a/doc/utils/deprecationWarning.html b/docs/html/utils/deprecationWarning.html similarity index 89% rename from doc/utils/deprecationWarning.html rename to docs/html/utils/deprecationWarning.html index 16276a7a..beb0d492 100644 --- a/doc/utils/deprecationWarning.html +++ b/docs/html/utils/deprecationWarning.html @@ -30,16 +30,16 @@

    DESCRIPTION ^
     deprecationWarning  Warn once per session that a function is deprecated.
     
      Used by the wrappers in the deprecated/ folder. Each function warns the
    - first time it is called in a MATLAB session and stays quiet afterwards,
    - so a loop over thousands of reactions does not bury the console. Call
    - "clear functions" to reset.
    + first time it is called in a MATLAB session and does not warn again, also
    + when it is called repeatedly in a loop. "clear functions" resets the
    + record of functions that have warned.
     
      Parameters
      ----------
      oldFunction : char
          name of the deprecated function.
      replacement : char
    -     what to call instead, written as it would be typed, e.g.
    +     what to call instead, written as it would be typed, for example
          'findLeakMetabolite(model,''produce'',...)'.
     
      Examples
    @@ -63,16 +63,16 @@ 

    SOURCE CODE ^% deprecationWarning Warn once per session that a function is deprecated. 0003 % 0004 % Used by the wrappers in the deprecated/ folder. Each function warns the -0005 % first time it is called in a MATLAB session and stays quiet afterwards, -0006 % so a loop over thousands of reactions does not bury the console. Call -0007 % "clear functions" to reset. +0005 % first time it is called in a MATLAB session and does not warn again, also +0006 % when it is called repeatedly in a loop. "clear functions" resets the +0007 % record of functions that have warned. 0008 % 0009 % Parameters 0010 % ---------- 0011 % oldFunction : char 0012 % name of the deprecated function. 0013 % replacement : char -0014 % what to call instead, written as it would be typed, e.g. +0014 % what to call instead, written as it would be typed, for example 0015 % 'findLeakMetabolite(model,''produce'',...)'. 0016 % 0017 % Examples diff --git a/doc/utils/grRuleToDNF.html b/docs/html/utils/grRuleToDNF.html similarity index 53% rename from doc/utils/grRuleToDNF.html rename to docs/html/utils/grRuleToDNF.html index 61c0941e..ee1fd64e 100644 --- a/doc/utils/grRuleToDNF.html +++ b/docs/html/utils/grRuleToDNF.html @@ -36,7 +36,10 @@

    DESCRIPTION ^DESCRIPTION ^SOURCE CODE ^% Parameters 0009 % ---------- 0010 % rule : char or string or struct -0011 % a grRule, or a tree from parseGrRule. -0012 % -0013 % Returns -0014 % ------- -0015 % clauses : cell -0016 % one cell array of gene IDs per AND-clause. An empty rule yields {}. A -0017 % rule with no OR anywhere yields a single clause. -0018 % -0019 % Notes -0020 % ----- -0021 % Gene order within a clause follows the order the genes appear in the rule. -0022 % Duplicates are not removed: "G1 and G1" stays a two-element clause, since -0023 % collapsing it would silently rewrite the user's rule. -0024 % -0025 % The number of clauses is the product of the OR-arities across a complex, -0026 % so a pathological rule can expand combinatorially. Callers that build one -0027 % object per clause should check numel(clauses) first. -0028 % -0029 % Examples -0030 % -------- -0031 % grRuleToDNF('(G1 and G2) or G3') % {{'G1','G2'}, {'G3'}} -0032 % grRuleToDNF('G1 and (G2 or G3)') % {{'G1','G2'}, {'G1','G3'}} -0033 % -0034 % See also: parseGrRule, isDnfGrRule, expandModel -0035 -0036 if ~isstruct(rule) -0037 rule=parseGrRule(rule); -0038 end -0039 if isempty(rule) -0040 clauses={}; -0041 return -0042 end -0043 clauses=nodeToDNF(rule); -0044 end -0045 -0046 -0047 function clauses=nodeToDNF(node) -0048 switch node.type -0049 case 'gene' -0050 clauses={{node.id}}; -0051 case 'or' -0052 % OR: concatenate the disjuncts' clauses. -0053 clauses={}; -0054 for i=1:numel(node.children) -0055 clauses=[clauses nodeToDNF(node.children{i})]; %#ok<AGROW> -0056 end -0057 case 'and' -0058 % AND: cross-product of the children's clauses. -0059 clauses={{}}; -0060 for i=1:numel(node.children) -0061 childDNF=nodeToDNF(node.children{i}); -0062 newClauses=cell(1,numel(clauses)*numel(childDNF)); -0063 k=0; -0064 for a=1:numel(clauses) -0065 for b=1:numel(childDNF) -0066 k=k+1; -0067 newClauses{k}=[clauses{a} childDNF{b}]; -0068 end -0069 end -0070 clauses=newClauses; -0071 end -0072 otherwise -0073 error('RAVEN:badGrRule',['Unexpected grRule node type: ' node.type]); -0074 end -0075 end

    +0011 % a grRule, or a tree from parseGrRule. A grRule is read by parseGrRule: +0012 % genes joined by "and" or "or" in any case or by "&", "&&", "|", "||", +0013 % each operator separated from the gene ids by whitespace or a bracket, +0014 % with AND binding tighter than OR. +0015 % +0016 % Returns +0017 % ------- +0018 % clauses : cell +0019 % one cell array of gene IDs per AND-clause. An empty rule yields {}. A +0020 % rule with no OR anywhere yields a single clause. +0021 % +0022 % Notes +0023 % ----- +0024 % A malformed rule (an operator without an operand, unbalanced or empty +0025 % parentheses, two genes without an operator between them) raises +0026 % RAVEN:badGrRule. +0027 % +0028 % Gene order within a clause follows the order the genes appear in the rule. +0029 % Duplicates are not removed: "G1 and G1" stays a two-element clause, since +0030 % collapsing it would change the rule. +0031 % +0032 % The number of clauses is the product of the OR-arities across a complex, +0033 % so the number of clauses can grow combinatorially with the rule. A caller +0034 % that builds one object per clause can check numel(clauses) first. +0035 % +0036 % Examples +0037 % -------- +0038 % grRuleToDNF('(G1 and G2) or G3') % {{'G1','G2'}, {'G3'}} +0039 % grRuleToDNF('G1 and (G2 or G3)') % {{'G1','G2'}, {'G1','G3'}} +0040 % +0041 % See also: parseGrRule, isDnfGrRule, expandModel +0042 +0043 if ~isstruct(rule) +0044 rule=parseGrRule(rule); +0045 end +0046 if isempty(rule) +0047 clauses={}; +0048 return +0049 end +0050 clauses=nodeToDNF(rule); +0051 end +0052 +0053 +0054 function clauses=nodeToDNF(node) +0055 switch node.type +0056 case 'gene' +0057 clauses={{node.id}}; +0058 case 'or' +0059 % OR: concatenate the disjuncts' clauses. +0060 clauses={}; +0061 for i=1:numel(node.children) +0062 clauses=[clauses nodeToDNF(node.children{i})]; %#ok<AGROW> +0063 end +0064 case 'and' +0065 % AND: cross-product of the children's clauses. +0066 clauses={{}}; +0067 for i=1:numel(node.children) +0068 childDNF=nodeToDNF(node.children{i}); +0069 newClauses=cell(1,numel(clauses)*numel(childDNF)); +0070 k=0; +0071 for a=1:numel(clauses) +0072 for b=1:numel(childDNF) +0073 k=k+1; +0074 newClauses{k}=[clauses{a} childDNF{b}]; +0075 end +0076 end +0077 clauses=newClauses; +0078 end +0079 otherwise +0080 error('RAVEN:badGrRule',['Unexpected grRule node type: ' node.type]); +0081 end +0082 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/utils/grRuleToString.html b/docs/html/utils/grRuleToString.html similarity index 57% rename from doc/utils/grRuleToString.html rename to docs/html/utils/grRuleToString.html index 86e21a27..d0f1fa05 100644 --- a/doc/utils/grRuleToString.html +++ b/docs/html/utils/grRuleToString.html @@ -29,9 +29,10 @@

    SYNOPSIS ^DESCRIPTION ^

     grRuleToString  Render a grRule syntax tree back to a string.
     
    - Writes the standard RAVEN format: " and " / " or " as operators, with
    - parentheses added only where precedence requires them, so that
    - parseGrRule(grRuleToString(tree)) round-trips to the same structure.
    + Writes the standard RAVEN format, with " and " and " or " as operators. An
    + AND node inside an OR node and an OR node inside an AND node are placed in
    + parentheses. The result parses with parseGrRule to a rule with the same
    + logic.
     
      Parameters
      ----------
    @@ -67,60 +68,61 @@ 

    SOURCE CODE ^
    0001 function rule=grRuleToString(tree)
     0002 % grRuleToString  Render a grRule syntax tree back to a string.
     0003 %
    -0004 % Writes the standard RAVEN format: " and " / " or " as operators, with
    -0005 % parentheses added only where precedence requires them, so that
    -0006 % parseGrRule(grRuleToString(tree)) round-trips to the same structure.
    -0007 %
    -0008 % Parameters
    -0009 % ----------
    -0010 % tree : struct
    -0011 %     a tree from parseGrRule. An empty tree yields ''.
    -0012 %
    -0013 % Returns
    -0014 % -------
    -0015 % rule : char
    -0016 %     the rendered grRule.
    -0017 %
    -0018 % Examples
    -0019 % --------
    -0020 %     grRuleToString(parseGrRule('(G1 and G2) or G3'))   % '(G1 and G2) or G3'
    -0021 %
    -0022 % See also: parseGrRule, standardizeGrRules
    -0023 
    -0024 if isempty(tree)
    -0025     rule='';
    -0026     return
    -0027 end
    -0028 rule=nodeToString(tree);
    -0029 end
    -0030 
    +0004 % Writes the standard RAVEN format, with " and " and " or " as operators. An
    +0005 % AND node inside an OR node and an OR node inside an AND node are placed in
    +0006 % parentheses. The result parses with parseGrRule to a rule with the same
    +0007 % logic.
    +0008 %
    +0009 % Parameters
    +0010 % ----------
    +0011 % tree : struct
    +0012 %     a tree from parseGrRule. An empty tree yields ''.
    +0013 %
    +0014 % Returns
    +0015 % -------
    +0016 % rule : char
    +0017 %     the rendered grRule.
    +0018 %
    +0019 % Examples
    +0020 % --------
    +0021 %     grRuleToString(parseGrRule('(G1 and G2) or G3'))   % '(G1 and G2) or G3'
    +0022 %
    +0023 % See also: parseGrRule, standardizeGrRules
    +0024 
    +0025 if isempty(tree)
    +0026     rule='';
    +0027     return
    +0028 end
    +0029 rule=nodeToString(tree);
    +0030 end
     0031 
    -0032 function str=nodeToString(node)
    -0033 switch node.type
    -0034     case 'gene'
    -0035         str=node.id;
    -0036     case 'and'
    -0037         % An OR child inside an AND must be bracketed to keep precedence.
    -0038         parts=cellfun(@(c) bracketIf(c,'or'),node.children,'UniformOutput',false);
    -0039         str=strjoin(parts,' and ');
    -0040     case 'or'
    -0041         % An AND child inside an OR needs no brackets for correctness, but
    -0042         % RAVEN writes complexes bracketed, matching standardizeGrRules and
    -0043         % the joinOR helper in removeLowScoreGenes.
    -0044         parts=cellfun(@(c) bracketIf(c,'and'),node.children,'UniformOutput',false);
    -0045         str=strjoin(parts,' or ');
    -0046     otherwise
    -0047         error('RAVEN:badGrRule',['Unexpected grRule node type: ' node.type]);
    -0048 end
    +0032 
    +0033 function str=nodeToString(node)
    +0034 switch node.type
    +0035     case 'gene'
    +0036         str=node.id;
    +0037     case 'and'
    +0038         % An OR child inside an AND must be bracketed to keep precedence.
    +0039         parts=cellfun(@(c) bracketIf(c,'or'),node.children,'UniformOutput',false);
    +0040         str=strjoin(parts,' and ');
    +0041     case 'or'
    +0042         % An AND child inside an OR needs no brackets for correctness, but
    +0043         % RAVEN writes complexes bracketed, matching standardizeGrRules and
    +0044         % the joinOR helper in removeLowScoreGenes.
    +0045         parts=cellfun(@(c) bracketIf(c,'and'),node.children,'UniformOutput',false);
    +0046         str=strjoin(parts,' or ');
    +0047     otherwise
    +0048         error('RAVEN:badGrRule',['Unexpected grRule node type: ' node.type]);
     0049 end
    -0050 
    +0050 end
     0051 
    -0052 function str=bracketIf(node,type)
    -0053 str=nodeToString(node);
    -0054 if strcmp(node.type,type)
    -0055     str=['(' str ')'];
    -0056 end
    -0057 end

    +0052 +0053 function str=bracketIf(node,type) +0054 str=nodeToString(node); +0055 if strcmp(node.type,type) +0056 str=['(' str ')']; +0057 end +0058 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/utils/index.html b/docs/html/utils/index.html similarity index 55% rename from doc/utils/index.html rename to docs/html/utils/index.html index f4b0b191..2cb8129a 100644 --- a/doc/utils/index.html +++ b/docs/html/utils/index.html @@ -19,7 +19,7 @@

    Index for utils

    Matlab files in this directory:

    -
     convertCharArrayconvertCharArray Ensure input is a cell array of character vectors.
     deprecationWarningdeprecationWarning Warn once per session that a function is deprecated.
     grRuleToDNFgrRuleToDNF Convert a grRule to disjunctive normal form.
     grRuleToStringgrRuleToString Render a grRule syntax tree back to a string.
     isDnfGrRuleisDnfGrRule Test whether a grRule is in disjunctive normal form.
     legacyMethodNoticelegacyMethodNotice Point a caller at the recommended method, once.
     parallelWorkersRAVENparallelWorkersRAVEN Return the parfor worker count for a given setting.
     parseGrRuleparseGrRule Parse a grRule into a syntax tree.
     parseRAVENargsparseRAVENargs Resolve optional arguments passed positionally or by name.
     printOrangeprintOrange Print an orange-coloured string to the MATLAB Command Window.
     progressReport
     ravenListravenList Format a header and item list for warning() or error().
     ravenModelFieldsravenModelFields Declarative registry of all RAVEN model struct fields.
     runRAVENtestsrunRAVENtests Run all RAVEN function tests.
     setRavenProgresssetRavenProgress Set the RAVEN progress-reporting backend.
    + convertCharArrayconvertCharArray Ensure input is a cell array of character vectors.  deprecationWarningdeprecationWarning Warn once per session that a function is deprecated.  grRuleToDNFgrRuleToDNF Convert a grRule to disjunctive normal form.  grRuleToStringgrRuleToString Render a grRule syntax tree back to a string.  isDnfGrRuleisDnfGrRule Test whether a grRule is in disjunctive normal form.  legacyMethodNoticelegacyMethodNotice Warn once that a function has a recommended replacement.  parallelWorkersRAVENparallelWorkersRAVEN Return the parfor worker count for a given setting.  parseGrRuleparseGrRule Parse a grRule into a syntax tree.  parseRAVENargsparseRAVENargs Resolve optional arguments passed positionally or by name.  printOrangeprintOrange Print an orange-coloured string to the MATLAB Command Window.  progressReport  ravenListravenList Format a header and a list of items for warning() or error().  ravenModelFieldsravenModelFields Declarative registry of all RAVEN model struct fields.  runRAVENtestsrunRAVENtests Run all RAVEN function tests.  setRavenProgresssetRavenProgress Set the RAVEN progress-reporting backend. diff --git a/doc/utils/isDnfGrRule.html b/docs/html/utils/isDnfGrRule.html similarity index 88% rename from doc/utils/isDnfGrRule.html rename to docs/html/utils/isDnfGrRule.html index 823f918a..b7bee886 100644 --- a/doc/utils/isDnfGrRule.html +++ b/docs/html/utils/isDnfGrRule.html @@ -29,11 +29,11 @@

    SYNOPSIS ^DESCRIPTION ^

     isDnfGrRule  Test whether a grRule is in disjunctive normal form.
     
    - DNF ("OR of AND-complexes") means no AND operator has an OR anywhere
    - beneath it, i.e. the rule is a single gene, a pure AND-complex, or an OR
    - of those. Standard RAVEN format requires DNF: subunits that must all be
    - present are ANDed into a complex, and the reaction is catalysed by at
    - least one such complex.
    + A rule in disjunctive normal form (DNF, an OR of AND-complexes) has no OR
    + operator beneath an AND operator. It is a single gene, an AND-complex, or an
    + OR of genes and AND-complexes. The standard RAVEN format requires DNF: the
    + subunits that must all be present are joined by AND into a complex, and the
    + reaction is catalysed by at least one such complex.
     
      Parameters
      ----------
    @@ -70,11 +70,11 @@ 

    SOURCE CODE ^
    0001 function tf=isDnfGrRule(rule)
     0002 % isDnfGrRule  Test whether a grRule is in disjunctive normal form.
     0003 %
    -0004 % DNF ("OR of AND-complexes") means no AND operator has an OR anywhere
    -0005 % beneath it, i.e. the rule is a single gene, a pure AND-complex, or an OR
    -0006 % of those. Standard RAVEN format requires DNF: subunits that must all be
    -0007 % present are ANDed into a complex, and the reaction is catalysed by at
    -0008 % least one such complex.
    +0004 % A rule in disjunctive normal form (DNF, an OR of AND-complexes) has no OR
    +0005 % operator beneath an AND operator. It is a single gene, an AND-complex, or an
    +0006 % OR of genes and AND-complexes. The standard RAVEN format requires DNF: the
    +0007 % subunits that must all be present are joined by AND into a complex, and the
    +0008 % reaction is catalysed by at least one such complex.
     0009 %
     0010 % Parameters
     0011 % ----------
    diff --git a/docs/html/utils/legacyMethodNotice.html b/docs/html/utils/legacyMethodNotice.html
    new file mode 100644
    index 00000000..5aabd841
    --- /dev/null
    +++ b/docs/html/utils/legacyMethodNotice.html
    @@ -0,0 +1,117 @@
    +
    +
    +
    +  Description of legacyMethodNotice
    +  
    +  
    +  
    +  
    +  
    +  
    +
    +
    +
    +
    Home > utils > legacyMethodNotice.m
    + + + +

    legacyMethodNotice +

    + +

    PURPOSE ^

    +
    legacyMethodNotice Warn once that a function has a recommended replacement.
    + +

    SYNOPSIS ^

    +
    function legacyMethodNotice(oldFunction, preferred)
    + +

    DESCRIPTION ^

    +
     legacyMethodNotice  Warn once that a function has a recommended replacement.
    +
    + For functions that remain supported and maintained but have a recommended
    + replacement for new work. Unlike deprecationWarning, the notice announces no
    + removal.
    +
    + The warning RAVEN:legacyMethod is issued at the first call of each
    + oldFunction in a MATLAB session; later calls with the same oldFunction issue
    + nothing. "clear functions" resets this. warning('off','RAVEN:legacyMethod')
    + silences the notice.
    +
    + Parameters
    + ----------
    + oldFunction : char
    +     name of the function being called. The notice is issued once per name.
    + preferred : char
    +     name of the recommended replacement for new work, written as in the
    +     warning text.
    +
    + Examples
    + --------
    +     legacyMethodNotice('getINITModel','ftINIT');
    +
    + See also
    + --------
    + deprecationWarning
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function legacyMethodNotice(oldFunction, preferred)
    +0002 % legacyMethodNotice  Warn once that a function has a recommended replacement.
    +0003 %
    +0004 % For functions that remain supported and maintained but have a recommended
    +0005 % replacement for new work. Unlike deprecationWarning, the notice announces no
    +0006 % removal.
    +0007 %
    +0008 % The warning RAVEN:legacyMethod is issued at the first call of each
    +0009 % oldFunction in a MATLAB session; later calls with the same oldFunction issue
    +0010 % nothing. "clear functions" resets this. warning('off','RAVEN:legacyMethod')
    +0011 % silences the notice.
    +0012 %
    +0013 % Parameters
    +0014 % ----------
    +0015 % oldFunction : char
    +0016 %     name of the function being called. The notice is issued once per name.
    +0017 % preferred : char
    +0018 %     name of the recommended replacement for new work, written as in the
    +0019 %     warning text.
    +0020 %
    +0021 % Examples
    +0022 % --------
    +0023 %     legacyMethodNotice('getINITModel','ftINIT');
    +0024 %
    +0025 % See also
    +0026 % --------
    +0027 % deprecationWarning
    +0028 
    +0029 persistent alreadyNoticed
    +0030 
    +0031 if isempty(alreadyNoticed)
    +0032     alreadyNoticed=containers.Map('KeyType','char','ValueType','logical');
    +0033 end
    +0034 
    +0035 oldFunction=char(oldFunction);
    +0036 if isKey(alreadyNoticed,oldFunction)
    +0037     return
    +0038 end
    +0039 alreadyNoticed(oldFunction)=true;
    +0040 
    +0041 warning('RAVEN:legacyMethod', ...
    +0042     ['%s is the legacy implementation and remains supported, but %s is the ' ...
    +0043      'recommended method for new work. Silence this with ' ...
    +0044      'warning(''off'',''RAVEN:legacyMethod'').'], oldFunction, char(preferred));
    +0045 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/utils/parallelWorkersRAVEN.html b/docs/html/utils/parallelWorkersRAVEN.html similarity index 54% rename from doc/utils/parallelWorkersRAVEN.html rename to docs/html/utils/parallelWorkersRAVEN.html index 1eb425ac..f5b2cb78 100644 --- a/doc/utils/parallelWorkersRAVEN.html +++ b/docs/html/utils/parallelWorkersRAVEN.html @@ -29,29 +29,31 @@

    SYNOPSIS ^DESCRIPTION ^

     parallelWorkersRAVEN  Return the parfor worker count for a given setting.
     
    - Returns a value for use with the parfor worker-count syntax:
    + Returns the value for the maximum-workers argument of parfor:
     
          nW = parallelWorkersRAVEN(runParallel);
          parfor (i = 1:N, nW)
              ...
          end
     
    - This avoids mutating global parallel-pool state: nW == 0 forces parfor
    - to run as a plain for loop; nW == Inf lets the Parallel Computing Toolbox
    - manage the pool size and auto-creation.
    + The function leaves the global parallel pool unchanged. With nW == 0, parfor
    + runs as a plain for loop. With nW == Inf, the Parallel Computing Toolbox
    + manages the pool size and creates a pool when none exists. When
    + runParallel is true but the Parallel Computing Toolbox is not installed, the
    + function prints a message and returns 0.
     
      Parameters
      ----------
      runParallel : logical
    -     whether the caller should use parallel workers.
    +     true to use parallel workers, false for sequential execution.
     
      Returns
      -------
      nW : double
    -     0   to force sequential execution (parfor behaves as a for loop,
    -         no pool required or created).
    -     Inf to use available pool workers (PCT manages pool size and
    -         auto-creation).
    + 0 sequential execution (parfor behaves as a for loop, no pool is + required or created). + Inf available pool workers (the Parallel Computing Toolbox manages + the pool size and creates a pool when none exists).

    CROSS-REFERENCE INFORMATION ^

    @@ -69,41 +71,43 @@

    SOURCE CODE ^
    0001 function nW = parallelWorkersRAVEN(runParallel)
     0002 % parallelWorkersRAVEN  Return the parfor worker count for a given setting.
     0003 %
    -0004 % Returns a value for use with the parfor worker-count syntax:
    +0004 % Returns the value for the maximum-workers argument of parfor:
     0005 %
     0006 %     nW = parallelWorkersRAVEN(runParallel);
     0007 %     parfor (i = 1:N, nW)
     0008 %         ...
     0009 %     end
     0010 %
    -0011 % This avoids mutating global parallel-pool state: nW == 0 forces parfor
    -0012 % to run as a plain for loop; nW == Inf lets the Parallel Computing Toolbox
    -0013 % manage the pool size and auto-creation.
    -0014 %
    -0015 % Parameters
    -0016 % ----------
    -0017 % runParallel : logical
    -0018 %     whether the caller should use parallel workers.
    -0019 %
    -0020 % Returns
    -0021 % -------
    -0022 % nW : double
    -0023 %     0   to force sequential execution (parfor behaves as a for loop,
    -0024 %         no pool required or created).
    -0025 %     Inf to use available pool workers (PCT manages pool size and
    -0026 %         auto-creation).
    -0027 
    -0028 if ~runParallel
    -0029     nW = 0;
    -0030     return
    -0031 end
    -0032 if isempty(ver('parallel'))
    -0033     disp('Cannot find MATLAB Parallel Computing Toolbox, process is not parallelized.')
    -0034     nW = 0;
    -0035 else
    -0036     nW = Inf;
    -0037 end
    -0038 end

    +0011 % The function leaves the global parallel pool unchanged. With nW == 0, parfor +0012 % runs as a plain for loop. With nW == Inf, the Parallel Computing Toolbox +0013 % manages the pool size and creates a pool when none exists. When +0014 % runParallel is true but the Parallel Computing Toolbox is not installed, the +0015 % function prints a message and returns 0. +0016 % +0017 % Parameters +0018 % ---------- +0019 % runParallel : logical +0020 % true to use parallel workers, false for sequential execution. +0021 % +0022 % Returns +0023 % ------- +0024 % nW : double +0025 % 0 sequential execution (parfor behaves as a for loop, no pool is +0026 % required or created). +0027 % Inf available pool workers (the Parallel Computing Toolbox manages +0028 % the pool size and creates a pool when none exists). +0029 +0030 if ~runParallel +0031 nW = 0; +0032 return +0033 end +0034 if isempty(ver('parallel')) +0035 disp('Cannot find MATLAB Parallel Computing Toolbox, process is not parallelized.') +0036 nW = 0; +0037 else +0038 nW = Inf; +0039 end +0040 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/utils/parseGrRule.html b/docs/html/utils/parseGrRule.html new file mode 100644 index 00000000..49402413 --- /dev/null +++ b/docs/html/utils/parseGrRule.html @@ -0,0 +1,265 @@ + + + + Description of parseGrRule + + + + + + + + + +
    Home > utils > parseGrRule.m
    + + + +

    parseGrRule +

    + +

    PURPOSE ^

    +
    parseGrRule Parse a grRule into a syntax tree.
    + +

    SYNOPSIS ^

    +
    function tree=parseGrRule(rule)
    + +

    DESCRIPTION ^

    +
     parseGrRule  Parse a grRule into a syntax tree.
    +
    + Parses a gene-protein-reaction rule into a tree of AND, OR and gene nodes.
    + Gene IDs are matched as whole tokens, so IDs that contain "and" or "or" as a
    + substring (RAND1), or that are prefixes of one another (10 and 100), are
    + distinct from operators and from each other.
    +
    + Parameters
    + ----------
    + rule : char or string
    +     a grRule, for example "(G1 and G2) or G3". Both the word forms ("and", "or",
    +     any case) and the symbol forms ("&", "&&", "|", "||") are accepted.
    +     An empty rule yields an empty tree.
    +
    + Returns
    + -------
    + tree : struct
    +     a scalar struct with fields:
    +
    +     - type     : char, 'gene', 'and' or 'or'.
    +     - id       : char, the gene ID; '' for 'and' and 'or' nodes.
    +     - children : cell, the child nodes; {} for 'gene' nodes.
    +
    +     An empty rule returns a 0x0 struct array, for which isempty is true.
    +
    + Notes
    + -----
    + AND binds tighter than OR. The grammar is:
    +
    +     expr   := term ( OR term )*
    +     term   := factor ( AND factor )*
    +     factor := gene | '(' expr ')'
    +
    + Malformed rules raise the error RAVEN:badGrRule. findPotentialErrors catches
    + this error and reports the rule.
    +
    + Examples
    + --------
    +     tree = parseGrRule('(G1 and G2) or G3');
    +     tree.type            % 'or'
    +     numel(tree.children) % 2
    +
    + See also: isDnfGrRule, grRuleToDNF, grRuleToString
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    • grRuleToDNF grRuleToDNF Convert a grRule to disjunctive normal form.
    • isDnfGrRule isDnfGrRule Test whether a grRule is in disjunctive normal form.
    + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function tree=parseGrRule(rule)
    +0002 % parseGrRule  Parse a grRule into a syntax tree.
    +0003 %
    +0004 % Parses a gene-protein-reaction rule into a tree of AND, OR and gene nodes.
    +0005 % Gene IDs are matched as whole tokens, so IDs that contain "and" or "or" as a
    +0006 % substring (RAND1), or that are prefixes of one another (10 and 100), are
    +0007 % distinct from operators and from each other.
    +0008 %
    +0009 % Parameters
    +0010 % ----------
    +0011 % rule : char or string
    +0012 %     a grRule, for example "(G1 and G2) or G3". Both the word forms ("and", "or",
    +0013 %     any case) and the symbol forms ("&", "&&", "|", "||") are accepted.
    +0014 %     An empty rule yields an empty tree.
    +0015 %
    +0016 % Returns
    +0017 % -------
    +0018 % tree : struct
    +0019 %     a scalar struct with fields:
    +0020 %
    +0021 %     - type     : char, 'gene', 'and' or 'or'.
    +0022 %     - id       : char, the gene ID; '' for 'and' and 'or' nodes.
    +0023 %     - children : cell, the child nodes; {} for 'gene' nodes.
    +0024 %
    +0025 %     An empty rule returns a 0x0 struct array, for which isempty is true.
    +0026 %
    +0027 % Notes
    +0028 % -----
    +0029 % AND binds tighter than OR. The grammar is:
    +0030 %
    +0031 %     expr   := term ( OR term )*
    +0032 %     term   := factor ( AND factor )*
    +0033 %     factor := gene | '(' expr ')'
    +0034 %
    +0035 % Malformed rules raise the error RAVEN:badGrRule. findPotentialErrors catches
    +0036 % this error and reports the rule.
    +0037 %
    +0038 % Examples
    +0039 % --------
    +0040 %     tree = parseGrRule('(G1 and G2) or G3');
    +0041 %     tree.type            % 'or'
    +0042 %     numel(tree.children) % 2
    +0043 %
    +0044 % See also: isDnfGrRule, grRuleToDNF, grRuleToString
    +0045 
    +0046 if nargin<1 || isempty(rule)
    +0047     tree=emptyTree();
    +0048     return
    +0049 end
    +0050 rule=char(rule);
    +0051 if all(isspace(rule))
    +0052     tree=emptyTree();
    +0053     return
    +0054 end
    +0055 
    +0056 toks=tokenizeGrRule(rule);
    +0057 if isempty(toks)
    +0058     tree=emptyTree();
    +0059     return
    +0060 end
    +0061 
    +0062 [tree,pos]=parseExpr(toks,1,rule);
    +0063 if pos<=numel(toks)
    +0064     error('RAVEN:badGrRule',['Unexpected "' tokenText(toks{pos}) '" in grRule: ' rule]);
    +0065 end
    +0066 end
    +0067 
    +0068 
    +0069 function tree=emptyTree()
    +0070 tree=struct('type',{},'id',{},'children',{});
    +0071 end
    +0072 
    +0073 
    +0074 function toks=tokenizeGrRule(rule)
    +0075 % Split into '(' , ')' , 'AND' , 'OR' and gene tokens. Operators are matched
    +0076 % as whole words, which is what keeps gene IDs containing "and"/"or" intact.
    +0077 toks={};
    +0078 i=1;
    +0079 n=numel(rule);
    +0080 while i<=n
    +0081     c=rule(i);
    +0082     if isspace(c)
    +0083         i=i+1;
    +0084         continue
    +0085     end
    +0086     if c=='(' || c==')'
    +0087         toks{end+1}=c; %#ok<AGROW>
    +0088         i=i+1;
    +0089         continue
    +0090     end
    +0091     % Read a word: everything up to whitespace or a bracket.
    +0092     j=i;
    +0093     while j<=n && ~isspace(rule(j)) && rule(j)~='(' && rule(j)~=')'
    +0094         j=j+1;
    +0095     end
    +0096     word=rule(i:j-1);
    +0097     switch lower(word)
    +0098         case {'and','&','&&'}
    +0099             toks{end+1}='AND'; %#ok<AGROW>
    +0100         case {'or','|','||'}
    +0101             toks{end+1}='OR'; %#ok<AGROW>
    +0102         otherwise
    +0103             toks{end+1}=struct('gene',word); %#ok<AGROW>
    +0104     end
    +0105     i=j;
    +0106 end
    +0107 end
    +0108 
    +0109 
    +0110 function [node,pos]=parseExpr(toks,pos,rule)
    +0111 % expr := term ( OR term )*
    +0112 [node,pos]=parseTerm(toks,pos,rule);
    +0113 if pos>numel(toks) || ~isOp(toks{pos},'OR')
    +0114     return
    +0115 end
    +0116 children={node};
    +0117 while pos<=numel(toks) && isOp(toks{pos},'OR')
    +0118     [child,pos]=parseTerm(toks,pos+1,rule);
    +0119     children{end+1}=child; %#ok<AGROW>
    +0120 end
    +0121 node=struct('type','or','id','','children',{children});
    +0122 end
    +0123 
    +0124 
    +0125 function [node,pos]=parseTerm(toks,pos,rule)
    +0126 % term := factor ( AND factor )*
    +0127 [node,pos]=parseFactor(toks,pos,rule);
    +0128 if pos>numel(toks) || ~isOp(toks{pos},'AND')
    +0129     return
    +0130 end
    +0131 children={node};
    +0132 while pos<=numel(toks) && isOp(toks{pos},'AND')
    +0133     [child,pos]=parseFactor(toks,pos+1,rule);
    +0134     children{end+1}=child; %#ok<AGROW>
    +0135 end
    +0136 node=struct('type','and','id','','children',{children});
    +0137 end
    +0138 
    +0139 
    +0140 function [node,pos]=parseFactor(toks,pos,rule)
    +0141 % factor := gene | '(' expr ')'
    +0142 if pos>numel(toks)
    +0143     error('RAVEN:badGrRule',['grRule ends after an operator: ' rule]);
    +0144 end
    +0145 tok=toks{pos};
    +0146 if isOp(tok,'(')
    +0147     [node,pos]=parseExpr(toks,pos+1,rule);
    +0148     if pos>numel(toks) || ~isOp(toks{pos},')')
    +0149         error('RAVEN:badGrRule',['Unbalanced parentheses in grRule: ' rule]);
    +0150     end
    +0151     pos=pos+1;
    +0152     return
    +0153 end
    +0154 if isstruct(tok)
    +0155     node=struct('type','gene','id',tok.gene,'children',{{}});
    +0156     pos=pos+1;
    +0157     return
    +0158 end
    +0159 error('RAVEN:badGrRule',['Expected a gene but found "' tokenText(tok) '" in grRule: ' rule]);
    +0160 end
    +0161 
    +0162 
    +0163 function tf=isOp(tok,op)
    +0164 tf=ischar(tok) && strcmp(tok,op);
    +0165 end
    +0166 
    +0167 
    +0168 function txt=tokenText(tok)
    +0169 if isstruct(tok)
    +0170     txt=tok.gene;
    +0171 else
    +0172     txt=tok;
    +0173 end
    +0174 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/docs/html/utils/parseRAVENargs.html b/docs/html/utils/parseRAVENargs.html new file mode 100644 index 00000000..70ae9426 --- /dev/null +++ b/docs/html/utils/parseRAVENargs.html @@ -0,0 +1,251 @@ + + + + Description of parseRAVENargs + + + + + + + + + +
    Home > utils > parseRAVENargs.m
    + + + +

    parseRAVENargs +

    + +

    PURPOSE ^

    +
    parseRAVENargs Resolve optional arguments passed positionally or by name.
    + +

    SYNOPSIS ^

    +
    function args = parseRAVENargs(rawArgs, spec)
    + +

    DESCRIPTION ^

    +
     parseRAVENargs  Resolve optional arguments passed positionally or by name.
    +
    + Helper for RAVEN functions that accept optional arguments in positional
    + order, as name-value pairs, or as leading positional values followed by
    + name-value pairs. The required leading arguments are explicit in the function
    + signature. The optional arguments are collected in varargin and passed to
    + this function together with a specification of the optional parameters.
    +
    + Parameters
    + ----------
    + rawArgs : cell
    +     the varargin of the function, holding the arguments supplied after the
    +     required positional arguments.
    + spec : cell
    +     an N-by-2 or N-by-3 cell array describing the optional parameters, one
    +     per row:
    +
    +     - column 1 : parameter name (char).
    +     - column 2 : default value, used when the parameter is not supplied.
    +     - column 3 : optional validator, a function handle that is called with
    +       the resolved value (the default when the parameter was not supplied)
    +       and should error on an invalid value, for example @mustBeNumeric. [] means no
    +       validation.
    +
    + Returns
    + -------
    + args : struct
    +     a struct with one field per parameter name, holding the resolved value.
    +
    + Notes
    + -----
    + The calling convention is resolved in these steps:
    +
    +   1. If rawArgs is empty, all parameters take their defaults.
    +   2. The first element in rawArgs that is a string matching a parameter
    +      name (case-insensitive) marks position k. The elements before k are
    +      positional and are assigned in the order of spec. The elements from k
    +      onward must form valid name-value pairs: an even count in which every
    +      name-position element is a parameter name.
    +   3. If no element is a matching string, or the elements from k do not form
    +      valid name-value pairs, all of rawArgs is positional.
    +
    + Three styles are accepted:
    +
    +     f(model, v1, v2)                   % purely positional
    +     f(model, "p1", v1, "p2", v2)       % purely named
    +     f(model, v1, "p2", v2, "p3", v3)   % hybrid: v1 positional, rest named
    +
    + A positional value that is a string equal to a parameter name is read as a
    + parameter name only when the arguments from there on form valid name-value
    + pairs; otherwise all arguments are positional. The name-value form passes
    + such a value unambiguously.
    +
    + Examples
    + --------
    +     % inside exportToExcelFormat(model, varargin):
    +     p = parseRAVENargs(varargin, { ...
    +         "fileName", []; ...
    +         "sortIds",  false});
    +     fileName = p.fileName;
    +     sortIds  = p.sortIds;
    +
    +     % the caller can use any of these:
    +     exportToExcelFormat(model, "model.xlsx", true)              % positional
    +     exportToExcelFormat(model, "sortIds", true)                 % named
    +     exportToExcelFormat(model, "model.xlsx", "sortIds", true)   % hybrid
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: +
      +
    + + + + +

    SOURCE CODE ^

    +
    0001 function args = parseRAVENargs(rawArgs, spec)
    +0002 % parseRAVENargs  Resolve optional arguments passed positionally or by name.
    +0003 %
    +0004 % Helper for RAVEN functions that accept optional arguments in positional
    +0005 % order, as name-value pairs, or as leading positional values followed by
    +0006 % name-value pairs. The required leading arguments are explicit in the function
    +0007 % signature. The optional arguments are collected in varargin and passed to
    +0008 % this function together with a specification of the optional parameters.
    +0009 %
    +0010 % Parameters
    +0011 % ----------
    +0012 % rawArgs : cell
    +0013 %     the varargin of the function, holding the arguments supplied after the
    +0014 %     required positional arguments.
    +0015 % spec : cell
    +0016 %     an N-by-2 or N-by-3 cell array describing the optional parameters, one
    +0017 %     per row:
    +0018 %
    +0019 %     - column 1 : parameter name (char).
    +0020 %     - column 2 : default value, used when the parameter is not supplied.
    +0021 %     - column 3 : optional validator, a function handle that is called with
    +0022 %       the resolved value (the default when the parameter was not supplied)
    +0023 %       and should error on an invalid value, for example @mustBeNumeric. [] means no
    +0024 %       validation.
    +0025 %
    +0026 % Returns
    +0027 % -------
    +0028 % args : struct
    +0029 %     a struct with one field per parameter name, holding the resolved value.
    +0030 %
    +0031 % Notes
    +0032 % -----
    +0033 % The calling convention is resolved in these steps:
    +0034 %
    +0035 %   1. If rawArgs is empty, all parameters take their defaults.
    +0036 %   2. The first element in rawArgs that is a string matching a parameter
    +0037 %      name (case-insensitive) marks position k. The elements before k are
    +0038 %      positional and are assigned in the order of spec. The elements from k
    +0039 %      onward must form valid name-value pairs: an even count in which every
    +0040 %      name-position element is a parameter name.
    +0041 %   3. If no element is a matching string, or the elements from k do not form
    +0042 %      valid name-value pairs, all of rawArgs is positional.
    +0043 %
    +0044 % Three styles are accepted:
    +0045 %
    +0046 %     f(model, v1, v2)                   % purely positional
    +0047 %     f(model, "p1", v1, "p2", v2)       % purely named
    +0048 %     f(model, v1, "p2", v2, "p3", v3)   % hybrid: v1 positional, rest named
    +0049 %
    +0050 % A positional value that is a string equal to a parameter name is read as a
    +0051 % parameter name only when the arguments from there on form valid name-value
    +0052 % pairs; otherwise all arguments are positional. The name-value form passes
    +0053 % such a value unambiguously.
    +0054 %
    +0055 % Examples
    +0056 % --------
    +0057 %     % inside exportToExcelFormat(model, varargin):
    +0058 %     p = parseRAVENargs(varargin, { ...
    +0059 %         "fileName", []; ...
    +0060 %         "sortIds",  false});
    +0061 %     fileName = p.fileName;
    +0062 %     sortIds  = p.sortIds;
    +0063 %
    +0064 %     % the caller can use any of these:
    +0065 %     exportToExcelFormat(model, "model.xlsx", true)              % positional
    +0066 %     exportToExcelFormat(model, "sortIds", true)                 % named
    +0067 %     exportToExcelFormat(model, "model.xlsx", "sortIds", true)   % hybrid
    +0068 
    +0069 if nargin < 2 || isempty(spec)
    +0070     error('parseRAVENargs:noSpec', 'A parameter specification is required.');
    +0071 end
    +0072 
    +0073 names = spec(:, 1);
    +0074 defaults = spec(:, 2);
    +0075 hasValidators = size(spec, 2) >= 3;
    +0076 
    +0077 % Seed every parameter with its default.
    +0078 args = struct();
    +0079 for i = 1:numel(names)
    +0080     args.(names{i}) = defaults{i};
    +0081 end
    +0082 
    +0083 if ~isempty(rawArgs)
    +0084     % Find which elements are strings matching a known parameter name.
    +0085     isParamName = cellfun(@(x) (ischar(x) || isstring(x)) && isscalar(string(x)) && ...
    +0086                           any(strcmpi(x, names)), rawArgs);
    +0087 
    +0088     % Position of the first parameter-name string — where name-value begins.
    +0089     nvStart = find(isParamName, 1);
    +0090 
    +0091     nvOK = false;
    +0092     if ~isempty(nvStart)
    +0093         nv = rawArgs(nvStart:end);
    +0094         % Valid name-value section: even count, all name-position elements
    +0095         % are known parameter names.
    +0096         nvOK = mod(numel(nv), 2) == 0 && ...
    +0097                all(cellfun(@(x) (ischar(x) || isstring(x)) && isscalar(string(x)) && ...
    +0098                    any(strcmpi(x, names)), nv(1:2:end)));
    +0099     end
    +0100 
    +0101     if ~isempty(nvStart) && nvOK
    +0102         % Assign leading positional arguments (may be zero when nvStart == 1).
    +0103         nPos = nvStart - 1;
    +0104         if nPos > numel(names)
    +0105             error('parseRAVENargs:tooManyArgs', ...
    +0106                 'Too many positional arguments: expected at most %d, got %d.', ...
    +0107                 numel(names), nPos);
    +0108         end
    +0109         for k = 1:nPos
    +0110             args.(names{k}) = rawArgs{k};
    +0111         end
    +0112         % Assign name-value pairs.
    +0113         nv = rawArgs(nvStart:end);
    +0114         for k = 1:2:numel(nv)
    +0115             args.(names{strcmpi(nv{k}, names)}) = nv{k + 1};
    +0116         end
    +0117     else
    +0118         % Purely positional.
    +0119         if numel(rawArgs) > numel(names)
    +0120             error('parseRAVENargs:tooManyArgs', ...
    +0121                 'Too many positional arguments: expected at most %d, got %d.', ...
    +0122                 numel(names), numel(rawArgs));
    +0123         end
    +0124         for k = 1:numel(rawArgs)
    +0125             args.(names{k}) = rawArgs{k};
    +0126         end
    +0127     end
    +0128 end
    +0129 
    +0130 % Apply optional validators.
    +0131 if hasValidators
    +0132     for i = 1:numel(names)
    +0133         validator = spec{i, 3};
    +0134         if ~isempty(validator)
    +0135             validator(args.(names{i}));
    +0136         end
    +0137     end
    +0138 end
    +0139 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/utils/printOrange.html b/docs/html/utils/printOrange.html similarity index 51% rename from doc/utils/printOrange.html rename to docs/html/utils/printOrange.html index d91ea7c2..1cf5f7fd 100644 --- a/doc/utils/printOrange.html +++ b/docs/html/utils/printOrange.html @@ -29,14 +29,18 @@

    SYNOPSIS ^DESCRIPTION ^

     printOrange  Print an orange-coloured string to the MATLAB Command Window.
     
    - Prints stringToPrint in orange colour to the MATLAB Command Window. Only
    - works if MATLAB is open with a GUI; it does not work with command-line
    - MATLAB.
    + Prints stringToPrint in orange colour to the MATLAB Command Window. The
    + colour requires the MATLAB desktop; in command-line MATLAB the text is
    + printed without colour. When called without an output argument, the text is
    + wrapped to the width of the Command Window and printed. With an output
    + argument, nothing is printed.
     
      Parameters
      ----------
      stringToPrint : char
    -     string that should be printed in orange colour.
    +     string to print in orange colour. "\n" marks a line
    +     break, as in fprintf; no other escape sequence or format directive
    +     is interpreted, so text containing "%" prints as-is.
     
      Returns
      -------
    @@ -63,45 +67,50 @@ 

    SOURCE CODE ^
    0001 function orangeString = printOrange(stringToPrint)
     0002 % printOrange  Print an orange-coloured string to the MATLAB Command Window.
     0003 %
    -0004 % Prints stringToPrint in orange colour to the MATLAB Command Window. Only
    -0005 % works if MATLAB is open with a GUI; it does not work with command-line
    -0006 % MATLAB.
    -0007 %
    -0008 % Parameters
    -0009 % ----------
    -0010 % stringToPrint : char
    -0011 %     string that should be printed in orange colour.
    -0012 %
    -0013 % Returns
    -0014 % -------
    -0015 % orangeString : char
    -0016 %     the input string wrapped with the formatting codes for orange colour.
    -0017 %
    -0018 % Examples
    -0019 % --------
    -0020 %     printOrange(stringToPrint);
    -0021 
    -0022 try useDesktop = usejava('desktop'); catch, useDesktop = false; end
    -0023 if useDesktop
    -0024     orangeString = ['[\b' stringToPrint,']\b'];
    -0025 else
    -0026     orangeString = stringToPrint;
    -0027 end
    -0028 if nargout < 1
    -0029     % Wrap text to command window size
    -0030     sz = get(0, 'CommandWindowSize');
    -0031     orangeString = textwrap({orangeString},sz(1));
    -0032     orangeString = strjoin(orangeString,newline);
    -0033     %stringToPrint is arbitrary text (e.g. a model id) and may contain a
    -0034     %literal "%", which fprintf would otherwise reinterpret as a format
    -0035     %directive; the desktop "[\b ... ]\b" highlight markers still need
    -0036     %interpreting, so expand those explicitly instead of using orangeString
    -0037     %itself as the format string.
    -0038     orangeString = strrep(orangeString,'[\b',sprintf('[\b'));
    -0039     orangeString = strrep(orangeString,']\b',sprintf(']\b'));
    -0040     fprintf('%s',orangeString);
    -0041 end
    -0042 end

    +0004 % Prints stringToPrint in orange colour to the MATLAB Command Window. The +0005 % colour requires the MATLAB desktop; in command-line MATLAB the text is +0006 % printed without colour. When called without an output argument, the text is +0007 % wrapped to the width of the Command Window and printed. With an output +0008 % argument, nothing is printed. +0009 % +0010 % Parameters +0011 % ---------- +0012 % stringToPrint : char +0013 % string to print in orange colour. "\n" marks a line +0014 % break, as in fprintf; no other escape sequence or format directive +0015 % is interpreted, so text containing "%" prints as-is. +0016 % +0017 % Returns +0018 % ------- +0019 % orangeString : char +0020 % the input string wrapped with the formatting codes for orange colour. +0021 % +0022 % Examples +0023 % -------- +0024 % printOrange(stringToPrint); +0025 +0026 try useDesktop = usejava('desktop'); catch, useDesktop = false; end +0027 if useDesktop +0028 orangeString = ['[\b' stringToPrint,']\b']; +0029 else +0030 orangeString = stringToPrint; +0031 end +0032 if nargout < 1 +0033 % Wrap text to command window size +0034 sz = get(0, 'CommandWindowSize'); +0035 orangeString = textwrap({orangeString},sz(1)); +0036 orangeString = strjoin(orangeString,newline); +0037 %stringToPrint is arbitrary text (e.g. a model id) and may contain a +0038 %literal "%", which fprintf would otherwise reinterpret as a format +0039 %directive; the "\n" line breaks and the desktop "[\b ... ]\b" +0040 %highlight markers still need interpreting, so expand those explicitly +0041 %instead of using orangeString itself as the format string. +0042 orangeString = strrep(orangeString,'\n',newline); +0043 orangeString = strrep(orangeString,'[\b',sprintf('[\b')); +0044 orangeString = strrep(orangeString,']\b',sprintf(']\b')); +0045 fprintf('%s',orangeString); +0046 end +0047 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/docs/html/utils/progressReport.html b/docs/html/utils/progressReport.html new file mode 100644 index 00000000..87d8715b --- /dev/null +++ b/docs/html/utils/progressReport.html @@ -0,0 +1,362 @@ + + + + Description of progressReport + + + + + + + + + +
    Home > utils > progressReport.m
    + + + +

    progressReport +

    + +

    PURPOSE ^

    +
    + +

    SYNOPSIS ^

    +
    This is a script file.
    + +

    DESCRIPTION ^

    +
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: + + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 classdef progressReport < handle
    +0002 % progressReport  Universal progress reporting for RAVEN functions.
    +0003 %
    +0004 % Progress-reporting class used by RAVEN functions. The rendering backend is
    +0005 % selected automatically. The same calls work on an interactive desktop, in a
    +0006 % command-line session and on a headless server. During unit tests no output
    +0007 % is produced. The backends are:
    +0008 %
    +0009 %   - 'gui'  : MATLAB waitbar (interactive desktop).
    +0010 %   - 'cli'  : animated command-line bar (interactive terminal).
    +0011 %   - 'log'  : plain milestone lines, for example "Running BLAST: 30%", printed
    +0012 %              every 10%. The lines contain no control characters, so they
    +0013 %              are readable in redirected logs and on headless and -batch
    +0014 %              servers.
    +0015 %   - 'none' : no output (during tests, or when disabled).
    +0016 %
    +0017 % setRavenProgress sets the backend: setRavenProgress('log') selects plain
    +0018 % lines, for example on a compute cluster, and setRavenProgress('off')
    +0019 % disables all progress reporting.
    +0020 %
    +0021 % Usage
    +0022 % -----
    +0023 % To register each iteration, call count once per pass:
    +0024 %
    +0025 %     PB = progressReport(N, 'Task name');   % before the loop
    +0026 %     for i = 1:N
    +0027 %         ...                                % loop body
    +0028 %         PB.count;                          % or count(PB), also in parfor
    +0029 %     end
    +0030 %     PB.done;                               % optional; finalises the bar on early exit
    +0031 %
    +0032 % To avoid the per-iteration overhead in long loops of short iterations, set
    +0033 % the absolute position at intervals:
    +0034 %
    +0035 %     PB = progressReport(N, 'Task name');
    +0036 %     for i = 1:N
    +0037 %         ...
    +0038 %         if rem(i-1, 1000) == 0
    +0039 %             PB.update(i);                  % or update(PB, i)
    +0040 %         end
    +0041 %     end
    +0042 %     PB.done;
    +0043 %
    +0044 % Parameters
    +0045 % ----------
    +0046 % N : double
    +0047 %     number of iterations the loop performs (a positive integer). For any other
    +0048 %     value, no progress is shown.
    +0049 % taskName : char, optional
    +0050 %     label shown alongside the progress (default '').
    +0051 %
    +0052 % See also
    +0053 % --------
    +0054 % setRavenProgress
    +0055 
    +0056     properties (SetAccess = private)
    +0057         mode char            % resolved backend: 'gui', 'cli', 'log' or 'none'
    +0058         N    double
    +0059         taskName char
    +0060     end
    +0061     properties (Access = private, Transient)
    +0062         counter   = 0
    +0063         lastPct   = -1       % last percentage rendered
    +0064         logStep   = 10       % percent between 'log' lines
    +0065         barWidth  = 30
    +0066         prevLen   = 0        % characters on the current 'cli' line
    +0067         guiHandle = []
    +0068         Queue     = []       % DataQueue for parfor counting
    +0069         Listener  = []
    +0070         noPCT     = true
    +0071         finished  = false
    +0072     end
    +0073 
    +0074     methods
    +0075         function obj = progressReport(N, taskName)
    +0076             if nargin < 2 || isempty(taskName)
    +0077                 taskName = '';
    +0078             end
    +0079             obj.N = double(N);
    +0080             obj.taskName = truncateName(char(taskName), 40);
    +0081             obj.mode = resolveProgressMode();
    +0082 
    +0083             % Nothing sensible to show for an empty or unknown-length loop.
    +0084             if ~(isscalar(obj.N) && isfinite(obj.N) && obj.N >= 1)
    +0085                 obj.mode = 'none';
    +0086             end
    +0087             if strcmp(obj.mode,'none')
    +0088                 return
    +0089             end
    +0090 
    +0091             obj.noPCT = isempty(ver('parallel'));
    +0092             if ~obj.noPCT
    +0093                 obj.Queue = parallel.pool.DataQueue;
    +0094                 obj.Listener = afterEach(obj.Queue, @(v) localUpdate(obj, v));
    +0095             end
    +0096 
    +0097             % Initial render.
    +0098             switch obj.mode
    +0099                 case 'gui'
    +0100                     name = obj.taskName;
    +0101                     if isempty(name); name = 'Progress'; end
    +0102                     try
    +0103                         obj.guiHandle = waitbar(0, '0%', 'Name', name);
    +0104                     catch
    +0105                         obj.mode = 'log';   % no display available
    +0106                         emitLog(obj, 0);
    +0107                     end
    +0108                     obj.lastPct = 0;
    +0109                 case 'cli'
    +0110                     drawCli(obj, 0);
    +0111                     obj.lastPct = 0;
    +0112                 case 'log'
    +0113                     emitLog(obj, 0);
    +0114                     obj.lastPct = 0;
    +0115             end
    +0116         end
    +0117 
    +0118         function count(obj)
    +0119             % Register one completed iteration.
    +0120             if strcmp(obj.mode,'none')
    +0121                 return
    +0122             end
    +0123             if obj.noPCT || isempty(getCurrentJob)
    +0124                 localUpdate(obj, -1);       % serial execution
    +0125             else
    +0126                 send(obj.Queue, -1);        % parfor worker -> client
    +0127             end
    +0128         end
    +0129 
    +0130         function update(obj, n)
    +0131             % Set the absolute number of completed iterations to n.
    +0132             if strcmp(obj.mode,'none')
    +0133                 return
    +0134             end
    +0135             if obj.noPCT || isempty(getCurrentJob)
    +0136                 localUpdate(obj, n);        % serial execution
    +0137             else
    +0138                 send(obj.Queue, n);         % parfor worker -> client
    +0139             end
    +0140         end
    +0141 
    +0142         function done(obj)
    +0143             % Finalise the bar (call when a loop may exit before reaching N).
    +0144             finishUp(obj);
    +0145         end
    +0146     end
    +0147 
    +0148     methods (Hidden = true)
    +0149         function delete(obj)
    +0150             if ~isempty(obj.Queue)
    +0151                 delete(obj.Queue);
    +0152             end
    +0153             finishUp(obj);
    +0154         end
    +0155     end
    +0156 
    +0157     methods (Access = private)
    +0158         function localUpdate(obj, v)
    +0159             if v < 0
    +0160                 obj.counter = obj.counter + 1;
    +0161             else
    +0162                 obj.counter = max(obj.counter, v);
    +0163             end
    +0164             render(obj);
    +0165         end
    +0166 
    +0167         function render(obj)
    +0168             if obj.finished
    +0169                 return
    +0170             end
    +0171             pct = min(100, floor(100 * obj.counter / obj.N));
    +0172             switch obj.mode
    +0173                 case 'gui'
    +0174                     if pct ~= obj.lastPct
    +0175                         obj.lastPct = pct;
    +0176                         if ~isempty(obj.guiHandle) && isvalid(obj.guiHandle)
    +0177                             waitbar(pct/100, obj.guiHandle, sprintf('%d%%', pct));
    +0178                         end
    +0179                     end
    +0180                     if obj.counter >= obj.N; finishUp(obj); end
    +0181                 case 'cli'
    +0182                     if pct ~= obj.lastPct
    +0183                         obj.lastPct = pct;
    +0184                         drawCli(obj, pct);
    +0185                     end
    +0186                     if obj.counter >= obj.N; finishUp(obj); end
    +0187                 case 'log'
    +0188                     if pct >= obj.lastPct + obj.logStep ...
    +0189                             || (obj.counter >= obj.N && obj.lastPct < 100)
    +0190                         if obj.counter >= obj.N; pct = 100; end
    +0191                         obj.lastPct = pct;
    +0192                         emitLog(obj, pct);
    +0193                     end
    +0194             end
    +0195         end
    +0196 
    +0197         function drawCli(obj, pct)
    +0198             nfill = round(obj.barWidth * pct / 100);
    +0199             bar = [repmat('#',1,nfill), repmat('-',1,obj.barWidth-nfill)];
    +0200             if isempty(obj.taskName)
    +0201                 s = sprintf('[%s] %3d%%', bar, pct);
    +0202             else
    +0203                 s = sprintf('%s [%s] %3d%%', obj.taskName, bar, pct);
    +0204             end
    +0205             fprintf('%s%s', repmat(sprintf('\b'),1,obj.prevLen), s);
    +0206             obj.prevLen = numel(s);
    +0207         end
    +0208 
    +0209         function emitLog(obj, pct)
    +0210             if isempty(obj.taskName)
    +0211                 fprintf('Progress: %d%%\n', pct);
    +0212             else
    +0213                 fprintf('%s: %d%%\n', obj.taskName, pct);
    +0214             end
    +0215         end
    +0216 
    +0217         function finishUp(obj)
    +0218             if obj.finished
    +0219                 return
    +0220             end
    +0221             switch obj.mode
    +0222                 case 'gui'
    +0223                     if ~isempty(obj.guiHandle) && isvalid(obj.guiHandle)
    +0224                         delete(obj.guiHandle);
    +0225                     end
    +0226                 case 'cli'
    +0227                     if obj.lastPct < 100
    +0228                         drawCli(obj, 100);  % show completion
    +0229                     end
    +0230                     if obj.prevLen > 0
    +0231                         fprintf('\n');      % move off the progress line
    +0232                     end
    +0233                 case 'log'
    +0234                     if obj.lastPct < 100
    +0235                         emitLog(obj, 100);
    +0236                     end
    +0237             end
    +0238             obj.finished = true;
    +0239         end
    +0240     end
    +0241 end
    +0242 
    +0243 % ---------------------------- Local functions ----------------------------
    +0244 function mode = resolveProgressMode()
    +0245 % Decide the backend from the RAVEN preference, the test context and the
    +0246 % runtime environment.
    +0247 mode = 'auto';
    +0248 try
    +0249     mode = char(getpref('RAVEN','progressBar','auto'));
    +0250 catch
    +0251 end
    +0252 if strcmpi(mode,'off')
    +0253     mode = 'none';
    +0254 end
    +0255 if ~strcmpi(mode,'auto')
    +0256     if ~ismember(lower(mode),{'gui','cli','log','none'})
    +0257         mode = 'cli';
    +0258     end
    +0259     mode = lower(mode);
    +0260     return
    +0261 end
    +0262 
    +0263 % 'auto': silence inside the test framework first.
    +0264 if inTestContext()
    +0265     mode = 'none';
    +0266     return
    +0267 end
    +0268 
    +0269 % Headless / deployed / -batch -> clean log lines.
    +0270 isBatch = false;
    +0271 try
    +0272     isBatch = batchStartupOptionUsed;
    +0273 catch
    +0274 end
    +0275 if isBatch || isdeployed
    +0276     mode = 'log';
    +0277     return
    +0278 end
    +0279 
    +0280 % Desktop available -> GUI, otherwise an interactive terminal -> CLI.
    +0281 hasDesktop = false;
    +0282 try
    +0283     hasDesktop = usejava('desktop');
    +0284 catch
    +0285 end
    +0286 if hasDesktop
    +0287     mode = 'gui';
    +0288 else
    +0289     mode = 'cli';
    +0290 end
    +0291 end
    +0292 
    +0293 function tf = inTestContext()
    +0294 % Best-effort detection of the matlab.unittest framework on the call stack.
    +0295 % RavenTestCase also forces the preference to 'none', so the maintained
    +0296 % suite is silenced even if this heuristic misses a frame.
    +0297 tf = false;
    +0298 st = dbstack('-completenames');
    +0299 for i = 1:numel(st)
    +0300     if contains(st(i).name,'matlab.unittest','IgnoreCase',true) ...
    +0301             || contains(st(i).name,'TestRunner','IgnoreCase',true) ...
    +0302             || contains(st(i).name,'runtests','IgnoreCase',true)
    +0303         tf = true;
    +0304         return
    +0305     end
    +0306 end
    +0307 end
    +0308 
    +0309 function name = truncateName(name, maxLen)
    +0310 if numel(name) > maxLen
    +0311     name = name(1:maxLen);
    +0312 end
    +0313 end
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/utils/ravenList.html b/docs/html/utils/ravenList.html similarity index 56% rename from doc/utils/ravenList.html rename to docs/html/utils/ravenList.html index 64f92383..6ce5a11d 100644 --- a/doc/utils/ravenList.html +++ b/docs/html/utils/ravenList.html @@ -4,7 +4,7 @@ Description of ravenList - + @@ -21,28 +21,31 @@

    ravenList

    PURPOSE ^

    -
    ravenList Format a header and item list for warning() or error().
    +
    ravenList Format a header and a list of items for warning() or error().

    SYNOPSIS ^

    function msg = ravenList(header, items, trim)

    DESCRIPTION ^

    -
     ravenList  Format a header and item list for warning() or error().
    +
     ravenList  Format a header and a list of items for warning() or error().
     
      Parameters
      ----------
      header : char
          message header string.
      items : cell
    -     cell array of items to list below the header.
    +     cell array of items, listed one per line after the header.
      trim : logical
    -     cap the list at 10 items (default true).
    +     true to limit the list to 10 lines (default true). When the list has more
    +     than 10 items, the tenth line reads '...and N more' and replaces items
    +     10 to the end, with N the number of replaced items.
     
      Returns
      -------
      msg : char
    -     header + newline + tab-indented items, ready to pass to warning() or
    -     error() as the format string.
    +     header, a newline, and the items separated by newlines with a tab before
    +     each item. If items is empty, msg is header. Passed to warning() or
    +     error() as an argument after the format '%s'.
     
      Examples
      --------
    @@ -63,44 +66,47 @@ 

    CROSS-REFERENCE INFORMATION ^
 
 <h2><a name=SOURCE CODE ^

    0001 function msg = ravenList(header, items, trim)
    -0002 % ravenList  Format a header and item list for warning() or error().
    +0002 % ravenList  Format a header and a list of items for warning() or error().
     0003 %
     0004 % Parameters
     0005 % ----------
     0006 % header : char
     0007 %     message header string.
     0008 % items : cell
    -0009 %     cell array of items to list below the header.
    +0009 %     cell array of items, listed one per line after the header.
     0010 % trim : logical
    -0011 %     cap the list at 10 items (default true).
    -0012 %
    -0013 % Returns
    -0014 % -------
    -0015 % msg : char
    -0016 %     header + newline + tab-indented items, ready to pass to warning() or
    -0017 %     error() as the format string.
    -0018 %
    -0019 % Examples
    -0020 % --------
    -0021 %     warning("RAVEN:warning", "%s", ravenList("Missing metabolites:", badMets));
    -0022 %     error("RAVEN:badInput", "%s", ravenList("Invalid reactions:", badRxns, false));
    -0023 
    -0024 if nargin < 3; trim = true; end
    -0025 items = convertCharArray(items);
    -0026 if isempty(items)
    -0027     msg = header;
    -0028     return
    -0029 end
    -0030 if trim && numel(items) > 10
    -0031     items{10} = sprintf('...and %d more', numel(items)-9);
    -0032     items(11:end) = [];
    -0033 end
    -0034 % {char(9)} rather than char(9): strcat strips trailing whitespace from a
    -0035 % plain char argument, and a lone tab is entirely trailing whitespace, so
    -0036 % every item's indent was silently dropped. Wrapped in a cell, strcat
    -0037 % treats it as a cell-array input instead, which it does not trim.
    -0038 msg = [header, newline, strjoin(strcat({char(9)}, items), newline)];
    -0039 end
    +0011 % true to limit the list to 10 lines (default true). When the list has more +0012 % than 10 items, the tenth line reads '...and N more' and replaces items +0013 % 10 to the end, with N the number of replaced items. +0014 % +0015 % Returns +0016 % ------- +0017 % msg : char +0018 % header, a newline, and the items separated by newlines with a tab before +0019 % each item. If items is empty, msg is header. Passed to warning() or +0020 % error() as an argument after the format '%s'. +0021 % +0022 % Examples +0023 % -------- +0024 % warning("RAVEN:warning", "%s", ravenList("Missing metabolites:", badMets)); +0025 % error("RAVEN:badInput", "%s", ravenList("Invalid reactions:", badRxns, false)); +0026 +0027 if nargin < 3; trim = true; end +0028 items = convertCharArray(items); +0029 if isempty(items) +0030 msg = header; +0031 return +0032 end +0033 if trim && numel(items) > 10 +0034 items{10} = sprintf('...and %d more', numel(items)-9); +0035 items(11:end) = []; +0036 end +0037 % {char(9)} rather than char(9): strcat strips trailing whitespace from a +0038 % plain char argument, and a lone tab is entirely trailing whitespace, so +0039 % every item's indent was silently dropped. Wrapped in a cell, strcat +0040 % treats it as a cell-array input instead, which it does not trim. +0041 msg = [header, newline, strjoin(strcat({char(9)}, items), newline)]; +0042 end

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/utils/ravenModelFields.html b/docs/html/utils/ravenModelFields.html similarity index 53% rename from doc/utils/ravenModelFields.html rename to docs/html/utils/ravenModelFields.html index b7afad47..a7405242 100644 --- a/doc/utils/ravenModelFields.html +++ b/docs/html/utils/ravenModelFields.html @@ -29,20 +29,21 @@

    SYNOPSIS ^DESCRIPTION ^

     ravenModelFields  Declarative registry of all RAVEN model struct fields.
     
    - Returns a struct array. Each element describes one field:
    + Returns a struct array with one element per field. The elements have the
    + fields:
        .name    - field name (char)
    -   .type    - entity type: 'rxn', 'met', 'gene', or 'comp' (char)
    +   .type    - entity type: 'rxn', 'met', 'gene' or 'comp' (char)
        .default - default value when padding new entries
     
    - Only 1-D (vector-per-entity) fields are listed here. Fields that require
    - custom 2-D indexing or value-remapping are excluded and must be handled
    - explicitly by callers:
    + Only 1-D (vector-per-entity) fields are listed. Fields that need 2-D
    + indexing or value remapping are excluded, and callers handle them
    + explicitly:
     
        S          - sparse (mets x rxns); use S(:,perm) for rxns, S(perm,:) for mets
        rxnGeneMat - sparse (rxns x genes); use rxnGeneMat(perm,:) or rxnGeneMat(:,perm)
        b          - double (mets x N), may be 2-D; use b(perm,:)
    -   metComps, rxnComps, geneComps - when permuting comps, their VALUES must
    -       be remapped (not the arrays themselves permuted)
    + metComps, rxnComps, geneComps - when the compartments are permuted, the + values of these arrays are remapped; the arrays are not permuted

    CROSS-REFERENCE INFORMATION ^

    @@ -60,74 +61,75 @@

    SOURCE CODE ^
    0001 function reg = ravenModelFields()
     0002 % ravenModelFields  Declarative registry of all RAVEN model struct fields.
     0003 %
    -0004 % Returns a struct array. Each element describes one field:
    -0005 %   .name    - field name (char)
    -0006 %   .type    - entity type: 'rxn', 'met', 'gene', or 'comp' (char)
    -0007 %   .default - default value when padding new entries
    -0008 %
    -0009 % Only 1-D (vector-per-entity) fields are listed here. Fields that require
    -0010 % custom 2-D indexing or value-remapping are excluded and must be handled
    -0011 % explicitly by callers:
    -0012 %
    -0013 %   S          - sparse (mets x rxns); use S(:,perm) for rxns, S(perm,:) for mets
    -0014 %   rxnGeneMat - sparse (rxns x genes); use rxnGeneMat(perm,:) or rxnGeneMat(:,perm)
    -0015 %   b          - double (mets x N), may be 2-D; use b(perm,:)
    -0016 %   metComps, rxnComps, geneComps - when permuting comps, their VALUES must
    -0017 %       be remapped (not the arrays themselves permuted)
    -0018 
    -0019 rows = {
    -0020 % name                   type      default
    -0021   'rxns',                'rxn',    '';
    -0022   'lb',                  'rxn',    0;
    -0023   'ub',                  'rxn',    1000;
    -0024   'rev',                 'rxn',    false;
    -0025   'c',                   'rxn',    0;
    -0026   'grRules',             'rxn',    '';
    -0027   'rxnNames',            'rxn',    '';
    -0028   'eccodes',             'rxn',    '';
    -0029   'subSystems',          'rxn',    '';
    -0030   'rxnFrom',             'rxn',    '';
    -0031   'rxnNotes',            'rxn',    '';
    -0032   'rxnReferences',       'rxn',    '';
    -0033   'pwys',                'rxn',    '';
    -0034   'equations',           'rxn',    '';
    -0035   'rxnComps',            'rxn',    NaN;
    -0036   'rxnConfidenceScores', 'rxn',    NaN;
    -0037   'rxnDeltaG',           'rxn',    NaN;
    -0038   'rxnScores',           'rxn',    NaN;
    -0039   'spontaneous',         'rxn',    false;
    -0040   'rxnMiriams',          'rxn',    {};
    -0041   'mets',                'met',    '';
    -0042   'metComps',            'met',    0;
    -0043   'metNames',            'met',    '';
    -0044   'metFormulas',         'met',    '';
    -0045   'inchis',              'met',    '';
    -0046   'metSmiles',           'met',    '';
    -0047   'metNotes',            'met',    '';
    -0048   'metFrom',             'met',    '';
    -0049   'metCharges',          'met',    NaN;
    -0050   'metDeltaG',           'met',    NaN;
    -0051   'unconstrained',       'met',    0;
    -0052   'metMiriams',          'met',    {};
    -0053   'genes',               'gene',   '';
    -0054   'geneShortNames',      'gene',   '';
    -0055   'geneFrom',            'gene',   '';
    -0056   'proteins',            'gene',   '';
    -0057   'geneComps',           'gene',   NaN;
    -0058   'geneMiriams',         'gene',   {};
    -0059   'comps',               'comp',   '';
    -0060   'compNames',           'comp',   '';
    -0061   'compOutside',         'comp',   '';
    -0062   'compMiriams',         'comp',   {};
    -0063 };
    -0064 
    -0065 n = size(rows, 1);
    -0066 for i = 1:n
    -0067     reg(i).name    = rows{i,1};
    -0068     reg(i).type    = rows{i,2};
    -0069     reg(i).default = rows{i,3};
    -0070 end
    -0071 end
    +0004 % Returns a struct array with one element per field. The elements have the +0005 % fields: +0006 % .name - field name (char) +0007 % .type - entity type: 'rxn', 'met', 'gene' or 'comp' (char) +0008 % .default - default value when padding new entries +0009 % +0010 % Only 1-D (vector-per-entity) fields are listed. Fields that need 2-D +0011 % indexing or value remapping are excluded, and callers handle them +0012 % explicitly: +0013 % +0014 % S - sparse (mets x rxns); use S(:,perm) for rxns, S(perm,:) for mets +0015 % rxnGeneMat - sparse (rxns x genes); use rxnGeneMat(perm,:) or rxnGeneMat(:,perm) +0016 % b - double (mets x N), may be 2-D; use b(perm,:) +0017 % metComps, rxnComps, geneComps - when the compartments are permuted, the +0018 % values of these arrays are remapped; the arrays are not permuted +0019 +0020 rows = { +0021 % name type default +0022 'rxns', 'rxn', ''; +0023 'lb', 'rxn', 0; +0024 'ub', 'rxn', 1000; +0025 'rev', 'rxn', false; +0026 'c', 'rxn', 0; +0027 'grRules', 'rxn', ''; +0028 'rxnNames', 'rxn', ''; +0029 'eccodes', 'rxn', ''; +0030 'subSystems', 'rxn', ''; +0031 'rxnFrom', 'rxn', ''; +0032 'rxnNotes', 'rxn', ''; +0033 'rxnReferences', 'rxn', ''; +0034 'pwys', 'rxn', ''; +0035 'equations', 'rxn', ''; +0036 'rxnComps', 'rxn', NaN; +0037 'rxnConfidenceScores', 'rxn', NaN; +0038 'rxnDeltaG', 'rxn', NaN; +0039 'rxnScores', 'rxn', NaN; +0040 'spontaneous', 'rxn', false; +0041 'rxnMiriams', 'rxn', {}; +0042 'mets', 'met', ''; +0043 'metComps', 'met', 0; +0044 'metNames', 'met', ''; +0045 'metFormulas', 'met', ''; +0046 'inchis', 'met', ''; +0047 'metSmiles', 'met', ''; +0048 'metNotes', 'met', ''; +0049 'metFrom', 'met', ''; +0050 'metCharges', 'met', NaN; +0051 'metDeltaG', 'met', NaN; +0052 'unconstrained', 'met', 0; +0053 'metMiriams', 'met', {}; +0054 'genes', 'gene', ''; +0055 'geneShortNames', 'gene', ''; +0056 'geneFrom', 'gene', ''; +0057 'proteins', 'gene', ''; +0058 'geneComps', 'gene', NaN; +0059 'geneMiriams', 'gene', {}; +0060 'comps', 'comp', ''; +0061 'compNames', 'comp', ''; +0062 'compOutside', 'comp', ''; +0063 'compMiriams', 'comp', {}; +0064 }; +0065 +0066 n = size(rows, 1); +0067 for i = 1:n +0068 reg(i).name = rows{i,1}; +0069 reg(i).type = rows{i,2}; +0070 reg(i).default = rows{i,3}; +0071 end +0072 end
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/utils/runRAVENtests.html b/docs/html/utils/runRAVENtests.html similarity index 65% rename from doc/utils/runRAVENtests.html rename to docs/html/utils/runRAVENtests.html index e0dcbe86..d6db3460 100644 --- a/doc/utils/runRAVENtests.html +++ b/docs/html/utils/runRAVENtests.html @@ -29,8 +29,9 @@

    SYNOPSIS ^DESCRIPTION ^

     runRAVENtests  Run all RAVEN function tests.
     
    - Runs all unit tests found at RAVEN/test/function_tests, and prints output in
    - the command window.
    + Runs all unit tests found in the folder tests/function_tests of the RAVEN
    + root folder, except the abstract base class RavenTestCase, and prints the
    + output in the command window.
     
      Returns
      -------
    @@ -57,30 +58,31 @@ 

    SOURCE CODE ^
    0001 function testResults = runRAVENtests
     0002 % runRAVENtests  Run all RAVEN function tests.
     0003 %
    -0004 % Runs all unit tests found at RAVEN/test/function_tests, and prints output in
    -0005 % the command window.
    -0006 %
    -0007 % Returns
    -0008 % -------
    -0009 % testResults : matlab.unittest.TestResult
    -0010 %     array of test results returned by runtests.
    -0011 %
    -0012 % Examples
    -0013 % --------
    -0014 %     testResults = runRAVENtests;
    -0015 
    -0016 ravenPath = findRAVENroot;
    -0017 curwd = pwd;
    -0018 cd(fullfile(ravenPath,'testing','function_tests'));
    -0019 
    -0020 % Collect the concrete test classes only. RavenTestCase is an abstract base
    -0021 % class that the test classes subclass, and runtests cannot build a suite
    -0022 % from an abstract class, so it must be excluded from the file list.
    -0023 testFiles = struct2table(dir('*.m')).name;
    -0024 testFiles(strcmp(testFiles,'RavenTestCase.m')) = [];
    -0025 testResults = runtests(testFiles);
    -0026 
    -0027 cd(curwd);

    +0004 % Runs all unit tests found in the folder tests/function_tests of the RAVEN +0005 % root folder, except the abstract base class RavenTestCase, and prints the +0006 % output in the command window. +0007 % +0008 % Returns +0009 % ------- +0010 % testResults : matlab.unittest.TestResult +0011 % array of test results returned by runtests. +0012 % +0013 % Examples +0014 % -------- +0015 % testResults = runRAVENtests; +0016 +0017 ravenPath = findRAVENroot; +0018 curwd = pwd; +0019 cd(fullfile(ravenPath,'tests','function_tests')); +0020 +0021 % Collect the concrete test classes only. RavenTestCase is an abstract base +0022 % class that the test classes subclass, and runtests cannot build a suite +0023 % from an abstract class, so it must be excluded from the file list. +0024 testFiles = struct2table(dir('*.m')).name; +0025 testFiles(strcmp(testFiles,'RavenTestCase.m')) = []; +0026 testResults = runtests(testFiles); +0027 +0028 cd(curwd);
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/utils/setRavenProgress.html b/docs/html/utils/setRavenProgress.html similarity index 55% rename from doc/utils/setRavenProgress.html rename to docs/html/utils/setRavenProgress.html index 9f07ab3d..742cb612 100644 --- a/doc/utils/setRavenProgress.html +++ b/docs/html/utils/setRavenProgress.html @@ -30,25 +30,26 @@

    DESCRIPTION ^
     setRavenProgress  Set the RAVEN progress-reporting backend.
     
      Sets the preference that progressReport uses to decide how progress is
    - shown. The setting is persistent across MATLAB sessions.
    + shown. The setting is stored as the RAVEN preference progressBar and
    + persists across MATLAB sessions.
     
      Parameters
      ----------
      mode : char
          one of:
     
    -     - 'auto' : choose automatically (default). GUI on an interactive
    -       desktop, an animated bar in an interactive terminal, plain log lines
    -       on a headless / -batch server, and no output during unit tests.
    +     - 'auto' : choose automatically (default): 'gui' on the MATLAB desktop,
    +       'cli' in a terminal, 'log' on a -batch or deployed session, and no
    +       output during unit tests.
          - 'gui'  : always use a MATLAB waitbar.
          - 'cli'  : always use the animated command-line bar.
    -     - 'log'  : always print plain milestone lines (safe in redirected
    -       logs and on headless servers).
    -     - 'none' / 'off' : never show progress.
    +     - 'log'  : always print plain milestone lines, which are readable in
    +       redirected logs and on headless servers.
    +     - 'none' or 'off' : never show progress.
     
      Examples
      --------
    -     setRavenProgress('log');   % e.g. on a compute cluster
    +     setRavenProgress('log');   % for example on a compute cluster
          setRavenProgress('off');   % silence all progress reporting
          setRavenProgress('auto');  % restore the default behaviour
     
    @@ -73,44 +74,45 @@ 

    SOURCE CODE ^% setRavenProgress Set the RAVEN progress-reporting backend. 0003 % 0004 % Sets the preference that progressReport uses to decide how progress is -0005 % shown. The setting is persistent across MATLAB sessions. -0006 % -0007 % Parameters -0008 % ---------- -0009 % mode : char -0010 % one of: -0011 % -0012 % - 'auto' : choose automatically (default). GUI on an interactive -0013 % desktop, an animated bar in an interactive terminal, plain log lines -0014 % on a headless / -batch server, and no output during unit tests. -0015 % - 'gui' : always use a MATLAB waitbar. -0016 % - 'cli' : always use the animated command-line bar. -0017 % - 'log' : always print plain milestone lines (safe in redirected -0018 % logs and on headless servers). -0019 % - 'none' / 'off' : never show progress. -0020 % -0021 % Examples -0022 % -------- -0023 % setRavenProgress('log'); % e.g. on a compute cluster -0024 % setRavenProgress('off'); % silence all progress reporting -0025 % setRavenProgress('auto'); % restore the default behaviour -0026 % -0027 % See also -0028 % -------- -0029 % progressReport -0030 -0031 if nargin < 1 || isempty(mode) -0032 mode = 'auto'; -0033 end -0034 mode = lower(char(mode)); -0035 if strcmp(mode,'off') -0036 mode = 'none'; -0037 end -0038 if ~ismember(mode,{'auto','gui','cli','log','none'}) -0039 error('Unknown progress mode "%s". Use auto, gui, cli, log or off.', mode); -0040 end -0041 setpref('RAVEN','progressBar',mode); -0042 end

    +0005 % shown. The setting is stored as the RAVEN preference progressBar and +0006 % persists across MATLAB sessions. +0007 % +0008 % Parameters +0009 % ---------- +0010 % mode : char +0011 % one of: +0012 % +0013 % - 'auto' : choose automatically (default): 'gui' on the MATLAB desktop, +0014 % 'cli' in a terminal, 'log' on a -batch or deployed session, and no +0015 % output during unit tests. +0016 % - 'gui' : always use a MATLAB waitbar. +0017 % - 'cli' : always use the animated command-line bar. +0018 % - 'log' : always print plain milestone lines, which are readable in +0019 % redirected logs and on headless servers. +0020 % - 'none' or 'off' : never show progress. +0021 % +0022 % Examples +0023 % -------- +0024 % setRavenProgress('log'); % for example on a compute cluster +0025 % setRavenProgress('off'); % silence all progress reporting +0026 % setRavenProgress('auto'); % restore the default behaviour +0027 % +0028 % See also +0029 % -------- +0030 % progressReport +0031 +0032 if nargin < 1 || isempty(mode) +0033 mode = 'auto'; +0034 end +0035 mode = lower(char(mode)); +0036 if strcmp(mode,'off') +0037 mode = 'none'; +0038 end +0039 if ~ismember(mode,{'auto','gui','cli','log','none'}) +0040 error('Unknown progress mode "%s". Use auto, gui, cli, log or off.', mode); +0041 end +0042 setpref('RAVEN','progressBar',mode); +0043 end
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/windows.png b/docs/html/windows.png similarity index 100% rename from doc/windows.png rename to docs/html/windows.png diff --git a/readme/GPL3.MD b/docs/readme/GPL3.MD similarity index 100% rename from readme/GPL3.MD rename to docs/readme/GPL3.MD diff --git a/readme/RAVEN_structure_fields.csv b/docs/readme/RAVEN_structure_fields.csv similarity index 100% rename from readme/RAVEN_structure_fields.csv rename to docs/readme/RAVEN_structure_fields.csv diff --git a/readme/changelog.MD b/docs/readme/changelog.MD similarity index 100% rename from readme/changelog.MD rename to docs/readme/changelog.MD diff --git a/docs/v3_review.md b/docs/v3_review.md index 98b1a52f..38ae4440 100644 --- a/docs/v3_review.md +++ b/docs/v3_review.md @@ -1,7 +1,6 @@ # RAVEN 3 — development review and roadmap -A review of the RAVEN toolbox aimed at the next major release (**v3**, currently on the -`develop3` branch). The brief: make the codebase **leaner** (remove old/unused/low-value +A review of the RAVEN toolbox for RAVEN 3. The brief: make the codebase **leaner** (remove old/unused/low-value functions) while gaining **a few critical new capabilities** that increase value and usability — with an appetite for ambitious additions. @@ -11,7 +10,7 @@ This document is a proposal and a prioritized to-do list, not a record of work a ## Scope and method -- Reviewed against the **current `develop3` tree**, not `develop`. The four review lenses: +- Reviewed against the RAVEN 3 tree. The four review lenses: **(a)** unnecessary functions to remove, **(b)** missing functionality worth adding, **(c)** existing functionality worth expanding, **(d)** functions worth simplifying. - Method: subsystem-by-subsystem audit of every `.m` file outside `software/`, plus @@ -21,7 +20,7 @@ This document is a proposal and a prioritized to-do list, not a record of work a **COBRA Toolbox (MATLAB)** is wanted; **MetaNetX mapping** is explicitly wanted (an old stale branch exists); new file formats are *not* a priority. -### Already done in `develop3` +### Already done in RAVEN 3 So the review does not repeat completed work. v3 already: @@ -30,7 +29,7 @@ So the review does not repeat completed work. v3 already: and `combnk`. - Dropped the Statistics, Bioinformatics and Text Analytics toolbox dependencies. - Moved KEGG HMM libraries and external binaries out of git to on-demand fetch from the - `raven-data` release. + `toolbox-assets` release. - Reorganized the repo into functional folders; reformatted docstrings to NumPy style. - Added `progressReport`, `parseRAVENargs` (positional-or-named arguments), and a `matlab.unittest` suite. @@ -70,12 +69,12 @@ So the review does not repeat completed work. v3 already: as a new `UniProt` table column (§6.3). - Removed `followFluxes` (SysBioChalmers/RAVEN#725): a strict subset of `printFluxes`'s cutoff filtering plus `followChanged`'s single-reference-flux comparison, with nothing of - its own (SysBioChalmers/raven-gecko-parity#46). + its own (CompMetEng/raven-gecko-parity#46). - Removed `exportToTabDelimited` and the `exportToExcelFormat` bare-path fallback that called it (SysBioChalmers/RAVEN#727): the fallback silently switched output format depending on whether `fileName` looked like a path, contradicting `exportToExcelFormat`'s own error message ("only xlsx format is supported" as of RAVEN 1.9) whenever a full filename *was* - given (SysBioChalmers/raven-gecko-parity#55). + given (CompMetEng/raven-gecko-parity#55). --- @@ -109,7 +108,7 @@ reconstruction / strain-design audience. | 13 | **Regulatory integration** — PROM / CoRegFlux (new application domain) (§1.4) | (b) | M each | med | | 14 | **Community modeling** — SteadyCom (new application domain) (§1.5) | (b) | M | med | | 15 | **E-Flux** next to ftINIT (§1.6) | (b)(c) | S | low–med | -| 14 | **HPA omics parser consolidation** — extract shared file-read + header-validation into `omics/private/` helper *(§6.2 column fix already done; this is the remaining extract step)* (§5.5) | (d) | S | low | +| 14 | **HPA omics parser consolidation** — extract shared file-read + header-validation into `INIT/private/` helper *(§6.2 column fix already done; this is the remaining extract step)* (§5.5) | (d) | S | low | --- @@ -141,14 +140,14 @@ Plus a standalone `mapIDsViaMNXref(type, ids, fromDB, toDB)` ID translator. - **No structural matching** — maps only by existing xref IDs and fuzzy names; ignores the **InChIKey** column, which is the modern primary metabolite key. - **Pre-reorg layout** — lives in `external/MetaNetX/`, carries duplicate `struct_conversion/*` - that has since diverged on develop3. + that has since diverged in RAVEN 3. **Refactor plan** (one focused PR; core is M, the polish items are S each): -1. Relocate to the develop3 layout (e.g. `reconstruction/metanetx/` or `annotation/metanetx/`), - drop the duplicated `struct_conversion/*`, rebase on develop3's `convertMiriams`. +1. Relocate to the RAVEN 3 layout (e.g. `reconstruction/metanetx/` or `annotation/metanetx/`), + drop the duplicated `struct_conversion/*`, rebase on RAVEN 3's `convertMiriams`. 2. **Delete the committed data**; host MNXref 4.5 (gzipped TSVs, and ideally a prebuilt `.mat`) - as a `raven-data` release asset and add a `downloadMNXref.m` modeled exactly on the - develop3 KEGG-HMM / `downloadRavenBinaries` on-demand pattern (sentinel-file check, cache + as a `toolbox-assets` release asset and add a `downloadMNXref.m` modeled exactly on the + RAVEN 3 KEGG-HMM / `downloadRavenBinaries` on-demand pattern (sentinel-file check, cache under a gitignored dir). 3. Update parsing to the documented **4.5** columns (header-driven `readtable`, handle the `#` comment block and deprecations). @@ -326,8 +325,8 @@ See §3 — split the monolith into directional internals. - **`annotation/loadDeltaGfromCSV.m` + `saveDeltaGtoCSV.m`** *(done)* — collapsed into `deltaGCSV(model, direction, metCsv, rxnCsv)`. The four near-identical met/rxn blocks are handled by two shared local functions (`loadField`/`saveField`). -- **`omics/parseHPA.m` vs `parseHPArna.m`** — column detection is now header-driven (§6.2 done). - Remaining: extract the shared file-read loop into `omics/private/`. **S.** +- **`INIT/parseHPA.m` vs `parseHPArna.m`** — column detection is now header-driven (§6.2 done). + Remaining: extract the shared file-read loop into `INIT/private/`. **S.** - **`localization/getWoLFScores.m` vs `parseScores.m`** *(already done)* — `getWoLFScores` already delegates to `parseScores(file,'wolf')`; no normalization duplication exists. Remaining question: whether the Linux+Perl-only WoLF runner still earns its place. @@ -352,7 +351,7 @@ MEMOTE-style, cheap given `extractMiriam` already unpacks the data. Pairs with t also auto-detect the delimiter (tab vs comma). Upstream format changes that add or reorder columns will no longer silently break the functions. -The remaining consolidation item (extracting a shared `omics/private/` file-read helper) is +The remaining consolidation item (extracting a shared `INIT/private/` file-read helper) is tracked as a low-priority item in the Tier-3 table. ### 6.3 Smaller expansions *(done)* @@ -379,6 +378,6 @@ tracked as a low-priority item in the Tier-3 table. 2. **Where should new strain-design / community code that needs metaheuristics live**, given the dependency question — lean on the Global Optimization Toolbox (`gamultiobj`), or keep v3's "minimize toolbox dependencies" stance and ship a self-contained EA? -4. **MNXref data hosting** — confirm the `raven-data` release is the right home for the MNXref +4. **MNXref data hosting** — confirm the `toolbox-assets` release is the right home for the MNXref 4.5 assets (mirroring KEGG HMMs/binaries), and whether to ship a prebuilt `.mat` (7 MB, fast) or only the gzipped TSVs (slower first build, always current). diff --git a/img/raven-logo-white.png b/img/raven-logo-white.png new file mode 100644 index 00000000..ec34f826 Binary files /dev/null and b/img/raven-logo-white.png differ diff --git a/img/raven-logo.png b/img/raven-logo.png new file mode 100644 index 00000000..e4c38b9a Binary files /dev/null and b/img/raven-logo.png differ diff --git a/installation/fetchRavenDataAsset.m b/installation/fetchRavenDataAsset.m deleted file mode 100644 index 5e5266eb..00000000 --- a/installation/fetchRavenDataAsset.m +++ /dev/null @@ -1,43 +0,0 @@ -function localPath=fetchRavenDataAsset(destDir,releaseTag,assetName) -% fetchRavenDataAsset Download (if needed) a raven-data release asset. -% -% Downloads assetName from the given raven-data release tag into destDir -% and returns its local path. An already-downloaded copy is reused as-is. -% Generic across raven-data's release families (KEGG artefacts, HMM -% libraries, binaries, ...): nothing here is specific to any one of -% them, so callers supply the release tag and asset name rather than -% this function hardcoding either. -% -% Parameters -% ---------- -% destDir : char -% directory to download into (created if it does not exist yet). -% releaseTag : char -% the raven-data release tag, e.g. 'kegg118'. -% assetName : char -% the release asset file name, e.g. 'kegg118_core.tar.gz'. -% -% Returns -% ------- -% localPath : char -% path to the downloaded file. - -if ~isfolder(destDir) - mkdir(destDir); -end -localPath=fullfile(destDir,assetName); -if isfile(localPath) - return; -end -fprintf(['Downloading ' assetName '... ']); -try - websave(localPath,['https://github.com/SysBioChalmers/raven-data/releases/download/' releaseTag '/' assetName]); -catch ME - if strcmp(ME.identifier,'MATLAB:webservices:HTTP404StatusCodeError') - error('Failed to download %s, the server returned a 404 error, try again later. If the problem persists please report it on the RAVEN GitHub Issues page: https://github.com/SysBioChalmers/RAVEN/issues',assetName) - else - rethrow(ME); - end -end -fprintf('COMPLETE\n'); -end diff --git a/localization/defaultCompartmentMap.m b/localization/defaultCompartmentMap.m deleted file mode 100644 index a623e3a0..00000000 --- a/localization/defaultCompartmentMap.m +++ /dev/null @@ -1,48 +0,0 @@ -function map = defaultCompartmentMap() -% defaultCompartmentMap Default predictor/database compartment label -> model compartment id. -% -% Returns a containers.Map from lower-case predictor / database compartment labels (as used by -% DeepLoc, MULocDeep, COMPARTMENTS and UniProt) to compartment ids, tuned for yeast/fungal -% models (e.g. yeast-GEM codes). Pass it (or your own map) to parseScores / getUniProtScores as -% 'compartmentMap'. -% -% The table below is also the worked example of how such a map is built, so read it as three -% deliberate decisions rather than a lookup list: -% -% - **Which compartments exist.** A label absent from the map is dropped along with its score -% column, so listing only the ids your model has is how unwanted compartments are excluded. -% 'plastid' is absent because fungi lack one; a plant model would add it. -% - **Which labels are the same thing.** Predictors disagree on wording, so 'cytoplasm' and -% 'cytosol' both map to 'c', and 'mitochondrion' / 'mitochondria' / 'mitochondrial' all map -% to 'm'. Merged columns are combined by maximum, not sum. -% - **Which distinct compartments to collapse.** 'lysosome' maps to 'v' not because it is a -% vacuole but because a yeast model has no lysosome and the vacuole is its functional -% equivalent. Change this if your model separates them. -% -% Extend a copy rather than editing this function, e.g. -% -% map = defaultCompartmentMap; -% map('cell wall') = 'ce'; -% GSS = parseScores(file, 'compartmentMap', map); -% -% Returns -% ------- -% map : containers.Map -% label (char, lower case) -> compartment id (char). -% -% See also -% -------- -% parseScores, getUniProtScores, predictLocalization - -labels = {'cytoplasm','cytosol','nucleus','nucleoplasm','mitochondrion','mitochondria', ... - 'mitochondrial','peroxisome','endoplasmic reticulum','golgi apparatus','golgi', ... - 'vacuole','lysosome/vacuole','lysosome','extracellular','extracellular space', ... - 'extracellular region','secreted','cell membrane','plasma membrane','cell envelope', ... - 'lipid particle','lipid droplet'}; -ids = {'c','c','n','n','m','m', ... - 'm','p','er','g','g', ... - 'v','v','v','e','e', ... - 'e','e','ce','ce','ce', ... - 'lp','lp'}; -map = containers.Map(labels, ids); -end diff --git a/manipulation/reverseRxns.m b/manipulation/reverseRxns.m deleted file mode 100644 index efb5f6fc..00000000 --- a/manipulation/reverseRxns.m +++ /dev/null @@ -1,20 +0,0 @@ -function model=reverseRxns(model, rxns) -% reverseRxns -% Reverses the given reactions in place: negates their stoichiometry and -% objective coefficient, and swaps/negates their lower and upper bounds, -% so flux in the new positive direction is what was previously negative flux. -% model the model to change (input/output) -% rxns the rxns to reverse - -rxnInd = find(ismember(model.rxns,rxns)); - -%reverse the reactions -model.S(:,rxnInd) = model.S(:,rxnInd)*-1; -%swap the bounds -ub = model.ub(rxnInd); -model.ub(rxnInd) = -model.lb(rxnInd); -model.lb(rxnInd) = -ub; -%flip the objective -model.c(rxnInd) = -model.c(rxnInd); - -end diff --git a/reconstruction/kegg/keggDataVersion.m b/reconstruction/kegg/keggDataVersion.m deleted file mode 100644 index f8a8e9d0..00000000 --- a/reconstruction/kegg/keggDataVersion.m +++ /dev/null @@ -1,23 +0,0 @@ -function version=keggDataVersion() -% keggDataVersion The raven-data release tag for KEGG artefacts. -% -% Reads reconstruction/kegg/KEGG_VERSION.md --- the single place this -% version is recorded --- so every function that downloads a KEGG -% artefact (getModelFromKEGG, getKEGGModelForOrganism) stays in sync -% when raven-data publishes a new KEGG release. The version is the -% file's first line. -% -% Returns -% ------- -% version : char -% the raven-data release tag, e.g. 'kegg118'. - -ravenPath=findRAVENroot(); -versionFile=fullfile(ravenPath,'reconstruction','kegg','KEGG_VERSION.md'); -fid=fopen(versionFile,'r'); -if fid==-1 - error('keggDataVersion:fileNotFound','Cannot read %s.',strrep(versionFile,'\','/')) -end -version=strtrim(fgetl(fid)); -fclose(fid); -end diff --git a/INIT/INITStepDesc.m b/src/INIT/INITStepDesc.m similarity index 95% rename from INIT/INITStepDesc.m rename to src/INIT/INITStepDesc.m index 6bc3acb7..5f9fce4d 100644 --- a/INIT/INITStepDesc.m +++ b/src/INIT/INITStepDesc.m @@ -1,6 +1,6 @@ classdef INITStepDesc % Describes a step in the ftINIT algorithm. A cell array of objects of this -% class is used as input to ftINIT to specify how the algorithm should be run. +% class is the input to ftINIT that specifies how the algorithm is run. properties PosRevOff %If true, positive-scored reversible reactions are removed %from the MILP problem (used in step 1 of ftINIT) @@ -21,7 +21,7 @@ % b8 - All rxns without GPRs MetsToIgnore % Structure describing mets that can be removed from the model - % before running ftINIT, such as water etc. + % before running ftINIT, such as water and other ubiquitous metabolites. % .simpleMets % .mets Names of metabolites to remove % .compsToKeep Compartments for which metabolites should be kept. diff --git a/INIT/ftINIT.m b/src/INIT/ftINIT.m similarity index 73% rename from INIT/ftINIT.m rename to src/INIT/ftINIT.m index d4dcbd07..a81496c4 100644 --- a/INIT/ftINIT.m +++ b/src/INIT/ftINIT.m @@ -1,121 +1,147 @@ function [model, metProduction, addedRxnsForTasks, deletedRxnsInINIT, fullMipRes] = ftINIT(prepData, tissue, celltype, hpaData, varargin) % ftINIT Generate a model using the ftINIT algorithm. % -% Main function for generating a model using the ftINIT algorithm, based on -% proteomics and/or transcriptomics and/or metabolomics and/or metabolic tasks. -% The algorithm is not designed for running with metabolomics only. The -% function prepINITModel needs to be run first for the template model (such as -% the generic Human-GEM), but only needs to be run once. This function -% precalculates things independent of the omics to speed up the model -% generation process, and outputs the prepData, which is input to this +% Generates a model from proteomics, transcriptomics, metabolomics and +% metabolic tasks, in any combination except metabolomics alone. The function +% prepINITModel must be run first on the template model (such as the generic +% Human-GEM). It runs once per template and precalculates the parts that are +% independent of the omics data. Its output, prepData, is the input to this % function. % % Parameters % ---------- % prepData : struct -% the prepData for the model. +% the prepData for the model, as returned by prepINITModel. % tissue : char -% tissue to score for. Should exist in either hpaData.tissues or +% tissue to score for. Must exist in hpaData.tissues or % transcrData.tissues. % celltype : char -% cell type to score for. Should exist in either hpaData.celltypes or -% transcrData.celltypes for this tissue (default is to use the max values -% among all the cell types for the tissue). +% cell type to score for. Must exist in hpaData.celltypes or +% transcrData.celltypes for this tissue. If empty, the maximum value +% among all the cell types of the tissue is used. % hpaData : struct -% HPA data structure from parseHPA (optional if transcrData is supplied, -% default []). +% HPA data structure from parseHPA (required if transcrData is not +% supplied, default []). % % Name-Value Arguments % -------------------- % transcrData : struct -% gene expression data structure (optional if hpaData is supplied, default -% []). With fields: +% gene expression data structure (required if hpaData is not supplied, +% default []). With fields: % % - genes : cell array with the unique gene names. -% - tissues : cell array with the tissue names. The list may not be unique, -% as there can be multiple cell types per tissue. +% - tissues : cell array with the tissue names. The list can contain +% duplicates, as there can be multiple cell types per tissue. % - celltypes : cell array with the cell type names for each tissue. % - levels : GENESxTISSUES array with the expression level for each gene in -% each tissue/celltype. NaN should be used when no measurement was -% performed. +% each tissue and cell type. NaN marks a gene that was not measured. % - threshold : a single value or a vector of gene expression thresholds, -% above which genes are considered to be "expressed". (default 1; by -% default, the mean expression levels of each gene across all tissues in -% transcrData will be used as the threshold values). -% - singleCells : binary value selecting whether to use the single-cell -% algorithm to identify expressed genes. If used, specify cell -% subpopulations in CELLTYPES (default []). -% - plotResults : true if single cell probability distributions should be -% plotted (default false). +% above which genes are considered to be "expressed". If omitted, the +% mean expression level of each gene across all tissues in transcrData +% is used as the threshold. % metabolomicsData : cell % cell array with metabolite names that the model should produce (default % []). % INITSteps : cell % specifies the steps in the algorithm. For more info, see INITStepDesc and -% getINITSteps (default getINITSteps(), which is the standard ftINIT). +% getINITSteps (default getINITSteps([],'1+1'), the standard ftINIT). % removeGenes : logical -% if true, low-abundance genes will be removed from grRules, unless they +% if true, low-score genes are removed from grRules, unless they % are the only gene associated with a reaction, or a subunit of an enzyme % complex (see "removeLowScoreGenes" function for details). If false, -% grRules will not be modified; however, genes that were associated only -% with removed reactions will not be present in the final model (default -% true). +% grRules are not modified; genes that were associated only with removed +% reactions are not present in the final model (default true). % useScoresForTasks : logical -% true if the calculated reaction scores should be used as weights when -% fitting to tasks (default true). +% true if the calculated reaction scores are used as weights when +% fitting to tasks. Scores above -0.1 are set to -0.1 for this step +% (default true). % paramsFT : struct -% *obsolete option*. +% solver parameters for the gap-filling step that fits the tasks, passed +% to fitTasks as params (default []). % verbose : logical -% if true, the MILP progression will be shown (default false). +% if true, the MILP progression is shown (default false). % resolveTies : logical -% if true, pin each step's degenerate MILP to a canonical answer instead -% of relying on the solver Seed alone: hold the score objective at its -% optimum, then minimise the count of removable ("on") reactions, then, -% among the sparsest, minimise their summed reaction-id rank. Applies to -% every main-extraction step (ftINITInternalAlg) and to the final -% gap-filling step (fitTasks' resolveTies) -- see raven-gecko-parity#104 -% and raven-toolbox's resolve_ties (default false). +% if true, the degenerate MILP of each step is solved to a canonical +% answer in addition to the solver Seed: the score objective is held at +% its optimum, then the count of removable ("on") reactions is +% minimised, then, among the sparsest solutions, their summed +% reaction-id rank is minimised. This applies to every main-extraction +% step and to the final gap-filling step (resolveTies of fitTasks); see +% raven-gecko-parity#104 and resolve_ties in raven-toolbox. In each +% main-extraction step except the last (ftINITInternalAlg), the fluxes +% set the direction and magnitude at which a reaction is forced in the +% next step. They are taken from the least-total-flux solution for that +% on/off state and, among those, from the solution with the least +% id-rank-weighted backward flux (a merged reaction is ranked by the +% smallest original id in its group). The last step skips these flux +% phases, as no later step reads its fluxes. A step whose primary solve +% stops at the time limit keeps its primary solution without any of +% these phases. The count and id-rank phases get half of the step's +% TimeLimit each. A warning names each phase that does not prove optimal +% or finds no solution. Measured with raven-toolbox's ftinit, whose phase +% handling differs from ftINIT, on one cell line (Human-GEM v2.0.0-38, +% seeds 1234 and 7, time limit 1800 s): two seeds with the default +% escalation differ by 110 reactions and 17 predicted essential genes, +% and with resolveTies alone by 41 reactions and 1 gene, at about 3.6 +% times the run time (about 14 min against 4 min) (default false). % proveAbsGap : double -% if set, prove each step to this fixed *absolute* MILP gap in a single -% solve, instead of the relative-gap escalation below (stp.MILPParams). -% The escalation's final near-zero-objective run otherwise accepts an -% arbitrary within-gap incumbent -- measured on genome-scale Human-GEM to -% return a solution 2.0-4.0 objective units below the true optimum (see -% raven-toolbox's prove_abs_gap and the ftINIT reproducibility study on -% raven-docs). 1.0 is the recommended value: tighter (down to 0.05) -% proves nothing further and costs substantially more runtime. Pairs -% naturally with resolveTies (default [], i.e. use the escalation). +% if set, each step is proven to this fixed absolute MILP gap in a single +% solve, replacing the relative-gap escalation in stp.MILPParams. The +% final near-zero-objective run of the escalation accepts an arbitrary +% incumbent within the gap. A warning is raised when a step reaches the +% time limit before the proof. The figures below were measured with +% raven-toolbox's ftinit, whose phase handling differs from ftINIT, on one +% cell line (Human-GEM v2.0.0-38, time limit 1800 s) with resolveTies on +% and without gap-filling, in a sweep over four seeds (1234, 7, 1 and 2). +% The step 1 objective of the escalation ended 4.0 to 12.0 units below the +% proven optimum; with seeds 1234 and 7 alone it ended 7.0 and 12.0 +% below. A value of 5.0 stopped 2.0 below the optimum for one seed (seed +% 2), and values from 2.0 to 0.05 reached it for all four seeds and gave +% the same models. Every primary solve of the steps (48 solves) ended +% optimal; the flux phases phase2c-minflux (3 of 4 seeds) and +% phase2d-direction (4 of 4 seeds) reached the time limit and were adopted +% as unproven incumbents. With 1.0 and resolveTies the models of two seeds +% differ by 1 reaction and 0 predicted essential genes, at about 26 times +% the run time of the default escalation (about 100 min against 4 min); +% the mean of a whole build was 5766 s at 0.05 and 5641 s at 1.0. The +% study recommends 1.0 together with resolveTies when the variation of +% resolveTies alone is too large and the run time is acceptable. Its +% full-pipeline runs used a time limit of 600 s per step and its +% determinism builds 1800 s (default [], which uses the escalation). % % Returns % ------- % model : struct % the resulting model structure. -% metProduction : double -% array that indicates which of the metabolites in metabolomicsData that -% could be produced. Note that this is before the gap-filling process to -% enable defined tasks. To see which metabolites that can be produced in -% the final model, use canExchange: -% -% - -2 : metabolite name not found in model. -% - -1 : metabolite found, but it could not be produced. -% - 1 : metabolite could be produced. +% metProduction : logical +% array from the last MILP step that indicates which of the metabolites in +% metabolomicsData could be produced (true) or not (false). Metabolites +% with a producing reaction that is essential are not part of the MILP +% and are not in the array. The array is determined before the +% gap-filling for the defined tasks. To see which metabolites can be +% produced in the final model, use canExchange. % addedRxnsForTasks : cell % cell array of the reactions which were added in order to perform the % tasks. % deletedRxnsInINIT : cell -% cell array of reactions deleted because they could not carry flux (INIT -% requires a functional input model). +% cell array of the reactions of the template model that are not in the +% final model, excluding exchange reactions (these are all kept). It +% contains the reactions removed by the MILP steps that gap-filling did +% not add back. % fullMipRes : struct % the solver results from the last MILP step run. % % Notes % ----- -% Exchange metabolites should normally not be removed from the model when using -% this approach, since checkTasks/fitTasks rely on putting specific constraints -% for each task. The INIT algorithm will remove exchange metabolites if any are +% The template model keeps its exchange metabolites, +% since checkTasks and fitTasks put specific constraints on them for each +% task. The INIT algorithm removes exchange metabolites if any are % present. Use importModel(file,false) to import a model with exchange % metabolites remaining. % +% ftINIT passes a prodWeight of 5 (the reward for each produced metabolite of +% metabolomicsData) to ftINITInternalAlg, whose own default is 0.5. +% p=parseRAVENargs(varargin, {'transcrData',[]; 'metabolomicsData',[]; 'INITSteps',[]; 'removeGenes',[]; 'useScoresForTasks',[]; 'paramsFT',[]; 'verbose',false; 'resolveTies',false; 'proveAbsGap',[]}); transcrData=p.transcrData; @@ -191,6 +217,18 @@ %essential rxn. fluxes = ones(length(prepData.minModel.rxns), 1).*0.1; +%resolveTies ranks a merged reaction by its group's smallest original id, +%which does not depend on which member names the merged reaction. +rankKeys = prepData.minModel.rxns; +[inRef, refInd] = ismember(rankKeys, prepData.refModel.rxns); +for i = find(inRef)' + g = prepData.groupIds(refInd(i)); + if g ~= 0 + members = sort(prepData.refModel.rxns(prepData.groupIds == g)); + rankKeys{i} = members{1}; + end +end + for initStep = 1:length(INITSteps) disp(['ftINIT: Running step ' num2str(initStep)]) stp = INITSteps{initStep}; @@ -199,6 +237,9 @@ error('RAVEN:badInput', '%s', 'RxnsToIgnoreMask may not cover rxns not covered in previous steps, but the other way around is fine.'); end rxnsToIgnoreLastStep = stp.RxnsToIgnoreMask; + %The fluxes of a step set the forced directions of the next one, so the + %last step skips the flux phases of resolveTies. + pinFluxes = initStep < numel(INITSteps); mm = prepData.minModel; @@ -244,10 +285,10 @@ lastError = []; if ~isempty(proveAbsGap) %One solve proven to this fixed absolute gap, replacing the relative-gap - %escalation below. The escalation's final near-zero-objective run otherwise - %accepts an arbitrary within-gap incumbent -- measured on genome-scale - %Human-GEM to cost 2.0-4.0 objective units (raven-toolbox's prove_abs_gap, - %raven-gecko-parity#104). Pairs naturally with resolveTies. + %escalation below. The final near-zero-objective run of the escalation + %accepts an arbitrary incumbent within the gap; on genome-scale Human-GEM + %the step 1 objective of the escalation ended 4.0 to 12.0 units below the + %proven optimum (raven-toolbox's prove_abs_gap, raven-gecko-parity#104). if ~isempty(stp.MILPParams) && isfield(stp.MILPParams{1}, 'TimeLimit') params = struct('TimeLimit', stp.MILPParams{1}.TimeLimit); else @@ -256,7 +297,7 @@ params.MIPGap = 0; params.MIPGapAbs = proveAbsGap; try - [deletedRxnsInINIT1, metProduction,fullMipRes,rxnsTurnedOn1,fluxes1] = ftINITInternalAlg(mm,rxnScores,metData,essentialRxns,5,stp.AllowMetSecr,stp.PosRevOff,params, [], fluxes, verbose, 0.1, resolveTies); + [deletedRxnsInINIT1, metProduction,fullMipRes,rxnsTurnedOn1,fluxes1] = ftINITInternalAlg(mm,rxnScores,metData,essentialRxns,5,stp.AllowMetSecr,stp.PosRevOff,params, [], fluxes, verbose, 0.1, resolveTies, rankKeys, pinFluxes); fluxes1(toRev) = -fluxes1(toRev); success = true; if fullMipRes.stat ~= 1 @@ -310,7 +351,7 @@ if ~isempty(fullMipRes) startVals = fullMipRes.full; end - [deletedRxnsInINIT1, metProduction,fullMipRes,rxnsTurnedOn1,fluxes1] = ftINITInternalAlg(mm,rxnScores,metData,essentialRxns,5,stp.AllowMetSecr,stp.PosRevOff,params, startVals, fluxes, verbose, 0.1, resolveTies); + [deletedRxnsInINIT1, metProduction,fullMipRes,rxnsTurnedOn1,fluxes1] = ftINITInternalAlg(mm,rxnScores,metData,essentialRxns,5,stp.AllowMetSecr,stp.PosRevOff,params, startVals, fluxes, verbose, 0.1, resolveTies, rankKeys, pinFluxes); %This is a bit tricky - since we reversed some reactions, those fluxes also need to be reversed fluxes1(toRev) = -fluxes1(toRev); @@ -448,7 +489,7 @@ %be performed also without manipulating the b vector in the model (which is what is done in the gap-filling). exchRxns = getExchangeRxns(prepData.refModel); deletedRxnsInINIT = setdiff(prepData.refModel.rxns,union(union(initModel.rxns, addedRxnsForTasks), exchRxns)); -outModel = removeReactions(prepData.refModel, deletedRxnsInINIT, true); %we skip removing the genes for now, I'm not sure it is desirable +outModel = removeReactions(prepData.refModel, deletedRxnsInINIT, true, true); % If requested, attempt to remove negative-score genes from the model, % depending on their role (isozyme or complex subunit) in each grRule. diff --git a/INIT/ftINITInternalAlg.m b/src/INIT/ftINITInternalAlg.m similarity index 72% rename from INIT/ftINITInternalAlg.m rename to src/INIT/ftINITInternalAlg.m index 8a082cce..1665b356 100644 --- a/INIT/ftINITInternalAlg.m +++ b/src/INIT/ftINITInternalAlg.m @@ -1,78 +1,111 @@ -function [deletedRxns,metProduction,res,turnedOnRxns,fluxes]=ftINITInternalAlg(model,rxnScores,metData,essentialRxns,prodWeight,allowExcretion,remPosRev,params,startVals,fluxes,verbose,forceOn,resolveTies) +function [deletedRxns,metProduction,res,turnedOnRxns,fluxes]=ftINITInternalAlg(model,rxnScores,metData,essentialRxns,prodWeight,allowExcretion,remPosRev,params,startVals,fluxes,verbose,forceOn,resolveTies,rankKeys,pinFluxes) % ftINITInternalAlg -% This function runs the MILP for a step in ftINIT. +% Run the MILP for one step of ftINIT. % % model a reference model structure % rxnScores a vector of scores for the reactions in the model. % Positive scores are reactions to keep and negative -% scores are reactions to exclude. Rxns set to 0 are excluded -% from the problem. +% scores are reactions to exclude. Reactions with score 0 +% are not part of the problem % metData boolean matrix with mets as rows and rxns as columns % saying which reaction produces each detected met (optional, default []) % essentialRxns cell array of reactions that are essential and that -% have to be in the resulting model. This is normally -% used when fitting a model to task (see fitTasks) -% prodWeight a score that determines the value of having -% net-production of metabolites. This is a way of having -% a more functional network as it provides a reason for -% including bad reactions for connectivity reasons. This -% score is for each metabolite, and the sum of these weights -% and the scores for the reactions is what is optimized -% allowExcretion true if excretion of all metabolites should be allowed. +% have to be in the resulting model. ftINIT passes the +% reactions that the tasks require (see fitTasks) +% prodWeight a reward for each detected metabolite that has at least +% one producing reaction turned on; net production is not +% checked. The sum of these rewards and the scores for the +% reactions is optimised. The reward gives a reason for +% including reactions with a negative score for +% connectivity reasons, which gives a more functional +% network (default 0.5) +% allowExcretion true if excretion of all metabolites is allowed. % This results in fewer reactions being considered -% dead-ends, but all reactions in the resulting model may -% not be able to carry flux. If this is "false" then the +% dead-ends, but not every reaction in the resulting model +% can carry flux. If this is "false" then the % equality constraints are taken from model.b. If the -% input model lacks exchange reactions then this should -% probably be "true", or a large proportion of the model -% would be excluded for connectivity reasons +% input model lacks exchange reactions, set it to true; +% with false a large part of the model is excluded for +% connectivity reasons % remPosRev If true, the positive reversible reactions are removed from the problem. % This is used in step 1 of ftINIT % params parameters for the MILP, for example MIPGap and TimeLimit -% startVals Start values for the MILP, typically used when rerunning -% with a higher MIPGap, to use the results from the previous -% run -% fluxes Fluxes from the last run. -% verbose If true, the MILP progression will be shown. +% startVals Start values for the MILP. ftINIT passes the result of +% the previous run when it reruns the MILP with a higher +% MIPGap +% fluxes Fluxes from the last run. The flux of an essential +% irreversible reaction is forced to at least +% min(0.99*|flux|, 0.1), clamped to the reaction's capacity +% in the forced direction (its upper bound, as ftINIT +% passes reactions in the forward direction) +% verbose If true, the MILP progression is shown. % forceOn minimum flux magnitude enforced through a reaction once % it is turned on in the MILP (optional, default 0.1) -% resolveTies if true, pin the MILP's degenerate optimum to a canonical -% answer instead of relying on the solver Seed alone: hold -% the score objective at its optimum, then minimise the -% count of "on" removable (negative-score) reactions, then, -% among the sparsest, minimise their summed reaction-id -% rank. Positive-score reactions carry a continuous, not a -% free binary, on/off variable (pinned near 1 by the score -% objective), so only the negative-score ones are a genuine -% tie to resolve -- matches raven-toolbox's resolve_ties / -% _resolve_ties (raven-gecko-parity#104) (optional, default -% false) +% resolveTies if true, the MILP's degenerate optimum is solved to a +% canonical answer in addition to the solver Seed: the +% score objective is held at its optimum, then the count +% of "on" removable (negative-score) reactions is +% minimised, then, among the sparsest solutions, their +% summed reaction-id rank is minimised. Positive-score +% reactions have a continuous on/off variable (pinned near +% 1 by the score objective) and only the negative-score +% ones have a binary on/off variable, so only these are +% tied. The fluxes are then taken from the +% least-total-flux solution for that on/off state and, +% among those, from the one with the least id-rank-weighted +% backward flux, so the directions and forced magnitudes +% the next ftINIT step reads from them are fixed too. +% This follows resolve_ties in raven-toolbox +% (raven-gecko-parity#104). The phases are skipped when +% the primary solve stops at params.TimeLimit, and the +% primary solution is kept. Phases 2a and 2b get half of +% params.TimeLimit each (rounded down, at least 1 s), the +% flux phases the full limit. A warning names each phase +% that does not prove optimal or finds no solution; when a +% flux phase finds none, the fluxes of the phase before it +% are kept (optional, default false) +% rankKeys cell array with, for each reaction in model.rxns, the key +% that the id-rank phases of resolveTies sort it by +% (optional, default model.rxns). ftINIT passes the smallest +% original reaction id of each merge group, so the order +% does not depend on which member names the merged reaction +% pinFluxes if false, resolveTies skips phases 2c and 2d and the +% fluxes and metProduction are those of phase 2b +% (optional, default true). +% ftINIT passes false in its last step, whose fluxes no +% later step reads % % deletedRxns reactions which were deleted by the algorithm (only % rxns included in the problem) -% metProduction array that indicates which of the -% metabolites in presentMets that could be -% produced -% 0: metabolite could not be produced -% 1: metabolite could be produced +% metProduction logical array that indicates which of the metabolites in +% metData could be produced (true) or not (false). +% Metabolites with a producing reaction that is essential +% are not part of the MILP and are not in the array % res The result from the MILP % turnedOnRxns The reactions determined to be present (only % rxns included in the problem) % fluxes The fluxes from the MILP % -% This function is the actual implementation of the algorithm. See +% This function is the implementation of the algorithm. See % ftINIT for a higher-level function for model reconstruction. % -% Usage: [deletedRxns,metProduction,res,turnedOnRxns,fluxes]=runINIT9(model,... -% rxnScores,presentMets,essentialRxns,prodWeight,allowExcretion,... -% remPosRev,params) +% Usage: [deletedRxns,metProduction,res,turnedOnRxns,fluxes]=ftINITInternalAlg(model,... +% rxnScores,metData,essentialRxns,prodWeight,allowExcretion,... +% remPosRev,params,startVals,fluxes,verbose,forceOn,resolveTies,... +% rankKeys,pinFluxes) if nargin < 12 || isempty(forceOn) forceOn = 0.1; end +if nargin < 14 || isempty(rankKeys) + rankKeys = model.rxns; +end if nargin < 13 || isempty(resolveTies) resolveTies = false; end +if nargin < 15 || isempty(pinFluxes) + pinFluxes = true; +end if isempty(essentialRxns) essentialRxns={}; end @@ -445,7 +478,7 @@ if ~checkSolution(res) - if strcmp(res.origStat, 'TIME_LIMIT') + if res.hitTimeLimit EM='Time limit reached without finding a solution. Try increasing the TimeLimit parameter.'; else EM='The problem is infeasible'; @@ -453,7 +486,10 @@ error('RAVEN:badInput', '%s', EM); end -if resolveTies && (nNegIrrev + nNegRev) > 0 +if resolveTies && (nNegIrrev + nNegRev) > 0 && ~res.hitTimeLimit + %Skipped when the primary solve stopped at the time limit: the floor + %constraint would pin an unproven incumbent, so the phases cannot select + %among true optima. %This MILP is highly degenerate: many reaction subsets reach the same %score optimum, and the solver returns an arbitrary one -- reproducible %for a fixed solver build + Seed, but fragile to a solver version bump @@ -483,6 +519,10 @@ tieParams = params; tieParams.MIPGap = 0; tieParams.MIPGapAbs = 0.4; + %Phases 2a and 2b get half of the time limit each, as in raven-toolbox. + if isfield(tieParams,'TimeLimit') + tieParams.TimeLimit = max(1,floor(tieParams.TimeLimit*0.5)); + end unproven = {}; @@ -508,7 +548,7 @@ tieProb2b.b = [tieProb.b; kmin+0.5]; tieProb2b.csense = [tieProb.csense 'L']; - [~, sortOrder] = sort(model.rxns(negRxnIdx)); + [~, sortOrder] = sort(rankKeys(negRxnIdx)); ranks = zeros(numel(negRxnIdx),1); ranks(sortOrder) = (1:numel(negRxnIdx))'; idObj = zeros(size(prob.c)); @@ -522,6 +562,106 @@ if isFeasible2b %Adopt the tie-resolved solution for the on/off read-out below. res = res2b; + + %Phases 2c and 2d change the fluxes, not the on/off state; the next + %ftINIT step reads the fluxes, and pinFluxes is false when no step + %follows. + if pinFluxes + %Phase 2c: the least total absolute flux for this on/off state. + %The next step forces each turned-on reaction in the direction + %of its flux, at min(0.99*|flux|,0.1), so the flux read-out + %comes from a minimal-flux solution instead of an arbitrary + %vertex. A binary on/off variable is fixed at its value; a + %continuous one that is on keeps at least its value, one that + %is off at most its value. Directions of reversible scored + %reactions stay free, so this is still a MILP. + %It stops at a relative or absolute gap of 1e-6, as raven-toolbox's + %phase 2c. + nCols = numel(prob.c); + minProb = tieProb2b; + if isfield(minProb,'start') + minProb = rmfield(minProb,'start'); + end + x = res2b.full; + negVal = round(x(negCols)); + minProb.lb(negCols) = negVal; + minProb.ub(negCols) = negVal; + posCols = [onoffPosIrrev, onoffPosRev]; + posVal = min(max(x(posCols),0),1); + posOn = posVal >= 0.5; + minProb.lb(posCols(posOn)) = posVal(posOn); + minProb.ub(posCols(~posOn)) = posVal(~posOn); + %|v| is v for a forward-only flux, -v for a backward-only one, + %and an added t >= v, t >= -v otherwise. + fwdOnly = minProb.lb(1:nRxns) >= 0; + bwdOnly = ~fwdOnly & minProb.ub(1:nRxns) <= 0; + tRxns = find(~fwdOnly & ~bwdOnly); + nT = numel(tRxns); + selV = sparse(1:nT, tRxns, 1, nT, nCols); + minProb.A = [minProb.A sparse(size(minProb.A,1),nT); -selV speye(nT); selV speye(nT)]; + minProb.a = minProb.A; + minProb.b = [minProb.b; zeros(2*nT,1)]; + minProb.csense = [minProb.csense repmat('G',1,2*nT)]; + minProb.lb = [minProb.lb; zeros(nT,1)]; + minProb.ub = [minProb.ub; inf(nT,1)]; + minProb.vartype = [minProb.vartype repmat('C',1,nT)]; + minProb.c = [zeros(nCols,1); ones(nT,1)]; + minProb.c(fwdOnly) = 1; + minProb.c(bwdOnly) = -1; + minParams = params; + minParams.MIPGap = 1e-6; + minParams.MIPGapAbs = 1e-6; + res2c = optimizeProb(minProb,minParams,verbose); + [isFeasible2c, isOptimal2c] = checkSolution(res2c); + if ~isOptimal2c + unproven{end+1} = 'phase2c-minflux'; + end + if isFeasible2c + %Only the fluxes change; the status stays that of phase 2b. + res.full = res2c.full(1:nCols); + + %Phase 2d: the least total flux can leave a direction open + %when two directions carry the same total flux, for example + %a pair of reversible reactions closing a loop either way. + %Keep the on/off state of phase 2c, hold the total absolute + %flux at most at its phase-2c value plus + %max(1e-7*total, 1e-6), and minimise the backward flux + %max(0,-v) of the reactions whose flux can take either sign, + %weighted by id rank from the end: sorted by rankKeys, the + %first of n reactions gets weight n and the last 1, so a + %reaction with a lower id prefers its forward direction. + if nT > 0 + total = minProb.c' * res2c.full; + dirProb = minProb; + nCols2c = numel(minProb.c); + [~, ord] = sort(rankKeys(tRxns)); + w = zeros(nT,1); + w(ord) = (nT:-1:1)'; + dirProb.A = [minProb.A sparse(size(minProb.A,1),nT); ... + minProb.c' sparse(1,nT); ... + selV sparse(nT,nT) speye(nT)]; + dirProb.a = dirProb.A; + dirProb.b = [minProb.b; total + max(abs(total)*1e-7, 1e-6); zeros(nT,1)]; + dirProb.csense = [minProb.csense 'L' repmat('G',1,nT)]; + dirProb.lb = [minProb.lb; zeros(nT,1)]; + dirProb.ub = [minProb.ub; inf(nT,1)]; + dirProb.vartype = [minProb.vartype repmat('C',1,nT)]; + dirProb.c = [zeros(nCols2c,1); w]; + %The phase-2c solution is the MIP start: the tight cap on + %the total flux leaves the solver's heuristics little room + %to find a first solution. + dirProb.start = [res2c.full; max(0, -res2c.full(tRxns))]; + res2d = optimizeProb(dirProb,minParams,verbose); + [isFeasible2d, isOptimal2d] = checkSolution(res2d); + if ~isOptimal2d + unproven{end+1} = 'phase2d-direction'; + end + if isFeasible2d + res.full = res2d.full(1:nCols); + end + end + end + end end end if ~isempty(unproven) diff --git a/INIT/getINITSteps.m b/src/INIT/getINITSteps.m similarity index 78% rename from INIT/getINITSteps.m rename to src/INIT/getINITSteps.m index 7cbcdff1..afa7e051 100644 --- a/INIT/getINITSteps.m +++ b/src/INIT/getINITSteps.m @@ -1,38 +1,36 @@ function steps = getINITSteps(varargin) % getINITSteps Define the steps used as input to ftINIT. % -% Defines the steps in the way ftINIT is run. Note that all combinations of -% steps will not work. Avoid "exclude" if you want to define new -% ways to run the algorithm. +% Defines the steps in the way ftINIT is run. Not all combinations of steps +% work. A new series that sets the HowToUsePrevResults option to "exclude", +% which no predefined series uses, may not work. % % Name-Value Arguments % -------------------- % metsToIgnore : struct % structure describing mets that can be removed from the model before -% running ftINIT, such as water etc. With fields: +% running ftINIT, such as water and other ubiquitous metabolites. With fields: % % - simpleMets.mets : names of metabolites to remove. % - simpleMets.compsToKeep : compartments for which metabolites are kept. % -% (default []). +% (default []). Ignored for series "full". % series : char % describes the way to run ftINIT (default "1+1"): % -% - "1+1" : standard behavior. Step 1 and 2 described in the paper are +% - "1+1" : standard behaviour. Step 1 and 2 described in the paper are % merged into 1. % - "2+1" : the 3-step procedure described in the paper. Faster and -% slightly less accurate than "1+1 steps". -% - "1+0" : same as "1+1 steps", but skips step 3 described in the paper. -% This will result in a model including a lot of reactions without GPRs. -% It is particularly useful for structural comparison, since the -% reactions removed in step 3 may be a bit random and does not really add -% any extra information. Faster than "1+1 steps". -% - "2+0" : same as "2+1 steps", but skips step 3 described in the paper. -% Faster and slightly less accurate than "1+0 steps", but will yield -% similar results. -% - "full" : 1-step run - similar to the old tINIT version but without -% simplifications. Accurate, but very slow. This is mainly used for -% testing purposes. +% slightly less accurate than "1+1". +% - "1+0" : same as "1+1", but skips step 3 described in the paper. +% The model includes many reactions without GPRs. The series suits +% structural comparison, because the reactions removed in step 3 can +% be arbitrary and add no information. Faster than "1+1". +% - "2+0" : same as "2+1", but skips step 3 described in the paper. +% Faster and slightly less accurate than "1+0", with similar results. +% - "full" : 1-step run, similar to tINIT, without the model +% simplifications that ftINIT applies in the other series. Accurate, but +% slow. This series is used for testing. % % Returns % ------- diff --git a/INIT/groupRxnScores.m b/src/INIT/groupRxnScores.m similarity index 85% rename from INIT/groupRxnScores.m rename to src/INIT/groupRxnScores.m index 453af242..28e01e05 100644 --- a/INIT/groupRxnScores.m +++ b/src/INIT/groupRxnScores.m @@ -1,8 +1,8 @@ function newRxnScores = groupRxnScores(model, origRxnScores, origRxnIds, groupIds, origRxnsToZero) % groupRxnScores -% This function sums up the reaction scores for all reactions that were merged -% into one by the linear merge. -% +% Sums the reaction scores of all reactions that were merged into one by the +% linear merge. +% % model The model with linearly merged rxns. % origRxnScores The rxnScores from the model before the linear merge. % origRxnIds The rxn ids of the model before the linear merge. @@ -11,10 +11,11 @@ % the reaction was not merged with any other reaction. A % non-zero integer means the reaction was merged with all % other rxns sharing the same integer. -% origRxnsToZero A logical vector saying which of the original rxns that should not +% origRxnsToZero A logical vector saying which of the original rxns should not % be part of the problem; the score for these reactions is set to 0. -% To keep 0 uniquely meaning "excluded", any genuine rxnScore that is -% (or sums to) exactly 0 is nudged to 0.01 instead. +% Since 0 means "excluded", an original score of 0 and a group +% score that sums to 0 while at least one score of the group +% is not 0 are set to 0.01. newRxnScores = zeros(length(model.rxns),1); diff --git a/INIT/mergeLinear.m b/src/INIT/mergeLinear.m similarity index 89% rename from INIT/mergeLinear.m rename to src/INIT/mergeLinear.m index 068fab65..cf08d8ac 100644 --- a/INIT/mergeLinear.m +++ b/src/INIT/mergeLinear.m @@ -1,10 +1,10 @@ function [reducedModel,origRxnIds,groupIds,reversedRxns]=mergeLinear(model, noMergeRxns) % mergeLinear -% Simplifies a model by merging rxns with linear dependencies. There are two -% differences from the function in simplifyModel: -% 1) Here we return which reactions were merged -% 2) Here we allow reversible reactions to be merged as well, which will -% probably reduce the size of the model more. +% Simplifies a model by merging rxns with linear dependencies. It differs +% from the grouping in simplifyModel in two ways: +% 1) It returns which reactions were merged. +% 2) It also merges reversible reactions. +% The reduced model has no genes and empty grRules. % % model a model structure % noMergeRxns Cell array with reaction IDs that are not allowed to be @@ -13,10 +13,11 @@ % reducedModel an updated model structure % origRxnIds Cell array: original rxn ids, used together with the groupIds variable % groupIds Vector of ids: merged rxns have the same id, same length as origRxnIds -% 0 means a reaction was not merged. +% 0 means a reaction was not merged. The groups are numbered 1, 2, ... +% in the order of their first reaction in origRxnIds. % reversedRxns Vector of booleans, saying for each rxn if it is reversed in the combined rxns. % -% Usage: [reducedModel,origRxnIds,groupIds]=mergeLinear(model,noMergeRxns) +% Usage: [reducedModel,origRxnIds,groupIds,reversedRxns]=mergeLinear(model,noMergeRxns) reducedModel=model; @@ -196,7 +197,12 @@ notInUse=sum(reducedModel.S~=0,2)==0; reducedModel=removeMets(reducedModel,notInUse); end - + +%Number the groups in the order of their first reaction in origRxnIds, so +%the numbers do not depend on the order in which the reactions were merged +merged=groupIds~=0; +[~,~,groupIds(merged)]=unique(groupIds(merged),'stable'); + function [model1,reversedRxns1] = flipRxn(model1, rxnInd, groupIds1, reversedRxns1) model1.S(:,rxnInd) = model1.S(:,rxnInd)*-1; %swap the bounds @@ -208,10 +214,13 @@ %now take care of the reversedRxns - if this is a group, reverse all of the %reactions in the group in the reversedRxns index - they will all be reversed at the - %same time since they are the same rxn. - rxnIndices = rxnInd; - if groupIds1(rxnInd) > 0 - rxnIndices = find(groupIds1 == groupIds1(rxnInd)); + %same time since they are the same rxn. rxnInd indexes model1, which loses + %reactions after each pass, while groupIds1 and reversedRxns1 follow + %origRxnIds, so the reaction is looked up by its id. + origInd = origRxnIdMap(model1.rxns{rxnInd}); + rxnIndices = origInd; + if groupIds1(origInd) > 0 + rxnIndices = find(groupIds1 == groupIds1(origInd)); end reversedRxns1(rxnIndices) = ~reversedRxns1(rxnIndices); end diff --git a/omics/parseHPA.m b/src/INIT/parseHPA.m similarity index 93% rename from omics/parseHPA.m rename to src/INIT/parseHPA.m index 1207f49e..5baf8c2f 100755 --- a/omics/parseHPA.m +++ b/src/INIT/parseHPA.m @@ -10,8 +10,7 @@ % Name-Value Arguments % -------------------- % version : double -% accepted for backward compatibility but ignored; the format is now -% inferred from the column headers (default 19). +% ignored; the format is inferred from the column headers (default 19). % % Returns % ------- @@ -20,7 +19,7 @@ % % - genes : cell array with the unique gene IDs (Ensembl in v>=18) % - geneNames : cell array with the gene symbols (present in v>=18 only) -% - tissues : cell array with the tissue names (may not be unique; one +% - tissues : cell array with the tissue names (can repeat; one % entry per tissue-cell-type combination) % - celltypes : cell array with the cell type names for each tissue % - levels : cell array with the unique expression level labels diff --git a/omics/parseHPArna.m b/src/INIT/parseHPArna.m similarity index 95% rename from omics/parseHPArna.m rename to src/INIT/parseHPArna.m index 3d6e7631..a955cbdf 100755 --- a/omics/parseHPArna.m +++ b/src/INIT/parseHPArna.m @@ -10,8 +10,7 @@ % Name-Value Arguments % -------------------- % version : double -% accepted for backward compatibility but ignored; the format is now -% inferred from the column headers (default 19). +% ignored; the format is inferred from the column headers (default 19). % % Returns % ------- diff --git a/INIT/prepINITModel.m b/src/INIT/prepINITModel.m similarity index 85% rename from INIT/prepINITModel.m rename to src/INIT/prepINITModel.m index 21ed6d83..d2f96933 100644 --- a/INIT/prepINITModel.m +++ b/src/INIT/prepINITModel.m @@ -1,17 +1,20 @@ function prepData = prepINITModel(origRefModel, taskStruct, varargin) % prepINITModel Precompute omics-independent data for ftINIT. % -% Runs time-consuming calculation steps that are not dependent on the RNA-Seq -% data. +% Runs the calculation steps that do not depend on the omics data, once per +% template model. % % Parameters % ---------- % origRefModel : struct -% the model to use. Expected to be something such as Human-GEM, Mouse-GEM, -% etc. +% the template model, such as Human-GEM or Mouse-GEM. % taskStruct : struct -% the essential tasks. Can be loaded with for example +% the essential tasks, for example loaded with % taskStruct = parseTaskList('../data/metabolicTasks_Essential.txt'). +% The task ids must be character vectors: a non-text id or a missing id +% field raises RAVEN:badInput. Ids may repeat (for example as category +% codes) and may be empty; each task is one row group of the task list, +% and a task that cannot be performed is removed by its position. % % Name-Value Arguments % -------------------- @@ -19,31 +22,34 @@ % the spontaneous rxns (default {}). % convertGenes : logical % if true the genes are converted to gene names (from ENSEMBL). This -% relies on translateGrRules, which is distributed with Human-GEM rather -% than with RAVEN, so Human-GEM has to be on the MATLAB path (default -% false). +% uses translateGrRules, which is distributed with Human-GEM and not with +% RAVEN, so Human-GEM must be on the MATLAB path (default false). % customRxnsToIgnore : cell -% these reactions can be ignored in the ignore mask (specifying b7=1) -% (default {}). +% reactions that are left outside the MILP when bit b7 of the +% RxnsToIgnoreMask of INITStepDesc is 1 (default {}). % extComp : char -% name of the external compartment, typically 's' or 'e'. This is used for -% identifying exch and import rxns (default 'e'). +% name of the external compartment, such as 's' or 'e'. This is used for +% identifying exchange and import reactions (default 'e'). % skipScaling : logical -% if true the scaling step is not run on the minimal model. The scaling is -% there to remove large differences between the stoichiometric coefficients -% within a reaction, since such differences creates numerical issues in the -% ftINIT algorithm due to limitations in solver resolution. However, the -% current scaling step is also risky and may lead to that some reactions -% cannot carry flux, since it changes the stoichiometry of the reactions -% with large differences. If you experience problems where the solution is -% infeasible, it may be worth trying to turn off the scaling. Note that it -% is only the minModel that is scaled, the scaling will not be present in -% the final model (default false). +% if true the scaling step is not run on the minimal model. The scaling +% removes large differences between the stoichiometric coefficients +% within a reaction (see rescaleModelForINIT), since such differences +% create numerical issues in the ftINIT algorithm due to limitations in +% solver resolution. The scaling changes the stoichiometry of the +% reactions with large differences, and some reactions can become unable +% to carry flux, which can make the solution infeasible. If the solution +% is infeasible, turning the scaling off may help; the bounds then stay as in +% the template and the MILP caps the per-reaction big-M at 1000 (bigMCap +% in ftINITInternalAlg). In raven-toolbox's ftinit on Human-GEM (one cell +% line, big_m 1000) the pipeline without scaling was feasible but took +% 157 s against 83 s with scaling. The bounds of the scaled model are set to 1000 where the upper bound is above 0 and +% to -1000 where the lower bound is below 0; zero bounds stay 0. Only the +% minModel is scaled; the scaling is not present in the final model +% (default false). % runParallel : logical -% true to run the task-essentiality check in Step 3 (checkTasks) across -% parallel workers, since each task is independent of the others (see -% parallelWorkersRAVEN). Default false, matching the previous (serial) -% behaviour. +% true to run the task-essentiality check (checkTasks) across parallel +% workers, since each task is independent of the others (see +% parallelWorkersRAVEN) (default false, serial). % % Returns % ------- @@ -58,6 +64,17 @@ extComp=p.extComp; skipScaling=p.skipScaling; runParallel=p.runParallel; + +if ~isempty(taskStruct) + if ~isfield(taskStruct,'id') + error('RAVEN:badInput','The task structure must have an id field'); + end + taskIds = {taskStruct.id}; + if ~all(cellfun(@(x) ischar(x) && (isrow(x) || isempty(x)),taskIds)) + error('RAVEN:badInput','The task ids must be character vectors'); + end +end + disp('Step 1: Gene rules') [origRefModel.grRules, origRefModel.rxnGeneMat] = standardizeGrRules(origRefModel, true); diff --git a/INIT/rescaleModelForINIT.m b/src/INIT/rescaleModelForINIT.m similarity index 73% rename from INIT/rescaleModelForINIT.m rename to src/INIT/rescaleModelForINIT.m index 4082b70e..f0b02d37 100644 --- a/INIT/rescaleModelForINIT.m +++ b/src/INIT/rescaleModelForINIT.m @@ -1,21 +1,24 @@ function model=rescaleModelForINIT(model, varargin) % rescaleModelForINIT Rescale a model to avoid large stoichiometric differences. % -% Rescales the MILP problem in ftINIT to avoid large differences in flux -% magnitudes between reactions. Such differences cause among other things -% difficulties regarding tolerances for integer variables. For now it just -% scales down all reactions with high stoichiometric coefficients. +% Rescales the stoichiometric matrix for the MILP problem in ftINIT to reduce +% differences in flux magnitudes between reactions. Such differences cause, +% among other effects, difficulties with the tolerances for integer variables. +% In each reaction, each coefficient with an absolute value above +% maxStoichDiff times the smallest absolute coefficient of the reaction is +% set to that limit (keeping its sign). The reaction is then multiplied by a +% factor that sets the mean absolute coefficient to 1. % % Parameters % ---------- % model : struct -% the model to be modified (input and output). +% a model structure. % % Name-Value Arguments % -------------------- % maxStoichDiff : double -% all reactions with a stoichiometric coefficient higher than this will be -% scaled down (default 25). +% largest allowed ratio between the largest and the smallest absolute +% stoichiometric coefficient in a reaction (default 25). % % Returns % ------- diff --git a/omics/scoreModel.m b/src/INIT/scoreModel.m similarity index 91% rename from omics/scoreModel.m rename to src/INIT/scoreModel.m index 60380615..cd0ee606 100644 --- a/omics/scoreModel.m +++ b/src/INIT/scoreModel.m @@ -2,11 +2,8 @@ % scoreModel Score reactions and genes in a model with complex gene rules. % % Scores the reactions and genes in a model containing complex gene rules -% based on expression data from HPA and/or gene arrays. -% -% It is highly recommended that the model grRules are "cleaned" and verified by -% the "cleanModelGeneRules" function prior to scoring the model with -% scoreModel. +% based on expression data from HPA, gene arrays or both. The model grRules +% should be standardised with standardizeGrRules before scoring. % % Parameters % ---------- @@ -20,26 +17,27 @@ % []). With fields: % % - genes : cell array with the unique gene names. -% - tissues : cell array with the tissue names. The list may not be unique, -% as there can be multiple cell types per tissue. +% - tissues : cell array with the tissue names. The list can contain +% duplicates, as there can be multiple cell types per tissue. % - celltypes : cell array with the cell type names for each tissue. % - levels : GENESxTISSUES array with the expression level for each gene in -% each tissue/celltype. NaN should be used when no measurement was -% performed. +% each tissue and cell type. NaN marks a gene that was not measured. % - threshold : a single value or a vector of gene expression thresholds, -% above which genes are considered to be "expressed". (optional, by -% default, the mean expression levels of each gene across all tissues in -% arrayData will be used as the threshold values). +% above which genes are considered to be "expressed". If omitted, the +% mean expression level of each gene across all tissues in arrayData +% is used as the threshold. % tissue : char -% tissue to score for. Should exist in either hpaData.tissues or -% arrayData.tissues. +% tissue to score for. Must exist in either hpaData.tissues or +% arrayData.tissues (matched without regard to case); otherwise an error +% is raised. % % Name-Value Arguments % -------------------- % celltype : char -% cell type to score for. Should exist in either hpaData.celltypes or -% arrayData.celltypes for this tissue (default is to use the max values -% among all the cell types for the tissue). +% cell type to score for. Must exist in either hpaData.celltypes or +% arrayData.celltypes (matched without regard to case); otherwise an error +% is raised (default [], which uses all cell types of the tissue, +% combined as set by multipleCellScoring). % noGeneScore : double % score for reactions without genes (default -2). % isozymeScoring : char @@ -55,8 +53,9 @@ % ("max" or "average") (default "max"). % hpaLevelScores : struct % structure with numerical scores for the expression level categories from -% HPA. The structure should have a "names" and a "scores" field (default, -% see code for default scores). +% HPA. The structure has a "names" and a "scores" field. The default names +% are High, Medium, Low, None, Strong, Moderate, Weak, Negative and Not +% detected, with the scores 20, 15, 10, -8, 20, 15, 10, -8 and -8. % dataPrecedence : char % how hpaData takes precedence over arrayData when both are supplied % (default "gene"): @@ -73,8 +72,9 @@ % scores for each of the reactions in model. % geneScores : double % scores for each of the genes in model. Genes which are not in the -% dataset(s) have NaN as scores, which removeLowScoreGenes reads as "no -% evidence" and leaves in place, where a negative score would be pruned. +% dataset(s) have NaN as scores. removeLowScoreGenes reads NaN as "no +% evidence" and leaves the gene in place, and prunes genes with a +% negative score. % hpaScores : double % scores for each of the genes in model if only taking hpaData into % account. Genes which are not in the dataset(s) have -Inf as scores. diff --git a/INIT/tINIT/README.md b/src/INIT/tINIT/README.md similarity index 95% rename from INIT/tINIT/README.md rename to src/INIT/tINIT/README.md index 9b88823e..d7d875bc 100644 --- a/INIT/tINIT/README.md +++ b/src/INIT/tINIT/README.md @@ -13,7 +13,7 @@ appear to overlap they do not: | | tINIT | ftINIT | |---|---|---| | Entry point | `getINITModel` | `prepINITModel`, then `ftINIT` | -| Reaction scoring | `scoreModel` (isozyme/complex scoring fixed to `'max'`, `dataPrecedence` `'reaction'`) | `scoreModel` (`omics/`), `groupRxnScores` | +| Reaction scoring | `scoreModel` (isozyme/complex scoring fixed to `'max'`, `dataPrecedence` `'reaction'`) | `scoreModel` (`INIT/`), `groupRxnScores` | | Core MILP | `runINIT` | `ftINITInternalAlg`, scheduled by `getINITSteps` | | Task gap-filling | `fitTasks` | `fitTasks` (`gapFillMode` `'preMerged'`), `ftINITFillGaps` | | Gene pruning | inline in `getINITModel` | `removeLowScoreGenes` | @@ -24,7 +24,7 @@ solver layer, and the model-manipulation and I/O functions. Reaction scoring and task gap-filling look forked in the table above only because the two entry points call their shared functions with different -settings, not because two implementations exist. `scoreModel` (`omics/`) is +settings, not because two implementations exist. `scoreModel` (`INIT/`) is one function for both: `getINITModel` calls it with the fixed argument combination the original tINIT algorithm needs — a single operator for both `and`/`or` in a grRule, `dataPrecedence` `'reaction'`, and geneScores rewritten diff --git a/INIT/tINIT/getINITModel.m b/src/INIT/tINIT/getINITModel.m similarity index 87% rename from INIT/tINIT/getINITModel.m rename to src/INIT/tINIT/getINITModel.m index e2bd52db..c7f3551f 100644 --- a/INIT/tINIT/getINITModel.m +++ b/src/INIT/tINIT/getINITModel.m @@ -1,36 +1,36 @@ function [model, metProduction, essentialRxnsForTasks, addedRxnsForTasks, deletedDeadEndRxns, deletedRxnsInINIT, taskReport]=getINITModel(refModel, tissue, varargin) % getINITModel Generate a model using the original tINIT algorithm. % -% Generates a model using the tINIT algorithm, based on proteomics and/or -% transcriptomics and/or metabolomics and/or metabolic tasks (PLoS Comput Biol. -% 2012;8(5):e1002518, Mol Syst Biol. 2014;10:721). +% Generates a model using the tINIT algorithm, from proteomics, +% transcriptomics, metabolomics and metabolic tasks in any combination (PLoS +% Comput Biol. 2012;8(5):e1002518, Mol Syst Biol. 2014;10:721). % -% ftINIT is the recommended method for context-specific model extraction. Use -% this function to reproduce models built with the original tINIT. It remains -% supported, and issues a RAVEN:legacyMethod notice once per session, which +% ftINIT replaces this function for context-specific model extraction. This +% function is kept to reproduce models built with the original tINIT. It +% issues a RAVEN:legacyMethod notice once per session, which % warning('off','RAVEN:legacyMethod') silences. % -% Not all settings are reachable through this function; runINIT and fitTasks -% can be called individually for the ones that are not. +% Not all settings are available through this function; runINIT and fitTasks +% can be called individually to set the others. % % Parameters % ---------- % refModel : struct -% a model structure. The model should be in the closed form (no exchange -% reactions open). Import using import(filename,false). If the model is not -% loaded using importModel, it might be that there is no "unconstrained" -% field. In that case, manually add the field like: +% a model structure in the closed form (no exchange reactions open), +% imported with importModel(filename,false). A model that is not loaded +% with importModel can lack the "unconstrained" field, and the function +% then raises RAVEN:badInput. The field can be added with % model.unconstrained=false(numel(model.mets),1). % tissue : char -% tissue to score for. Should exist in either hpaData.tissues or -% arrayData.tissues. +% tissue to score for. Must exist in either hpaData.tissues or +% arrayData.tissues (scoreModel raises an error otherwise). % % Name-Value Arguments % -------------------- % celltype : char -% cell type to score for. Should exist in either hpaData.celltypes or -% arrayData.celltypes for this tissue (default is to use the best values -% among all the cell types for the tissue). +% cell type to score for. Must exist in either hpaData.celltypes or +% arrayData.celltypes (scoreModel raises an error otherwise). The default +% is to use the maximum values among all the cell types for the tissue. % hpaData : struct % HPA data structure from parseHPA (optional if arrayData is supplied, % default []). @@ -39,20 +39,19 @@ % []). With fields: % % - genes : cell array with the unique gene names. -% - tissues : cell array with the tissue names. The list may not be unique, -% as there can be multiple cell types per tissue. +% - tissues : cell array with the tissue names. The list can contain +% duplicates, as there can be multiple cell types per tissue. % - celltypes : cell array with the cell type names for each tissue. % - levels : GENESxTISSUES array with the expression level for each gene in -% each tissue/celltype. NaN should be used when no measurement was -% performed. +% each tissue and cell type. NaN marks a gene that was not measured. % - threshold : a single value or a vector of gene expression thresholds, -% above which genes are considered to be "expressed". (default, by -% default the mean expression levels of each gene across all tissues in -% arrayData will be used as the threshold values). +% above which genes are considered to be "expressed". If omitted, the +% mean expression level of each gene across all tissues in arrayData +% is used as the threshold. % - singleCells : binary value selecting whether to use the single-cell -% algorithm to identify expressed genes. If used, specify cell -% subpopulations in CELLTYPES (default []). -% - plotResults : true if single cell probability distributions should be +% algorithm to identify expressed genes. If used, the cell +% subpopulations are specified in celltypes (default []). +% - plotResults : true if single cell probability distributions are % plotted (default false). % metabolomicsData : cell % cell array with metabolite names that the model should produce (default @@ -60,33 +59,32 @@ % taskFile : char % a task list in Excel format. See parseTaskList for details (default []). % useScoresForTasks : logical -% true if the calculated reaction scores should be used as weights in the -% fitting to tasks (default true). +% true if the calculated reaction scores are used as weights in the +% fitting to tasks. Scores above -0.1 are set to -0.1 (default true). % printReport : logical -% true if a report should be printed to the screen (default true). +% true if a report is printed to the screen (default true). % taskStructure : struct -% task structure as from parseTaskList. Can be used as an alternative way -% to define tasks when Excel sheets are not suitable. Overrides taskFile -% (default []). +% task structure as returned by parseTaskList. It defines the tasks when +% Excel sheets are not used and overrides taskFile (default []). % params : struct % parameter structure for use by the INIT solver (default []). % paramsFT : struct % parameter structure for the fitTasks step (default []). % seed : double -% seed for the random number generator used to break ties among -% genes with equally uninformative (-Inf) scores, so which gene is -% kept is reproducible across runs (default: current rng state is -% left untouched). +% seed for the random number generator used to break ties among genes +% with equal uninformative (-Inf) scores, which makes the choice of the +% kept gene reproducible across runs (default [], which leaves the rng +% state unchanged). % % Returns % ------- % model : struct % the resulting model structure. % metProduction : double -% array that indicates which of the metabolites in metabolomicsData that -% could be produced. Note that this is before the gap-filling process to -% enable defined tasks. To see which metabolites that can be produced in -% the final model, use canExchange: +% array that indicates which of the metabolites in metabolomicsData could +% be produced. The array is determined before the gap-filling for the +% defined tasks. To see which metabolites can be produced in the final +% model, use canExchange: % % - -2 : metabolite name not found in model. % - -1 : metabolite found, but it could not be produced. @@ -114,13 +112,13 @@ % % Notes % ----- -% Exchange metabolites should normally not be removed from the model when using -% this approach, since checkTasks/fitTasks rely on putting specific constraints -% for each task. The INIT algorithm will remove exchange metabolites if any are -% present. Use importModel(file,false) to import a model with exchange -% metabolites remaining. +% The model keeps its exchange metabolites, since checkTasks +% and fitTasks put specific constraints on them for each task. The INIT +% algorithm removes exchange metabolites if any are present. Use +% importModel(file,false) to import a model with exchange metabolites +% remaining. % -% See Also +% See also % -------- % ftINIT, prepINITModel, runINIT, scoreModel diff --git a/INIT/tINIT/runINIT.m b/src/INIT/tINIT/runINIT.m similarity index 86% rename from INIT/tINIT/runINIT.m rename to src/INIT/tINIT/runINIT.m index 5cc71cbf..fb0d705c 100644 --- a/INIT/tINIT/runINIT.m +++ b/src/INIT/tINIT/runINIT.m @@ -1,15 +1,16 @@ function [outModel, deletedRxns, metProduction, fValue]=runINIT(model,varargin) % runINIT Solve the MILP of the original INIT algorithm. % -% Generates a model using the INIT algorithm, based on proteomics and/or -% transcriptomics and/or metabolomics and/or metabolic tasks. This function is -% the actual implementation of the algorithm; see getINITModel for the +% Generates a model using the INIT algorithm, from proteomics, +% transcriptomics, metabolomics and metabolic tasks in any combination. This +% function is the implementation of the algorithm; see getINITModel for the % higher-level reconstruction function, and PLoS Comput Biol. 2012;8(5):e1002518 % for the formulation. % -% ftINIT is the recommended method for context-specific model extraction, and -% solves a different MILP in ftINITInternalAlg. This function remains -% supported, and issues a RAVEN:legacyMethod notice once per session, which +% ftINIT replaces this function for context-specific model extraction and +% solves a different MILP in ftINITInternalAlg. This function is kept to +% reproduce models built with the original INIT. It issues a +% RAVEN:legacyMethod notice once per session, which % warning('off','RAVEN:legacyMethod') silences. % % Parameters @@ -28,31 +29,35 @@ % (default []). % essentialRxns : cell % cell array of reactions that are essential and that have to be in the -% resulting model. This is normally used when fitting a model to task (see -% fitTasks) (default []). +% resulting model. fitTasks passes the reactions that the tasks require +% (default []). % prodWeight : double -% a score that determines the value of having net-production of -% metabolites. This is a way of having a more functional network as it -% provides a reason for including bad reactions for connectivity reasons. -% This score is for each metabolite, and the sum of these weights and the -% scores for the reactions is what is optimized (default 0.5). +% a score for each metabolite that has net production. The sum of these +% weights and the scores for the reactions is optimised. Net production +% of metabolites gives a reason for including reactions with a negative +% score for connectivity reasons, which gives a more functional network +% (default 0.5). % allowExcretion : logical -% true if excretion of all metabolites should be allowed. This results in -% fewer reactions being considered dead-ends, but all reactions in the -% resulting model may not be able to carry flux. If this is "false" then +% true if excretion of all metabolites is allowed. This results in +% fewer reactions being considered dead-ends, but not every reaction in the +% resulting model can carry flux. If this is "false" then % the equality constraints are taken from model.b. If the input model -% lacks exchange reactions then this should probably be "true", or a large -% proportion of the model would be excluded for connectivity reasons +% lacks exchange reactions, set it to true; with false a large part of +% the model is excluded for connectivity reasons % (default false). % noRevLoops : logical -% true if reversible reactions should be constrained to only carry flux in -% one direction. This prevents reversible reactions from being wrongly -% assigned as connected (the forward and backward reactions can form a loop -% and therefore appear connected), but it makes the problem significantly -% more computationally intensive to solve (two more integer constraints per -% reversible reaction) (default false). +% true if reversible reactions are constrained to carry flux in only one +% direction. This prevents reversible reactions from being assigned as +% connected wrongly (the forward and backward reactions can form a loop +% and therefore appear connected). It adds two binary variables per +% reversible reaction to the problem, which makes the MILP larger and more +% computationally intensive (default false). % params : struct % parameter structure for use by optimizeProb. +% eps : double +% the lower flux bound of a reaction that is turned on, which also +% applies to the essential reactions (capped by their upper bound) +% (default 1). % % Returns % ------- @@ -71,7 +76,7 @@ % objective value (sum of (the negative of) reaction scores for the % included reactions and prodWeight*number of produced metabolites). % -% See Also +% See also % -------- % getINITModel, ftINITInternalAlg, ftINIT @@ -336,7 +341,7 @@ %Problem should not be infeasible, but it is possible that the time limit %was reached before finding any solutions. if ~checkSolution(res) - if strcmp(res.origStat, 'TIME_LIMIT') + if res.hitTimeLimit EM='Time limit reached without finding a solution. Try increasing the TimeLimit parameter.'; else EM='The problem is infeasible'; diff --git a/analysis/FSEOF.m b/src/analysis/FSEOF.m similarity index 79% rename from analysis/FSEOF.m rename to src/analysis/FSEOF.m index 627cfebf..85437177 100755 --- a/analysis/FSEOF.m +++ b/src/analysis/FSEOF.m @@ -3,10 +3,10 @@ % % Implements the Flux Scanning based on Enforced Objective Flux algorithm. % This function writes a tab-delimited file or prints to the command -% window. If an output has been specified (targets), it will also generate -% a structure indicating for each model reaction whether it is identified -% by FSEOF as a target and the slope of the reaction when switching from -% biomass formation to product formation. +% window. If an output is specified (targets), it also returns a structure +% indicating for each model reaction whether FSEOF identifies it as a target +% and the slope of the reaction when switching from biomass formation to +% product formation. % % Parameters % ---------- @@ -20,33 +20,35 @@ % Name-Value Arguments % -------------------- % iterations : double -% number of iterations (default 10). +% number of enforced target flux levels, equally spaced up to coefficient +% times the maximum target flux (default 10). % coefficient : double -% ratio of optimal target reaction flux, must be less than 1 -% (default 0.9). +% ratio of the maximum target reaction flux that is enforced at the last +% iteration, less than 1 (default 0.9). % outputFile : char % output filename (default prints to command window). % corrThreshold : double -% minimum absolute Pearson correlation coefficient between a -% reaction absolute flux and the enforced product flux for the -% reaction to be selected as a target. Values in [0, 1]; higher -% values require a more linear response and suppress LP alternative- -% optima noise (default 0.9). +% minimum absolute Pearson correlation coefficient between the absolute +% flux of a reaction and the enforced product flux for the reaction to be +% selected as a target. Values are in [0, 1]. The filter keeps reactions +% whose flux is close to linear in the enforced product flux, and a higher +% value keeps fewer reactions (default 0.9). % % Returns % ------- % targets : struct % structure with information for identified targets, with fields: % -% - logical : logical array, true for amplification targets (backward -% compatible alias for amplify). +% - logical : logical array, true for amplification targets (identical to +% amplify). % - amplify : logical array, reactions whose absolute flux increases % with enforced product flux. -% - knockdown : logical array, reactions driven toward but not to zero. -% - knockout : logical array, reactions driven to zero at max product. +% - knockdown : logical array, reactions whose absolute flux decreases but +% stays above zero at the highest enforced product flux. +% - knockout : logical array, reactions whose absolute flux decreases to +% zero at the highest enforced product flux. % - slope : numeric array with per-reaction regression slopes of -% absolute flux versus enforced product flux (all reactions, not -% just targets). +% absolute flux versus enforced product flux (for all reactions). % % Examples % -------- diff --git a/analysis/analyzeSampling.m b/src/analysis/analyzeSampling.m similarity index 89% rename from analysis/analyzeSampling.m rename to src/analysis/analyzeSampling.m index 56d52d34..d5da5e7c 100755 --- a/analysis/analyzeSampling.m +++ b/src/analysis/analyzeSampling.m @@ -8,14 +8,12 @@ % ---------- % Tex : double % a vector of t-scores for the change in gene expression for each -% reaction. This score could be the Student t between the two -% conditions, or you can calculate it from a p-value (by computing the -% inverse of the so called error function). If you choose the second -% alternative you should be aware that the transcripts that increased -% in expression level should have positive values and those who -% decreased in expression level should have negative values (the -% p-values only tell you if the fluxes changed or not but not in which -% direction). +% reaction. The score is the Student t between the two conditions, or +% is calculated from a p-value by the inverse of the error function. +% Transcripts that increased in expression level have positive values +% and transcripts that decreased have negative values. A p-value does +% not give the direction of the change, so a score calculated from a +% p-value takes its sign from the direction of the expression change. % df : double % the degrees of freedom in the t-test. % solutionsA : double @@ -28,8 +26,8 @@ % Name-Value Arguments % -------------------- % printResults : logical -% prints the most significant reactions in each category -% (default false). +% true to print the 10 reactions with the highest probability in each +% category (default false). % % Returns % ------- diff --git a/analysis/compareFluxes.m b/src/analysis/compareFluxes.m similarity index 76% rename from analysis/compareFluxes.m rename to src/analysis/compareFluxes.m index 4e9e303b..26361dfa 100644 --- a/analysis/compareFluxes.m +++ b/src/analysis/compareFluxes.m @@ -1,5 +1,5 @@ function result = compareFluxes(model, fluxes1, fluxes2, varargin) -% compareFluxes Find the most significant flux changes between two conditions. +% compareFluxes Find the reactions whose flux differs between two flux vectors. % % Compares two flux vectors from the same model and returns all reactions % that changed, sorted by absolute flux difference. Classifies reactions as @@ -8,22 +8,23 @@ % Parameters % ---------- % model : struct -% RAVEN model structure. +% a model structure. % fluxes1 : double -% reference flux vector (condition A, e.g. wild-type). +% reference flux vector (condition A, such as wild-type). % fluxes2 : double -% comparison flux vector (condition B, e.g. mutant or knockin). +% comparison flux vector (condition B, such as a mutant or knock-in). % % Name-Value Arguments % -------------------- % cutoff : double -% minimum |flux| to consider a reaction active (default 1e-8). +% minimum |flux| for a reaction to be active, and minimum |flux2 - flux1| +% for a reaction to be changed (default 1e-8). % metaboliteList : cell % cell array of metabolite names. Only reactions involving at least one -% of these metabolites are compared; every other reaction is left out of -% the result entirely. Matching is against model.metNames and is case -% insensitive. A name that matches no metabolite is reported as a warning -% rather than an error (default all reactions). +% of these metabolites are compared; all other reactions are excluded +% from every field of the result. Matching is against model.metNames and +% is case insensitive. A name that matches no metabolite is reported as +% a warning (default all reactions). % nMax : double % maximum rows printed in the summary table (default 20). % verbose : logical @@ -32,18 +33,25 @@ % Returns % ------- % result : struct -% .turnedOn — cell, rxn IDs newly active in fluxes2 -% .turnedOff — cell, rxn IDs silenced in fluxes2 -% .flipped — cell, rxn IDs that reversed sign between conditions -% .changed — struct with fields (all reactions with |dflux|>cutoff, -% sorted by |dflux| descending): -% .rxn — cell of reaction IDs -% .flux1 — double, flux in condition A -% .flux2 — double, flux in condition B -% .absDelta — double, |flux2 - flux1| -% .relChange — double, (flux2-flux1)/|flux1|; NaN when -% flux1 is near-zero (reaction emerged) -% .type — cell, 'on'|'off'|'flip'|'' per reaction +% structure with fields: +% +% - turnedOn : cell array with the IDs of reactions that are active in +% fluxes2 and not in fluxes1. +% - turnedOff : cell array with the IDs of reactions that are active in +% fluxes1 and not in fluxes2. +% - flipped : cell array with the IDs of reactions that are active in +% both conditions and change sign. +% - changed : struct with fields for all reactions with |flux2 - flux1| +% above cutoff, sorted by |flux2 - flux1| descending: +% +% - rxn : cell array with the reaction IDs. +% - flux1 : double array with the flux in condition A. +% - flux2 : double array with the flux in condition B. +% - absDelta : double array with |flux2 - flux1|. +% - relChange : double array with (flux2 - flux1)/|flux1|. NaN when +% |flux1| is below cutoff. +% - type : cell array with 'on', 'off', 'flip' or '' (changed +% magnitude) for each reaction. % % Examples % -------- @@ -55,7 +63,8 @@ % result = compareFluxes(model, solA.x, solB.x, 'metaboliteList', {'ATP','NADH'}) % % See also -% walkFluxes, traceFluxPath, modelSummary +% -------- +% walkFluxes, traceFluxPath, modelSummary p = parseRAVENargs(varargin, {'cutoff',1e-8; 'metaboliteList',[]; 'nMax',20; 'verbose',true}); cutoff = p.cutoff; diff --git a/analysis/findGeneDeletions.m b/src/analysis/findGeneDeletions.m similarity index 77% rename from analysis/findGeneDeletions.m rename to src/analysis/findGeneDeletions.m index dd0e6602..e944e2de 100755 --- a/analysis/findGeneDeletions.m +++ b/src/analysis/findGeneDeletions.m @@ -1,8 +1,9 @@ function [genes, fluxes, originalGenes, details, grRatioMuts]=findGeneDeletions(model,varargin) % findGeneDeletions Delete genes and track the resulting fluxes. % -% Deletes genes, optimizes the model by FBA, and keeps track of the -% resulting fluxes. This is used for identifying gene deletion targets. +% Deletes single genes or gene pairs, solves the model by FBA after each +% deletion, and returns the resulting fluxes. The output identifies gene +% deletion targets. % % Parameters % ---------- @@ -12,7 +13,7 @@ % Name-Value Arguments % -------------------- % testType : char -% single/double gene deletion (default "sgd"): +% type of gene deletion (default "sgd"): % % - "sgd" : single gene deletion % - "dgd" : double gene deletion @@ -20,7 +21,7 @@ % Returns % ------- % genes : double -% a matrix with the genes that were deleted in each optimization (the +% a matrix with the genes that were deleted in each simulation (the % gene indexes in originalGenes). Each row corresponds to a column in % fluxes. % fluxes : double @@ -28,23 +29,23 @@ % an unsolvable problem have all zero flux. Single deletions that % result in an unsolvable problem are indicated in details instead. % originalGenes : cell -% simply the genes in the input model. Included for simple -% presentation of the output. +% the genes in the input model. % details : double -% not all genes will be deleted in all analyses. It is for example not -% necessary to delete genes for dead end reactions. This is a vector -% with details about each gene in originalGenes and why or why not it -% was deleted: +% a vector with one element per gene in originalGenes that states +% whether the gene was deleted and why. Genes that only belong to +% dead-end reactions, for example, are not deleted: % -% - 1 : was deleted -% - 2 : proved lethal in sgd (single gene deletion) -% - 3 : redundant, no longer used +% - 1 : deleted, and the problem was solved (a lethal deletion +% with zero growth also gets 1) +% - 2 : the single-deletion problem had no optimal solution +% (infeasible or failed); fluxes and grRatioMuts are 0 +% - 3 : redundant (not assigned) % - 4 : involved in dead-end reaction % grRatioMuts : double % growth rate ratio between mutated strain and wild type, matching the -% originalGenes(genes) mutants. Note that this does not directly map -% to model.genes, as is the case for COBRA getEssentialGenes. However, -% this can be obtained by afterwards running: +% originalGenes(genes) mutants. The elements do not map directly to +% model.genes, unlike the output of COBRA getEssentialGenes. The +% mapping to model.genes is obtained with: % % grRatio=zeros(1,numel(model.genes)); % grRatio(genes)=grRatioMuts; @@ -112,7 +113,7 @@ [~, I]=ismember(originalGenes(details==1),model.genes); genesToModify=nchoosek(I,2); genes=geneMapping(genesToModify); - grRatioMuts=zeros(1,numel(genes)); + grRatioMuts=zeros(1,size(genesToModify,1)); fluxes=sparse(numel(model.rxns),size(genesToModify,1)); for i=1:size(genesToModify,1) tempModel=removeGenes(model,genesToModify(i,:),false,false,false); diff --git a/analysis/getAllSubGraphs.m b/src/analysis/getAllSubGraphs.m similarity index 89% rename from analysis/getAllSubGraphs.m rename to src/analysis/getAllSubGraphs.m index e8a869ef..17f6cdac 100755 --- a/analysis/getAllSubGraphs.m +++ b/src/analysis/getAllSubGraphs.m @@ -11,9 +11,9 @@ % Returns % ------- % subGraphs : logical -% a boolean matrix where the rows correspond to the metabolites and -% the columns to which subgraph they are assigned to. The columns are -% ordered so that larger subgraphs come first. +% a logical matrix with one row per metabolite and one column per +% subgraph. An element is true if the metabolite belongs to the +% subgraph. The columns are ordered by decreasing subgraph size. % % Examples % -------- diff --git a/analysis/getAllowedBounds.m b/src/analysis/getAllowedBounds.m similarity index 79% rename from analysis/getAllowedBounds.m rename to src/analysis/getAllowedBounds.m index 41d9b08e..68a88805 100755 --- a/analysis/getAllowedBounds.m +++ b/src/analysis/getAllowedBounds.m @@ -13,12 +13,11 @@ % number of elements as reactions in the model, or a vector of % reaction indexes (default model.rxns). % runParallel : logical -% speed up calculations by parallel processing. This is not beneficial -% if allowed bounds are calculated for only a few reactions, as the -% overhead of parallel processing will take longer. It requires MATLAB -% Parallel Computing Toolbox. If this is not installed, the -% calculations will not be parallelized, regardless of what is -% indicated as runParallel (default true). +% run the calculations in parallel (default true). Parallel processing +% requires the MATLAB Parallel Computing Toolbox; without it, the +% calculations run serially regardless of this setting. For a few +% reactions, the overhead of parallel processing exceeds the time it +% saves. % % Returns % ------- @@ -27,8 +26,9 @@ % maxFluxes : double % maximal allowed fluxes. % exitFlags : double -% exit flags for min/max for each of the reactions. True if it was -% possible to calculate a flux. +% a matrix with one row per reaction. Column 1 holds the solver status +% of the minimisation and column 2 the status of the maximisation +% (1 if a flux could be calculated). % % Notes % ----- diff --git a/analysis/getEssentialRxns.m b/src/analysis/getEssentialRxns.m similarity index 100% rename from analysis/getEssentialRxns.m rename to src/analysis/getEssentialRxns.m diff --git a/analysis/getFluxZ.m b/src/analysis/getFluxZ.m similarity index 83% rename from analysis/getFluxZ.m rename to src/analysis/getFluxZ.m index f705cfe3..3f28155d 100755 --- a/analysis/getFluxZ.m +++ b/src/analysis/getFluxZ.m @@ -13,9 +13,12 @@ % Returns % ------- % Z : double -% a vector with Z-scores that tells you for each reaction how likely it -% is for its flux to have increased (positive sign) or decreased -% (negative sign) in the second condition with respect to the first. +% a vector with one Z-score per reaction: the difference between the +% mean fluxes of solutionsB and solutionsA, divided by the square root +% of the sum of their variances. A positive sign indicates an increase +% of the flux in the second condition with respect to the first, a +% negative sign a decrease. Values are limited to the range -100 to +% 100. % % Examples % -------- diff --git a/analysis/getMinimalMedium.m b/src/analysis/getMinimalMedium.m similarity index 83% rename from analysis/getMinimalMedium.m rename to src/analysis/getMinimalMedium.m index 754c8c30..b4c7c496 100644 --- a/analysis/getMinimalMedium.m +++ b/src/analysis/getMinimalMedium.m @@ -2,21 +2,22 @@ % getMinimalMedium Find the minimal set of uptake reactions for growth. % % Uses MILP to find the smallest set of exchange reactions that can carry -% uptake flux (lb < 0) while still allowing the model to achieve a target -% growth rate. Unlike iterative heuristics, the MILP formulation finds the -% globally minimal medium. +% uptake flux (lb < 0) while the model achieves a target growth rate. The +% MILP formulation gives a medium with the globally minimal number of +% uptake reactions. % % Parameters % ---------- % model : struct -% RAVEN model structure. +% a model structure. % % Name-Value Arguments % -------------------- % minGrowth : double -% minimum objective value (growth rate) the medium must support. -% Defaults to 10 % of the unconstrained optimum computed automatically. -% Set explicitly to avoid an extra FBA call (e.g. 'minGrowth', 0.1). +% minimum objective value (growth rate) the medium must support. If +% empty, 10 % of the optimum objective value under the current bounds, +% which requires one extra FBA call. A value given explicitly avoids +% that call (for example 'minGrowth', 0.1) (default empty). % verbose : logical % print the resulting minimal medium (default true). % @@ -36,9 +37,8 @@ % % Notes % ----- -% Only exchange reactions with lb < 0 are considered as candidate uptake -% reactions. Exchange reactions that are always secretion-only (lb >= 0) or -% that are completely blocked are not included. +% Only exchange reactions with lb < 0 are candidate uptake reactions. +% Exchange reactions with lb >= 0 are not included. % % The MILP formulation: % minimise sum(y_i) @@ -50,6 +50,16 @@ % % A y_i = 0 forces reaction i to carry non-negative flux (no net uptake); % y_i = 1 allows it to carry flux as low as lb_i. +% +% The number of uptake reactions is minimal, but the medium is not +% necessarily unique: when several media of the same size support the +% growth rate, the solver returns one of them, and another solver or +% solver version can return a different one. +% +% The MILP is solved with the solver set by setRavenSolver. RAVEN's bundled +% GLPK solves it for small models. The RAVEN:glpkMILP warning issued with +% GLPK states that GLPK works for small problems and that Gurobi is much +% faster and more reliable for larger ones (setRavenSolver('gurobi')). p = parseRAVENargs(varargin, {'minGrowth',[]; 'verbose',true}); minGrowth = p.minGrowth; diff --git a/analysis/haveFlux.m b/src/analysis/haveFlux.m similarity index 62% rename from analysis/haveFlux.m rename to src/analysis/haveFlux.m index 8c005dca..c76a020a 100755 --- a/analysis/haveFlux.m +++ b/src/analysis/haveFlux.m @@ -1,9 +1,11 @@ function I=haveFlux(model,varargin) % haveFlux Check which reactions can carry a flux. % -% Checks which reactions can carry a (positive or negative) flux. Is used as -% a faster version of getAllowedBounds if it is only interesting whether the -% reactions can carry a flux or not. +% Checks which reactions can carry a (positive or negative) flux under the +% model's current bounds; other media or bounds are not explored. Unlike +% getAllowedBounds, which minimises and maximises every reaction, it skips a +% reaction once an earlier solution has given it a flux, and minimises only +% reversible reactions that still have none. % % Parameters % ---------- @@ -13,22 +15,22 @@ % Name-Value Arguments % -------------------- % cutOff : double -% the flux value that a reaction has to carry to be identified as -% positive (default 10^-6). +% the absolute flux value that a reaction has to exceed to be identified +% as carrying flux (default 10^-6). % rxns : cell or logical or double % either a cell array of IDs, a logical vector with the same number of -% elements as metabolites in the model, or a vector of indexes (default +% elements as reactions in the model, or a vector of indexes (default % model.rxns). % seed : double -% seed for the random number generator used to shuffle the order in -% which reactions are tested, so that order is reproducible across -% runs (default: current rng state is left untouched). +% seed for the random number generator that shuffles the order in +% which reactions are tested. The order is then reproducible across +% runs (default: the current rng state is left untouched). % % Returns % ------- % I : logical % logical array with true if the corresponding reaction can carry a -% flux. +% flux under the tested bounds. % % Examples % -------- @@ -36,8 +38,13 @@ % % Notes % ----- -% If a model has +/- Inf bounds then those are replaced with an arbitary -% large value of +/- 10000 prior to solving. +% Bounds of +/- Inf are replaced with +/- 10000 before solving. +% +% Dead-end reactions are removed (simplifyModel) before solving. A dead-end +% reaction with a forced flux (lb > 0 or ub < 0) makes the model infeasible, +% but it is removed like any other dead end, so the remaining reactions are +% tested as if it were absent. A forced flux elsewhere in an infeasible +% model gives false for all reactions. p=parseRAVENargs(varargin, {'cutOff',10^-6; 'rxns',[]; 'seed',[]}); if ~isempty(p.seed) @@ -66,6 +73,13 @@ %First remove all dead-end reactions smallModel=simplifyModel(model,false,false,true,true); +%Nothing is left to solve when every reaction is a dead end, and reactions +%without metabolites are limited by their bounds only +if isempty(smallModel.rxns) || isempty(smallModel.mets) + I=ismember(rxns,smallModel.rxns(smallModel.ub>cutOff | smallModel.lb<-cutOff)); + return +end + %Get the indexes of the reactions in the connected model indexes=getIndexes(smallModel,intersect(smallModel.rxns,rxns),'rxns'); J=false(numel(indexes),1); diff --git a/analysis/randomSampling.m b/src/analysis/randomSampling.m similarity index 79% rename from analysis/randomSampling.m rename to src/analysis/randomSampling.m index 95f674e6..2f1360dd 100755 --- a/analysis/randomSampling.m +++ b/src/analysis/randomSampling.m @@ -5,18 +5,19 @@ % 'achr' (default) Artificially Centered Hit-and-Run; near-uniform MCMC % sampling of the polytope interior (see sampleACHR). % 'chrr' Coordinate Hit-and-Run with Rounding; near-uniform MCMC -% with maximum-volume-ellipsoid rounding, for better mixing -% on thin/ill-conditioned polytopes such as enzyme- -% constrained models (see sampleCHRR). +% with maximum-volume-ellipsoid rounding, which improves +% mixing on thin or ill-conditioned polytopes such as +% enzyme-constrained models (see sampleCHRR). % 'randomObjective' the random-objective vertex method of Bordel et al. (2010) % PLoS Comput Biol (doi:10.1371/journal.pcbi.1000859): each -% sample maximises a small random objective, returning a -% polytope vertex. +% sample optimises a random objective of nObjectives +% reactions with random weights, returning a polytope +% vertex. % -% The 'achr'/'chrr' methods draw the (near-)uniform interior distribution; the -% 'randomObjective' method draws diverse vertices. Arguments below marked -% [randomObjective] apply only to that method; 'thinning'/'nBurnin' apply only -% to the MCMC methods. +% The 'achr' and 'chrr' methods draw from the (near-)uniform distribution +% over the interior; the 'randomObjective' method draws vertices. Arguments +% below marked [randomObjective] apply only to that method; 'thinning' and +% 'nBurnin' apply only to the MCMC methods. % % Parameters % ---------- @@ -30,35 +31,36 @@ % method : char % sampling method: 'achr' (default), 'chrr', or 'randomObjective'. % thinning : double -% MCMC steps between recorded samples for 'achr'/'chrr' (default 100). +% MCMC steps between recorded samples for 'achr' and 'chrr' (default 100). % nBurnin : double % burn-in steps discarded before the first sample for 'chrr' (default 1000). % replaceBoundsWithInf : logical % [randomObjective] % replace the largest upper bounds with Inf and the smallest lower -% bounds with -Inf. This is needed to get solutions without loops if -% your model has, for example, 1000/-1000 as arbitrarily large bounds. -% If your model only has "biologically relevant" bounds, then set this -% to false (default true). +% bounds with -Inf (the latter only if the smallest lower bound is +% negative). This is required to get solutions without loops if the +% model has arbitrarily large bounds, for example 1000 and -1000. A +% model with only biologically relevant bounds can use false +% (default true). % supressErrors : logical -% the program will halt if it has problems finding non-zero solutions -% which are not involved in loops. This could be because the constraints -% on the model are too relaxed (such as unlimited glucose uptake) or too -% strict (such as too many and too narrow constraints) (default false). +% if false, randomSampling raises an error after 99 consecutive failed +% attempts to find a non-zero solution that is not involved in loops. +% Failures can result from constraints that are too relaxed (such as +% unlimited glucose uptake) or too strict (such as many narrow +% constraints). If true, the attempts continue without an error +% (default false). % runParallel : logical -% speed up calculations by parallel processing. Requires the MATLAB -% Parallel Computing Toolbox. If this is not installed, the calculations -% will not be parallelized, regardless of what is indicated as -% runParallel (default true). +% run the calculations in parallel (default true). Requires the MATLAB +% Parallel Computing Toolbox; without it, the calculations run serially +% regardless of this setting. % goodRxns : double % vector of indexes of those reactions that are not involved in loops % and can be used as random objective functions, as generated by a % previous run of randomSampling on the same model (default empty). % minFlux : logical -% determines if a second optimization should be performed for each -% random sample, to minimize the number of fluxes and thereby prevent -% loops. Typically, loops are averaged out when a large number of -% samples are taken, but this is not always the case (default false). +% determines if a second optimisation is performed for each random +% sample, which minimises the sum of absolute fluxes to prevent loops +% (default false). % nObjectives : double % number of reactions included in the random objective function per % sample iteration (default 2). @@ -81,13 +83,17 @@ % % Notes % ----- -% The solutions are generated by maximizing (with random weights) for a -% random set of nObjectives reactions (default 2). For reversible reactions -% it randomly chooses between maximizing and minimizing. +% With 'randomObjective', the solutions are generated by optimising (with +% random weights) a random set of nObjectives reactions (default 2). The +% sign of each weight is chosen at random, which selects maximisation or +% minimisation of the reaction; reactions with an upper bound below 0 always +% receive a positive weight. The default 'achr' and the 'chrr' methods use +% no random objectives; they take hit-and-run steps through the interior of +% the solution space. % % If the model is a GECKO v3+ ecModel, then usage_prot reactions are not -% selected for sampling, instead focusing on sampling from the metabolic -% aspects that form the solution space. +% selected as random objectives; the objectives are drawn from the +% metabolic reactions. % % Examples % -------- @@ -95,7 +101,7 @@ % solutions = randomSampling(model, 1000); % % Coordinate hit-and-run with rounding: % solutions = randomSampling(model, 1000, 'method', 'chrr', 'seed', 1); -% % Historical random-objective vertex method: +% % Random-objective vertex method: % solutions = randomSampling(model, 1000, 'method', 'randomObjective'); % % See also diff --git a/analysis/reporterMetabolites.m b/src/analysis/reporterMetabolites.m similarity index 65% rename from analysis/reporterMetabolites.m rename to src/analysis/reporterMetabolites.m index 46ee4eb3..209d3cc6 100755 --- a/analysis/reporterMetabolites.m +++ b/src/analysis/reporterMetabolites.m @@ -1,29 +1,31 @@ function repMets=reporterMetabolites(model,genes,genePValues,varargin) % reporterMetabolites Identify metabolites around which transcriptional changes occur. % -% The Reporter Metabolites algorithm for identifying metabolites around -% which transcriptional changes occur. +% Applies the Reporter Metabolites algorithm, which scores each metabolite +% by the differential expression of the genes around it. % % Parameters % ---------- % model : struct % a model structure. % genes : cell -% a cell array of gene names (should match with model.genes). +% a cell array of gene names (matching model.genes). % genePValues : double -% P-values for differential expression of the genes. +% P-values for differential expression of the genes. Values must lie in +% [0, 1]; NaN values are ignored, any other value raises an error. % % Name-Value Arguments % -------------------- % printResults : logical -% true if the top 20 Reporter Metabolites should be printed to the -% screen (default false). +% true to print the top 20 Reporter Metabolites to the screen +% (default false). % outputFile : char % the results are printed to this file (default none). % geneFoldChanges : double -% log-fold changes for the genes. If supplied, then Reporter -% Metabolites are calculated for only up/down-regulated genes in -% addition to the full test (default none). +% log-fold changes for the genes. If supplied, Reporter Metabolites are +% also calculated for the upregulated genes (fold change >= 0) alone and +% for the downregulated genes (fold change < 0) alone, in addition to +% the full test (default none). % % Returns % ------- @@ -31,15 +33,17 @@ % an array of structures with the following fields: % % - test : a string that describes the genes used to calculate the -% Reporter Metabolites ("all", "only up", or "only down"). The two -% latter structures are only calculated if geneFoldChanges are +% Reporter Metabolites ("all", "only up", or "only down"). The tests +% "only up" and "only down" are only calculated if geneFoldChanges is % supplied. % - mets : a cell array of metabolite IDs for the metabolites for which % a score could be calculated. -% - metZScores : Z-scores for differential expression around each -% metabolite in "mets". -% - metPValues : P-values for differential expression around each -% metabolite in "mets". +% - metNames : a cell array with the names of the metabolites in +% "mets". +% - metZScores : background-corrected Z-scores for differential +% expression around each metabolite in "mets". The entries of all +% fields are sorted by this score, highest first. +% - metPValues : one-sided (upper-tail) P-values of "metZScores". % - metNGenes : number of neighbouring genes for each metabolite in % "mets". % - meanZ : average Z-scores for the genes around each metabolite in @@ -57,6 +61,10 @@ % For details about the algorithm, see Patil KR, Nielsen J, Uncovering % transcriptional regulation of metabolism by using metabolic network % topology. Proc. Natl Acad. Sci. USA 2005;102:2685-2689. +% +% The background correction uses the closed-form mean and standard +% deviation of the aggregate Z-score of n genes drawn at random from all +% scored genes, so the results are deterministic. genes=convertCharArray(genes); p=parseRAVENargs(varargin, {'printResults',false; 'outputFile',[]; 'geneFoldChanges',[]}); @@ -74,6 +82,15 @@ error('RAVEN:badInput', '%s', EM); end +%All P-values that are given must lie in [0,1]; NaN means that no P-value +%is available and is removed below +badP=~isnan(genePValues) & (genePValues<0 | genePValues>1); +if any(badP) + firstBad=find(badP,1); + EM=sprintf('The P-values must lie between 0 and 1, but %d of them do not. The first is %g (gene %s)',nnz(badP),genePValues(firstBad),genes{firstBad}); + error('RAVEN:badInput', '%s', EM); +end + %Remove the genes which are not in the model genePValues(~ismember(genes,model.genes))=[]; if any(geneFoldChanges) @@ -99,6 +116,14 @@ %For each metabolite, calculate the aggregate Z-score and keep track of the %number of neighbouring genes metZScores=nan(numel(model.mets),1); +%Sums are taken on Z-scores relative to their mean (found in two passes, the +%second relative to the first gene's Z-score), which keeps the rounding error +%proportional to the spread of the Z-scores instead of to their size +metZShifted=nan(numel(model.mets),1); +zCentre=0; +if ~isempty(geneZScores) + zCentre=geneZScores(1)+mean(geneZScores-geneZScores(1)); +end metNGenes=nan(numel(model.mets),1); meanZ=nan(numel(model.mets),1); stdZ=nan(numel(model.mets),1); @@ -120,28 +145,43 @@ %Calculate the aggregated Z-score for the metabolite if any(K) metZScores(i)=sum(geneZScores(K))/sqrt(numel(K)); + metZShifted(i)=sum(geneZScores(K)-zCentre)/sqrt(numel(K)); meanZ(i)=mean(geneZScores(K)); stdZ(i)=std(geneZScores(K)); metNGenes(i)=numel(K); end end -%Remove the metabolites which have no Z-scores -mets=model.mets(~isnan(metZScores)); -metNames=model.metNames(~isnan(metZScores)); -metZScores(isnan(metZScores))=[]; -metNGenes(isnan(metNGenes))=[]; -meanZ(isnan(meanZ))=[]; -stdZ(isnan(stdZ))=[]; +%Remove the metabolites which have no Z-scores, from all fields alike +noScore=isnan(metZScores); +mets=model.mets(~noScore); +metNames=model.metNames(~noScore); +metZShifted(noScore)=[]; +metZScores(noScore)=[]; +metNGenes(noScore)=[]; +meanZ(noScore)=[]; +stdZ(noScore)=[]; %Correct for background using the closed-form moments of the sampling %distribution. For n genes drawn with replacement from the scored-gene pool -%(mean mu, std sigma), the aggregate Z = sum(z)/sqrt(n) has: +%(mean mu, population std sigma), the aggregate Z = sum(z)/sqrt(n) has: % E[Z] = sqrt(n)*mu and Std[Z] = sigma (independent of n) -%so the corrected score is (metZ - sqrt(n)*mu) / sigma. -mu = mean(geneZScores); -sigma = std(geneZScores); -metZScores = (metZScores - sqrt(metNGenes) .* mu) ./ sigma; +%so the corrected score is (metZ - sqrt(n)*mu) / sigma. When sigma is 0 (all +%scored genes have the same Z-score) no metabolite stands out from the +%background, and the corrected score is 0 (P-value 0.5). The moments are +%taken on Z-scores shifted by their mean, so that mu and the numerator keep +%their precision when the Z-scores are close to each other or the first one is +%an outlier. Identical Z-scores then give exactly 0 for the numerator, and the +%test of the spread of the Z-scores (0 up to 100*eps(max(abs(Z)))) only +%prevents a division of 0 by 0. +zShifted = geneZScores - zCentre; +muShifted = mean(zShifted); +sigma = std(zShifted,1); +if max(geneZScores)-min(geneZScores) > 100*eps(max(abs(geneZScores))) + metZScores = (metZShifted - sqrt(metNGenes) .* muShifted) ./ sigma; +else + metZScores = zeros(size(metZScores)); +end %Calculate the P-values (upper-tail of the standard normal). Uses the base %MATLAB erfc so this does not depend on the Statistics Toolbox normcdf: diff --git a/analysis/runDynamicFBA.m b/src/analysis/runDynamicFBA.m similarity index 88% rename from analysis/runDynamicFBA.m rename to src/analysis/runDynamicFBA.m index f07d466b..088539f6 100755 --- a/analysis/runDynamicFBA.m +++ b/src/analysis/runDynamicFBA.m @@ -1,5 +1,5 @@ function [concentrationMatrix, excRxnNames, timeVec, biomassVec] = runDynamicFBA(model, substrateRxns, initConcentrations, initBiomass, timeStep, nSteps, plotRxns, exclUptakeRxns) -% runDynamicFBA Perform dynamic FBA using the static optimization approach. +% runDynamicFBA Perform dynamic FBA using the static optimisation approach. % % Parameters % ---------- @@ -7,8 +7,8 @@ % a model structure. % substrateRxns : cell % cell array with exchange reaction identifiers for substrates that are -% initially in the media, whose concentration may change (e.g. not h2o -% or co2). +% initially in the media, whose concentration may change (for example +% not h2o or co2). % initConcentrations : double % initial concentrations of substrates (matching substrateRxns). % initBiomass : double @@ -22,7 +22,7 @@ % concentration should be plotted. % exclUptakeRxns : cell % cell array with exchange reaction identifiers for substrates whose -% concentration does not change (e.g. co2, o2, h2o, h). +% concentration does not change (for example co2, o2, h2o, h). % % Returns % ------- @@ -45,12 +45,11 @@ % Notes % ----- % If no initial concentration is given for a substrate that has an open -% uptake in the model (i.e. model.lb < 0) the concentration is assumed to -% be high enough to not be limiting. If the uptake rate for a nutrient is -% calculated to exceed the maximum uptake rate for that nutrient specified -% in the model and the max uptake rate specified is > 0, the maximum uptake -% rate specified in the model is used instead of the calculated uptake -% rate. +% uptake in the model (that is, model.lb < 0), the initial concentration is set +% to 1000. If the uptake bound calculated from the concentration exceeds +% the maximum uptake rate specified in the model for that nutrient, and the +% maximum uptake rate specified is > 0, the maximum uptake rate specified +% in the model is used as the uptake bound. % % Modified from COBRA Toolbox dynamicFBA.m. diff --git a/analysis/runPhenotypePhasePlane.m b/src/analysis/runPhenotypePhasePlane.m similarity index 100% rename from analysis/runPhenotypePhasePlane.m rename to src/analysis/runPhenotypePhasePlane.m diff --git a/analysis/runProductionEnvelope.m b/src/analysis/runProductionEnvelope.m similarity index 84% rename from analysis/runProductionEnvelope.m rename to src/analysis/runProductionEnvelope.m index a4fa966b..238437ca 100755 --- a/analysis/runProductionEnvelope.m +++ b/src/analysis/runProductionEnvelope.m @@ -13,14 +13,18 @@ % Name-Value Arguments % -------------------- % nPts : double -% number of points in the plot (default 20). +% number of biomass values at which the target bounds are calculated +% (default 20). % % Returns % ------- % biomassValues : double -% biomass values for plotting. +% nPts biomass values, evenly spaced from the minimum to the maximum +% biomass flux. % targetValues : double -% target upper and lower bounds for plotting. +% a matrix with two rows: the lower bounds (row 1) and the upper bounds +% (row 2) of the target reaction flux at each biomass value. NaN where +% the problem has no solution. % % Examples % -------- diff --git a/analysis/runRobustnessAnalysis.m b/src/analysis/runRobustnessAnalysis.m similarity index 85% rename from analysis/runRobustnessAnalysis.m rename to src/analysis/runRobustnessAnalysis.m index c80c8299..032ffceb 100755 --- a/analysis/runRobustnessAnalysis.m +++ b/src/analysis/runRobustnessAnalysis.m @@ -1,8 +1,9 @@ function [controlFlux, objFlux] = runRobustnessAnalysis(model, controlRxn, varargin) % runRobustnessAnalysis Perform robustness analysis for a reaction and objective. % -% Performs robustness analysis for a reaction of interest and an objective -% of interest. +% Varies the flux of a reaction of interest from its minimum to its maximum +% and calculates the optimal value of an objective of interest at each +% flux. % % Parameters % ---------- @@ -14,12 +15,13 @@ % Name-Value Arguments % -------------------- % nPoints : double -% number of points to show on plot (default 20). +% number of control flux values (default 20). % objRxn : char -% reaction identifier of objective to be maximized (default uses the +% reaction identifier of the objective to be maximised (default: the % objective defined in the model). % plotRedCost : logical -% whether reduced cost should also be plotted (default false). +% whether the reduced cost of the control reaction is also plotted on +% the right axis (default false). % % Returns % ------- diff --git a/analysis/runSimpleOptKnock.m b/src/analysis/runSimpleOptKnock.m similarity index 85% rename from analysis/runSimpleOptKnock.m rename to src/analysis/runSimpleOptKnock.m index b3fd9346..173505cd 100755 --- a/analysis/runSimpleOptKnock.m +++ b/src/analysis/runSimpleOptKnock.m @@ -1,10 +1,14 @@ function out = runSimpleOptKnock(model, targetRxn, biomassRxn, varargin) -% runSimpleOptKnock Simple OptKnock for growth-coupled production. +% runSimpleOptKnock Find deletions that couple metabolite production to growth. % -% Simple OptKnock algorithm that checks all gene or reaction deletions for -% growth-coupled metabolite production, by testing all possible -% combinations. This is not defined as MILP, and is therefore slow (but -% simple). +% Checks all gene or reaction deletions for growth-coupled metabolite +% production, by testing combinations of up to maxNumKO deletions. It tries +% a deeper deletion only after a deletion whose growth rate is above +% minGrowth. A combination is reported if the growth rate exceeds +% minGrowth and the product of growth rate and production rate exceeds the +% value without the last added deletion (the wild type for the first +% deletion) by more than 1 %. The method is not defined as a MILP and is +% therefore slow. % % Parameters % ---------- @@ -37,6 +41,7 @@ % - KO : cell array with gene(s) or reaction(s) to be deleted. % - growthRate : vector with growth rates after deletion. % - prodRate : vector with production rates after deletion. +% - score : vector with the product of growth rate and production rate. % % Examples % -------- diff --git a/src/analysis/sampleACHR.m b/src/analysis/sampleACHR.m new file mode 100644 index 00000000..124843b6 --- /dev/null +++ b/src/analysis/sampleACHR.m @@ -0,0 +1,148 @@ +function solutions = sampleACHR(model, nSamples, thinning, warmup, seed) +% sampleACHR Artificially Centered Hit-and-Run flux sampling. +% +% Draws nSamples flux vectors approximately uniformly from the flux polytope +% {v : S*v = 0, lb <= v <= ub}, using the ACHR algorithm (Kaufman & Smith 1998). +% No rounding is performed, so on strongly elongated polytopes (for example +% enzyme-constrained models) mixing is slower than with sampleCHRR, which +% rounds the polytope. +% Reactions whose bounds or warmup values coincide never move. The remaining +% reactions are corrected towards S*v = 0 every few steps and before each +% recorded sample, with a sparse Cholesky factor of the row-normalised S*S' +% (no dense null-space basis), so the rounding error in S*v does not grow with +% the number of steps and samples stay inside the bounds. The guarantee +% S*v = 0 holds for a well-conditioned S and warmup points that are feasible +% for the model. When the largest residual of a recorded sample exceeds 1e-9 +% times max(1, max(abs(v))) (nearly dependent rows of S, a noisy or infeasible +% warmup), sampleACHR issues the warning sampleACHR:steadyStateNotRestored. +% +% Parameters +% ---------- +% model : struct +% a model structure. +% nSamples : double +% number of flux vectors to return (default 1000). +% thinning : double +% Markov-chain steps between recorded samples (default 100). +% warmup : double +% nPoints-by-nRxns warmup vertices (default: generated by sampleWarmupPoints). +% seed : double +% random seed for reproducibility (default: current RNG state). +% +% Returns +% ------- +% solutions : double +% nRxns-by-nSamples matrix of sampled flux distributions. +% +% See also +% -------- +% randomSampling, sampleCHRR, sampleWarmupPoints + +if nargin < 2 || isempty(nSamples); nSamples = 1000; end +if nargin < 3 || isempty(thinning); thinning = 100; end +if nargin < 4 || isempty(warmup); warmup = sampleWarmupPoints(model); end +if nargin < 5; seed = []; end +if ~isempty(seed) + rng(seed); +end + +nRxns = numel(model.rxns); +lb = model.lb(:); +ub = model.ub(:); +nW = size(warmup, 1); + +% Reactions that cannot move: fixed bounds, or identical value in all warmup +% points. Directions and corrections act on the other reactions only. +spread = (max(warmup, [], 1) - min(warmup, [], 1))'; +free = (ub - lb > 1e-9) & (spread > 1e-9); +proj = buildSteadyStateCorrection(model.S, free, lb, ub); +corrEvery = min(thinning, 20); + +center = mean(warmup, 1)'; % nRxns-by-1 +prev = restoreSteadyState(center, proj); +center = prev; +nTotal = 0; + +solutions = zeros(nRxns, nSamples); +rec = 0; +worstResidual = 0; +nSteps = thinning*nSamples; + +for stepno = 1:nSteps + pidx = randi(nW); + delta = warmup(pidx, :)' - center; + delta(~free) = 0; + dirTol = 1e-12*max(1, max(abs(delta))); + + posD = delta > dirTol; + negD = delta < -dirTol; + ubp = ub - prev; + lbp = lb - prev; + % alpha range keeping lb <= prev + alpha*delta <= ub + upperLims = [ubp(posD)./delta(posD); lbp(negD)./delta(negD)]; + lowerLims = [lbp(posD)./delta(posD); ubp(negD)./delta(negD)]; + if isempty(upperLims); tHi = 0; else; tHi = min(upperLims); end + if isempty(lowerLims); tLo = 0; else; tLo = max(lowerLims); end + + if tHi > tLo + alpha = tLo + rand*(tHi - tLo); + prev = min(max(prev + alpha*delta, lb), ub); + end + + % Running centre (quasi-Markovian ACHR: centre converges and steadies). + center = (nTotal*center + prev) / (nTotal + 1); + nTotal = nTotal + 1; + + if mod(stepno, corrEvery) == 0 || mod(stepno, thinning) == 0 + prev = restoreSteadyState(prev, proj); + center = restoreSteadyState(center, proj); + end + + if mod(stepno, thinning) == 0 + rec = rec + 1; + solutions(:, rec) = prev; + worstResidual = max(worstResidual, ... + max(abs(model.S*prev))/max(1, max(abs(prev)))); + end +end +if worstResidual > 1e-9 + warning('sampleACHR:steadyStateNotRestored', ... + ['The largest residual of S*v over the samples is %.3g times max(1, max(abs(v))), ' ... + 'above 1e-9. The rows of S are nearly dependent or the warmup points are not ' ... + 'feasible for the model.'], worstResidual); +end +end + +function proj = buildSteadyStateCorrection(S, free, lb, ub) +% Sparse factor of the row-normalised S(:,free)*S(:,free)' with a small ridge, +% used to move a flux vector onto S*v = 0 through the free reactions only. +Sf = S(:, free); +rowNorm = sqrt(full(sum(Sf.^2, 2))); +keep = rowNorm > 0; +rowNorm = rowNorm(keep); +Sn = spdiags(1./rowNorm, 0, numel(rowNorm), numel(rowNorm))*Sf(keep, :); +A = Sn*Sn' + 1e-10*speye(size(Sn, 1)); +[R, flag, p] = chol(A, 'vector'); +if flag ~= 0 + error('sampleACHR:factorization', 'The stoichiometric matrix could not be factorized.'); +end +proj = struct('S', S, 'free', free, 'keep', keep, 'rowNorm', rowNorm, ... + 'Sn', Sn, 'R', R, 'Rt', R', 'p', p, 'lb', lb, 'ub', ub); +end + +function v = restoreSteadyState(v, proj) +% Removes the residual of S*v through the free reactions (a few ridge-regularised +% projection passes), then clips v to its bounds. +tol = 1e-12*max(1, max(abs(v))); +for it = 1:8 + r = proj.S*v; + if max(abs(r)) <= tol + break + end + rn = r(proj.keep) ./ proj.rowNorm; + x = zeros(size(rn)); + x(proj.p) = proj.R \ (proj.Rt \ rn(proj.p)); + v(proj.free) = v(proj.free) - proj.Sn'*x; +end +v = min(max(v, proj.lb), proj.ub); +end diff --git a/analysis/sampleCHRR.m b/src/analysis/sampleCHRR.m similarity index 87% rename from analysis/sampleCHRR.m rename to src/analysis/sampleCHRR.m index fe81ad59..53a9548d 100644 --- a/analysis/sampleCHRR.m +++ b/src/analysis/sampleCHRR.m @@ -3,17 +3,18 @@ % % Draws nSamples flux vectors approximately uniformly from the flux polytope % {v : S*v = 0, lb <= v <= ub}, using the CHRR algorithm (Haraldsdottir et al. -% 2017, Bioinformatics 33:1741). The polytope is rounded before sampling, so -% mixing stays fast even on strongly elongated polytopes (e.g. -% enzyme-constrained models), unlike sampleACHR. +% 2017, Bioinformatics 33:1741). The polytope is rounded before sampling, +% which improves mixing on strongly elongated polytopes (for example +% enzyme-constrained models) in comparison with sampleACHR, which does not +% round. % -% Set any constraints you want to condition on (e.g. a biomass lower bound or -% measured exchange fluxes) on the model before calling. +% Constraints to condition on (for example a biomass lower bound or measured +% exchange fluxes) are set on the model before the call. % % Parameters % ---------- % model : struct -% a RAVEN model structure. +% a model structure. % nSamples : double % number of flux vectors to return (default 1000). % thinning : double @@ -24,17 +25,18 @@ % random seed for reproducibility (default: current RNG state). % fixedWidthTol : double % a reaction whose allowed flux range is narrower than this is treated as -% stoichiometrically fixed and folded into the equality system, keeping the -% reduced polytope full-dimensional (default 1e-7). +% stoichiometrically fixed and added to the equality system at the middle +% of its range, which keeps the reduced polytope full-dimensional +% (default 1e-7). % % Returns % ------- % solutions : double % nRxns-by-nSamples matrix of sampled flux distributions. % info : struct -% .nDimensions — dimension of the sampled flux polytope -% .mveConverged — whether MVE rounding reached tolerance -% .fixedRxns — IDs of reactions folded in as fixed +% .nDimensions : dimension of the sampled flux polytope +% .mveConverged : whether MVE rounding reached tolerance +% .fixedRxns : IDs of reactions added to the equality system as fixed % % See also % -------- diff --git a/analysis/sampleChebyshevCenter.m b/src/analysis/sampleChebyshevCenter.m similarity index 94% rename from analysis/sampleChebyshevCenter.m rename to src/analysis/sampleChebyshevCenter.m index 7204a639..8c8198cd 100644 --- a/analysis/sampleChebyshevCenter.m +++ b/src/analysis/sampleChebyshevCenter.m @@ -26,7 +26,7 @@ norms = sqrt(sum(A.^2, 2)); % Variables [x (n, free); r (1, >=0)]; minimise -r (maximises the ball radius). -prob.A = [A, norms]; +prob.A = sparse([A, norms]); % Gurobi and SCIP require a sparse A prob.a = prob.A; prob.b = b; prob.csense = repmat('L', 1, m); diff --git a/analysis/sampleMaxVolEllipse.m b/src/analysis/sampleMaxVolEllipse.m similarity index 90% rename from analysis/sampleMaxVolEllipse.m rename to src/analysis/sampleMaxVolEllipse.m index 314e6630..ac2259e4 100644 --- a/analysis/sampleMaxVolEllipse.m +++ b/src/analysis/sampleMaxVolEllipse.m @@ -6,7 +6,7 @@ % {z : A*z <= b}, using the Zhang & Gao (2003) primal-dual interior-point % method (the regularised variant used in COBRA's CHRR sampler). This is the % rounding step of sampleCHRR: the returned transform E maps the polytope to a -% well-conditioned body in which coordinate hit-and-run mixes rapidly. +% well-conditioned body in which coordinate hit-and-run samples. % % This function is pure linear algebra (no LP solver) except when x0 is empty, % in which case a Chebyshev centre is obtained via sampleChebyshevCenter. @@ -26,8 +26,8 @@ % tol : double (optional) % convergence tolerance (default 1e-6). % reg : double (optional) -% diagonal / Levenberg regularisation keeping the Newton solves -% well-conditioned (default 1e-8). +% diagonal (Levenberg) regularisation of the Newton solves +% (default 1e-8). % % Returns % ------- @@ -37,14 +37,15 @@ % n-by-n lower-triangular rounding transform with E*E' = E2 (the SPD % ellipsoid matrix). The rounding substitution is z = x + E*y. % converged : logical -% whether tol was reached within maxiter. A false value is not fatal — -% the last iterate is still a valid (if looser) rounding. +% whether tol was reached within maxiter. If false, the last iterate +% is returned as the centre and transform, which is a valid but looser +% rounding. % % Notes % ----- -% Validated against analytic cases: a box maps to the unit ball (E = I); a -% sheared box {z : -1 <= M*z <= 1} gives E*E' = inv(M)*inv(M)'; a triangle -% gives its Steiner inellipse. +% Analytic cases, tested in tSampling: a box maps to the unit ball (E = I); +% a sheared box {z : -1 <= M*z <= 1} gives E*E' = inv(M)*inv(M)'; a +% triangle gives its Steiner inellipse. % % Based on Y. Zhang & L. Gao (2003), SIAM J. Optim. 14:53-76; as used in % Haraldsdottir et al. (2017) Bioinformatics 33:1741 (CHRR). diff --git a/analysis/sampleWarmupPoints.m b/src/analysis/sampleWarmupPoints.m similarity index 89% rename from analysis/sampleWarmupPoints.m rename to src/analysis/sampleWarmupPoints.m index 6edff507..688e7fff 100644 --- a/analysis/sampleWarmupPoints.m +++ b/src/analysis/sampleWarmupPoints.m @@ -3,18 +3,19 @@ % % Generates warmup points for sampleACHR by maximising and minimising each % reaction in turn and storing the full flux distribution at each optimum. -% These vertices span the flux polytope and seed the hit-and-run directions. +% These vertices span the flux polytope and define the hit-and-run +% directions. % % Parameters % ---------- % model : struct -% a RAVEN model structure. +% a model structure. % % Name-Value Arguments % -------------------- % runParallel : logical -% speed up calculations by parallel processing, as in getAllowedBounds, -% which solves the same per-reaction min/max LPs (default true). +% run the calculations in parallel, as in getAllowedBounds, which solves +% the same per-reaction minimisation and maximisation LPs (default true). % % Returns % ------- diff --git a/analysis/traceFluxPath.m b/src/analysis/traceFluxPath.m similarity index 90% rename from analysis/traceFluxPath.m rename to src/analysis/traceFluxPath.m index 45b01bce..1ea425a9 100644 --- a/analysis/traceFluxPath.m +++ b/src/analysis/traceFluxPath.m @@ -5,16 +5,16 @@ % network, quantifying what fraction of the source reaction's flux reaches % the target. At each metabolite junction the flux is split proportionally % among all consuming reactions; the path with the highest cumulative -% fraction is returned and printed. +% fraction is returned, and printed if verbose is true. % -% By default the search excludes currency metabolites (ATP, NAD, H2O, etc.) -% so the returned path represents material (carbon-skeleton) flow rather -% than cofactor shortcuts. Set traceMaterial=false to disable this. +% By default the search excludes currency metabolites (for example ATP, +% NAD, H2O), which makes the returned path follow material flow. The +% setting traceMaterial=false disables this. % % Parameters % ---------- % model : struct -% RAVEN model structure. +% a model structure. % fluxes : double % flux vector (length = numel(model.rxns)). % fromRxn : char @@ -25,25 +25,27 @@ % Name-Value Arguments % -------------------- % cutoff : double -% minimum |flux| for a reaction to be considered flux-carrying -% (default 1e-8). +% minimum net flux through a metabolite (|S_ij * v_j|) for a consuming +% reaction to be considered (default 1e-8). % maxHops : double -% maximum number of reactions in the path; longer paths are pruned -% (default 30). +% paths of more than maxHops reactions are not extended further, so a +% returned path has at most maxHops + 1 reactions (default 30). % verbose : logical % print the path to the command window (default true). % traceMaterial : logical -% exclude common currency/cofactor metabolites (ATP, ADP, NAD, NADH, -% H2O, CoA, Pi, CO2, …) so the path follows material (carbon-skeleton) -% flux rather than energy-carrier shortcuts (default true). +% exclude common currency and cofactor metabolites (ATP, ADP, NAD, NADH, +% H2O, CoA, Pi, CO2 and others); the path then follows material flux +% (default true). % carbonOnly : logical % additionally require each intermediate metabolite to contain at least % one carbon atom, inferred from model.metFormulas. Drops H+, H2O, Pi, -% O2, NH3, CO2 that are not already caught by the currency list. -% Silently ignored when model.metFormulas is absent (default false). +% O2, NH3, CO2 that are not already caught by the currency list. Ignored +% with a warning when model.metFormulas is absent or empty +% (default false). % excludeMets : cell of char % extra metabolite names or IDs (after stripping compartment suffix) to -% exclude, in addition to the built-in currency list (default {}). +% exclude, in addition to the built-in currency list (which applies when +% traceMaterial is true) (default {}). % % Returns % ------- @@ -53,13 +55,13 @@ % pathMets : cell % metabolite IDs at each junction (numel = numel(pathRxns)-1). % cumFrac : double -% fraction of fromRxn's flux that routes through this exact path +% fraction of fromRxn's flux that passes through this exact path % (0 = no path found, 1 = all flux reaches toRxn directly). % % Examples % -------- % [p, m, f] = traceFluxPath(model, sol.x, 'PFK', 'ATPS4rpp') -% % disable material filter to see all connections including ATP/ADP: +% % disable material filter to see all connections including ATP and ADP: % [p, m, f] = traceFluxPath(model, sol.x, 'PFK', 'ATPS4rpp', 'traceMaterial', false) % % add model-specific cofactors to the exclusion list: % [p, m, f] = traceFluxPath(model, sol.x, 'PFK', 'CS', 'excludeMets', {'acetyl-CoA'}) @@ -67,12 +69,12 @@ % Notes % ----- % The algorithm follows metabolites in the forward direction only: it -% traces the metabolites that fromRxn PRODUCES and finds which consuming +% traces the metabolites that fromRxn produces and finds which consuming % reactions eventually reach toRxn. If fromRxn and toRxn are not connected -% in the forward flux direction (e.g. they are in parallel branches), an -% empty path is returned. A best-first (greedy) search ensures the -% highest-fraction path is found efficiently; a visited set prevents -% revisiting reactions. +% in the forward flux direction (for example they are in parallel branches), an +% empty path is returned. A best-first search, which expands the entry with +% the highest cumulative fraction first, finds the highest-fraction path; +% a path does not revisit a reaction. p = parseRAVENargs(varargin, {'cutoff',1e-8; 'maxHops',30; 'verbose',true; ... 'traceMaterial',true; 'carbonOnly',false; 'excludeMets',{}}); diff --git a/analysis/walkFluxes.m b/src/analysis/walkFluxes.m similarity index 86% rename from analysis/walkFluxes.m rename to src/analysis/walkFluxes.m index 3ec9cc93..7e27ebe6 100644 --- a/analysis/walkFluxes.m +++ b/src/analysis/walkFluxes.m @@ -1,26 +1,30 @@ function walkFluxes(model, fluxes, startRxn, varargin) % walkFluxes Interactively navigate a flux distribution reaction by reaction. % -% Starting from a chosen reaction, displays all flux-carrying reactions +% Starting from a chosen reaction, displays the flux-carrying reactions % connected through shared metabolites, grouped by metabolite and labelled -% with each neighbour's role (produces / consumes). Select a neighbour by -% number to step to it; build a history and rewind with 'b'. +% with each neighbour's role (produces or consumes). Entering the number of +% a neighbour steps to it. The visited reactions are kept in a history, and +% 'b' steps back through it. % % Parameters % ---------- % model : struct -% RAVEN model structure. +% a model structure. % fluxes : double % flux vector (length = numel(model.rxns)). % startRxn : char or double -% starting reaction ID (string) or column index. +% starting reaction ID (string) or reaction index. % % Name-Value Arguments % -------------------- % cutoff : double -% minimum |flux| to display a reaction as a neighbour (default 1e-8). +% minimum net flux through a metabolite (|S_ij * v_j|) for the metabolite +% to be listed for the current reaction and for a reaction to be +% displayed as a neighbour (default 1e-8). % maxPerMet : double -% maximum neighbour reactions displayed per metabolite (default 8). +% maximum neighbour reactions displayed per metabolite, selected by +% decreasing absolute flux (default 8). % % Examples % -------- @@ -31,14 +35,12 @@ function walkFluxes(model, fluxes, startRxn, varargin) % Notes % ----- % Navigation commands: -% — step to that neighbour reaction -% b — go back to the previous reaction -% q — quit +% : step to that neighbour reaction +% b : go back to the previous reaction +% q : quit % -% This function is interactive and reads from the terminal at every step; -% it errors immediately under -batch, in deployed code, or otherwise without -% a real terminal, rather than blocking forever waiting for input that will -% never arrive. +% This function is interactive and reads from the terminal at every step. +% It raises an error under -batch and in deployed code. isBatch = false; try diff --git a/annotation/assignSBOterms.m b/src/annotation/assignSBOterms.m similarity index 71% rename from annotation/assignSBOterms.m rename to src/annotation/assignSBOterms.m index 6aa0a983..ab071724 100644 --- a/annotation/assignSBOterms.m +++ b/src/annotation/assignSBOterms.m @@ -1,23 +1,26 @@ function model = assignSBOterms(model, varargin) % assignSBOterms Assign SBO terms to metabolites and reactions. % -% Assign SBO terms to metabolites and reactions following a generic rule -% set. The MATLAB counterpart of yeast-GEM's legacy addSBOterms; -% organism-agnostic, parameterised entirely by opts. +% Assigns SBO terms to metabolites and reactions following a generic rule +% set that opts parameterises. The function is the MATLAB counterpart of +% yeast-GEM's addSBOterms. % -% SBO is written via editMiriam(..., 'fill') so pre-existing SBO -% annotations are preserved. +% SBO is written via editMiriam(..., 'fill'), which preserves pre-existing +% SBO annotations. The terms are stored in model.metMiriams and +% model.rxnMiriams under the namespace 'sbo' (there is no separate SBO +% field); they can be read with extractMiriam(model.rxnMiriams, 'sbo'). +% exportModel writes them as the SBML sboTerm attribute. % % Parameters % ---------- % model : struct -% RAVEN model struct. +% a model structure. % % Name-Value Arguments % -------------------- % opts : struct -% Struct with any of the following fields. Missing fields take the -% defaults shown: +% structure with any of the following fields. Missing or empty fields +% take the defaults shown: % % - biomassMetNames : {'biomass','DNA','RNA','protein','carbohydrate', % 'lipid','cofactor','ion'} @@ -25,15 +28,19 @@ % - biomassRxnName : 'biomass pseudoreaction' % - ngamRxnName : 'non-growth associated maintenance reaction' % - pseudoreactionSubstrings : {'pseudoreaction','SLIME rxn'} -% - onlyLastReactionForPseudo : false. yeast-GEM bug-compat flag that -% replicates the legacy `for i=numel(...)` typo (pseudoreaction SBOs -% applied only to the last reaction). Off by default; turn ON for -% byte-equivalent yeast-GEM output. +% - onlyLastReactionForPseudo : false. If true, the biomass, NGAM and +% pseudoreaction terms are applied only to the last reaction of the +% model. This reproduces yeast-GEM's addSBOterms.m, which applies the +% pseudoreaction rule to the last reaction only, because of a typo in +% the loop header (for i=numel(...)). +% - transportRxnIdxs : indexes of the transport reactions (default: +% the reactions identified by getTransportRxns). % % Returns % ------- % model : struct -% Modified model. +% model structure with the SBO terms added to model.metMiriams and +% model.rxnMiriams. % % Examples % -------- @@ -44,22 +51,21 @@ % ----- % Metabolites: % -% SBO:0000649 (Biomass) when met.name is in opts.biomassMetNames, or -% ends with any of opts.biomassMetSuffixes. Otherwise SBO:0000247 -% (Simple chemical). +% SBO:0000649 (Biomass) when the metabolite name is in +% opts.biomassMetNames, or ends with any of opts.biomassMetSuffixes. +% Otherwise SBO:0000247 (Simple chemical). % % Reactions (default → override → pseudoreaction override): % % SBO:0000176 (Metabolic reaction) default. % Single-reactant reactions become: % SBO:0000627 (exchange) if the lone metabolite is extracellular -% (compartment 'e' or compartment name containing -% 'extracellular'), +% (compartment 'e' or compartment name 'extracellular'), % SBO:0000632 (sink) if coef < 0, % SBO:0000628 (demand) otherwise. -% Transport reactions (detected by opts.transportDetector or the -% default heuristic: same metName in ≥ 2 compartments in a single -% reaction) → SBO:0000655. +% Transport reactions (opts.transportRxnIdxs or, by default, the +% reactions identified by getTransportRxns: the same metabolite +% name more than once in a single reaction) → SBO:0000655. % Reactions whose name matches opts.biomassRxnName → SBO:0000629. % Reactions whose name matches opts.ngamRxnName → SBO:0000630. % Reactions whose name contains any of opts.pseudoreactionSubstrings diff --git a/annotation/editMiriam.m b/src/annotation/editMiriam.m similarity index 83% rename from annotation/editMiriam.m rename to src/annotation/editMiriam.m index b287573e..66ea9a89 100755 --- a/annotation/editMiriam.m +++ b/src/annotation/editMiriam.m @@ -4,34 +4,32 @@ % Parameters % ---------- % model : struct -% model structure. +% a model structure. % type : char -% 'met', 'rxn', 'gene' or 'comp' dependent on which objects the -% annotations should be assigned to. +% 'met', 'rxn', 'gene' or 'comp', the type of object that the +% annotations are assigned to. % object : cell or logical or double or char % either a cell array of IDs, a logical vector with the same number of -% elements as the type (see above) in the model, a vector of indexes, +% elements as objects of that type in the model, a vector of indexes, % or 'all'. % miriamName : char % string specifying the namespace of the identifier, for instance -% 'bigg.metabolite'. Should be a valid prefix from identifiers.org -% (e.g. https://registry.identifiers.org/registry/bigg.metabolite). +% 'bigg.metabolite'. A valid prefix from identifiers.org is expected +% (for example https://registry.identifiers.org/registry/bigg.metabolite). % miriams : char or cell -% string or cell array of strings with annotation identifiers, e.g. +% string or cell array of strings with annotation identifiers, for example % '12dgr161'. % keep : char % one of the following strings, specifying what should be done if an % object already has existing MIRIAM annotations with the same % miriamName: % -% - 'replace' : discard all existing annotations, all will be -% overwritten, even if the new annotation is an empty field. Should -% only be used if you do not want to keep any of the old annotation -% with the same miriamName. -% - 'fill' : only add annotations to those objects that did not yet -% have an annotation with that miriamName. Otherwise, the existing -% annotation is kept, even if it is different from the suggested new -% annotation. +% - 'replace' : discard all existing annotations with the same +% miriamName; they are overwritten, even if the new annotation is an +% empty field. +% - 'fill' : add annotations only to those objects that have no +% annotation with that miriamName. Otherwise, the existing annotation +% is kept, even if it differs from the new annotation. % - 'add' : keep all existing annotations, and add any new % annotations, after removing duplicates. % - 'remove' : remove all annotations with the given miriamName from diff --git a/annotation/extractMiriam.m b/src/annotation/extractMiriam.m similarity index 75% rename from annotation/extractMiriam.m rename to src/annotation/extractMiriam.m index cafab891..be509d11 100755 --- a/annotation/extractMiriam.m +++ b/src/annotation/extractMiriam.m @@ -1,32 +1,28 @@ function [miriams,extractedMiriamNames]=extractMiriam(modelMiriams,varargin) -% extractMiriam Unpack MIRIAM annotations into a human-readable table. +% extractMiriam Unpack MIRIAM annotations into a table. % -% Unpacks metMiriams, rxnMiriams, geneMiriams or compMiriams into a -% human-readable cell array, formatted as in Excel, with the columns split -% so each miriam name gets its own column. +% Unpacks metMiriams, rxnMiriams, geneMiriams or compMiriams into a cell +% array with one column per miriam name. % % Parameters % ---------- % modelMiriams : cell -% a miriam structure (e.g. model.metMiriams) for one or multiple -% metabolites. +% a miriam structure (for example model.metMiriams) for one or more +% metabolites, reactions, genes or compartments. % % Name-Value Arguments % -------------------- % miriamNames : cell or char -% cell array with miriam names to be extracted (default 'all', meaning -% that annotation for all miriam names found in modelMiriams will be -% extracted). +% cell array with miriam names to be extracted (default 'all', which +% extracts the annotation for all miriam names found in modelMiriams). % % Returns % ------- % miriams : cell -% a cell array with extracted miriams. If several miriam names are -% requested, the corresponding information is saved in different -% columns. If there are several ids available for the same entity -% (metabolite, gene, reaction or compartment), they are concatenated -% into one column. The total number of columns represents the number -% of unique miriam names per entity. +% a cell array with extracted miriams, with one row per entity and one +% column per extracted miriam name. If several ids are available for +% the same entity (metabolite, gene, reaction or compartment) and miriam +% name, they are concatenated into one cell, separated by '; '. % extractedMiriamNames : cell % cell array with extracted miriam names. % diff --git a/annotation/loadDeltaGCSV.m b/src/annotation/loadDeltaGCSV.m similarity index 66% rename from annotation/loadDeltaGCSV.m rename to src/annotation/loadDeltaGCSV.m index af787ec5..fe488c07 100644 --- a/annotation/loadDeltaGCSV.m +++ b/src/annotation/loadDeltaGCSV.m @@ -2,29 +2,30 @@ % loadDeltaGCSV Load metDeltaG and rxnDeltaG from CSV files. % % Each CSV is a two-column table: identifier, deltaG. On load, rows whose -% identifier is not in the model are silently skipped, and every matched -% value is stored exactly as it appears in the CSV -- including yeast-GEM's -% own "no measurement" placeholder (10000000.0). Callers that need to treat -% a particular value as absent should filter it themselves after loading; -% this function does not interpret CSV values. Pass '' for either metCsv -% or rxnCsv to skip that side. +% identifier is not in the model are skipped without a message, and every +% matched value is stored as it appears in the CSV, including yeast-GEM's +% "no measurement" placeholder (10000000.0). The function does not +% interpret CSV values, so a value that is to be treated as absent has to +% be filtered after loading. An empty string ('') for metCsv or rxnCsv +% skips that side. % % Parameters % ---------- % model : struct -% RAVEN model struct. +% a model structure. % % Name-Value Arguments % -------------------- % metCsv : char -% Path to the metabolite deltaG CSV, or '' to skip. +% path to the metabolite deltaG CSV, or '' to skip (default ''). % rxnCsv : char -% Path to the reaction deltaG CSV, or '' to skip. +% path to the reaction deltaG CSV, or '' to skip (default ''). % % Returns % ------- % model : struct -% Updated model struct. +% model structure. The metDeltaG field is set only if metCsv is given and +% the rxnDeltaG field only if rxnCsv is given. % % Examples % -------- diff --git a/annotation/saveDeltaGCSV.m b/src/annotation/saveDeltaGCSV.m similarity index 79% rename from annotation/saveDeltaGCSV.m rename to src/annotation/saveDeltaGCSV.m index b205d1fc..af2a6e6b 100644 --- a/annotation/saveDeltaGCSV.m +++ b/src/annotation/saveDeltaGCSV.m @@ -1,11 +1,11 @@ function saveDeltaGCSV(model, varargin) % saveDeltaGCSV Save metDeltaG and rxnDeltaG to CSV files. % -% Writes a two-column table (identifier, deltaG) per side, one row per -% entity, in model order. An entity with no value stored is written as -% NaN, unchanged from whatever the model field holds -- this function does -% not interpret or substitute values. Pass '' for either metCsv or rxnCsv -% to skip that side. +% Writes a two-column table (identifier, deltaG) for metabolites and one for +% reactions, one row per entity, in model order. Values are written as the +% model field holds them, so an entity with no value stored is written as +% NaN. A file is skipped when its path is '' or when the model lacks the +% corresponding field. % % Parameters % ---------- diff --git a/biomass/fitParameters.m b/src/biomass/fitParameters.m similarity index 82% rename from biomass/fitParameters.m rename to src/biomass/fitParameters.m index 65670b3a..7965a91a 100755 --- a/biomass/fitParameters.m +++ b/src/biomass/fitParameters.m @@ -5,10 +5,11 @@ % between the fluxes the model predicts and the measured ones, using % fminsearch (Nelder-Mead). Each candidate parameter set is written into the % S matrix at the positions given by parameterPositions, and the model is -% solved once per data point. +% solved once per data point with the xRxns bounds fixed to the values in +% xValues. The fitted parameters are returned as absolute values. % -% Use this to *derive* a value from measurements; use setGAM to *apply* a -% known growth-associated maintenance value to a biomass pseudoreaction. +% The function derives a value from measurements. To apply a known +% growth-associated maintenance value to a biomass pseudoreaction, use setGAM. % % Parameters % ---------- @@ -32,30 +33,33 @@ % - position : cell array of vectors where each element contains the % positions in the S-matrix for that parameter % - isNegative : cell array of vectors where the elements are true if -% that position should be the negative of the fitted value (to -% differentiate between production/consumption) +% that position is set to the negative of the fitted value (to +% distinguish production from consumption) % % Name-Value Arguments % -------------------- % fitToRatio : logical -% if the ratio of simulated to measured values should be fitted -% instead of the absolute value. Used to prevent large fluxes from -% having too large an impact (default true). +% true to fit the ratio of simulated to measured values, false to fit +% the absolute difference. The ratio limits the influence of large +% fluxes on the residual sum of squares (default true). % initialGuess : double % initial guess of the parameters (default ones). % plotFitting : logical -% true if the resulting fitting should be plotted (default false). +% true to plot the measured values and the fitted fluxes against +% xValues (default false). % % Returns % ------- % parameters : double -% fitted parameters in the same order as in parameterPositions. +% fitted parameters (absolute values) in the same order as in +% parameterPositions. % fitnessScore : double % the corresponding residual sum of squares. % exitFlag : double % exit status returned by fminsearch. % newModel : struct -% updated model structure with the fitted parameters. +% updated model structure with the fitted parameters. It is assigned +% only when plotFitting is true. % % Examples % -------- diff --git a/biomass/getBiomassFractions.m b/src/biomass/getBiomassFractions.m similarity index 70% rename from biomass/getBiomassFractions.m rename to src/biomass/getBiomassFractions.m index 70ba89f1..6e8f2d5e 100644 --- a/biomass/getBiomassFractions.m +++ b/src/biomass/getBiomassFractions.m @@ -4,9 +4,16 @@ % Compute the mass fraction (g/gDW) per biomass component plus the total. % The MATLAB counterpart of yeast-GEM's legacy sumBioMass. % -% The biomassConfig struct describes the per-organism biomass layout — see -% Parameters below. Components whose pseudoreaction is missing from the -% model contribute 0. +% The biomassConfig struct describes the per-organism biomass layout (see +% Parameters). A component contributes 0 when its pseudoreaction is missing +% from the model or has no substrates. For a component whose pseudoreaction +% exists and has substrates, and whose mass_strategy is not 'grams', an error +% is raised when the molecular weight of a substrate is 0 (an empty +% metFormula field, or a formula that consists only of R), when its formula +% contains an element outside the built-in element table, or when +% mass_strategy is unknown. A component with an unknown mass_strategy whose +% pseudoreaction is missing or has no substrates contributes 0 without an +% error. % % Parameters % ---------- @@ -16,21 +23,22 @@ % Struct describing the biomass layout, with fields: % % - biomass_rxn : rxn id of the top-level biomass pseudoreaction. -% - proton_met : met id of cytosolic H+ (used only by -% rescalePseudoreaction; may be unused here). +% - proton_met : met id of cytosolic H+ (used by +% scaleBiomassPseudoreaction, not by getBiomassFractions). % - components : cell array of component structs, each with fields: % -% - name : component name (e.g. 'protein'). +% - name : component name (for example 'protein'). % - pseudoreaction_name : model.rxnNames entry to identify the % pseudoreaction. -% - mass_strategy : 'mw' | 'mw_minus_2h' | 'mw_minus_water' | -% 'grams' — see Notes below. +% - mass_strategy : 'mw', 'mw_minus_2h', 'mw_minus_water' or +% 'grams' (see Notes). % % Returns % ------- % fractions : struct -% Struct keyed by component name plus 'total': fractions.protein, -% fractions.RNA, ... etc. All values are in g/gDW. +% Struct with one field per component name and a field 'total' +% (for example fractions.protein and fractions.RNA). All values are in +% g/gDW. % % Examples % -------- @@ -41,11 +49,11 @@ % mass_strategy values: % % 'mw' MW from chemical formula -% 'mw_minus_2h' MW − 2.016 g/mol (two protons released per charged -% tRNA — protein-pseudoreaction substrates) -% 'mw_minus_water' MW − 18.015 g/mol (water released per -% polymerisation step — RNA / DNA) -% 'grams' stoichiometry already in g/gDW (lipid backbone) +% 'mw_minus_2h' MW - 2.016 g/mol (two protons released per charged +% tRNA; protein pseudoreaction substrates) +% 'mw_minus_water' MW - 18.015 g/mol (water released per +% polymerisation step; RNA and DNA) +% 'grams' stoichiometry is already in g/gDW (lipid backbone) fractions = struct(); total = 0; diff --git a/src/biomass/scaleBiomassFraction.m b/src/biomass/scaleBiomassFraction.m new file mode 100644 index 00000000..79a9e55a --- /dev/null +++ b/src/biomass/scaleBiomassFraction.m @@ -0,0 +1,88 @@ +function model = scaleBiomassFraction(model, biomassConfig, componentName, newValue, varargin) +% scaleBiomassFraction Rescale a biomass component to a target value. +% +% Rescale a biomass component to a target g/gDW value, optionally +% rescaling a second component so that the biomass total becomes +% targetTotal (1 g/gDW by default), whatever the total was before. +% The MATLAB counterpart of yeast-GEM's legacy scaleBioMass. +% +% Parameters +% ---------- +% model : struct +% RAVEN model struct. +% biomassConfig : struct +% Biomass layout struct, as described in getBiomassFractions. +% componentName : char +% Name of the component to rescale. An error is raised if its current +% fraction is 0. +% newValue : double +% Target fraction in g/gDW. +% +% Name-Value Arguments +% -------------------- +% balanceOut : char +% Second component name to adjust so the biomass total becomes +% targetTotal. An error is raised if its fraction would become zero or +% negative, or if it is 0 to begin with. No balancing is done when +% empty or omitted (default ''). +% targetTotal : double +% Biomass total in g/gDW that balanceOut brings the components to +% (default 1). +% +% Returns +% ------- +% model : struct +% Model with the rescaled pseudoreactions. +% +% Examples +% -------- +% model = scaleBiomassFraction(model, biomassConfig, 'protein', 0.5, 'carbohydrate'); +% model = scaleBiomassFraction(model, biomassConfig, 'protein', 0.5, ... +% 'balanceOut', 'carbohydrate', 'targetTotal', 0.95); +% +% See also +% -------- +% getBiomassFractions + +p=parseRAVENargs(varargin, {'balanceOut',''; 'targetTotal',1}); +balanceOut=p.balanceOut; +targetTotal=p.targetTotal; + +fractions = getBiomassFractions(model, biomassConfig); +if ~isfield(fractions, componentName) + error('scaleBiomassFraction:unknownComponent', ... + 'biomassConfig has no component named %s', componentName); +end +current = fractions.(componentName); +if current == 0 + error('scaleBiomassFraction:zeroCurrent', ... + ['Cannot scale %s to %g: current fraction is 0 ' ... + '(pseudoreaction missing or empty).'], componentName, newValue); +end +factor = newValue / current; +model = scaleBiomassPseudoreaction(model, biomassConfig, componentName, factor); + +if ~isempty(balanceOut) + fractions = getBiomassFractions(model, biomassConfig); + total = fractions.total; + if ~isfield(fractions, balanceOut) + error('scaleBiomassFraction:unknownComponent', ... + 'biomassConfig has no component named %s', balanceOut); + end + balanceCurrent = fractions.(balanceOut); + if balanceCurrent == 0 + error('scaleBiomassFraction:zeroCurrent', ... + ['Cannot balance with %s: current fraction is 0 ' ... + '(pseudoreaction missing or empty).'], balanceOut); + end + balanceNew = balanceCurrent + (targetTotal - total); + if balanceNew <= 0 + error('scaleBiomassFraction:negativeBalance', ... + ['Cannot balance with %s: its fraction would become %g g/gDW. ' ... + 'It is %g g/gDW, but the other components total %g g/gDW.'], ... + balanceOut, balanceNew, balanceCurrent, total - balanceCurrent); + end + balanceFactor = balanceNew / balanceCurrent; + model = scaleBiomassPseudoreaction(model, biomassConfig, balanceOut, balanceFactor); +end +end diff --git a/biomass/scaleBiomassPseudoreaction.m b/src/biomass/scaleBiomassPseudoreaction.m similarity index 77% rename from biomass/scaleBiomassPseudoreaction.m rename to src/biomass/scaleBiomassPseudoreaction.m index cfb6d89f..996bca00 100644 --- a/biomass/scaleBiomassPseudoreaction.m +++ b/src/biomass/scaleBiomassPseudoreaction.m @@ -6,10 +6,12 @@ % The MATLAB counterpart of yeast-GEM's legacy rescalePseudoReaction. % % "Substrate" means every metabolite in the pseudoreaction whose -% metabolite name does NOT match the component name (the component's -% product is left untouched). After rescaling, the coefficient of -% biomassConfig.proton_met is recomputed so the pseudoreaction's total -% ionic charge sums to zero. +% metabolite name differs from the component name; the coefficient of the +% component's product is unchanged. After rescaling, the coefficient of +% biomassConfig.proton_met is recomputed so that the total charge of the +% pseudoreaction is zero. An error is raised if the model has no +% metCharges field or if a metabolite other than proton_met in the +% pseudoreaction has an undefined (NaN) charge. % % Parameters % ---------- @@ -18,12 +20,11 @@ % biomassConfig : struct % Struct (see getBiomassFractions). % componentName : char -% Name of the component to rescale (must match -% biomassConfig.components{i}.name for some i, AND be the -% model.metNames of the produced metabolite in the matching -% pseudoreaction). +% Name of the component to rescale. It must match +% biomassConfig.components{i}.name for some i and the model.metNames +% entry of the produced metabolite in the matching pseudoreaction. % factor : double -% Multiplicative factor. +% Factor by which the substrate coefficients are multiplied. % % Returns % ------- @@ -45,6 +46,12 @@ 'No reaction named %s in model.', comp.pseudoreaction_name); end +Hc = find(strcmp(model.mets, biomassConfig.proton_met)); +if isempty(Hc) + error('scaleBiomassPseudoreaction:missingProton', ... + 'Proton metabolite %s not found in model.', biomassConfig.proton_met); +end + for i = 1:length(model.mets) S_ir = model.S(i, rxnPos); isProd = strcmp(model.metNames{i}, componentName); @@ -54,7 +61,6 @@ end % Rebalance H+ to keep charge neutrality. -Hc = find(strcmp(model.mets, biomassConfig.proton_met)); model.S(Hc, rxnPos) = 0; model.S(Hc, rxnPos) = -chargeResidual(model, rxnPos); end diff --git a/biomass/setGAM.m b/src/biomass/setGAM.m similarity index 60% rename from biomass/setGAM.m rename to src/biomass/setGAM.m index 2d7da9f4..0c99bc98 100644 --- a/biomass/setGAM.m +++ b/src/biomass/setGAM.m @@ -8,9 +8,11 @@ % % For every metabolite in the biomass pseudoreaction whose model.metNames % entry is in cofactorMetNames, the stoichiometric coefficient is set to -% ±value preserving the sign of the current coefficient. Yeast-GEM scales -% ATP, ADP, H2O, H+ and phosphate (with ATP and H2O on the substrate side, -% ADP / H+ / phosphate on the product side). +% value with the sign of the current coefficient. An error is raised +% before the model is changed if ngamRxn is given and is not in the model, +% and if ngamValue is given without ngamRxn (RAVEN:badInput). +% For yeast-GEM, the cofactors are ATP, ADP, H2O, H+ and phosphate (ATP and +% H2O are substrates; ADP, H+ and phosphate are products). % % Parameters % ---------- @@ -21,7 +23,8 @@ % biomassRxn : char % Reaction id of the biomass pseudoreaction. % cofactorMetNames : cell -% Cell array of metabolite NAMES (not IDs) to rescale, e.g. +% Cell array of metabolite names (matched against model.metNames, not +% metabolite ids) to rescale, for example % {'ATP','ADP','H2O','H+','phosphate'}. % % Name-Value Arguments @@ -29,8 +32,8 @@ % ngamRxn : char % NGAM reaction id. Required when ngamValue is supplied. % ngamValue : double -% NGAM flux to fix. Sets the NGAM reaction's bounds to (ngamValue, -% ngamValue). +% NGAM flux to fix. Both bounds of the NGAM reaction are set to +% ngamValue. % % Returns % ------- @@ -43,8 +46,8 @@ % % See also % -------- -% fitParameters : estimates such a value from measured fluxes instead of -% applying a known one. The two are complementary - fit first, then set. +% fitParameters : derives such a value from measured fluxes; setGAM then +% applies it. if nargin < 4 error('setGAM:missingArgs', ... @@ -54,6 +57,15 @@ p=parseRAVENargs(varargin, {'ngamRxn',[]; 'ngamValue',[]}); ngamRxn=p.ngamRxn; ngamValue=p.ngamValue; +%An empty id counts as not given +if ~isempty(ngamRxn) + ngamRxn=cellstr(ngamRxn); + ngamRxn=ngamRxn(~cellfun(@isempty,ngamRxn)); +end +if ~isempty(ngamValue) && isempty(ngamRxn) + error('RAVEN:badInput', ... + 'ngamValue is given without ngamRxn. Give the NGAM reaction id as ''ngamRxn''.'); +end bioPos = strcmp(model.rxns, biomassRxn); if ~any(bioPos) @@ -61,6 +73,12 @@ 'Reaction %s not found in model.', biomassRxn); end +setNgam = ~isempty(ngamRxn) && ~isempty(ngamValue); +if ~isempty(ngamRxn) && ~all(ismember(ngamRxn, model.rxns)) + error('setGAM:missingNgamRxn', ... + 'NGAM reaction %s not found in model.', strjoin(ngamRxn, ', ')); +end + for i = 1:length(model.mets) S_ix = model.S(i, bioPos); isCofactor = any(strcmp(cofactorMetNames, model.metNames{i})); @@ -69,7 +87,7 @@ end end -if ~isempty(ngamRxn) && ~isempty(ngamValue) +if setNgam model = setParam(model, 'eq', ngamRxn, ngamValue); end end diff --git a/comparison/compareMultipleModels.m b/src/comparison/compareMultipleModels.m similarity index 89% rename from comparison/compareMultipleModels.m rename to src/comparison/compareMultipleModels.m index 6407a403..147338c3 100755 --- a/comparison/compareMultipleModels.m +++ b/src/comparison/compareMultipleModels.m @@ -3,10 +3,10 @@ % % Compares two or more models on overlap of reactions, metabolites, genes, % EC-codes, metabolite names and reaction equations (with and without -% compartmentalization), and on high-dimensional comparisons in the -% reaction-space: subsystem utilization, structural similarity, an optional -% low-dimensional structural projection, and an optional functional -% (task-based) comparison. +% compartmentalisation), and on high-dimensional comparisons of the +% reaction content: subsystem coverage, structural similarity, a +% low-dimensional structural projection (skipped when neither tsne nor +% mdscale is available), and an optional functional (task-based) comparison. % % Parameters % ---------- @@ -16,18 +16,19 @@ % Name-Value Arguments % -------------------- % printResults : logical -% true if the results should be printed on the screen (default false). +% true to print the results to the command window (default false). % plotResults : logical -% true if the results should be plotted (default false). +% true to plot the results (default false). % groupVector : double or cell -% numeric vector or cell array for grouping similar models, i.e. by -% tissue (default all models ungrouped). +% numeric vector or cell array that assigns each model to a group, for +% example by tissue. The groups colour the points of the structural +% projection plot (default [], no grouping). % funcCompare : logical -% should a functional comparison be run (default false). +% true to run the functional comparison, which requires taskFile +% (default false). % taskFile : char -% string containing the name of the task file to use for the functional -% comparison (should be an .xls or .xlsx file, required for functional -% comparison). +% name of the task file for the functional comparison, in a format that +% parseTaskList reads. Required when funcCompare is true. % % Returns % ------- @@ -40,24 +41,28 @@ % row indicates which models are included in the comparison) and % nElements (vector with the number of elements for each comparison). % "equ" are the reaction equations after sorting and "uEqu" are the -% equations when not taking compartmentalization into account. These -% are the only fields of the seven that use EC-code, metabolite name or -% reaction equation to compare models that do not share an id -% convention; the rest compare by identifier only. +% equations without compartments. The fields rxns, mets and genes +% compare identifiers; eccodes, metNames, equ and uEqu compare EC +% codes, metabolite names and reaction equations, which allows +% overlap between models that use different identifiers. % - subsystems : substructure containing subsystem information, with -% fields matrix (matrix with comparison of number of rxns per -% subsystem) and ID (vector consisting of names of all subsystems) +% fields matrix (number of reactions per subsystem, subsystems in rows +% and models in columns) and ID (cell array with the names of all +% subsystems). Only the first subsystem of each reaction is counted. +% The field is absent when a model lacks the subSystems field. % - reactions : substructure containing reaction information, with -% fields matrix (binary matrix composed of reactions (rows) in each -% model (column), used as the input for the model comparisons) and IDs +% fields matrix (binary matrix with reactions in rows and models in +% columns, used as the input for the model comparisons) and IDs % (list of the reactions contained in the reaction matrix) % - structComp : matrix with pairwise comparisons of model structure -% based on (1-Hamming distance) between models -% - structCompMap : matrix with 3D tSNE (or MDS) mapping of model -% structures based on Hamming distances +% based on (1 - Hamming distance) between models +% - structCompMap : matrix with the 3D tSNE (or MDS) mapping of model +% structures based on Hamming distances. It is empty when neither +% tsne nor mdscale is available. % - funcComp : substructure containing function comparison results, with -% fields matrix (matrix with PASS / FAIL (1 / 0) values for each task) -% and tasks (vector containing names of all tasks) +% fields matrix (1 for a passed task and 0 for a failed task, tasks in +% rows and models in columns) and tasks (the descriptions of all +% tasks). It is present only when funcCompare is true. % % Examples % -------- @@ -71,9 +76,8 @@ % % See also % -------- -% diffModels : takes exactly two models and reports the differing *values* -% (stoichiometry, bounds, grRules), where this reports overlap counts, -% similarity and clustering. +% diffModels : reports the differing values (stoichiometry, bounds, grRules) +% between two models. %% Set up input defaults p=parseRAVENargs(varargin, {'printResults',false; 'plotResults',false; 'groupVector',[]; 'funcCompare',false; 'taskFile',[]}); @@ -429,10 +433,11 @@ function printList(models,toCheck,nElements) % % clust_dist Distance metric to be used for clustering, ignored if % clust_dim is "none". Options are the same as those for -% distance in, e.g., PDIST ("euclidean", "hamming", etc.). +% distance in pdist (for example "euclidean", "hamming" and +% other metrics). % (DEFAULT = "euclidean") % -% col_map Colormap, provided as string (e.g., "parula", "hot", "etc.") +% col_map Colormap, provided as string (for example "parula" or "hot") % or an Nx3 RGB matrix of N colors. % (DEFAULT = "hot") % diff --git a/comparison/diffModels.m b/src/comparison/diffModels.m similarity index 82% rename from comparison/diffModels.m rename to src/comparison/diffModels.m index 00aed406..1b1f91e0 100644 --- a/comparison/diffModels.m +++ b/src/comparison/diffModels.m @@ -1,20 +1,17 @@ function report = diffModels(modelA, modelB, varargin) % diffModels Report the semantic differences between two models. % -% Compares two models entry by entry, keyed by identifier, and returns a report listing -% every difference found. Reactions, metabolites and genes are matched by -% id; for the ids present in both models the reaction stoichiometry, bounds, -% objective coefficient, grRule, EC codes, and the metabolite formula, -% charge and compartment are compared. This is the structured -% added/removed/changed diff that compareMultipleModels (which reports -% overlap counts, similarity and clustering across N models) does not -% provide. +% Compares two models entry by entry, keyed by identifier, and returns a +% report listing every difference found. Reactions, metabolites and genes +% are matched by id and reported when present in only one model. For the +% ids present in both models, the reaction stoichiometry, bounds, +% objective coefficient, grRule and EC codes, and the metabolite formula, +% charge and compartment are compared. % -% grRules are compared for logical equality rather than as strings: each -% rule is expanded to disjunctive normal form and the genes within each -% isozyme, and the isozymes themselves, are sorted before comparison, so -% "a and b" equals "b and a" and "(a or b)" equals "(b or a)". This is -% stronger than a case/whitespace string match. +% grRules are compared by logical content: each rule is expanded to +% disjunctive normal form, and the genes within each isozyme and the +% isozymes themselves are sorted before comparison, so "a and b" equals +% "b and a" and "(a or b)" equals "(b or a)". % % Parameters % ---------- @@ -29,10 +26,11 @@ % absolute tolerance for stoichiometric coefficient differences % (default 1e-9). % maxPerCategory : double -% the maximum number of differences reported per category before the -% rest are truncated with a single summary line (default 50). +% maximum number of entries in report.differences per category. Further +% differences of that category are replaced by a single summary line +% (default 50). % printResults : logical -% print the report to the command window (default false). +% true to print the report to the command window (default false). % % Returns % ------- @@ -41,19 +39,18 @@ % % - equal : true if no differences were found. % - differences : cell array of human-readable difference strings, each -% naming the category and the entity affected. -% - rxnsOnlyInA / rxnsOnlyInB : reaction ids present in only one model. -% - metsOnlyInA / metsOnlyInB : metabolite ids present in only one model. -% - genesOnlyInA / genesOnlyInB : gene ids present in only one model. -% -% Usage: report = diffModels(modelA, modelB, ...) +% naming the entity affected. +% - rxnsOnlyInA and rxnsOnlyInB : reaction ids present in only one +% model. +% - metsOnlyInA and metsOnlyInB : metabolite ids present in only one +% model. +% - genesOnlyInA and genesOnlyInB : gene ids present in only one model. +% Both are empty when either model lacks the genes field. % % See also % -------- -% compareMultipleModels : N models, set membership across seven identity -% notions (ids, EC codes, metabolite names, equations), similarity -% matrix, clustering and an optional task-based functional comparison -- -% no values. +% compareMultipleModels : overlap counts, similarity and clustering across +% two or more models. p = parseRAVENargs(varargin, {'stoichTol', 1e-9; 'maxPerCategory', 50; ... 'printResults', false}); diff --git a/conditions/applyCondition.m b/src/conditions/applyCondition.m similarity index 74% rename from conditions/applyCondition.m rename to src/conditions/applyCondition.m index 02ad2456..0b8ae844 100644 --- a/conditions/applyCondition.m +++ b/src/conditions/applyCondition.m @@ -1,28 +1,39 @@ function model = applyCondition(model, condition) % applyCondition Apply a deterministic condition to a model. % -% Apply a deterministic "condition" to a model: a prelude that resets -% exchange bounds, optional metabolite removals + automatic charge -% rebalancing of a pseudoreaction, optional biomass-stoichiometry delta, -% and a per-reaction bounds diff. The schema is intentionally narrow so a -% condition can be reviewed as data. +% The steps of a condition are applied in this order: a prelude that resets +% exchange bounds, metabolite removals from a pseudoreaction with automatic +% charge rebalancing, a change of biomass stoichiometry, and per-reaction +% bound changes. A final check compares the number of uptake bounds with an +% expected count. The schema covers only these steps, so a condition can be +% reviewed as data. % -% The same schema works for any GEM that keeps its condition presets as -% data rather than as code. Project-specific extensions (e.g. yeast-GEM's amino_acid_ratio step that -% rewrites a protein pseudoreaction's stoichiometry from a side-car TSV) -% are handled by the caller before / after this function — kept -% upstream-narrow on purpose. +% The schema applies to any GEM whose condition presets are kept as data. +% Project-specific extensions, for example yeast-GEM's amino_acid_ratio +% step that rewrites the stoichiometry of a protein pseudoreaction from a +% separate TSV file, are applied by the caller before or after this +% function. % % Parameters % ---------- % model : struct % RAVEN model struct. % condition : char or struct -% Either a path to a YAML condition file or a struct already produced -% by parseYAML. The expected schema (all keys optional): +% Path to a YAML condition file, or a struct as produced by parseYAML. +% The schema is as follows (all keys optional). reset_exchanges is +% passed as the reaction type to getExchangeRxns; the lower bound of +% the matching reactions is set to 0 and the upper bound to 1000. +% charge_balance_met receives the coefficient that makes the charge of +% rxn_id zero, which requires the metCharges field; a participant of +% rxn_id other than charge_balance_met with a NaN charge raises +% applyCondition:unknownCharge. add entries are +% added to the existing coefficients. A bounds entry whose rxn is not +% in the model is skipped with a warning. expected_uptake_count is +% compared with the number of bounds entries with lb of -1000, and a +% warning is issued when they differ. % % prelude: -% reset_exchanges: out % truthy -> reset all +% reset_exchanges: out % % cofactor_pseudoreaction: % rxn_id: r_4598 @@ -47,7 +58,7 @@ % Returns % ------- % model : struct -% Modified model. +% Model with the condition applied. % % Examples % -------- diff --git a/conversion/COBRA_structure_fields.csv b/src/conversion/COBRA_structure_fields.csv similarity index 100% rename from conversion/COBRA_structure_fields.csv rename to src/conversion/COBRA_structure_fields.csv diff --git a/conversion/addIdentifierPrefix.m b/src/conversion/addIdentifierPrefix.m similarity index 55% rename from conversion/addIdentifierPrefix.m rename to src/conversion/addIdentifierPrefix.m index 3f0629da..8267d2e2 100644 --- a/conversion/addIdentifierPrefix.m +++ b/src/conversion/addIdentifierPrefix.m @@ -1,13 +1,13 @@ function [model, hasChanged]=addIdentifierPrefix(model,varargin) % addIdentifierPrefix Add identifier prefixes required by SBML. % -% If reaction, metabolite, compartment, gene or model identifiers do not -% start with a letter or _, which conflicts with SBML specifications, -% prefixes are added for all identifiers in the respective model field. +% If any reaction, metabolite, compartment, gene or model identifier does +% not start with a letter or _, which conflicts with SBML specifications, +% a prefix is added to all identifiers in the respective model field. % The prefixes are: % % "R_" for model.rxns, -% "M_" for model.mets, +% "M_" for model.mets and model.id, % "C_" for model.comps; % "G_" for model.genes (and also represented in model.grRules) % @@ -19,16 +19,17 @@ % Name-Value Arguments % -------------------- % fields : cell -% cell array with model field names that should be checked if prefixes -% should be added, possible values: 'rxns', 'mets', 'comps', 'genes', -% 'id' (default all listed model fields will be checked). +% cell array with the names of the model fields to check for the need +% of a prefix, possible values: 'rxns', 'mets', 'comps', 'genes', 'id' +% (default all of these fields). % % Returns % ------- % model : struct % modified model. % hasChanged : cell -% cell array with fields and prefixes that are added. +% cell array of char with one entry per field that received a prefix, +% for example 'model.rxns (R_ prefix)'. % % Examples % -------- @@ -60,10 +61,8 @@ currField = strcat(currPrefix, currField); hasChanged(toChangeIdx(i)) = true; - if strcmp(currName,'genes') - model.grRules = regexprep(model.grRules, '(\<[0-9_a-zA-Z])', 'G_$1'); - model.grRules = regexprep(model.grRules, ' G_or ', ' or '); - model.grRules = regexprep(model.grRules, ' G_and ', ' and '); + if strcmp(currName,'genes') && isfield(model,'grRules') + model.grRules = cellfun(@(r) prefixGenes(r,currPrefix), model.grRules, 'UniformOutput', false); end model.(currName) = currField; end @@ -72,3 +71,19 @@ hasChanged = modelFields(hasChanged,:); hasChanged = append('model.', hasChanged(:,1), ' (', hasChanged(:,2), ' prefix)'); end + + +function rule = prefixGenes(rule, prefix) +% Prefix every gene in a grRule, leaving "and", "or" and brackets alone. A +% gene is everything between spaces and brackets, so ids that contain +% "-", "." or ":" get one prefix, not one per word. +[tokens, separators] = regexp(rule, '[^\s()]+', 'match', 'split'); +out = separators{1}; +for i = 1:numel(tokens) + if ~any(strcmpi(tokens{i}, {'and','or'})) + tokens{i} = [prefix tokens{i}]; + end + out = [out tokens{i} separators{i+1}]; %#ok +end +rule = out; +end diff --git a/conversion/cobraNamespaces.csv b/src/conversion/cobraNamespaces.csv similarity index 100% rename from conversion/cobraNamespaces.csv rename to src/conversion/cobraNamespaces.csv diff --git a/conversion/orderRavenFields.csv b/src/conversion/orderRavenFields.csv similarity index 100% rename from conversion/orderRavenFields.csv rename to src/conversion/orderRavenFields.csv diff --git a/conversion/ravenCobraWrapper.m b/src/conversion/ravenCobraWrapper.m similarity index 94% rename from conversion/ravenCobraWrapper.m rename to src/conversion/ravenCobraWrapper.m index 652ad187..1fba8ee9 100755 --- a/conversion/ravenCobraWrapper.m +++ b/src/conversion/ravenCobraWrapper.m @@ -1,19 +1,21 @@ function newModel=ravenCobraWrapper(model) % ravenCobraWrapper Convert between RAVEN and COBRA structures. % -% Recognises a COBRA structure by checking for the field 'rules', found -% only in a COBRA Toolbox structure. If the COBRA model also has a grRules -% field, that is used instead of parsing rules. +% A structure with the field 'rules' is treated as a COBRA Toolbox +% structure and converted to RAVEN; a structure without it is treated as a +% RAVEN structure and converted to COBRA. If a COBRA structure also has a +% grRules field, that field is used and rules is not parsed. % % Parameters % ---------- % model : struct -% a RAVEN/COBRA-compatible model structure. +% a RAVEN or COBRA model structure. % % Returns % ------- % newModel : struct -% a COBRA/RAVEN-compatible model structure. +% the model structure in the other format: COBRA for a RAVEN input and +% RAVEN for a COBRA input. % % Examples % -------- @@ -24,17 +26,18 @@ % During the RAVEN -> COBRA -> RAVEN conversion cycle the following fields % are lost: annotation, compOutside, compMiriams, rxnComps, geneComps, % unconstrained. Boundary metabolites are lost, because the COBRA -% structure does not involve boundary metabolites, so they are removed -% using simplifyModel before RAVEN -> COBRA conversion. The field 'rev' is -% also partially lost, but during COBRA -> RAVEN conversion it is -% reconstructed based on lower bound reaction values. +% structure has no boundary metabolites, and are removed using +% simplifyModel before the RAVEN -> COBRA conversion. The field 'rev' is +% also partially lost; during the COBRA -> RAVEN conversion it is +% reconstructed from the lower bounds (a reaction is reversible if its +% lower bound is below zero). % % During the COBRA -> RAVEN -> COBRA conversion cycle the following fields % are lost: geneEntrezID, modelVersion, proteins. % % The information about mandatory RAVEN fields was taken from the % checkModelStruct function, whereas the corresponding information about -% COBRA fields was fetched from the verifyModel function. +% COBRA fields was taken from the verifyModel function. if isfield(model,'rules') isRaven=false; @@ -45,7 +48,7 @@ ravenPath=findRAVENroot(); % Load COBRA field information -fid = fopen(fullfile(ravenPath,'conversion','COBRA_structure_fields.csv')); % Taken from https://github.com/opencobra/cobratoolbox/blob/develop/src/base/io/definitions/COBRA_structure_fields.csv +fid = fopen(fullfile(ravenPath,'src','conversion','COBRA_structure_fields.csv')); % Taken from https://github.com/opencobra/cobratoolbox/blob/develop/src/base/io/definitions/COBRA_structure_fields.csv fieldFile = textscan(fid,repmat('%s',1,15),'Delimiter','\t','HeaderLines',1); dbFields = ~cellfun(@isempty,fieldFile{5}); % Only keep fields with database annotations that should be translated to xxxMiriams dbFields = dbFields & ~contains(fieldFile{1},{'metInChIString','metKEGGID','metPubChemID','rxnECNumbers'}); @@ -55,7 +58,7 @@ fclose(fid); % Load conversion between additional COBRA fields and namespaces: -fid = fopen(fullfile(ravenPath,'conversion','cobraNamespaces.csv')); +fid = fopen(fullfile(ravenPath,'src','conversion','cobraNamespaces.csv')); fieldFile = textscan(fid,'%s %s','Delimiter',',','HeaderLines',0); COBRAfields = [COBRAfields; fieldFile{1}]; COBRAnamespace = [COBRAnamespace; fieldFile{2}]; diff --git a/conversion/removeIdentifierPrefix.m b/src/conversion/removeIdentifierPrefix.m similarity index 74% rename from conversion/removeIdentifierPrefix.m rename to src/conversion/removeIdentifierPrefix.m index 72b951a2..f7905939 100644 --- a/conversion/removeIdentifierPrefix.m +++ b/src/conversion/removeIdentifierPrefix.m @@ -3,15 +3,15 @@ % % Removes identifier prefixes: % -% "R_" for model.rxns, model.rxnNames and model.id, -% "M_" for model.mets and model.metNames, +% "R_" for model.rxns and model.rxnNames, +% "M_" for model.mets, model.metNames and model.id, % "C_" for model.comps; % "G_" for model.genes (and also represented in model.grRules). % % By default, the prefixes are only removed if all entries in a particular -% field have the prefix. The prefixes might have been present because one -% or more identifiers do not start with a letter or _, which conflicts -% with SBML specifications. +% field have the prefix. The prefixes are required when one or more +% identifiers do not start with a letter or _, which conflicts with SBML +% specifications. % % Parameters % ---------- @@ -21,20 +21,20 @@ % Name-Value Arguments % -------------------- % fields : cell -% cell array with model field names from which the identifiers should -% be removed, possible values: 'rxns', 'mets', 'comps', 'genes', -% 'metNames', 'rxnNames', 'id' (default all listed model fields will -% be checked). +% cell array with the names of the model fields from which the prefix +% is removed, possible values: 'rxns', 'mets', 'comps', 'genes', +% 'metNames', 'rxnNames', 'id' (default all of these fields). % forceRemove : logical -% if prefixes should be removed even if not all entries in a model -% field have the prefix (default false). +% true to remove the prefix from the entries that have it when not all +% entries in a model field have the prefix (default false). % % Returns % ------- % model : struct % modified model. % hasChanged : cell -% cell array with fields and prefixes that are removed. +% cell array of char with one entry per field from which a prefix is +% removed, for example 'model.rxns (R_ prefix)'. % % Examples % -------- diff --git a/conversion/standardizeModelFieldOrder.m b/src/conversion/standardizeModelFieldOrder.m similarity index 63% rename from conversion/standardizeModelFieldOrder.m rename to src/conversion/standardizeModelFieldOrder.m index b81441f1..38134d26 100755 --- a/conversion/standardizeModelFieldOrder.m +++ b/src/conversion/standardizeModelFieldOrder.m @@ -1,12 +1,15 @@ function orderedModel=standardizeModelFieldOrder(model) % standardizeModelFieldOrder Order RAVEN model structure fields. % -% Orders fields of a RAVEN model structure as specified at -% https://github.com/SysBioChalmers/RAVEN/wiki/RAVEN-Model-Structure +% Orders the fields of a RAVEN or COBRA model structure. The RAVEN order is +% the one listed in orderRavenFields.csv, described at +% https://raven-docs.readthedocs.io/en/latest/guide/model-structure.html +% A structure with the field 'rules' is ordered as a COBRA structure, using +% COBRA_structure_fields.csv. Fields that are not listed in the file are +% placed last, in alphabetical order. % -% The model fields themselves are not changed, only the order is modified. -% For changing model fields between RAVEN and COBRA format, use -% ravenCobraWrapper(). +% The content of the fields is unchanged. To convert model fields between +% RAVEN and COBRA format, use ravenCobraWrapper(). % % Parameters % ---------- @@ -29,12 +32,12 @@ ravenPath=findRAVENroot(); if ~isfield(model,'rules') % Check if model is RAVEN - fid = fopen(fullfile(ravenPath,'conversion','orderRavenFields.csv')); + fid = fopen(fullfile(ravenPath,'src','conversion','orderRavenFields.csv')); fields = textscan(fid,'%s','Delimiter',',','HeaderLines',0); fields = fields{1}; fclose(fid); else % If model is COBRA - fid = fopen(fullfile(ravenPath,'conversion','COBRA_structure_fields.csv')); % Taken from https://github.com/opencobra/cobratoolbox/blob/develop/src/base/io/definitions/COBRA_structure_fields.csv + fid = fopen(fullfile(ravenPath,'src','conversion','COBRA_structure_fields.csv')); % Taken from https://github.com/opencobra/cobratoolbox/blob/develop/src/base/io/definitions/COBRA_structure_fields.csv fields = textscan(fid,repmat('%s',1,15),'Delimiter','\t','HeaderLines',1); fields = fields{1}; fclose(fid); diff --git a/src/curation/curateModelFromTables.m b/src/curation/curateModelFromTables.m new file mode 100644 index 00000000..62469ff5 --- /dev/null +++ b/src/curation/curateModelFromTables.m @@ -0,0 +1,728 @@ +function newModel=curateModelFromTables(model,metsInfo,varargin) +% curateModelFromTables Curate or add mets, rxns and genes from tables. +% +% Curates existing metabolites, reactions and genes and adds new ones from +% tabular data files. +% +% If the *.tsv files contain metabolites, reactions or genes that are +% already present in the model, the information in the model is +% overwritten. This includes empty annotations in the *.tsv files, unless +% 'overwriteWithEmpty' is false. Metabolites are matched by +% metaboliteName[comp]; reactions by the stoichiometry of their reactants +% and products; genes by their gene name. Entities that are not present in +% the model are added. +% +% Columns are identified by their header (case-insensitive, and spaces around +% a header are ignored, also inside its quotes), so their order +% does not matter and optional columns can be left out. Every field that a +% table supplies is written to new and existing entities alike; a field the +% model does not have yet is created, with its default value for the other +% entities, unless all supplied values for it are empty. An empty grRules +% cell leaves the rule of an existing reaction unchanged, and so does an +% empty lb, ub or rev cell: an empty bound means that no bound is given. +% A new reaction without a lb gets a lower bound of -Inf, and one without an +% ub gets model.annotation.defaultUB (Inf if the model has none). +% +% Parameters +% ---------- +% model : struct +% RAVEN model structure to be curated. +% metsInfo : char +% Path to a *.tsv file with metabolite information, or 'none' to skip +% metabolite curation. Columns: metNames, comps (both required), +% formula, charge, inchi, metNotes, and any number of MIRIAM-namespace +% columns. Singular forms and the RAVEN field names (metFormulas, +% metCharges, inchis) are accepted as headers too. +% +% Name-Value Arguments +% -------------------- +% genesInfo : char +% Path to a *.tsv file with gene information, or 'none'. Columns: +% genes (required), geneShortNames (or geneShortName), and MIRIAM. +% rxnsCoeffs : char +% Path to a *.tsv file with reaction stoichiometric coefficients, or +% 'none'. Columns (all required): rxnIdx (or index), rxnNames, +% metNames, comps, coefficient. One row per (reaction, metabolite) +% pair. +% rxnsInfo : char +% Path to a *.tsv file with reaction information, or 'none'. Columns: +% rxnIdx (or index), rxnNames (both required), grRules, lb, ub, rev, +% subSystems, eccodes, rxnNotes, rxnReferences, rxnConfidenceScores, +% and MIRIAM. An index appears in at most one row of this table, +% otherwise RAVEN:badInput is raised (the rxnsCoeffs file has one row per +% metabolite of the reaction and repeats it). The two reaction tables +% are matched on index and name, and reactions or indices that appear +% in only one of them raise RAVEN:badInput. +% metPrefix : char +% Prefix used to create new metabolite ids (for example 's_' for +% yeast-GEM, 'M_' for the BiGG default) (default 'M_'). +% rxnPrefix : char +% Prefix used to create new reaction ids (default 'R_'). +% overwriteWithEmpty : logical +% if true, an empty cell in a row of an existing metabolite, gene or +% reaction empties the corresponding field of the model. If false, an +% empty cell leaves the existing value unchanged and only the cells with a +% value are written (default true; [] also means the default). Must be a +% scalar logical, 0 or 1. A cell with only blanks counts as empty in both +% modes. Empty grRules cells never overwrite. An empty or NaN lb, ub or +% rev cell always keeps the existing value. For the charge and the +% confidence score, an empty or NaN cell is treated alike: when +% overwriteWithEmpty is true and at least one other row of the column has +% a value, it becomes 0; a column in which no row has a value is not +% written, and the existing values stay. The same holds for a MIRIAM +% column: a column in which no row of an existing entity has a value +% does not clear the annotations. +% +% Returns +% ------- +% newModel : struct +% Curated RAVEN model structure. +% +% Examples +% -------- +% newModel = curateModelFromTables(model, metsInfo, genesInfo, ... +% rxnsCoeffs, rxnsInfo, metPrefix, rxnPrefix); +% +% Notes +% ----- +% In all three info tables, any column whose header is not one of the +% listed core fields is treated as a MIRIAM annotation namespace and +% stored on the matching entity. +% +% Cell values: the charge, confidence score, lb, ub, rev, rxnIdx and +% coefficient cells are read as plain decimal numbers (digits with an +% optional sign, decimal point and exponent), Inf or NaN in any case. Only +% the lb, ub and rev cells also accept true and false in any case (read as +% 1 and 0). Any other text, including text with a comma, hexadecimal numbers +% and expressions, raises RAVEN:badInput; no expression is evaluated. A +% charge must be a finite whole number (or empty or NaN). A rev must be 0, 1, +% true or false (or empty or NaN). The rev column sets the reversibility of +% existing reactions only, independently of lb and ub, which are written as +% given; for new reactions the reversibility is derived from lb and ub. +% +% Gene rules: a grRules cell must be a well-formed gene rule (genes joined by +% the words and or or in any case, or by &, &&, | or ||, with balanced +% parentheses); a rule with an operator that lacks an operand, empty +% parentheses or two genes without an operator between them raises +% RAVEN:badInput before the model is changed. A valid rule is stored in +% RAVEN's standard format. Whether the genes exist is checked when the rule +% is applied. +% +% Genes: a gene id that the genesInfo table lists more than once raises +% RAVEN:badInput, whether the model has the gene or not. +% +% Bounds: a lower bound above the upper bound is written as given. solveLP +% rejects such a model when it is run ("Invalid set of bounds for +% reaction(s): ..."). +% +% Reaction tables: a row of the rxnsCoeffs table without a rxnIdx, rxnNames, +% metNames and comps value is ignored; a row with any of them needs a +% rxnIdx and a coefficient that is a finite number other than 0. A row of +% the rxnsInfo table without any value is ignored; a row with a value needs +% a rxnIdx. A rxnIdx is a whole number of at least 1; the indices (and +% names) of the two reaction tables must match, with indices compared by +% their numeric value (1, 1.0 and 1e0 are the same index). Otherwise +% RAVEN:badInput is raised. + +p=parseRAVENargs(varargin, {'genesInfo','none'; 'rxnsCoeffs','none'; 'rxnsInfo','none'; 'metPrefix',[]; 'rxnPrefix',[]; 'overwriteWithEmpty',true}); +overwriteWithEmpty=p.overwriteWithEmpty; +if isempty(overwriteWithEmpty) + overwriteWithEmpty=true; +end +if ~((islogical(overwriteWithEmpty) || isnumeric(overwriteWithEmpty)) && isscalar(overwriteWithEmpty) ... + && isreal(overwriteWithEmpty) && ~isnan(overwriteWithEmpty) && ismember(overwriteWithEmpty,[0 1])) + error('RAVEN:badInput','overwriteWithEmpty must be a scalar logical or 0/1'); +end +overwriteWithEmpty=logical(overwriteWithEmpty); +genesInfo=p.genesInfo; +rxnsCoeffs=p.rxnsCoeffs; +rxnsInfo=p.rxnsInfo; +metPrefix=p.metPrefix; +if isempty(metPrefix) + metPrefix = 'M_'; +end +rxnPrefix=p.rxnPrefix; +if isempty(rxnPrefix) + rxnPrefix = 'R_'; +end +if xor(strcmp(rxnsCoeffs,'none'), strcmp(rxnsInfo,'none')) + error('RAVEN:badInput','Provide both a ''rxnsInfo'' and a ''rxnsCoeffs'' file') +end +newModel=model; + +%% Metabolites +if ~strcmp(metsInfo,'none') + [header,data] = readCurationTable(metsInfo); + [col,miriamCols] = matchColumns(header,metsInfo,{'metNames','comps'},{ ... + 'metNames', {'metNames','metName'}; ... + 'comps', {'comps','comp'}; ... + 'metFormulas', {'formula','formulas','metFormula','metFormulas'}; ... + 'metCharges', {'charge','charges','metCharge','metCharges'}; ... + 'inchis', {'inchi','inchis'}; ... + 'metNotes', {'metNotes','metNote'}}); + metsToAdd.metNames = data(:,col.metNames); + metsToAdd.compartments = data(:,col.comps); + textFields = {'metFormulas','inchis','metNotes'}; + for f = textFields(isfield(col,textFields)) + metsToAdd.(f{1}) = data(:,col.(f{1})); + end + if isfield(col,'metCharges') + metsToAdd.metCharges = cellfun(@(x) parseChargeCell(x,'metsInfo'),data(:,col.metCharges),'UniformOutput',false); + isNanCharge = cellfun(@(x) isscalar(x) && isnan(x),metsToAdd.metCharges); + metsToAdd.metCharges(isNanCharge) = {[]}; % a NaN charge counts as not given + emptyEntry = cellfun(@isempty,metsToAdd.metCharges); + emptyCharge = emptyEntry; + if all(emptyEntry) + metsToAdd = rmfield(metsToAdd,'metCharges'); + else % If some charges are given, assume 0 for those without charges specified + metsToAdd.metCharges(emptyEntry) = {0}; + metsToAdd.metCharges = cell2mat(metsToAdd.metCharges); + end + end + optFields = intersect({'metFormulas','metCharges','inchis','metNotes'},fieldnames(metsToAdd)','stable'); + + % Check if metabolite already exists (check by metName[comp]) + existingMets = []; + existingMetsIdx = []; + newMets = []; + for i=1:numel(metsToAdd.metNames) + metIdx = find(strcmp(newModel.metNames,metsToAdd.metNames{i})); + existMet = strcmp(metsToAdd.compartments{i},newModel.comps(newModel.metComps(metIdx))); + if any(existMet) + existingMets = [existingMets, i]; + existingMetsIdx = [existingMetsIdx, metIdx(existMet)]; + else + newMets = [newMets, i]; + end + end + + % Overwrite annotation of existing entries + if any(existingMets) + for f = optFields + skip = []; + if ~overwriteWithEmpty + if strcmp(f{1},'metCharges') + skip = emptyCharge(existingMets); + else + skip = emptyCells(metsToAdd.(f{1})(existingMets)); + end + end + newModel = writeExistingField(newModel,f{1},existingMetsIdx,metsToAdd.(f{1})(existingMets),skip); + end + newModel = extractAndAddMiriam(newModel,header(miriamCols),data(:,miriamCols),existingMets,existingMetsIdx,'met',~overwriteWithEmpty); + warning(['The following metabolites are already present in the model, '... + 'their annotation will be overwritten to match the metsInfo file. '... + 'If you do not particularly want to curate their annotations, it '... + 'would be better to remove these metabolites from metsInfo:\n\t\t%s'],... + strjoin(strcat(metsToAdd.metNames(existingMets),'[',metsToAdd.compartments(existingMets),']'),'\n\t\t')); + end + + % Continue with new metabolites + if numel(newMets)>0 + metsToAdd.metNames = metsToAdd.metNames(newMets); + metsToAdd.compartments = metsToAdd.compartments(newMets); + for f = optFields + metsToAdd.(f{1}) = metsToAdd.(f{1})(newMets); + if iscell(metsToAdd.(f{1})) && all(cellfun(@isempty,metsToAdd.(f{1}))) + metsToAdd = rmfield(metsToAdd,f{1}); + end + end + % Add metabolites + newModel = addMets(newModel,metsToAdd,true,metPrefix); + addedIdx = numel(newModel.mets)-numel(newMets)+1:numel(newModel.mets); + newModel = extractAndAddMiriam(newModel,header(miriamCols),data(:,miriamCols),newMets,addedIdx,'met'); + end +end +%% Genes +if ~strcmp(genesInfo,'none') + [header,data] = readCurationTable(genesInfo); + [col,miriamCols] = matchColumns(header,genesInfo,{'genes'},{ ... + 'genes', {'genes','gene'}; ... + 'geneShortNames', {'geneShortNames','geneShortName'}}); + genesToAdd.genes = data(:,col.genes); + checkGenesListedOnce(genesToAdd.genes); + if isfield(col,'geneShortNames') + genesToAdd.geneShortNames = data(:,col.geneShortNames); + end + + [~,notNewGene,existingGene] = intersect(genesToAdd.genes,newModel.genes); + if ~isempty(notNewGene) + warning(['The following genes are already present in the model, their annotation '... + 'will be overwritten to match the genesInfo file: \n\t\t%s'],... + strjoin(genesToAdd.genes(notNewGene),'\n\t\t')); + if isfield(genesToAdd,'geneShortNames') + skip = []; + if ~overwriteWithEmpty + skip = emptyCells(genesToAdd.geneShortNames(notNewGene)); + end + newModel = writeExistingField(newModel,'geneShortNames',existingGene,genesToAdd.geneShortNames(notNewGene),skip); + end + newModel = extractAndAddMiriam(newModel,header(miriamCols),data(:,miriamCols),notNewGene,existingGene,'gene',~overwriteWithEmpty); + end + + % Continue with new genes + toAdd = 1:numel(genesToAdd.genes); + toAdd(notNewGene) = []; + if ~isempty(toAdd) + genesToAdd.genes = genesToAdd.genes(toAdd); + if isfield(genesToAdd,'geneShortNames') + genesToAdd.geneShortNames = genesToAdd.geneShortNames(toAdd); + if all(cellfun(@isempty,genesToAdd.geneShortNames)) + genesToAdd = rmfield(genesToAdd,'geneShortNames'); + end + end + % Add genes + newModel = addGenesRaven(newModel,genesToAdd); + addedIdx = numel(newModel.genes)-numel(toAdd)+1:numel(newModel.genes); + newModel = extractAndAddMiriam(newModel,header(miriamCols),data(:,miriamCols),toAdd,addedIdx,'gene'); + end +end +%% Reactions +if ~any(strcmp({rxnsCoeffs,rxnsInfo},'none')) + % Gather all data, first about reaction stoichiometries... + [header,data] = readCurationTable(rxnsCoeffs); + col = matchColumns(header,rxnsCoeffs,{'rxnIdx','rxnNames','metNames','comps','coefficient'},{ ... + 'rxnIdx', {'rxnIdx','index'}; ... + 'rxnNames', {'rxnNames','rxnName'}; ... + 'metNames', {'metNames','metName'}; ... + 'comps', {'comps','comp'}; ... + 'coefficient', {'coefficient','coefficients','coeffs','stoichCoeffs'}}); + % A row without a rxnIdx, rxnNames, metNames and comps value is ignored + rowNo = (1:size(data,1))'; + keyCells = data(:,[col.rxnIdx,col.rxnNames,col.metNames,col.comps]); + ignoredRow = all(reshape(emptyCells(keyCells(:)),size(keyCells)),2); + data(ignoredRow,:) = []; + rowNo(ignoredRow) = []; + rxnCheck.coeffsIdx = parseIndexCells(data(:,col.rxnIdx),'rxnIdx','rxnsCoeffs'); + rxns.rxnNames = data(:,col.rxnNames); + rxns.metNames = data(:,col.metNames); + rxns.comps = data(:,col.comps); + rxns.coeffs = parseNumericCells(data(:,col.coefficient),'coefficient','rxnsCoeffs'); + checkCoefficientsGiven(rxnCheck.coeffsIdx,rxns.coeffs,rowNo); + rxnCheck.coeffs = indexKeys(rxnCheck.coeffsIdx,rxns.rxnNames); + + % ... and then additional reaction-specific data. + [header,data] = readCurationTable(rxnsInfo); + [col,miriamCols] = matchColumns(header,rxnsInfo,{'rxnIdx','rxnNames'},{ ... + 'rxnIdx', {'rxnIdx','index'}; ... + 'rxnNames', {'rxnNames','rxnName'}; ... + 'grRules', {'grRules'}; ... + 'lb', {'lb'}; ... + 'ub', {'ub'}; ... + 'rev', {'rev'}; ... + 'subSystems', {'subSystems'}; ... + 'eccodes', {'eccodes'}; ... + 'rxnNotes', {'rxnNotes'}; ... + 'rxnReferences', {'rxnReferences'}; ... + 'rxnConfidenceScores', {'rxnConfidenceScores'}}); + % A row without any value is ignored; a row with a value needs a rxnIdx + rowNo = (1:size(data,1))'; + ignoredRow = all(reshape(emptyCells(data(:)),size(data)),2); + data(ignoredRow,:) = []; + rowNo(ignoredRow) = []; + rxnCheck.rxnsIdx = parseIndexCells(data(:,col.rxnIdx),'rxnIdx','rxnsInfo'); + noIdx = find(isnan(rxnCheck.rxnsIdx),1); + if ~isempty(noIdx) + error('RAVEN:badInput','Data row %d of the rxnsInfo file has values but its rxnIdx is empty or NaN',rowNo(noIdx)); + end + [uniqueIdx,~,idxGroup] = unique(rxnCheck.rxnsIdx); + repeated = find(accumarray(idxGroup,1)>1,1); + if ~isempty(repeated) + error('RAVEN:badInput','The rxnIdx %s is used by data rows %s of the rxnsInfo file; each reaction needs its own index', ... + num2str(uniqueIdx(repeated)),strjoin(arrayfun(@num2str,rowNo(idxGroup==repeated)','UniformOutput',false),', ')); + end + rxnCheck.rxns = indexKeys(rxnCheck.rxnsIdx,data(:,col.rxnNames)); + + notMatching = setxor(rxnCheck.coeffs,rxnCheck.rxns); + if ~isempty(notMatching) + error('RAVEN:badInput',['The following reactions and/or their indices are not matched '... + 'between the rxnsInfo and rxnsCoeffs files:\n\t\t%s'],... + strjoin(regexprep(notMatching,'^(\d+)\*\*\*(.*)$','index $1 ($2)'),'\n\t\t')) + end + + rxnsToAdd.rxnNames = data(:,col.rxnNames); + for f = intersect({'grRules','subSystems','eccodes','rxnNotes','rxnReferences'},fieldnames(col)','stable') + rxnsToAdd.(f{1}) = data(:,col.(f{1})); + end + if isfield(col,'grRules') + rxnsToAdd.grRules = standardGrRules(rxnsToAdd.grRules,rxnsToAdd.rxnNames,rowNo); + end + for f = intersect({'lb','ub','rev'},fieldnames(col)','stable') + rxnsToAdd.(f{1}) = parseNumericCells(data(:,col.(f{1})),f{1},'rxnsInfo'); + end + if isfield(rxnsToAdd,'rev') + checkRevValues(rxnsToAdd.rev,data(:,col.rev)); + end + if isfield(col,'rxnConfidenceScores') + rxnsToAdd.rxnConfidenceScores = cellfun(@(x) parseNumberCell(x,'rxnConfidenceScores','rxnsInfo'),data(:,col.rxnConfidenceScores),'UniformOutput',false); + isNanScore = cellfun(@(x) isscalar(x) && isnan(x),rxnsToAdd.rxnConfidenceScores); + rxnsToAdd.rxnConfidenceScores(isNanScore) = {[]}; % a NaN score counts as not given + emptyEntry = cellfun(@isempty,rxnsToAdd.rxnConfidenceScores); + emptyScore = emptyEntry; + if all(emptyEntry) + rxnsToAdd = rmfield(rxnsToAdd,'rxnConfidenceScores'); + else % If some rxnConfidenceScores are given, assume 0 for those without rxnConfidenceScores specified + rxnsToAdd.rxnConfidenceScores(emptyEntry) = {0}; + rxnsToAdd.rxnConfidenceScores = cell2mat(rxnsToAdd.rxnConfidenceScores); + end + end + optFields = intersect({'subSystems','eccodes','rxnNotes','rxnReferences','rxnConfidenceScores'},fieldnames(rxnsToAdd)','stable'); + + existingRxn=[]; + notNewRxn=[]; + for i=1:numel(rxnCheck.rxnsIdx) + rxnRows = find(rxnCheck.rxnsIdx(i)==rxnCheck.coeffsIdx); + rxnsToAdd.mets{i,1} = cell(1,1); + rxnsToAdd.stoichCoeffs{i,1} = cell(1,1); + for j=1:numel(rxnRows) + newMetComp = [rxns.metNames{rxnRows(j)}, '[', rxns.comps{rxnRows(j)}, ']']; + try + newMetComp = getIndexes(newModel,newMetComp,'metcomps'); + catch + error('RAVEN:badInput',['Not all metabolites in reaction "%s" are present in the '... + 'model or in the provided table with new metabolites.'],rxnsToAdd.rxnNames{i}) + end + rxnsToAdd.mets{i}(j) = newModel.mets(newMetComp); + rxnsToAdd.stoichCoeffs{i}{j} = rxns.coeffs(rxnRows(j)); + end + rxnsToAdd.stoichCoeffs{i}=cell2mat(rxnsToAdd.stoichCoeffs{i}); + %Check if the reaction not already exists, by stoichiometry of its + %products and reactants + metIdx = getIndexes(newModel,rxnsToAdd.mets{i},'mets'); + modelCoeffs = transpose(newModel.S(metIdx,:)); + modelCoeffs2 = find(~all(modelCoeffs==0,2)); + % Only reactions built from exactly these metabolites can be + % duplicates. Any model reaction that also involves other + % metabolites projects onto the same row of coefficients, but is a + % different reaction and must not be overwritten. + sameSupport = full(sum(newModel.S(:,modelCoeffs2)~=0,1))' == numel(metIdx); + modelCoeffs2 = modelCoeffs2(sameSupport); + modelCoeffs = modelCoeffs(modelCoeffs2,:); + [~, duplicateRxn] = intersect(modelCoeffs,rxnsToAdd.stoichCoeffs{i},'rows'); + if ~isempty(duplicateRxn) + existingRxn = [existingRxn,modelCoeffs2(duplicateRxn)]; + notNewRxn = [notNewRxn,i]; + end + end + + % If some reactions already existed, then replace its information + if ~isempty(existingRxn) + warning(['The following reactions are the same as existing model '... + 'reactions, their annotation will be overwritten to match the '... + 'rxnCoeffs file: \n\t\t(Existing reaction ID): New reaction name\n\t\t%s'],... + strjoin(strcat('(',newModel.rxns(existingRxn),{'): '},rxnsToAdd.rxnNames(notNewRxn)),'\n\t\t')); + nameValues = rxnsToAdd.rxnNames(notNewRxn); + nameIdx = existingRxn; + if ~overwriteWithEmpty + nameIdx = nameIdx(~emptyCells(nameValues)); + nameValues = nameValues(~emptyCells(nameValues)); + end + newModel.rxnNames(nameIdx) = nameValues; + for f = intersect({'lb','ub','rev'},fieldnames(rxnsToAdd)','stable') + values = rxnsToAdd.(f{1})(notNewRxn); + given = ~isnan(values); + newModel.(f{1})(existingRxn(given)) = values(given); + end + for f = optFields + values = rxnsToAdd.(f{1})(notNewRxn); + skip = []; + if ~overwriteWithEmpty + if strcmp(f{1},'rxnConfidenceScores') + skip = emptyScore(notNewRxn); + else + skip = emptyCells(values); + end + end + if strcmp(f{1},'subSystems') && isfield(newModel,'subSystems') ... + && any(cellfun(@iscell,newModel.subSystems)) + values = cellfun(@(x) {x}, values, 'UniformOutput', false); + end + newModel = writeExistingField(newModel,f{1},existingRxn,values,skip); + end + if isfield(rxnsToAdd,'grRules') + emptyEntries = emptyCells(rxnsToAdd.grRules(notNewRxn)); + if ~all(emptyEntries) + newModel = changeGrRules(newModel,newModel.rxns(existingRxn(~emptyEntries)),rxnsToAdd.grRules(notNewRxn(~emptyEntries)),true); + end + end + newModel = extractAndAddMiriam(newModel,header(miriamCols),data(:,miriamCols),notNewRxn,existingRxn,'rxn',~overwriteWithEmpty); + end + + % Continue with new reactions + toAdd = 1:numel(rxnsToAdd.rxnNames); + toAdd(notNewRxn) = []; + if ~isempty(toAdd) + rxnsToAdd.rxnNames = rxnsToAdd.rxnNames(toAdd); + for f = intersect({'lb','ub','rev','grRules'},fieldnames(rxnsToAdd)','stable') + rxnsToAdd.(f{1}) = rxnsToAdd.(f{1})(toAdd); + end + if isfield(rxnsToAdd,'lb') + rxnsToAdd.lb(isnan(rxnsToAdd.lb)) = -inf; + end + if isfield(rxnsToAdd,'ub') + defaultUB = inf; + if isfield(newModel,'annotation') && isfield(newModel.annotation,'defaultUB') + defaultUB = newModel.annotation.defaultUB; + end + rxnsToAdd.ub(isnan(rxnsToAdd.ub)) = defaultUB; + end + for f = optFields + rxnsToAdd.(f{1}) = rxnsToAdd.(f{1})(toAdd); + if iscell(rxnsToAdd.(f{1})) && all(cellfun(@isempty,rxnsToAdd.(f{1}))) + rxnsToAdd = rmfield(rxnsToAdd,f{1}); + end + end + rxnsToAdd.mets = rxnsToAdd.mets(toAdd); + rxnsToAdd.stoichCoeffs = rxnsToAdd.stoichCoeffs(toAdd); + rxnsToAdd.rxns = generateNewIds(model,'rxns',rxnPrefix,numel(rxnsToAdd.rxnNames)); + + newModel = addRxns(newModel,rxnsToAdd,1,[],false,false); + rxnsModelIdx = numel(newModel.rxns)-numel(toAdd)+1:numel(newModel.rxns); + newModel = extractAndAddMiriam(newModel,header(miriamCols),data(:,miriamCols),toAdd,rxnsModelIdx,'rxn'); + end +end +end + +function [header,data] = readCurationTable(fileName) +% Read a tab-delimited curation table into its header row and a cell +% array of text with one column per header field. +fid = fopen(fileName); +if fid==-1 + error('RAVEN:badInput','Could not open the file "%s"',fileName); +end +headerLine = fgetl(fid); +if ~ischar(headerLine) + fclose(fid); + error('RAVEN:badInput','The file "%s" is empty',fileName); +end +headerLine = strrep(headerLine,char(65279),''); % byte order mark +header = strtrim(strsplit(headerLine,'\t')); +header = strtrim(regexprep(header,'^"(.*)"$','$1')); +nCols = numel(header); +raw = textscan(fid,repmat('%q',1,nCols),'Delimiter','\t'); +fclose(fid); +nRows = max(cellfun(@numel,raw)); +data = repmat({''},nRows,nCols); +for i=1:nCols + data(1:numel(raw{i}),i) = raw{i}; +end +blank = cellfun(@(x) ischar(x) && isempty(strtrim(x)),data); +data(blank) = {''}; +end + +function [col,miriamCols] = matchColumns(header,fileName,required,spec) +% Locate the core columns of a curation table by header name (case +% insensitive). spec has one row per core field: {field, accepted headers}. +% Every other column is a MIRIAM namespace. +col = struct(); +isCore = false(1,numel(header)); +for i=1:size(spec,1) + match = find(ismember(lower(header),lower(spec{i,2}))); + if ~isempty(match) + col.(spec{i,1}) = match(1); + isCore(match) = true; + end +end +missing = setdiff(required,fieldnames(col)','stable'); +if ~isempty(missing) + error('RAVEN:badInput','The file "%s" has no column with header: %s',fileName,strjoin(missing,', ')); +end +miriamCols = find(~isCore & ~cellfun(@isempty,header)); +end + +function model = writeExistingField(model,field,modelIdx,values,skip) +% Write the values of an optional field onto existing entities. A field +% the model does not have is created from its ravenModelFields default, +% unless all supplied values are empty. Entries flagged in the logical +% vector skip are not written. +if nargin>4 && ~isempty(skip) + modelIdx = modelIdx(~skip(:)'); + values = values(~skip(:)); + if isempty(modelIdx) + return + end +end +if ~isfield(model,field) + if iscell(values) && all(cellfun(@isempty,values)) + return + end + reg = ravenModelFields(); + entry = reg(strcmp({reg.name},field)); + n = numel(model.([entry.type 's'])); + if iscell(values) + model.(field) = repmat({entry.default},n,1); + else + model.(field) = repmat(entry.default,n,1); + end +end +model.(field)(modelIdx) = values; +end + +function newModel = extractAndAddMiriam(model,miriamName,miriamData,inputIndex,modelIndex,type,skipEmpty) +% Replace the MIRIAM annotations of the entities in modelIndex with the +% values of the table rows in inputIndex. With skipEmpty, an empty cell +% leaves the annotation of that entity unchanged. +newModel=model; +miriamValues = miriamData(inputIndex,:); +if nargin>6 && skipEmpty + for i=1:numel(miriamName) + given = ~emptyCells(miriamValues(:,i)); + if any(given) + if ~isfield(newModel,[type 'Miriams']) + newModel.([type 'Miriams'])=cell(numel(newModel.([type 's'])),1); + end + idx = modelIndex(:); + newModel = editMiriam(newModel,type,idx(given),miriamName{i},miriamValues(given,i),'replace'); + end + end + return +end +emptyMiriam = all(cellfun(@isempty,miriamValues),1); +miriamName(emptyMiriam) = []; +miriamValues(:,emptyMiriam) = []; +if ~isfield(newModel,[type 'Miriams']) + newModel.([type 'Miriams'])=cell(numel(newModel.([type 's'])),1); +end +for i=1:numel(miriamName) + newModel = editMiriam(newModel,type,modelIndex,miriamName{i},miriamValues(:,i),'replace'); +end +end + +function empty = emptyCells(values) +% Logical column vector that is true for the empty entries of a cell array +% of text (a cell with only blanks counts as empty) or of a numeric cell. +empty = cellfun(@(x) isempty(x) || (ischar(x) && isempty(strtrim(x))),values(:)); +end + +function values = parseIndexCells(cells,field,fileLabel) +% Reaction indices from table cells: NaN where the cell is empty or says NaN +% (the row is ignored), otherwise a finite whole number of at least 1. +values = parseNumericCells(cells,field,fileLabel); +bad = find(~isnan(values) & (~isfinite(values) | values~=round(values) | values<1),1); +if ~isempty(bad) + error('RAVEN:badInput','The %s value "%s" in the %s file is not a whole number of at least 1',field,strtrim(cells{bad}),fileLabel); +end +end + +function values = parseNumericCells(cells,field,fileLabel) +% Numbers from table cells, NaN where the cell is empty or says NaN. A cell +% that is not a number (or true/false) raises an error. +values = nan(numel(cells),1); +for i=1:numel(cells) + v = parseNumberCell(cells{i},field,fileLabel); + if ~isempty(v) + values(i) = v; + end +end +end + +function v = parseChargeCell(text,fileLabel) +% Charge from a table cell: [] for an empty cell, NaN for NaN, otherwise a +% finite whole number. A fractional or infinite charge raises an error. +v = parseNumberCell(text,'charge',fileLabel); +if ~isempty(v) && ~isnan(v) && (~isfinite(v) || v~=round(v)) + error('RAVEN:badInput','The charge value "%s" in the %s file is not a finite whole number',strtrim(text),fileLabel); +end +end + +function checkCoefficientsGiven(idx,coeffs,rowNo) +% Every row of the rxnsCoeffs table that is kept has a rxnIdx and a +% coefficient that is a finite number other than 0. +noIdx = find(isnan(idx),1); +if ~isempty(noIdx) + error('RAVEN:badInput',['Data row %d of the rxnsCoeffs file has values but its rxnIdx is empty or NaN'],rowNo(noIdx)); +end +missing = find(isnan(coeffs),1); +if ~isempty(missing) + error('RAVEN:badInput',['The coefficient in data row %d of the rxnsCoeffs file is empty or NaN; ' ... + 'every row with a reaction, metabolite or compartment needs a coefficient'],rowNo(missing)); +end +bad = find(~isfinite(coeffs) | coeffs==0,1); +if ~isempty(bad) + error('RAVEN:badInput','The coefficient in data row %d of the rxnsCoeffs file must be a finite non-zero number',rowNo(bad)); +end +end + +function keys = indexKeys(idx,names) +% One key per table row from the numeric reaction index and the name, so that +% the indices of two tables are compared by value. +keys = cell(numel(idx),1); +for i=1:numel(idx) + keys{i} = sprintf('%.0f***%s',idx(i),names{i}); +end +end + +function checkGenesListedOnce(genes) +% Raise RAVEN:badInput for a gene id that the genesInfo table lists more than +% once, whether the model has the gene or not: two rows for one gene would +% say two things about it, and the second row would add a second gene with +% the same id. +[ids,~,j] = unique(genes); +repeated = ids(accumarray(j,1) > 1); +if ~isempty(repeated) + error('RAVEN:badInput',['The following genes are listed more than once '... + 'in the genesInfo file:\n\t\t%s'],strjoin(repeated,'\n\t\t')); +end +end + + +function rules = standardGrRules(rules,rxnNames,rowNo) +% Raise RAVEN:badInput for a grRules cell that is not a well-formed gene rule +% (unbalanced parentheses, an operator without two operands, empty +% parentheses, two genes without an operator between them) and write each +% other rule in RAVEN's standard format (" and " / " or ", parentheses only +% where needed), so that addRxns and changeGrRules read every spelling that +% parseGrRule reads. Empty cells are left as they are. +for i = find(~emptyCells(rules))' + try + rules{i} = grRuleToString(parseGrRule(rules{i})); + catch err + if strcmp(err.identifier,'RAVEN:badGrRule') + error('RAVEN:badInput','The grRules value "%s" of reaction "%s" (data row %d of the rxnsInfo file) is not a valid gene rule: %s',... + rules{i},rxnNames{i},rowNo(i),err.message); + end + rethrow(err); + end +end +end + + +function checkRevValues(values,cells) +% A rev cell is 0, 1, true or false; an empty or NaN cell is not given. +bad = find(~isnan(values) & values~=0 & values~=1,1); +if ~isempty(bad) + error('RAVEN:badInput','The rev value "%s" in the rxnsInfo file is not 0, 1, true or false',strtrim(cells{bad})); +end +end + +function v = parseNumberCell(text,field,fileLabel) +% Real number from a table cell: [] for an empty cell, 1 or 0 for true or +% false in any case (lb, ub and rev cells only), NaN or +/-Inf for those words in any case, otherwise a +% plain decimal number (digits with an optional sign, decimal point and +% exponent). Any other text raises an error; nothing is evaluated. +text = strtrim(text); +if isempty(text) + v = []; + return +end +word = lower(text); +if any(strcmp(word,{'true','false'})) && ~ismember(field,{'lb','ub','rev'}) + error('RAVEN:badInput','The %s value "%s" in the %s file is not a number',field,text,fileLabel); +end +if strcmp(word,'true') + v = 1; +elseif strcmp(word,'false') + v = 0; +elseif ~isempty(regexp(word,'^[+-]?nan$','once')) + v = NaN; +elseif ~isempty(regexp(word,'^[+-]?inf$','once')) + v = Inf; + if word(1)=='-' + v = -Inf; + end +elseif ~isempty(regexp(text,'^[+-]?([0-9]+\.?[0-9]*|\.[0-9]+)([eE][+-]?[0-9]+)?$','once')) + v = str2double(text); +else + error('RAVEN:badInput','The %s value "%s" in the %s file is not a number',field,text,fileLabel); +end +end diff --git a/curation/downloadGenomeData.m b/src/curation/downloadGenomeData.m similarity index 83% rename from curation/downloadGenomeData.m rename to src/curation/downloadGenomeData.m index 72877d1b..6f54ae09 100644 --- a/curation/downloadGenomeData.m +++ b/src/curation/downloadGenomeData.m @@ -1,16 +1,17 @@ function [gffFile, faaFile] = downloadGenomeData(accession, varargin) % downloadGenomeData Download genome annotation files from NCBI. % -% Retrieves the GFF3 annotation and protein FASTA (.faa) files for a given -% NCBI genome assembly accession using the NCBI Datasets v2 API, which -% returns both files in a single archive. The files are saved locally and -% can be used directly by getGeneData to build a gene-mapping table. +% Downloads the GFF3 annotation and protein FASTA (.faa) files for an NCBI +% genome assembly accession using the NCBI Datasets v2 API, which returns +% both files in a single archive. The files are saved as +% _genomic.gff and _protein.faa in outputDir. They +% are input files for getGeneData. % % Parameters % ---------- % accession : char -% NCBI genome assembly accession, e.g. 'GCF_000002595.2'. -% Both RefSeq (GCF_) and GenBank (GCA_) prefixes are accepted. +% NCBI genome assembly accession, for example 'GCF_000002595.2'. The +% prefix is 'GCF_' (RefSeq) or 'GCA_' (GenBank). % % Name-Value Arguments % -------------------- @@ -40,8 +41,10 @@ % % Notes % ----- -% Requires an active internet connection. Files already present in -% outputDir are not re-fetched; delete them manually to force a refresh. +% An internet connection is required. When both files are already present +% in outputDir, no download takes place; deleting them forces a new +% download. An error is raised when NCBI Datasets does not return both a +% GFF3 file and a protein FASTA file for the accession. if nargin < 1 || isempty(accession) error('downloadGenomeData:missingInput', ... diff --git a/curation/getGeneData.m b/src/curation/getGeneData.m similarity index 81% rename from curation/getGeneData.m rename to src/curation/getGeneData.m index 3f852c7c..497c3c88 100644 --- a/curation/getGeneData.m +++ b/src/curation/getGeneData.m @@ -1,39 +1,46 @@ function geneTable = getGeneData(accession, varargin) % getGeneData Build a gene-mapping table from NCBI genome annotation files. % -% Parses the GFF3 annotation and protein FASTA (.faa) files for a given -% NCBI genome assembly and produces a table mapping locus tags to gene -% symbols, protein IDs, and other identifiers. The resulting table can be -% passed directly to renameModelGenes to update gene identifiers in a -% RAVEN model. If no local files are provided, downloadGenomeData is -% called automatically to fetch them. +% Parses the GFF3 annotation file of an NCBI genome assembly and produces +% a table mapping locus tags to gene symbols, protein IDs and other +% identifiers. The table can be passed to renameModelGenes to change gene +% identifiers in a RAVEN model. If the input is an assembly accession, +% downloadGenomeData downloads the GFF3 and protein FASTA (.faa) files to +% downloadDir, unless both are already present there. % % Parameters % ---------- % accession : char -% NCBI genome assembly accession, e.g. 'GCF_000002595.2', or a path to a -% local GFF3 annotation file. +% NCBI genome assembly accession, for example 'GCF_000002595.2', or the +% path to a local GFF3 annotation file. An error is raised if the +% input is neither an existing file nor an accession of the form +% 'GCF_' or 'GCA_' followed by digits and an optional version. % % Name-Value Arguments % -------------------- % outputFile : char -% Path to save the resulting table as a tab-delimited .tsv file. If -% omitted or empty, no file is written and the table is only returned. +% Path of the tab-delimited .tsv file to which the table is written. +% No file is written if omitted or empty (default ''). % downloadDir : char -% Directory where genome files are downloaded if not already present -% (default: current working directory). +% Directory where genome files are downloaded if not already present. +% Only used when the input is an accession (default: current working +% directory). % % Returns % ------- % geneTable : table -% MATLAB table with one row per gene containing: -% locus_tag — stable locus identifier (e.g. 'Cre01.g000001') -% old_locus_tag — previous locus identifier when available -% GeneID — NCBI Gene ID (e.g. '5723799') -% gene_name — common gene symbol when available (e.g. 'rbcL') -% GenBank_protein — protein accession (e.g. 'XP_001698190.2'), matching -% the protein FASTA headers -% UniProt — UniProt accession from Dbxref when available +% MATLAB table with one row per distinct combination of gene and +% protein accession (a CDS feature with a protein accession, repeated +% CDS rows are merged), with the columns below. A column entry is empty +% when the annotation lacks the value. +% +% - locus_tag : locus identifier (for example 'Cre01.g000001') +% - old_locus_tag : earlier locus identifier +% - GeneID : NCBI Gene ID (for example '5723799') +% - gene_name : gene symbol (for example 'rbcL') +% - GenBank_protein : protein accession (for example 'XP_001698190.2'), +% as in the protein FASTA headers +% - UniProt : UniProt accession from the Dbxref attribute of the gene % % Examples % -------- @@ -49,8 +56,9 @@ % Notes % ----- % When outputFile is provided the table is written as UTF-8 encoded -% tab-delimited text with a header row, compatible with readtable and -% renameModelGenes. Rows without a locus_tag are silently discarded. +% tab-delimited text with a header row, which readtable and +% renameModelGenes can read. A CDS whose gene has no locus_tag, or whose +% gene feature is missing, gives a row with an empty locus_tag. if nargin < 1 || isempty(accession) error('getGeneData:missingInput', ... diff --git a/curation/processProteinFastaFile.m b/src/curation/processProteinFastaFile.m similarity index 80% rename from curation/processProteinFastaFile.m rename to src/curation/processProteinFastaFile.m index eeebf5b4..a5518bfe 100644 --- a/curation/processProteinFastaFile.m +++ b/src/curation/processProteinFastaFile.m @@ -1,11 +1,13 @@ function processedFaaFile = processProteinFastaFile(faaFile, geneTable, headerCol, varargin) % processProteinFastaFile Rename protein FASTA headers using a gene mapping table. % -% Reads a protein FASTA file (as downloaded by downloadGenomeData) and -% replaces each sequence header with the value from the specified geneTable -% column, matched via the GenBank_protein accession present in the original -% FASTA header. Sequences whose accession is not found in geneTable are -% kept with their original header unchanged. +% Reads a protein FASTA file (such as one downloaded by downloadGenomeData) +% and replaces each sequence header with the value from the specified +% geneTable column. A sequence is matched to a row through the +% GenBank_protein accession, which is the first token of the original +% FASTA header. Sequences whose accession is not found in geneTable keep +% their original header. The numbers of renamed and kept sequences are +% printed. % % Parameters % ---------- @@ -13,18 +15,19 @@ % Path to the protein FASTA file (.faa) to process. % geneTable : char | table % Either a path to a tab-delimited .tsv file produced by getGeneData, -% or a MATLAB table variable. Must contain at least the columns +% or a MATLAB table variable. It must contain the columns % 'GenBank_protein' and the column named by headerCol. % headerCol : char % Name of the geneTable column whose values will replace each FASTA -% header (e.g. 'locus_tag', 'gene_name', 'GenBank_protein'). +% header (for example 'locus_tag', 'gene_name', 'GenBank_protein'). % % Name-Value Arguments % -------------------- % outputDir : char -% Directory where the processed FASTA file is saved. The output file -% name is the original base name with '_processed' appended before the -% extension (default: current working directory). +% Directory where the processed FASTA file is saved. It is created if +% missing. The output file name is the original base name with +% '_processed' appended before the extension (default: current working +% directory). % % Returns % ------- @@ -46,10 +49,11 @@ % % Notes % ----- -% GenBank_protein must contain accessions matching the first token of each -% FASTA header (after '>'). For prokaryotes this is the protein_id -% (e.g. WP_012345678.1); for eukaryotes it is the GenBank accession stored -% in the GFF3 annotation by getGeneData. +% GenBank_protein must contain accessions equal to the first +% whitespace-delimited token of each FASTA header (after '>'), for example +% WP_012345678.1. getGeneData takes the accession from the protein_id +% attribute of the CDS features of the GFF3 annotation, or from a GenBank +% entry of the Dbxref attribute when protein_id is empty. if nargin < 1 || isempty(faaFile) error('processProteinFastaFile:missingInput', ... diff --git a/curation/renameModelGenes.m b/src/curation/renameModelGenes.m similarity index 80% rename from curation/renameModelGenes.m rename to src/curation/renameModelGenes.m index 6a7c8660..12425541 100644 --- a/curation/renameModelGenes.m +++ b/src/curation/renameModelGenes.m @@ -1,34 +1,35 @@ function model = renameModelGenes(model, geneTable, fromCol, toCol) % renameModelGenes Replace gene identifiers in a RAVEN model. % -% Updates model.genes and model.grRules by substituting the identifiers -% currently used in the model (fromCol) with new identifiers (toCol) from -% a mapping table produced by getGeneData or supplied manually. After -% renaming, model.rxnGeneMat is rebuilt automatically. +% Replaces the identifiers in model.genes and model.grRules that are listed +% in column fromCol of a mapping table with the identifiers in column +% toCol. The table is produced by getGeneData or supplied manually. After +% renaming, model.rxnGeneMat is rebuilt. % % Parameters % ---------- % model : struct -% RAVEN model struct. Must contain model.genes and model.grRules; -% model.rxnGeneMat is rebuilt automatically. +% RAVEN model struct. It must contain model.genes and model.grRules. % geneTable : table | char -% Gene-mapping table. Can be supplied as: -% (a) A MATLAB table variable already loaded in the workspace, or -% (b) A path to a tab-delimited .tsv file — loaded automatically. +% Gene-mapping table, either +% (a) a MATLAB table variable, or +% (b) the path to a tab-delimited .tsv file, which is read with +% readtable. % fromCol : char -% Column in geneTable whose values match the identifiers currently in -% model.genes (e.g. 'locus_tag'). +% Name of the column in geneTable whose values match the identifiers +% in model.genes (for example 'locus_tag'). % toCol : char -% Column in geneTable whose values will replace the current -% identifiers (e.g. 'gene_name'). +% Name of the column in geneTable with the new identifiers (for +% example 'gene_name'). % % Returns % ------- % model : struct -% Updated model struct with: -% model.genes — renamed gene list. -% model.grRules — gene-reaction rules using new identifiers. -% model.rxnGeneMat — rebuilt sparse matrix matching the new gene list. +% Model struct with these fields changed: +% +% - model.genes : gene list with the new identifiers. +% - model.grRules : gene-reaction rules with the new identifiers. +% - model.rxnGeneMat : sparse matrix rebuilt for the new gene list. % % Examples % -------- @@ -42,11 +43,17 @@ % Notes % ----- % * Genes with no entry in fromCol, or whose toCol value is empty, are left -% unchanged; a warning lists them for investigation. -% * Word-boundary matching prevents partial substitution (e.g. 'gene1' -% is not replaced inside 'gene10'). -% * grRules are standardized via standardizeGrRules before and after -% renaming to ensure consistent formatting. +% unchanged, and a warning lists them. +% * If a fromCol value occurs in several rows, the first row with a +% non-empty toCol value is used. Values are trimmed of surrounding +% whitespace. +% * An identifier is replaced only where it is delimited by whitespace, +% parentheses or the start or end of the rule, so 'gene1' is not +% replaced inside 'gene10'. +% * Unnecessary outer parentheses are removed from the grRules and the +% grRules are standardised with standardizeGrRules before and after +% renaming. A warning is issued when standardizeGrRules reports rules +% with potential issues. if nargin < 1 || isempty(model) error('renameModelGenes:missingInput', ... @@ -73,14 +80,15 @@ % Load table if a file path was supplied if ischar(geneTable) || isstring(geneTable) - if ~isfile(geneTable) + tableFile = char(geneTable); + if ~isfile(tableFile) error('renameModelGenes:fileNotFound', ... - 'Table file not found: %s', geneTable); + 'Table file not found: %s', tableFile); end - geneTable = readtable(geneTable, 'Delimiter', '\t', 'FileType', 'text', ... + geneTable = readtable(tableFile, 'Delimiter', '\t', 'FileType', 'text', ... 'TextType', 'char'); fprintf('Loaded gene table from file: %s (%d rows)\n', ... - geneTable, height(geneTable)); + tableFile, height(geneTable)); end % Validate column names diff --git a/deprecated/canConsume.m b/src/deprecated/canConsume.m similarity index 97% rename from deprecated/canConsume.m rename to src/deprecated/canConsume.m index 4ceb625c..e478f2b0 100644 --- a/deprecated/canConsume.m +++ b/src/deprecated/canConsume.m @@ -4,7 +4,7 @@ % Deprecated wrapper kept so existing scripts keep working; it will be % removed in the next major release. % -% See Also +% See also % -------- % canExchange diff --git a/deprecated/canProduce.m b/src/deprecated/canProduce.m similarity index 97% rename from deprecated/canProduce.m rename to src/deprecated/canProduce.m index f3df9ef7..e87c469a 100644 --- a/deprecated/canProduce.m +++ b/src/deprecated/canProduce.m @@ -4,7 +4,7 @@ % Deprecated wrapper kept so existing scripts keep working; it will be % removed in the next major release. % -% See Also +% See also % -------- % canExchange diff --git a/deprecated/consumeSomething.m b/src/deprecated/consumeSomething.m similarity index 66% rename from deprecated/consumeSomething.m rename to src/deprecated/consumeSomething.m index 536f8a70..71f5736a 100644 --- a/deprecated/consumeSomething.m +++ b/src/deprecated/consumeSomething.m @@ -4,18 +4,14 @@ % Deprecated wrapper kept so existing scripts keep working; it will be % removed in the next major release. % -% Its positional argument order predates findLeakMetabolite and is -% preserved here: +% The positional argument order is: % % consumeSomething(model, ignoreMets, isNames, minNrFluxes, params, ignoreIntBounds) % -% findLeakMetabolite inserts allowExcretion (a produce-only option) in -% position 4, so the arguments are translated rather than forwarded -% verbatim. Forwarding them verbatim, as this wrapper previously did, -% silently shifted params into allowExcretion and ignoreIntBounds into -% params, so a requested ignoreIntBounds was dropped. +% findLeakMetabolite takes allowExcretion (a produce-only option) in +% position 4, so the arguments are translated before they are passed on. % -% See Also +% See also % -------- % findLeakMetabolite diff --git a/deprecated/makeSomething.m b/src/deprecated/makeSomething.m similarity index 98% rename from deprecated/makeSomething.m rename to src/deprecated/makeSomething.m index b786755e..20d65b8a 100644 --- a/deprecated/makeSomething.m +++ b/src/deprecated/makeSomething.m @@ -5,7 +5,7 @@ % removed in the next major release. Its argument order already matches % findLeakMetabolite's, so the arguments are forwarded as they are. % -% See Also +% See also % -------- % findLeakMetabolite diff --git a/software/GLPKmex/COPYING b/src/external/GLPKmex/COPYING similarity index 100% rename from software/GLPKmex/COPYING rename to src/external/GLPKmex/COPYING diff --git a/software/GLPKmex/README.md b/src/external/GLPKmex/README.md similarity index 100% rename from software/GLPKmex/README.md rename to src/external/GLPKmex/README.md diff --git a/software/GLPKmex/glpk.m b/src/external/GLPKmex/glpk.m similarity index 100% rename from software/GLPKmex/glpk.m rename to src/external/GLPKmex/glpk.m diff --git a/software/GLPKmex/glpkcc.mexa64 b/src/external/GLPKmex/glpkcc.mexa64 similarity index 100% rename from software/GLPKmex/glpkcc.mexa64 rename to src/external/GLPKmex/glpkcc.mexa64 diff --git a/software/GLPKmex/glpkcc.mexmaca64 b/src/external/GLPKmex/glpkcc.mexmaca64 similarity index 100% rename from software/GLPKmex/glpkcc.mexmaca64 rename to src/external/GLPKmex/glpkcc.mexmaca64 diff --git a/software/GLPKmex/glpkcc.mexmaci64 b/src/external/GLPKmex/glpkcc.mexmaci64 similarity index 100% rename from software/GLPKmex/glpkcc.mexmaci64 rename to src/external/GLPKmex/glpkcc.mexmaci64 diff --git a/software/GLPKmex/glpkcc.mexw64 b/src/external/GLPKmex/glpkcc.mexw64 similarity index 100% rename from software/GLPKmex/glpkcc.mexw64 rename to src/external/GLPKmex/glpkcc.mexw64 diff --git a/software/blast+/.keep b/src/external/blast+/.keep similarity index 100% rename from software/blast+/.keep rename to src/external/blast+/.keep diff --git a/software/diamond/.keep b/src/external/diamond/.keep similarity index 100% rename from software/diamond/.keep rename to src/external/diamond/.keep diff --git a/software/hmmer/.keep b/src/external/hmmer/.keep similarity index 100% rename from software/hmmer/.keep rename to src/external/hmmer/.keep diff --git a/software/libSBML/CheckAndConvert.m b/src/external/libSBML/CheckAndConvert.m similarity index 100% rename from software/libSBML/CheckAndConvert.m rename to src/external/libSBML/CheckAndConvert.m diff --git a/software/libSBML/ConvertFormulaToMathML.m b/src/external/libSBML/ConvertFormulaToMathML.m similarity index 100% rename from software/libSBML/ConvertFormulaToMathML.m rename to src/external/libSBML/ConvertFormulaToMathML.m diff --git a/software/libSBML/OutputSBML_RAVEN.m b/src/external/libSBML/OutputSBML_RAVEN.m similarity index 100% rename from software/libSBML/OutputSBML_RAVEN.m rename to src/external/libSBML/OutputSBML_RAVEN.m diff --git a/software/libSBML/OutputSBML_RAVEN.mexa64 b/src/external/libSBML/OutputSBML_RAVEN.mexa64 old mode 100644 new mode 100755 similarity index 100% rename from software/libSBML/OutputSBML_RAVEN.mexa64 rename to src/external/libSBML/OutputSBML_RAVEN.mexa64 diff --git a/software/libSBML/OutputSBML_RAVEN.mexmaca64 b/src/external/libSBML/OutputSBML_RAVEN.mexmaca64 old mode 100644 new mode 100755 similarity index 100% rename from software/libSBML/OutputSBML_RAVEN.mexmaca64 rename to src/external/libSBML/OutputSBML_RAVEN.mexmaca64 diff --git a/software/libSBML/OutputSBML_RAVEN.mexmaci64 b/src/external/libSBML/OutputSBML_RAVEN.mexmaci64 old mode 100644 new mode 100755 similarity index 100% rename from software/libSBML/OutputSBML_RAVEN.mexmaci64 rename to src/external/libSBML/OutputSBML_RAVEN.mexmaci64 diff --git a/software/libSBML/OutputSBML_RAVEN.mexw64 b/src/external/libSBML/OutputSBML_RAVEN.mexw64 similarity index 100% rename from software/libSBML/OutputSBML_RAVEN.mexw64 rename to src/external/libSBML/OutputSBML_RAVEN.mexw64 diff --git a/software/libSBML/README.md b/src/external/libSBML/README.md similarity index 100% rename from software/libSBML/README.md rename to src/external/libSBML/README.md diff --git a/software/libSBML/TranslateSBML_RAVEN.m b/src/external/libSBML/TranslateSBML_RAVEN.m similarity index 100% rename from software/libSBML/TranslateSBML_RAVEN.m rename to src/external/libSBML/TranslateSBML_RAVEN.m diff --git a/software/libSBML/TranslateSBML_RAVEN.mexa64 b/src/external/libSBML/TranslateSBML_RAVEN.mexa64 old mode 100644 new mode 100755 similarity index 100% rename from software/libSBML/TranslateSBML_RAVEN.mexa64 rename to src/external/libSBML/TranslateSBML_RAVEN.mexa64 diff --git a/software/libSBML/TranslateSBML_RAVEN.mexmaca64 b/src/external/libSBML/TranslateSBML_RAVEN.mexmaca64 old mode 100644 new mode 100755 similarity index 100% rename from software/libSBML/TranslateSBML_RAVEN.mexmaca64 rename to src/external/libSBML/TranslateSBML_RAVEN.mexmaca64 diff --git a/software/libSBML/TranslateSBML_RAVEN.mexmaci64 b/src/external/libSBML/TranslateSBML_RAVEN.mexmaci64 old mode 100644 new mode 100755 similarity index 100% rename from software/libSBML/TranslateSBML_RAVEN.mexmaci64 rename to src/external/libSBML/TranslateSBML_RAVEN.mexmaci64 diff --git a/software/libSBML/TranslateSBML_RAVEN.mexw64 b/src/external/libSBML/TranslateSBML_RAVEN.mexw64 similarity index 100% rename from software/libSBML/TranslateSBML_RAVEN.mexw64 rename to src/external/libSBML/TranslateSBML_RAVEN.mexw64 diff --git a/software/libSBML/VERSION.txt b/src/external/libSBML/VERSION.txt similarity index 100% rename from software/libSBML/VERSION.txt rename to src/external/libSBML/VERSION.txt diff --git a/software/libSBML/addLevelVersion.m b/src/external/libSBML/addLevelVersion.m similarity index 100% rename from software/libSBML/addLevelVersion.m rename to src/external/libSBML/addLevelVersion.m diff --git a/software/libSBML/applyUserValidation.m b/src/external/libSBML/applyUserValidation.m similarity index 100% rename from software/libSBML/applyUserValidation.m rename to src/external/libSBML/applyUserValidation.m diff --git a/software/libSBML/getDefaultValues.m b/src/external/libSBML/getDefaultValues.m similarity index 100% rename from software/libSBML/getDefaultValues.m rename to src/external/libSBML/getDefaultValues.m diff --git a/software/libSBML/getStructure.m b/src/external/libSBML/getStructure.m similarity index 100% rename from software/libSBML/getStructure.m rename to src/external/libSBML/getStructure.m diff --git a/software/libSBML/getStructureEnum.m b/src/external/libSBML/getStructureEnum.m similarity index 100% rename from software/libSBML/getStructureEnum.m rename to src/external/libSBML/getStructureEnum.m diff --git a/software/libSBML/getStructureFieldnames.m b/src/external/libSBML/getStructureFieldnames.m similarity index 100% rename from software/libSBML/getStructureFieldnames.m rename to src/external/libSBML/getStructureFieldnames.m diff --git a/software/libSBML/getValueType.m b/src/external/libSBML/getValueType.m similarity index 100% rename from software/libSBML/getValueType.m rename to src/external/libSBML/getValueType.m diff --git a/software/libSBML/isSBML_Model.m b/src/external/libSBML/isSBML_Model.m similarity index 100% rename from software/libSBML/isSBML_Model.m rename to src/external/libSBML/isSBML_Model.m diff --git a/software/libSBML/isoctave.m b/src/external/libSBML/isoctave.m similarity index 100% rename from software/libSBML/isoctave.m rename to src/external/libSBML/isoctave.m diff --git a/software/m2html/@template/char.m b/src/external/m2html/@template/char.m similarity index 100% rename from software/m2html/@template/char.m rename to src/external/m2html/@template/char.m diff --git a/software/m2html/@template/display.m b/src/external/m2html/@template/display.m similarity index 100% rename from software/m2html/@template/display.m rename to src/external/m2html/@template/display.m diff --git a/software/m2html/@template/get.m b/src/external/m2html/@template/get.m similarity index 100% rename from software/m2html/@template/get.m rename to src/external/m2html/@template/get.m diff --git a/software/m2html/@template/parse.m b/src/external/m2html/@template/parse.m similarity index 100% rename from software/m2html/@template/parse.m rename to src/external/m2html/@template/parse.m diff --git a/software/m2html/@template/private/finish.m b/src/external/m2html/@template/private/finish.m similarity index 100% rename from software/m2html/@template/private/finish.m rename to src/external/m2html/@template/private/finish.m diff --git a/software/m2html/@template/private/loadtpl.m b/src/external/m2html/@template/private/loadtpl.m similarity index 100% rename from software/m2html/@template/private/loadtpl.m rename to src/external/m2html/@template/private/loadtpl.m diff --git a/software/m2html/@template/private/subst.m b/src/external/m2html/@template/private/subst.m similarity index 100% rename from software/m2html/@template/private/subst.m rename to src/external/m2html/@template/private/subst.m diff --git a/software/m2html/@template/set.m b/src/external/m2html/@template/set.m similarity index 100% rename from software/m2html/@template/set.m rename to src/external/m2html/@template/set.m diff --git a/software/m2html/@template/template.m b/src/external/m2html/@template/template.m similarity index 100% rename from software/m2html/@template/template.m rename to src/external/m2html/@template/template.m diff --git a/software/m2html/Changelog b/src/external/m2html/Changelog similarity index 100% rename from software/m2html/Changelog rename to src/external/m2html/Changelog diff --git a/software/m2html/Contents.m b/src/external/m2html/Contents.m similarity index 100% rename from software/m2html/Contents.m rename to src/external/m2html/Contents.m diff --git a/software/m2html/GPL b/src/external/m2html/GPL similarity index 100% rename from software/m2html/GPL rename to src/external/m2html/GPL diff --git a/software/m2html/INSTALL b/src/external/m2html/INSTALL similarity index 100% rename from software/m2html/INSTALL rename to src/external/m2html/INSTALL diff --git a/software/m2html/LICENSE b/src/external/m2html/LICENSE similarity index 100% rename from software/m2html/LICENSE rename to src/external/m2html/LICENSE diff --git a/software/m2html/README b/src/external/m2html/README similarity index 100% rename from software/m2html/README rename to src/external/m2html/README diff --git a/software/m2html/TODO b/src/external/m2html/TODO similarity index 100% rename from software/m2html/TODO rename to src/external/m2html/TODO diff --git a/software/m2html/m2html.m b/src/external/m2html/m2html.m similarity index 100% rename from software/m2html/m2html.m rename to src/external/m2html/m2html.m diff --git a/software/m2html/mdot.m b/src/external/m2html/mdot.m similarity index 100% rename from software/m2html/mdot.m rename to src/external/m2html/mdot.m diff --git a/software/m2html/private/doxyread.m b/src/external/m2html/private/doxyread.m similarity index 100% rename from software/m2html/private/doxyread.m rename to src/external/m2html/private/doxyread.m diff --git a/software/m2html/private/doxysearch.m b/src/external/m2html/private/doxysearch.m similarity index 100% rename from software/m2html/private/doxysearch.m rename to src/external/m2html/private/doxysearch.m diff --git a/software/m2html/private/doxywrite.m b/src/external/m2html/private/doxywrite.m similarity index 100% rename from software/m2html/private/doxywrite.m rename to src/external/m2html/private/doxywrite.m diff --git a/software/m2html/private/m2htmltoolbarimages.mat b/src/external/m2html/private/m2htmltoolbarimages.mat similarity index 100% rename from software/m2html/private/m2htmltoolbarimages.mat rename to src/external/m2html/private/m2htmltoolbarimages.mat diff --git a/software/m2html/private/mexexts.m b/src/external/m2html/private/mexexts.m similarity index 100% rename from software/m2html/private/mexexts.m rename to src/external/m2html/private/mexexts.m diff --git a/software/m2html/private/mfileparse.m b/src/external/m2html/private/mfileparse.m similarity index 100% rename from software/m2html/private/mfileparse.m rename to src/external/m2html/private/mfileparse.m diff --git a/software/m2html/private/openfile.m b/src/external/m2html/private/openfile.m similarity index 100% rename from software/m2html/private/openfile.m rename to src/external/m2html/private/openfile.m diff --git a/software/m2html/private/searchindex.m b/src/external/m2html/private/searchindex.m similarity index 100% rename from software/m2html/private/searchindex.m rename to src/external/m2html/private/searchindex.m diff --git a/software/m2html/private/splitcode.m b/src/external/m2html/private/splitcode.m similarity index 100% rename from software/m2html/private/splitcode.m rename to src/external/m2html/private/splitcode.m diff --git a/software/m2html/private/strtok.m b/src/external/m2html/private/strtok.m similarity index 100% rename from software/m2html/private/strtok.m rename to src/external/m2html/private/strtok.m diff --git a/software/m2html/templates/blue/Thumbs.db b/src/external/m2html/templates/blue/Thumbs.db similarity index 100% rename from software/m2html/templates/blue/Thumbs.db rename to src/external/m2html/templates/blue/Thumbs.db diff --git a/software/m2html/templates/blue/alpha.png b/src/external/m2html/templates/blue/alpha.png similarity index 100% rename from software/m2html/templates/blue/alpha.png rename to src/external/m2html/templates/blue/alpha.png diff --git a/software/m2html/templates/blue/c++.png b/src/external/m2html/templates/blue/c++.png similarity index 100% rename from software/m2html/templates/blue/c++.png rename to src/external/m2html/templates/blue/c++.png diff --git a/software/m2html/templates/blue/c.png b/src/external/m2html/templates/blue/c.png similarity index 100% rename from software/m2html/templates/blue/c.png rename to src/external/m2html/templates/blue/c.png diff --git a/software/m2html/templates/blue/demoicon.gif b/src/external/m2html/templates/blue/demoicon.gif similarity index 100% rename from software/m2html/templates/blue/demoicon.gif rename to src/external/m2html/templates/blue/demoicon.gif diff --git a/software/m2html/templates/blue/down.png b/src/external/m2html/templates/blue/down.png similarity index 100% rename from software/m2html/templates/blue/down.png rename to src/external/m2html/templates/blue/down.png diff --git a/software/m2html/templates/blue/doxysearch.php b/src/external/m2html/templates/blue/doxysearch.php similarity index 100% rename from software/m2html/templates/blue/doxysearch.php rename to src/external/m2html/templates/blue/doxysearch.php diff --git a/software/m2html/templates/blue/fortran.png b/src/external/m2html/templates/blue/fortran.png similarity index 100% rename from software/m2html/templates/blue/fortran.png rename to src/external/m2html/templates/blue/fortran.png diff --git a/software/m2html/templates/blue/graph.tpl b/src/external/m2html/templates/blue/graph.tpl similarity index 100% rename from software/m2html/templates/blue/graph.tpl rename to src/external/m2html/templates/blue/graph.tpl diff --git a/software/m2html/templates/blue/hp.png b/src/external/m2html/templates/blue/hp.png similarity index 100% rename from software/m2html/templates/blue/hp.png rename to src/external/m2html/templates/blue/hp.png diff --git a/software/m2html/templates/blue/left.png b/src/external/m2html/templates/blue/left.png similarity index 100% rename from software/m2html/templates/blue/left.png rename to src/external/m2html/templates/blue/left.png diff --git a/software/m2html/templates/blue/linux.png b/src/external/m2html/templates/blue/linux.png similarity index 100% rename from software/m2html/templates/blue/linux.png rename to src/external/m2html/templates/blue/linux.png diff --git a/software/m2html/templates/blue/m2html.css b/src/external/m2html/templates/blue/m2html.css similarity index 100% rename from software/m2html/templates/blue/m2html.css rename to src/external/m2html/templates/blue/m2html.css diff --git a/software/m2html/templates/blue/master.tpl b/src/external/m2html/templates/blue/master.tpl similarity index 100% rename from software/m2html/templates/blue/master.tpl rename to src/external/m2html/templates/blue/master.tpl diff --git a/software/m2html/templates/blue/matlabicon.gif b/src/external/m2html/templates/blue/matlabicon.gif similarity index 100% rename from software/m2html/templates/blue/matlabicon.gif rename to src/external/m2html/templates/blue/matlabicon.gif diff --git a/software/m2html/templates/blue/mdir.tpl b/src/external/m2html/templates/blue/mdir.tpl similarity index 100% rename from software/m2html/templates/blue/mdir.tpl rename to src/external/m2html/templates/blue/mdir.tpl diff --git a/software/m2html/templates/blue/mex.png b/src/external/m2html/templates/blue/mex.png similarity index 100% rename from software/m2html/templates/blue/mex.png rename to src/external/m2html/templates/blue/mex.png diff --git a/software/m2html/templates/blue/mfile.tpl b/src/external/m2html/templates/blue/mfile.tpl similarity index 100% rename from software/m2html/templates/blue/mfile.tpl rename to src/external/m2html/templates/blue/mfile.tpl diff --git a/software/m2html/templates/blue/pcode.png b/src/external/m2html/templates/blue/pcode.png similarity index 100% rename from software/m2html/templates/blue/pcode.png rename to src/external/m2html/templates/blue/pcode.png diff --git a/software/m2html/templates/blue/right.png b/src/external/m2html/templates/blue/right.png similarity index 100% rename from software/m2html/templates/blue/right.png rename to src/external/m2html/templates/blue/right.png diff --git a/software/m2html/templates/blue/search.tpl b/src/external/m2html/templates/blue/search.tpl similarity index 100% rename from software/m2html/templates/blue/search.tpl rename to src/external/m2html/templates/blue/search.tpl diff --git a/software/m2html/templates/blue/sgi.png b/src/external/m2html/templates/blue/sgi.png similarity index 100% rename from software/m2html/templates/blue/sgi.png rename to src/external/m2html/templates/blue/sgi.png diff --git a/software/m2html/templates/blue/simulinkicon.gif b/src/external/m2html/templates/blue/simulinkicon.gif similarity index 100% rename from software/m2html/templates/blue/simulinkicon.gif rename to src/external/m2html/templates/blue/simulinkicon.gif diff --git a/software/m2html/templates/blue/solaris.png b/src/external/m2html/templates/blue/solaris.png similarity index 100% rename from software/m2html/templates/blue/solaris.png rename to src/external/m2html/templates/blue/solaris.png diff --git a/software/m2html/templates/blue/todo.tpl b/src/external/m2html/templates/blue/todo.tpl similarity index 100% rename from software/m2html/templates/blue/todo.tpl rename to src/external/m2html/templates/blue/todo.tpl diff --git a/software/m2html/templates/blue/up.png b/src/external/m2html/templates/blue/up.png similarity index 100% rename from software/m2html/templates/blue/up.png rename to src/external/m2html/templates/blue/up.png diff --git a/software/m2html/templates/blue/windows.png b/src/external/m2html/templates/blue/windows.png similarity index 100% rename from software/m2html/templates/blue/windows.png rename to src/external/m2html/templates/blue/windows.png diff --git a/software/scip/.keep b/src/external/scip/.keep similarity index 100% rename from software/scip/.keep rename to src/external/scip/.keep diff --git a/software/versions.txt b/src/external/versions.txt similarity index 100% rename from software/versions.txt rename to src/external/versions.txt diff --git a/gapfilling/canExchange.m b/src/gapfilling/canExchange.m similarity index 79% rename from gapfilling/canExchange.m rename to src/gapfilling/canExchange.m index 6dbb0687..8654413a 100644 --- a/gapfilling/canExchange.m +++ b/src/gapfilling/canExchange.m @@ -2,12 +2,14 @@ % canExchange Check which metabolites a model can produce or consume. % % Adds an exchange reaction for each of the specified metabolites and -% checks which of them can carry a flux. This is the unified replacement -% for canProduce ('produce') and canConsume ('consume'). +% checks which of them can carry an absolute flux above 1e-5. The direction +% 'produce' corresponds to the deprecated function canProduce and 'consume' +% to canConsume. % -% This is a less advanced but faster version of checkProduction, which -% additionally reports which metabolites must be connected in order to make -% the remaining ones producible. +% canExchange only tests whether each added exchange reaction can carry flux. +% checkProduction also reports which metabolites must be connected in order +% to make the remaining ones producible, which requires further problems to be +% solved. % % Parameters % ---------- diff --git a/gapfilling/checkProduction.m b/src/gapfilling/checkProduction.m similarity index 73% rename from gapfilling/checkProduction.m rename to src/gapfilling/checkProduction.m index a27607f3..0f5cec0c 100755 --- a/gapfilling/checkProduction.m +++ b/src/gapfilling/checkProduction.m @@ -1,16 +1,16 @@ function [notProduced, notProducedNames, neededForProductionMat,minToConnect,model]=checkProduction(model,varargin) % checkProduction Check which metabolites can be produced from a model. % -% Checks which metabolites that can be produced from a model using the -% specified constraints. +% Checks which metabolites can be produced from a model under the current +% constraints. % -% The function is intended to be used to identify which metabolites must be -% connected in order to have a fully connected network. It does so by first -% identifying which metabolites could have a net production in the network. -% Then it calculates which other metabolites must be able to have net -% production in order to have production of all metabolites in the network. -% So, if a network contains the equations A[external]->B, C->D, and D->E it -% will identify that production of C will connect the metabolites D and E. +% The function identifies which metabolites must be connected in order to +% have a fully connected network. It first identifies which metabolites +% can have a net production in the network. It then calculates which other +% metabolites must be able to have net production in order to have +% production of all metabolites in the network. If a network contains the +% equations A[external]->B, C->D, and D->E, production of C connects the +% metabolites D and E. % % Parameters % ---------- @@ -20,31 +20,40 @@ % Name-Value Arguments % -------------------- % checkNeededForProduction : logical -% for each of the metabolites that could not be produced, include an -% artificial production reaction and calculate which new metabolites that -% could be produced as an effect of this (default false). +% true to calculate, for each metabolite that could not be produced, +% which of the other such metabolites can be produced when an uptake +% reaction for it is added (default false). % excretionFromCompartments : cell % cell array with compartment ids from which metabolites can be excreted % (default model.comps). % printDetails : logical -% print details to the screen (default true). +% true to print the metabolites that could not be produced to the command +% window (default true). % % Returns % ------- % notProduced : double -% indices of metabolites that could not be produced. +% indices of metabolites that could not be produced, counted among the +% metabolites in the compartments of excretionFromCompartments, in model +% order. % notProducedNames : cell % cell array with names and compartments for metabolites that could not % be produced. % neededForProductionMat : logical % matrix where n x m is true if metabolite n allows for production of -% metabolite m. +% metabolite m. The rows and columns follow the order of notProduced. +% It is empty if checkNeededForProduction is false. % minToConnect : cell -% the minimal number of metabolites that need to be connected in order to -% be able to produce all other metabolites, and which metabolites each of -% them connects. +% metabolites that need to be connected in order to be able to produce +% all other metabolites, each with the number of metabolites it +% connects. The metabolites are selected iteratively, each time the one +% that connects the most remaining metabolites, so the number of +% metabolites need not be the global minimum. It is empty if +% checkNeededForProduction is false. % model : struct -% updated model structure with excretion reactions added. +% model structure with an excretion reaction added for each metabolite +% in the compartments of excretionFromCompartments. All added reactions +% are excretion reactions, also when checkNeededForProduction is true. % % Examples % -------- @@ -92,6 +101,12 @@ %be produced now neededForProductionMat(i,:)=haveFlux(model,10^-5,addedRxns(notProduced)); end + if ~isempty(notProduced) + %Reset the last iteration, so that the returned model has excretion + %reactions only + lastCol=numel(model.rxns)-numel(addedRxns)+notProduced(end); + model.S(:,lastCol)=model.S(:,lastCol)*-1; + end %Calculate the smallest number of metabolites that must be connected to %make everything connected and return their names diff --git a/gapfilling/fillGaps.m b/src/gapfilling/fillGaps.m similarity index 67% rename from gapfilling/fillGaps.m rename to src/gapfilling/fillGaps.m index 6e7ebd4a..b0000105 100755 --- a/gapfilling/fillGaps.m +++ b/src/gapfilling/fillGaps.m @@ -1,89 +1,100 @@ function [newConnected, cannotConnect, addedRxns, newModel, exitFlag]=fillGaps(model,models,varargin) % fillGaps Use template model(s) to fill gaps in a model. % -% This method works by merging the model with the reference model(s) and -% checking which reactions can carry flux. All reactions that cannot carry -% flux are removed (cannotConnect). If useModelConstraints is false it -% then solves the MILP problem of minimizing the number of active -% reactions from the reference models that are required to have flux in -% all the reactions in model. This requires that the input model has -% exchange reactions present for the nutrients that are needed for its -% metabolism. If useModelConstraints is true then the problem is to -% include as few reactions as possible from the reference models in order -% to satisfy the model constraints. +% The model is merged with the reference model(s). If useModelConstraints +% is false, the reactions of the model that cannot carry flux in the model +% are tested for flux in the merged model. The non-reversible reactions that +% carry flux in the merged model are the newConnected reactions. The other +% reactions are returned as cannotConnect: the reactions that cannot carry +% flux in the merged model and the reversible reactions that can, which are +% left out of the MILP target set. A MILP then minimises the number of +% active reactions from the reference models that are required for flux in +% the reactions that can be connected (newConnected). This requires that +% the input model has exchange reactions for the nutrients that its +% metabolism needs. If useModelConstraints is true, the flux test and the +% split into newConnected and cannotConnect are not run, and the MILP +% includes as few reactions as possible from the reference models to +% satisfy the model constraints. % -% The intended use is that the user can attempt a general gap-filling -% using useModelConstraints=false, or a more targeted gap-filling by -% setting constraints in the model structure and then using -% useModelConstraints=true. For example, to include reactions so that all -% biomass components can be synthesized, the user could define a biomass -% equation and set its lower bound to >0. Running this function with -% useModelConstraints=true would then give the smallest set of reactions -% that have to be included in order for the model to produce biomass. +% A general gap-filling uses useModelConstraints=false. A targeted +% gap-filling sets constraints in the model structure and uses +% useModelConstraints=true. For example, a model with a biomass equation +% whose lower bound is set above 0 gets the smallest set of reactions from +% the reference models that allows it to produce biomass. % % Parameters % ---------- % model : struct % a model structure that may contain gaps to be filled. % models : cell or struct -% a cell array of reference models or a model structure. The gaps will -% be filled using reactions from these models. +% a cell array of reference models or a model structure. Reactions +% from these models are used to fill the gaps. % % Name-Value Arguments % -------------------- % allowNetProduction : logical -% true if net production of all metabolites is allowed. A reaction can -% be unable to carry flux because one of the reactants is unavailable -% or because one of the products cannot be further processed. If true, -% only the first type of unconnectivity is considered (default false). +% true to allow net production of all metabolites. A reaction can be +% unable to carry flux because one of the reactants is unavailable or +% because one of the products cannot be further processed. If true, +% only the first cause is considered (default false). % useModelConstraints : logical -% true if the constraints specified in the model structure should be -% used. If false then reactions are included from the template -% model(s) so that as many reactions as possible in model can carry -% flux (default false). +% true to use the constraints specified in the model structure. If +% false, reactions are included from the template model(s) so that as +% many reactions as possible in model can carry flux (default false). % supressWarnings : logical -% false if warnings should be displayed (default false). +% true to suppress the warning and the note about missing exchange +% reactions (default false). % rxnScores : double or cell -% array with scores for each of the reactions in the reference -% model(s). If more than one model is supplied, then rxnScores should -% be a cell array of vectors. The solver will try to maximize the sum -% of the scores for the included reactions (default is -1 for all -% reactions). +% scores for each of the reactions in the reference model(s). If more +% than one model is supplied, rxnScores is a cell array of vectors. The +% solver maximises the sum of the scores of the included reactions. +% Used by the 'reference' algorithm (default -1 for all reactions). +% params : struct +% solver parameters passed to getMinNrFluxes (algorithm 'reference') or +% to gapFillMILP (algorithm 'gapfillMILP') (default empty struct). % algorithm : char % gap-filling algorithm to use (default 'reference'): % % - 'reference' : MILP-based connectivity maximisation % - 'fastLP' : L1-norm LP relaxation via FASTCORE (gapFillFastLP) % - 'swiftLP' : SWIFTCORE single-LP variant (gapFillFastLP) -% - 'gapfillMILP' : growth-floor MILP with directionality repair +% - 'gapfillMILP' : growth-floor MILP adding database reactions % (gapFillMILP). Uses only models{1} as the database. % - 'topological' : BFS metabolite producibility pre-screening % (gapFillTopological); no model modification. % epsilon : double % minimum flux threshold used by fastLP and swiftLP (default 1e-4). % minGrowth : double -% minimum growth rate for gapfillMILP (default [] = 10% of max FBA). +% minimum growth rate for gapfillMILP (default [], which is 10% of the +% maximum growth rate found by FBA on the merged model). % verbose : logical -% print progress messages for new algorithms (default true). +% true to print progress messages for the algorithms other than +% 'reference' (default true). % % Returns % ------- % newConnected : cell -% cell array with the reactions that could be connected. This is not -% calculated if useModelConstraints is true. +% cell array with the non-reversible reactions of model that cannot +% carry flux in model but can in the merged model. Empty if +% useModelConstraints is true or if the algorithm is not 'reference'. % cannotConnect : cell -% cell array with reactions that could not be connected. This is not -% calculated if useModelConstraints is true. +% cell array with the other reactions of model that cannot carry flux +% in model. For the algorithms 'fastLP' and 'swiftLP' it holds the +% reactions that remain blocked after merging (from gapFillFastLP), and +% for 'topological' the ids of the metabolites that cannot be reached. +% Empty if useModelConstraints is true or for 'gapfillMILP'. % addedRxns : cell -% cell array with the reactions that were added from "models". +% cell array with the reactions added from "models". % newModel : struct % the model with reactions added to fill gaps. % exitFlag : double % exit status: % % - 1 : optimal solution found -% - -1 : no feasible solution found -% - -2 : optimization time out +% - -1 : no solution found, either because the problem is infeasible or +% because the solver reached its time limit before finding one +% - -2 : a solution was found but is not proven optimal, because the +% solver stopped on its time limit % % Examples % -------- @@ -91,11 +102,19 @@ % fillGaps(model,models,allowNetProduction,useModelConstraints,... % supressWarnings,rxnScores,params); % -% See Also +% Notes +% ----- +% The 'reference' and 'gapfillMILP' algorithms solve a MILP with the +% solver set by setRavenSolver. RAVEN's bundled GLPK solves it for small +% models. The RAVEN:glpkMILP warning issued with GLPK states that GLPK works +% for small problems and that Gurobi is much faster and more reliable for +% larger ones (setRavenSolver('gurobi')). +% +% See also % -------- -% ftINITFillGaps : the ftINIT-side counterpart, used when the reference -% model already contains the model's own reactions, so no per-task -% merge is needed. +% ftINITFillGaps : the variant of fillGaps for ftINIT, used when the +% reference model already contains the reactions of the model. No merge +% per task is needed in that case. %If the user only supplied a single template model if ~iscell(models) @@ -133,7 +152,7 @@ if numel(models) < 1 error('RAVEN:badInput', 'fillGaps: gapfillMILP requires at least one reference model.'); end - [addedRxns, ~, newModel, exitFlag] = gapFillMILP(model, models{1}, ... + [addedRxns, newModel, exitFlag] = gapFillMILP(model, models{1}, ... 'minGrowth', p.minGrowth, 'verbose', p.verbose, 'params', params); newConnected = {}; cannotConnect = {}; diff --git a/gapfilling/findLeakMetabolite.m b/src/gapfilling/findLeakMetabolite.m similarity index 78% rename from gapfilling/findLeakMetabolite.m rename to src/gapfilling/findLeakMetabolite.m index 96c9f7ae..2d6db847 100644 --- a/gapfilling/findLeakMetabolite.m +++ b/src/gapfilling/findLeakMetabolite.m @@ -2,10 +2,10 @@ % findLeakMetabolite Find a metabolite that can be freely made or consumed. % % Tests whether the model can produce (excrete) or consume (take up) any -% metabolite without balanced stoichiometric constraints — a sign that the -% model contains a stoichiometric leak, futile cycle, or unconstrained -% exchange. This is the unified replacement for makeSomething ('produce') -% and consumeSomething ('consume'). +% metabolite without balanced stoichiometric constraints. A positive result +% indicates that the model contains a stoichiometric leak, a futile cycle or +% an unconstrained exchange. The direction 'produce' corresponds to the +% deprecated function makeSomething and 'consume' to consumeSomething. % % Parameters % ---------- @@ -22,33 +22,33 @@ % number of elements as metabolites in the model, or a vector of indexes % for metabolites to exclude from this analysis (default []). % isNames : logical -% true if the supplied mets represent metabolite names (as opposed to -% IDs). This is a way to delete metabolites in several compartments at -% once without knowing the exact IDs. This only works if ignoreMets is a +% true if the supplied mets are metabolite names and false if they are +% IDs. Names exclude the metabolites of all compartments that carry the +% name, so the exact IDs are not needed. Only works if ignoreMets is a % cell array (default false). % minNrFluxes : logical -% solves the MILP problem of minimizing the number of fluxes instead of -% the sum. Slower, but can be used if the sum gives too many fluxes -% (default false). +% true to solve the MILP problem of minimising the number of fluxes. If +% false, the sum of fluxes is minimised. The MILP is slower and applies +% when the sum gives too many fluxes (default false). % allowExcretion : logical -% allow for excretion of all metabolites. Only used when direction is +% true to allow excretion of all metabolites. Only used when direction is % 'produce' (default true). % params : struct -% *obsolete option*. +% solver parameters, passed to solveLP when minNrFluxes is true (default +% a structure with relGap 0.8). % ignoreIntBounds : logical -% true if internal bounds (including reversibility) should be ignored. -% Exchange reactions are not affected. This can be used to find -% unbalanced solutions which are not possible using the default -% constraints (default false). +% true to ignore internal bounds (including reversibility). Exchange +% reactions are not affected. Finds unbalanced solutions that the +% default constraints do not allow (default false). % % Returns % ------- % solution : double % flux vector for the solution. % metabolite : double -% the index of the metabolite(s) which was produced or consumed. If -% possible only one metabolite is reported, but there are situations -% where metabolites can only be exchanged in pairs (or more). +% the index of the metabolite(s) produced or consumed. A single +% metabolite is reported when one suffices; some metabolites can only be +% exchanged in pairs or larger groups. % % Examples % -------- @@ -57,12 +57,12 @@ % % Notes % ----- -% Works by forcing at least 1 unit of "any metabolite" to be produced or -% consumed, then minimising for the sum of fluxes. If more than one -% metabolite is produced/consumed, it tries to find a single metabolite -% that satisfies the constraint on its own. +% The function forces a flux of at least 1 through a pooled exchange of all +% metabolites, then minimises the sum of fluxes. If more than one metabolite +% is produced or consumed, it searches for a single metabolite that +% satisfies the constraint on its own. % -% See Also +% See also % -------- % canExchange diff --git a/gapfilling/fitTasks.m b/src/gapfilling/fitTasks.m similarity index 92% rename from gapfilling/fitTasks.m rename to src/gapfilling/fitTasks.m index 78868d16..6ea251a3 100755 --- a/gapfilling/fitTasks.m +++ b/src/gapfilling/fitTasks.m @@ -3,9 +3,9 @@ % % Fills gaps in a model by including reactions from a reference model, so % that the resulting model can perform all the tasks in a task list. The -% gap-filling is done in a task-by-task manner, rather than solving for -% all tasks at once. This means that the order of the tasks could -% influence the result. +% tasks are gap-filled one at a time in list order, and the reactions added +% for one task are kept for the following tasks. The order of the tasks can +% therefore influence the result. % % Parameters % ---------- @@ -15,7 +15,7 @@ % reference model from which to include reactions. % inputFile : char, optional % a task list in Excel format. See the function parseTaskList for -% details (optional if taskStructure is supplied). +% details. Optional if taskStructure is supplied. % % Name-Value Arguments % -------------------- @@ -23,17 +23,16 @@ % true if the results of the test should be displayed (default true). % rxnScores : double % scores for each of the reactions in the reference model. Only -% negative scores are allowed. The solver will try to maximize the sum -% of the scores for the included reactions (default is -1 for all -% reactions). +% negative scores are allowed. The solver maximises the sum of the +% scores of the included reactions (default is -1 for all reactions). % taskStructure : struct -% structure with the tasks, as from parseTaskList. If supplied then +% structure with the tasks, as returned by parseTaskList. If supplied, % inputFile is ignored. % params : struct -% solver parameters, forwarded to the gap-filling back-end and from there -% to optimizeProb for each per-task MILP (default []). +% solver parameters, passed to the gap-filling function and from there to +% optimizeProb for each per-task MILP (default []). % gapFillMode : char -% which gap-filling back-end to use (default 'merge'): +% which gap-filling function to use (default 'merge'): % % - 'merge' : refModel holds only the candidate reactions, and fillGaps % merges it with the model for every task. @@ -41,12 +40,13 @@ % so no merge is needed per task. The task constraints are written % into refModel as well, and ftINITFillGaps does the gap-filling. % verbose : logical -% if true, the MILP progression will be shown. Only read when -% gapFillMode is 'preMerged' (default false). +% true to show the MILP progression. Only read when gapFillMode is +% 'preMerged' (default false). % resolveTies : logical -% if true, pin the gap-fill MILP's degenerate optimum to a canonical answer -% instead of relying on the solver Seed alone. Only read when gapFillMode is -% 'preMerged' -- see ftINITFillGaps' resolveTies (default false). +% true to fix the degenerate optimum of the gap-fill MILP to a canonical +% answer, which otherwise depends on the solver Seed. Only read when +% gapFillMode is 'preMerged'. See resolveTies of ftINITFillGaps +% (default false). % % Returns % ------- @@ -60,13 +60,13 @@ % failedTasks : logical % Nx1, true for each task that could not be gap-filled, either because % no feasible solution exists using the reference model or because the -% attempt threw. Such tasks add no reactions, so they are otherwise -% indistinguishable from tasks that already worked. +% attempt raised an error. Such tasks add no reactions, and the same +% holds for tasks that are feasible without gap-filling. % % Examples % -------- -% [outModel, addedRxns]=fitTasks(model,refModel,inputFile,printOutput,... -% rxnScores,taskStructure,params); +% [outModel, addedRxns, failedTasks]=fitTasks(model,refModel,inputFile,... +% 'printOutput',false,'rxnScores',rxnScores,'params',params); p=parseRAVENargs(varargin, {'printOutput',true; ... 'rxnScores',[]; ... @@ -262,11 +262,14 @@ end %Add changed bounds if ~isempty(taskStructure(i).changed) - tModel=setParam(tModel,'lb',taskStructure(i).changed,taskStructure(i).LBrxn); - tModel=setParam(tModel,'ub',taskStructure(i).changed,taskStructure(i).UBrxn); + %NaN bounds mean "leave unchanged" + lbSet=~isnan(taskStructure(i).LBrxn); + ubSet=~isnan(taskStructure(i).UBrxn); + tModel=setParam(tModel,'lb',taskStructure(i).changed(lbSet),taskStructure(i).LBrxn(lbSet)); + tModel=setParam(tModel,'ub',taskStructure(i).changed(ubSet),taskStructure(i).UBrxn(ubSet)); if preMerged - tRefModel=setParam(tRefModel,'lb',taskStructure(i).changed,taskStructure(i).LBrxn); - tRefModel=setParam(tRefModel,'ub',taskStructure(i).changed,taskStructure(i).UBrxn); + tRefModel=setParam(tRefModel,'lb',taskStructure(i).changed(lbSet),taskStructure(i).LBrxn(lbSet)); + tRefModel=setParam(tRefModel,'ub',taskStructure(i).changed(ubSet),taskStructure(i).UBrxn(ubSet)); end end diff --git a/gapfilling/ftINITFillGaps.m b/src/gapfilling/ftINITFillGaps.m similarity index 70% rename from gapfilling/ftINITFillGaps.m rename to src/gapfilling/ftINITFillGaps.m index 11c5ccdf..0fe836c6 100644 --- a/gapfilling/ftINITFillGaps.m +++ b/src/gapfilling/ftINITFillGaps.m @@ -1,48 +1,52 @@ function [addedRxns, newModel, exitFlag]=ftINITFillGaps(tModel, origModel, tRefModel,allowNetProduction,supressWarnings,rxnScores,params,verbose,resolveTies) % ftINITFillGaps -% Variant of fillGaps specially adapted to speed up generation of ftINIT models. +% Variant of fillGaps adapted to speed up the generation of ftINIT models. % % tModel model that contains the task-specific rxns. % origModel model without task-specific rxns. -% tRefModel reference tModel - the full tModel, containing all rxns +% tRefModel reference tModel: the full tModel, containing all rxns % used for gap-filling of tModel + the task-specific rxns % allowNetProduction true if net production of all metabolites is % allowed. A reaction can be unable to carry flux because one of % the reactants is unavailable or because one of the -% products can't be further processed. If this -% parameter is true, only the first type of -% unconnectivity is considered (optional, default false) +% products cannot be processed further. If this +% parameter is true, only the first cause of blocked +% flux is considered (optional, default false) % supressWarnings false if warnings should be displayed (optional, default % false) % rxnScores scores for each of the reactions in the -% reference tModel. -% The solver will try to maximize the sum of the -% scores for the included reactions -% params *obsolete option* -% verbose if true, the MILP progression will be shown. -% resolveTies if true, pin the gap-fill MILP's degenerate optimum to a -% canonical answer instead of relying on the solver Seed -% alone -- see getMinNrFluxes' resolveTies (optional, -% default false) +% reference tModel. The solver maximises the sum of +% the scores of the included reactions (optional, +% default -1 for all reactions) +% params solver parameters, passed to getMinNrFluxes (optional) +% verbose true to show the MILP progression. +% resolveTies true to fix the degenerate optimum of the gap-fill MILP +% to a canonical answer, which otherwise depends on the +% solver Seed. See resolveTies of getMinNrFluxes +% (optional, default false) % % addedRxns the rxns added % newModel the tModel with reactions added to fill gaps % exitFlag 1: optimal solution found -% -1: no feasible solution found +% -1: no solution found, either because the problem is +% infeasible or because the solver reached its time +% limit before finding one % -2: a solution was found but is not proven optimal, % because the solver stopped before reaching % optimality % -% This method works by merging the tModel to the reference model and -% checking which reactions can carry flux. All reactions that can't -% carry flux are removed. It then solves the MILP problem of including -% as few additional reactions as possible from the reference model so -% that the constraints already set on tModel (e.g. a required biomass -% flux, or exchange bounds) can be satisfied. +% This method works on the reference model, which already contains the +% tModel reactions. Reactions constrained to zero flux are removed +% (simplifyModel(tRefModel,false,false,true), deleteZeroInterval). It then +% solves the MILP problem of including as few additional reactions as +% possible from the reference model so that the constraints already set +% on tModel (for example a required biomass flux, or exchange bounds) can +% be satisfied. % -% See also: fillGaps, the tINIT-side counterpart, which merges a -% reference model with the model being gap-filled on every call instead -% of expecting them pre-merged. +% fillGaps merges the reference model with the model on every call, so it +% does not need a pre-merged reference model. +% +% See also: fillGaps % % Usage: [addedRxns, newModel, exitFlag]=... % ftINITFillGaps(tModel,origModel,tRefModel,allowNetProduction,... diff --git a/gapfilling/gapFillFastCore.m b/src/gapfilling/gapFillFastCore.m similarity index 92% rename from gapfilling/gapFillFastCore.m rename to src/gapfilling/gapFillFastCore.m index 9e6c9547..a791cb8e 100644 --- a/gapfilling/gapFillFastCore.m +++ b/src/gapfilling/gapFillFastCore.m @@ -2,8 +2,9 @@ % gapFillFastCore L1-norm LP subroutine for fastGapFill. % % Finds the minimum-L1-flux extension of the model that activates all -% reactions in the core set. Any reaction with non-zero flux in the -% solution is "consistent" with the core. Used internally by gapFillFastLP. +% reactions in the core set. Any reaction with a flux of at least +% epsilon / 2 in the solution is "consistent" with the core. gapFillFastLP +% calls this function. % % Based on FASTCORE (Vlassis et al. 2014, PLoS Comput Biol 10:e1003424). % @@ -21,9 +22,10 @@ % Returns % ------- % activeRxns : logical -% logical vector (length numel(model.rxns)) where true indicates the -% reaction carries flux in the L1-minimal solution. Empty if the LP is -% infeasible (core reactions cannot all carry flux simultaneously). +% logical vector (length numel(model.rxns)) where true indicates that the +% reaction carries flux in the L1-minimal solution. All elements are +% false if the LP is infeasible (the core reactions cannot all carry flux +% simultaneously). % % See also % -------- diff --git a/gapfilling/gapFillFastLP.m b/src/gapfilling/gapFillFastLP.m similarity index 85% rename from gapfilling/gapFillFastLP.m rename to src/gapfilling/gapFillFastLP.m index 4b56927b..b86ec965 100644 --- a/gapfilling/gapFillFastLP.m +++ b/src/gapfilling/gapFillFastLP.m @@ -1,5 +1,5 @@ function [addedRxns, newModel, cannotConnect] = gapFillFastLP(model, universalModel, varargin) -% gapFillFastLP LP-based gap-filling (fastGapFill / swiftGapFill). +% gapFillFastLP LP-based gap-filling (fastGapFill or swiftGapFill). % % Merges the draft model with a universal reaction database, then for each % blocked draft reaction calls an LP subroutine (FASTCORE or SWIFTCORE) to @@ -15,19 +15,23 @@ % model : struct % draft RAVEN model to be gap-filled. % universalModel : struct -% universal reaction database (RAVEN model). The caller is responsible -% for building this, e.g. via getKEGGModelForOrganism with broad scope. +% universal reaction database (RAVEN model), for example built with +% getKEGGModelForOrganism with broad scope. % -% Optional name-value parameters -% -------------------------------- -% 'epsilon' (default 1e-4) -% minimum flux threshold for a reaction to be considered active. -% 'variant' (default 'fast') -% 'fast' — use FASTCORE L1-norm LP (gapFillFastCore) -% 'swift' — use SWIFTCORE single-LP (gapFillSwiftCore); faster but -% stochastic (results may vary between runs/solvers). -% 'verbose' (default true) -% print progress messages. +% Name-Value Arguments +% -------------------- +% epsilon : double +% minimum flux threshold for a reaction to be considered active +% (default 1e-4). +% variant : char +% the LP subroutine (default 'fast'): +% +% - 'fast' : use the FASTCORE L1-norm LP (gapFillFastCore). +% - 'swift' : use the SWIFTCORE single LP (gapFillSwiftCore), which +% maximises the sum of the non-core fluxes. The LP is degenerate, so +% results can vary between runs and solvers. +% verbose : logical +% true to print progress messages (default true). % % Returns % ------- @@ -37,8 +41,8 @@ % newModel : struct % copy of model with addedRxns incorporated. % cannotConnect : cell -% blocked draft reaction IDs that remain blocked even after augmenting -% with the full universalModel (cannot be rescued by the database). +% IDs of blocked draft reactions that remain blocked after merging with +% the full universalModel (the database cannot rescue them). % % See also % -------- diff --git a/gapfilling/gapFillMILP.m b/src/gapfilling/gapFillMILP.m similarity index 54% rename from gapfilling/gapFillMILP.m rename to src/gapfilling/gapFillMILP.m index 0127fe1a..f391e4dd 100644 --- a/gapfilling/gapFillMILP.m +++ b/src/gapfilling/gapFillMILP.m @@ -1,18 +1,19 @@ -function [addedRxns, reversedRxns, newModel, exitFlag] = gapFillMILP(model, universalModel, varargin) +function [addedRxns, newModel, exitFlag] = gapFillMILP(model, universalModel, varargin) % gapFillMILP Objective-based gap-filling via a global growth-floor MILP. % -% Finds the minimum-cost set of modifications to make the model's objective -% (biomass) >= minGrowth. Supports two repair mechanisms from Kumar et al. -% 2007 (Bioinformatics 23:1626-1635): -% 1. Directionality reversal of existing draft reactions (binary y_rev) -% 2. Addition of reactions from a universal database (binary y_db) +% Finds the smallest set of reactions from a universal database that makes +% the model's objective (biomass) >= minGrowth, following the database +% repair of Kumar et al. 2007 (BMC Bioinformatics 8:212, +% doi:10.1186/1471-2105-8-212). Reactions already in the draft model keep +% their bounds, including a wrong reaction direction. % -% The universal model should contain all candidate reactions, including -% exchange and transport reactions if those repair classes are desired. +% The universal model contains all candidate reactions. Exchange and +% transport reactions are candidates only if the universal model includes +% them. % -% This is a SINGLE-level MILP (not bilevel). It is computationally more -% expensive than gapFillFastLP but supports the directionality-reversal -% repair mechanism and directly optimises for objective feasibility. +% The function solves a single-level MILP that minimises the number of added +% reactions subject to the objective floor. Compared with gapFillFastLP, the +% MILP takes more computation time. % % Parameters % ---------- @@ -21,36 +22,39 @@ % universalModel : struct % universal reaction database (RAVEN model). % -% Optional name-value parameters -% -------------------------------- -% 'minGrowth' (default []) -% minimum objective value to achieve. If empty, runs FBA on the merged -% model and uses 10% of the maximum as the floor. -% 'weights' (default [1 2 3 4]) -% cost weights [w_rev w_db w_exch w_trans]. w_rev is the cost per -% reversed draft reaction; w_db is the cost per added universal -% reaction. w_exch and w_trans are reserved for future use when -% exchange and transport reactions are tracked separately. -% 'bigM' (default 1000) -% Big-M constant for coupling constraints. Must be larger than any -% expected flux magnitude. -% 'params' (default []) +% Name-Value Arguments +% -------------------- +% minGrowth : double +% minimum objective value to achieve. If empty, FBA on the merged model +% gives the maximum objective value and 10% of it is the floor +% (default []). +% bigM : double +% Big-M constant for the coupling constraints of universal reactions +% with an infinite bound. A finite bound is used as it is (default 1000). +% params : struct % solver parameter struct passed to optimizeProb. Fields such as -% params.TimeLimit and params.intTol can be used to tune the MILP. -% 'verbose' (default true) -% print progress messages. +% params.TimeLimit and params.intTol tune the MILP. If empty, intTol is +% 1e-9 and TimeLimit is 600 (default []). +% verbose : logical +% true to print progress messages (default true). % % Returns % ------- % addedRxns : cell % reaction IDs from universalModel that are selected by the MILP. -% reversedRxns : cell -% draft reaction IDs whose directionality is reversed by the MILP. % newModel : struct -% model with addedRxns incorporated and reversedRxns bounds updated -% (lb set to -bigM for reversed reactions). +% model with addedRxns incorporated. % exitFlag : double -% 1 = optimal, -1 = infeasible, -2 = timeout or other failure. +% 1 = the MILP returned a solution, -1 = no gap-filling result: the FBA +% or MILP is infeasible, the objective of the merged model is not +% positive, or the solver stops without a solution. +% +% Notes +% ----- +% The MILP is solved with the solver set by setRavenSolver. RAVEN's bundled +% GLPK solves it for small models. The RAVEN:glpkMILP warning issued with +% GLPK states that GLPK works for small problems and that Gurobi is much +% faster and more reliable for larger ones (setRavenSolver('gurobi')). % % See also % -------- @@ -59,25 +63,19 @@ % ---- Parse optional arguments ---- p = inputParser(); addParameter(p, 'minGrowth', [], @(x) isempty(x) || isnumeric(x)); -addParameter(p, 'weights', [1 2 3 4], @isnumeric); addParameter(p, 'bigM', 1000, @isnumeric); addParameter(p, 'params', [], @(x) isempty(x) || isstruct(x)); addParameter(p, 'verbose', true, @islogical); parse(p, varargin{:}); minGrowth = p.Results.minGrowth; -weights = p.Results.weights; M = p.Results.bigM; params = p.Results.params; verbose = p.Results.verbose; -w_rev = weights(1); -w_db = weights(2); - % ---- Initialise outputs ---- -addedRxns = {}; -reversedRxns = {}; -newModel = model; -exitFlag = -1; +addedRxns = {}; +newModel = model; +exitFlag = -1; % Check that the model has an objective if all(model.c == 0) @@ -99,16 +97,10 @@ univIdx = find(isUniv); % positions in merged model nUniv = numel(univIdx); -% Reversal candidates: draft reactions that are currently irreversible -% (lb >= 0). Reversing their directionality means allowing negative flux. -revCandMask = isDraft & mergedModel.lb >= 0; -revCandIdx = find(revCandMask); % positions in merged model -nRev = numel(revCandIdx); - if verbose fprintf('gapFillMILP: merged model has %d mets, %d rxns (%d draft, %d universal).\n', ... nMergedMets, nMergedRxns, sum(isDraft), nUniv); - fprintf('gapFillMILP: %d reversal candidates, %d database candidates.\n', nRev, nUniv); + fprintf('gapFillMILP: %d database candidates.\n', nUniv); end % ---- Determine minGrowth ---- @@ -147,45 +139,42 @@ % ---- Build MILP ---- % Variable ordering: % cols 1 : nMergedRxns → v (all fluxes, continuous) -% cols nMergedRxns+1 : +nRev → y_rev (binary, directionality reversal) -% cols nMergedRxns+nRev+1 : end → y_db (binary, universal reactions) -nVar = nMergedRxns + nRev + nUniv; +% cols nMergedRxns+1 : end → y_db (binary, universal reactions) +nVar = nMergedRxns + nUniv; % --- Variable bounds --- lb = mergedModel.lb; ub = mergedModel.ub; -% Relax reversal candidates to allow negative flux (Big-M on lower bound) -lb(revCandIdx) = -M; - % Universal reactions: allow them to be forced to 0 (extend lb toward 0) lb(univIdx) = min(mergedModel.lb(univIdx), 0); % Binary variable bounds -lb = [lb; zeros(nRev + nUniv, 1)]; -ub = [ub; ones(nRev + nUniv, 1)]; +lb = [lb; zeros(nUniv, 1)]; +ub = [ub; ones(nUniv, 1)]; -% --- Objective --- +% --- Objective: number of added universal reactions --- c = zeros(nVar, 1); -c(nMergedRxns + 1 : nMergedRxns + nRev) = w_rev; % y_rev cost -c(nMergedRxns + nRev + 1 : end) = w_db; % y_db cost +c(nMergedRxns + 1 : end) = 1; % --- Constraint blocks --- % (1) Steady state: S * v = 0 [nMergedMets rows, 'E'] -A_SS = [mergedModel.S, sparse(nMergedMets, nRev + nUniv)]; +A_SS = [mergedModel.S, sparse(nMergedMets, nUniv)]; % (2) Growth floor: c' * v >= minGrowth [1 row, 'G'] -A_growth = [sparse(mergedModel.c'), sparse(1, nRev + nUniv)]; +A_growth = [sparse(mergedModel.c'), sparse(1, nUniv)]; % (3) Universal upper coupling: v_k - ub_k * y_db_k <= 0 [nUniv rows, 'L'] % (forces v_k <= 0 when y_db_k = 0) -% Coupling coefficients are capped at the Big-M constant: an unbounded -% universal reaction (ub = Inf, RAVEN's own default when no upper-bound -% annotation is set) would otherwise put an Inf coefficient into the -% constraint matrix, and Inf*0 (y_db_k = 0) is NaN, not 0. -yDbCols = nMergedRxns + nRev + (1:nUniv); -uniUb = min(mergedModel.ub(univIdx), M); -uniLb = max(mergedModel.lb(univIdx), -M); +% Coupling coefficients are the reaction bounds. An infinite bound is replaced +% by the Big-M constant: an unbounded universal reaction (ub = Inf, RAVEN's own +% default when no upper-bound annotation is set) would otherwise put an Inf +% coefficient into the constraint matrix, and Inf*0 (y_db_k = 0) is NaN, not 0. +yDbCols = nMergedRxns + (1:nUniv); +uniUb = mergedModel.ub(univIdx); +uniUb(isinf(uniUb)) = M; +uniLb = mergedModel.lb(univIdx); +uniLb(isinf(uniLb)) = -M; A_uniUp = sparse(... [1:nUniv, 1:nUniv], ... [univIdx(:)', yDbCols], ... @@ -200,29 +189,19 @@ [ones(1, nUniv), -uniLb'], ... nUniv, nVar); -% (5) Reversal coupling: v_j + M * y_rev_j >= 0 [nRev rows, 'G'] -% (forces v_j >= 0 when y_rev_j = 0, i.e., preserves original irreversibility) -yRevCols = nMergedRxns + (1:nRev); -A_rev = sparse(... - [1:nRev, 1:nRev], ... - [revCandIdx(:)', yRevCols], ... - [ones(1, nRev), M * ones(1, nRev)], ... - nRev, nVar); - % --- Assemble --- -prob.A = [A_SS; A_growth; A_uniUp; A_uniLo; A_rev]; +prob.A = [A_SS; A_growth; A_uniUp; A_uniLo]; prob.a = prob.A; -prob.b = [zeros(nMergedMets, 1); minGrowth; ... - zeros(nUniv, 1); zeros(nUniv, 1); zeros(nRev, 1)]; +prob.b = [zeros(nMergedMets, 1); minGrowth; zeros(2 * nUniv, 1)]; prob.csense = [repmat('E', 1, nMergedMets), 'G', ... repmat('L', 1, nUniv), ... - repmat('G', 1, nUniv + nRev)]; + repmat('G', 1, nUniv)]; prob.c = c; -prob.osense = 1; % minimise weighted sum of binary variables +prob.osense = 1; % minimise the number of added reactions prob.lb = lb; prob.ub = ub; prob.vartype = repmat('C', 1, nVar); -prob.vartype(nMergedRxns + 1 : end) = 'B'; % y_rev and y_db are binary +prob.vartype(nMergedRxns + 1 : end) = 'B'; % y_db is binary if isempty(params) params.intTol = 1e-9; @@ -232,7 +211,7 @@ % ---- Solve ---- if verbose fprintf('gapFillMILP: solving MILP (%d vars, %d binary, %d constraints)...\n', ... - nVar, nRev + nUniv, size(prob.A, 1)); + nVar, nUniv, size(prob.A, 1)); end sol = optimizeProb(prob, params, verbose); @@ -246,11 +225,8 @@ % ---- Extract solution ---- binThreshold = 1e-3; -yRev = sol.full(nMergedRxns + 1 : nMergedRxns + nRev); -yDb = sol.full(nMergedRxns + nRev + 1 : end); - -revSelected = revCandIdx(yRev > binThreshold); -dbSelected = univIdx(yDb > binThreshold); +yDb = sol.full(nMergedRxns + 1 : end); +dbSelected = univIdx(yDb > binThreshold); % Map back to original IDs. dbSelected is already restricted to universal % reactions via univIdx/isDraft above (an id-membership test against @@ -258,26 +234,15 @@ % further filtering against universalModel.rxns is needed here -- doing so % would silently drop a universal reaction that mergeModels renamed % because its id collided with one already in the draft model. -reversedRxns = mergedModel.rxns(revSelected); -addedRxns = mergedModel.rxns(dbSelected); +addedRxns = mergedModel.rxns(dbSelected); if verbose - fprintf('gapFillMILP: reversed %d draft reaction(s), added %d universal reaction(s).\n', ... - numel(reversedRxns), numel(addedRxns)); + fprintf('gapFillMILP: added %d universal reaction(s).\n', numel(addedRxns)); end % ---- Build new model ---- newModel = model; -% Apply directionality reversals: allow negative flux -for k = 1:numel(reversedRxns) - rxnPos = find(getIndexes(newModel, reversedRxns(k), 'rxns', true)); - if ~isempty(rxnPos) - newModel.lb(rxnPos) = -M; - newModel.rev(rxnPos) = 1; - end -end - % Add universal reactions. Extracted from mergedModel by the dbSelected % indices directly, not by looking addedRxns' names back up in % universalModel: that lookup fails whenever mergeModels renamed one of diff --git a/gapfilling/gapFillSwiftCore.m b/src/gapfilling/gapFillSwiftCore.m similarity index 89% rename from gapfilling/gapFillSwiftCore.m rename to src/gapfilling/gapFillSwiftCore.m index 876ee8e4..2ecea6a8 100644 --- a/gapfilling/gapFillSwiftCore.m +++ b/src/gapfilling/gapFillSwiftCore.m @@ -3,14 +3,13 @@ % % Single-LP alternative to gapFillFastCore. Maximises the sum of non-core % fluxes while enforcing that every core reaction carries at least epsilon -% flux. Any reaction with non-zero flux in the solution is "consistent" -% with the core. +% flux. Any reaction with a flux of at least epsilon / 2 in the solution is +% "consistent" with the core. % -% Because the maximisation objective makes the LP degenerate (many optima -% exist that differ only in which non-core reactions carry flux), the -% returned active set can vary between LP solvers and between runs with -% slightly different numerical conditions. This is the expected behaviour -% of SWIFTCORE (Tefagh & Boyd 2020). +% The maximisation objective makes the LP degenerate: many optima differ only +% in which non-core reactions carry flux. The returned active set can +% therefore vary between LP solvers and between runs with slightly different +% numerical conditions. % % Based on SWIFTCORE (Tefagh & Boyd 2020, BMC Bioinformatics 21:23). % @@ -28,8 +27,9 @@ % Returns % ------- % activeRxns : logical -% logical vector (length numel(model.rxns)) where true indicates the -% reaction carries flux in the solution. Empty on infeasibility. +% logical vector (length numel(model.rxns)) where true indicates that the +% reaction carries flux in the solution. All elements are false if the LP +% is infeasible. % % See also % -------- diff --git a/gapfilling/gapFillTopological.m b/src/gapfilling/gapFillTopological.m similarity index 86% rename from gapfilling/gapFillTopological.m rename to src/gapfilling/gapFillTopological.m index fe6e335e..2915866e 100644 --- a/gapfilling/gapFillTopological.m +++ b/src/gapfilling/gapFillTopological.m @@ -3,12 +3,11 @@ % % Identifies which metabolites in the draft model cannot be produced from a % given set of seed metabolites (medium), using a graph-based fixed-point -% "scope" computation (no solver required). For each unreachable metabolite, +% "scope" computation that needs no solver. For each unreachable metabolite, % finds candidate reactions in the universal database that produce it % directly. % -% This is a computationally cheap pre-screening step that prunes the -% universal database before a more expensive gap-filling solve. It is +% The step prunes the universal database before a gap-filling solve. It is % inspired by the topological analysis used in Meneco (Prigent et al. 2017, % PLoS Comput Biol). % @@ -27,27 +26,31 @@ % universalModel : struct % universal reaction database (RAVEN model). % -% Optional name-value parameters -% -------------------------------- -% 'seeds' (default []) -% Metabolite IDs (cell array) available from the medium. Default: +% Name-Value Arguments +% -------------------- +% seeds : cell +% metabolite IDs available from the medium. If empty, the seeds are the % metabolites of every exchange reaction whose bounds let it supply the -% metabolite, i.e. getExchangeRxns 'uptake' plus 'reverse'. -% 'targets' (default []) -% Metabolite IDs (cell array) that should be produced. Default: -% substrates of the objective (biomass) reaction. -% 'verbose' (default true) -% print a summary of the results. +% metabolite, that is getExchangeRxns 'uptake' plus 'reverse' +% (default []). +% targets : cell +% metabolite IDs that should be produced. If empty, the targets are every +% metabolite of the objective (biomass) reaction, substrates and +% products; the first one when there are several objective reactions. +% Without targets and without an objective reaction the function raises +% RAVEN:badInput (default []). +% verbose : logical +% true to print a summary of the results (default true). % % Returns % ------- % result : struct -% .reachableMets — logical vector (length nMets) marking producible mets -% .blockedMets — cell array of metabolite IDs that cannot be reached -% .candidateRxns — n-by-1 cell array of universal rxn ID lists, one -% cell per blocked metabolite (same order as blockedMets) -% .pruningFraction — fraction of universal reactions pruned (not needed -% for any blocked metabolite) +% .reachableMets: logical vector (length nMets) marking producible mets +% .blockedMets: cell array of metabolite IDs that cannot be reached +% .candidateRxns: n-by-1 cell array of universal rxn ID lists, one cell +% per blocked metabolite (same order as blockedMets) +% .pruningFraction: fraction of universal reactions pruned (not +% candidates for any blocked metabolite) % % See also % -------- @@ -99,10 +102,10 @@ % ---- Identify target metabolites ---- if isempty(targets) - % Default: substrates of the objective (biomass) reaction + % Default: every metabolite of the objective (biomass) reaction objRxns = find(model.c ~= 0); if isempty(objRxns) - error(['gapFillTopological: no objective function defined. ' ... + error('RAVEN:badInput', ['gapFillTopological: no objective function defined. ' ... 'Provide targets manually via the ''targets'' option.']); end biomassIdx = objRxns(1); diff --git a/gapfilling/gapReport.m b/src/gapfilling/gapReport.m similarity index 60% rename from gapfilling/gapReport.m rename to src/gapfilling/gapReport.m index b1816068..c5b1d1c8 100755 --- a/gapfilling/gapReport.m +++ b/src/gapfilling/gapReport.m @@ -1,16 +1,16 @@ function [noFluxRxns, noFluxRxnsRelaxed, subGraphs, notProducedMets, minToConnect,... neededForProductionMat, canProduceWithoutInput, canConsumeWithoutOutput, ... connectedFromTemplates, addedFromTemplates]=gapReport(model, varargin) -% gapReport Perform a gap analysis and summarize the results. +% gapReport Perform a gap analysis and summarise the results. % -% Runs a series of connectivity checks on the model - which reactions are +% Runs a series of connectivity checks on the model: which reactions are % blocked, how many isolated sub-networks exist, which metabolites cannot % be produced (and the minimal set of metabolites that would need to be -% connected to fix that), and, for models with exchange reactions, which -% metabolites can be produced/consumed without any input/output. If -% templateModels is supplied, it also attempts to gap-fill using those -% models. A formatted report is printed to the command window; the same -% results are returned as outputs for further use. +% connected to fix that), and which metabolites can be produced without any +% input or consumed without any output, with every boundary reaction closed. +% If templateModels is supplied, it also attempts to gap-fill using those +% models. A formatted report is printed to the command window, and the same +% results are returned as outputs. % % Parameters % ---------- @@ -20,8 +20,8 @@ % Name-Value Arguments % -------------------- % templateModels : cell -% a cell array of template models to use for gap filling. If omitted, -% the automated gap-filling section is skipped. +% a cell array of template models used for gap filling. If omitted, the +% automated gap-filling section is skipped. % % Returns % ------- @@ -43,22 +43,35 @@ % matrix where n x m is true if metabolite n allows for production of % metabolite m. % canProduceWithoutInput : cell -% metabolites that could be produced even when there is no input to -% the model. +% metabolites that could be produced with every boundary reaction +% closed. % canConsumeWithoutOutput : cell -% metabolites that could be consumed even when there is no output from -% the model. +% metabolites that could be consumed with every boundary reaction +% closed. % connectedFromTemplates : cell % the reactions that could be connected using the template models. % addedFromTemplates : struct % the reactions that were added from the template models and which % model they were added from. % +% Notes +% ----- +% The boundary reactions are those getExchangeRxns returns: exchanges, +% sinks and demands. For a model that keeps its boundary metabolites (an +% unconstrained field), those metabolites are deleted for the connectivity +% checks and kept, balanced, for the closed model. +% +% The model must be feasible under its own bounds and with its boundary +% closed. Otherwise gapReport stops with a RAVEN:infeasibleModel error that +% names the reactions with a forced flux. Such reactions, for example ATP +% maintenance, need bounds that allow zero flux. +% % Examples % -------- % [noFluxRxns, noFluxRxnsRelaxed, subGraphs, notProducedMets, ... -% minToConnect, neededForProductionMat, connectedFromTemplates, ... -% addedFromTemplates] = gapReport(model, templateModels); +% minToConnect, neededForProductionMat, canProduceWithoutInput, ... +% canConsumeWithoutOutput, connectedFromTemplates, addedFromTemplates] = ... +% gapReport(model, 'templateModels', templateModels); p=parseRAVENargs(varargin, {'templateModels',[]}); templateModels=p.templateModels; @@ -76,15 +89,18 @@ fprintf(['Gap analysis for ' esc(model.id) ' - ' esc(model.name) '\n\n']); if isfield(model,'unconstrained') - calculateINOUT=true; closedModel=model; model=simplifyModel(model); else - canConsumeWithoutOutput={}; - canProduceWithoutInput={}; - calculateINOUT=false; + closedModel=model; + [~,boundary]=getExchangeRxns(closedModel); + closedModel.lb(boundary)=0; + closedModel.ub(boundary)=0; end +requireFeasible(model,'under its own bounds'); +requireFeasible(closedModel,'once its boundary reactions are closed'); + model2=model; model2.b=[model2.b inf(numel(model2.mets),1)]; I=haveFlux(model); @@ -114,14 +130,12 @@ fprintf(['\t' num2str(i) '. ' esc(minToConnect{i}) '\n']); end -if calculateINOUT==true - fprintf('\n***Mass balancing\n'); - produced=canExchange(closedModel,'produce'); - canProduceWithoutInput=closedModel.mets(produced); - consumed=canExchange(closedModel,'consume'); - canConsumeWithoutOutput=closedModel.mets(consumed); - fprintf([num2str(numel(canConsumeWithoutOutput)) ' metabolites could be consumed without any outputs\n' num2str(numel(canProduceWithoutInput)) ' metabolites could be produced without any inputs\n']); -end +fprintf('\n***Mass balancing\n'); +produced=canExchange(closedModel,'produce'); +canProduceWithoutInput=closedModel.mets(produced); +consumed=canExchange(closedModel,'consume'); +canConsumeWithoutOutput=closedModel.mets(consumed); +fprintf([num2str(numel(canConsumeWithoutOutput)) ' metabolites could be consumed without any outputs\n' num2str(numel(canProduceWithoutInput)) ' metabolites could be produced without any inputs\n']); if ~isempty(templateModels) fprintf('\n***Automated gap-filling\n'); @@ -133,3 +147,20 @@ fprintf([num2str(numel(connectedFromTemplates)) ' unconnected reactions can be connected by including ' num2str(numel(addedFromTemplates)) ' reactions from\n' t '\n']); end end + +function requireFeasible(model,condition) +%Errors, naming the forced-flux reactions, if the model is infeasible +model.c(:)=0; +sol=solveLP(model); +if sol.stat~=-1 + return +end +forced=model.rxns(model.lb>0 | model.ub<0); +hint=''; +if ~isempty(forced) + hint=[' Reactions with a forced flux: ' strjoin(forced,', ') '.']; +end +error('RAVEN:infeasibleModel',['The model is infeasible ' condition ... + ', so gapReport cannot analyse it.' strrep(hint,'%','%%') ... + ' Set the bounds of ATP maintenance and any other forced flux to zero first.']); +end diff --git a/installation/addRavenToUserPath.m b/src/installation/addRavenToUserPath.m similarity index 74% rename from installation/addRavenToUserPath.m rename to src/installation/addRavenToUserPath.m index 1b44769c..3ff23a47 100755 --- a/installation/addRavenToUserPath.m +++ b/src/installation/addRavenToUserPath.m @@ -1,15 +1,16 @@ function addRavenToUserPath(varargin) % This function writes a startup.m file in the userpath, adding RAVEN (and -% all subdirectories) to the path each time Matlab is started. -% This function is useful if the user has no rights to save paths to the +% all subdirectories except those with .git or doc in their path) to the +% path each time MATLAB is started. +% The function applies when the user has no rights to save paths to the % pathdef.m file in the matlabroot, for instance due to a lack of admin -% rights. As the startup.m file in the userpath automatically runs with -% each Matlab start, the paths are automatically loaded. +% rights. The startup.m file in the userpath runs at each MATLAB start and +% loads the paths. % % Name-Value Arguments % -------------------- -% overwrite logical, whether startup.m in the userpath should -% overwritten (otherwise the RAVEN paths are appended) +% overwrite logical, true if startup.m in the userpath is +% overwritten and false if the RAVEN paths are appended % (default true) p=parseRAVENargs(varargin, {'overwrite',true}); diff --git a/installation/checkFunctionUniqueness.m b/src/installation/checkFunctionUniqueness.m similarity index 88% rename from installation/checkFunctionUniqueness.m rename to src/installation/checkFunctionUniqueness.m index e7c02649..ce23ce34 100755 --- a/installation/checkFunctionUniqueness.m +++ b/src/installation/checkFunctionUniqueness.m @@ -1,16 +1,18 @@ function status = checkFunctionUniqueness(varargin) % checkFunctionUniqueness % Checks whether RAVEN toolbox functions are unique across other -% toolboxes or user-created functions accessible in Matlab pathlist. +% toolboxes and user-created functions on the MATLAB path. % % Name-Value Arguments % -------------------- -% altDirs cell array of alternative directories with functions, -% should only be specified if this function is used for other -% purposes, NOT when looking for RAVEN toolbox functions. +% altDirs char with alternative directories with functions, separated +% by the path separator of the platform (';' on Windows, ':' +% otherwise). Specified only when the function checks +% directories other than the RAVEN toolbox. % % Output: -% status true if all functions are unique, otherwise false +% status true if all functions are unique, otherwise false. Without +% an output argument the function prints Pass or Fail. % % Usage: checkFunctionUniqueness() diff --git a/installation/downloadRavenBinaries.m b/src/installation/downloadRavenBinaries.m similarity index 73% rename from installation/downloadRavenBinaries.m rename to src/installation/downloadRavenBinaries.m index 16c8ebf9..881bb725 100644 --- a/installation/downloadRavenBinaries.m +++ b/src/installation/downloadRavenBinaries.m @@ -1,25 +1,27 @@ function downloadRavenBinaries(tools) % downloadRavenBinaries % Download RAVEN's external command-line binaries on demand from the -% raven-data repository into RAVEN's software/ directory. Only the binary -% matching the current platform is fetched, and only when needed. +% toolbox-assets repository into the src/external directory of RAVEN. Only +% the binary matching the current platform is fetched, and only when it is +% missing. % % Input: % tools cell array, subset of {'blast+','diamond','hmmer'} to % download. Defaults to all of them. Tools whose target executable is % already present are skipped. % -% This requires an internet connection. For an offline / air-gapped install, -% download the "*-binaries" RAVEN release (which bundles all binaries) and -% unzip it into the RAVEN software/ directory instead. +% The download requires an internet connection. An offline installation +% uses the offline bundle (RAVEN--full.zip) attached to each RAVEN +% release, which contains the binaries of all platforms in src/external. % % The binaries are hosted as release assets in -% https://github.com/SysBioChalmers/raven-data. +% https://github.com/CompMetEng/toolbox-assets. % % Usage: downloadRavenBinaries({'blast+','diamond'}) % -% NOTE: lazily invoked by getBlast/getDiamond/getKEGGModelForOrganism and -% offered by checkRaven when a binary is missing. +% getBlast, getDiamond and getKEGGModelForOrganism call this function +% when a binary is missing. checkRaven calls it for all three tools when its +% checkBinaries input is true. if nargin < 1 || isempty(tools) tools = {'blast+','diamond','hmmer'}; @@ -29,7 +31,7 @@ function downloadRavenBinaries(tools) ravenDir = findRAVENroot(); -% raven-data platform key for the per-platform binary ZIPs. +% toolbox-assets platform key for the per-platform binary ZIPs. if ispc plat = 'windows-x86_64'; elseif ismac @@ -44,13 +46,13 @@ function downloadRavenBinaries(tools) case 'blast+' tag = 'blast-2.17.0'; asset = ['blast-2.17.0-' plat '.zip']; - destDir = fullfile(ravenDir,'software','blast+'); + destDir = fullfile(ravenDir,'src','external','blast+'); sentinel = fullfile(destDir,'blastp'); % bare name on every OS (.exe on Windows) execs = {'blastp','makeblastdb'}; case 'diamond' tag = 'diamond-2.1.17'; asset = ['diamond-2.1.17-' plat '.zip']; - destDir = fullfile(ravenDir,'software','diamond'); + destDir = fullfile(ravenDir,'src','external','diamond'); sentinel = fullfile(destDir,'diamond'); execs = {'diamond'}; case 'hmmer' @@ -61,7 +63,7 @@ function downloadRavenBinaries(tools) tag = 'hmmer-3.4.0'; asset = ['hmmer-3.4.0-' plat '.zip']; end - destDir = fullfile(ravenDir,'software','hmmer'); + destDir = fullfile(ravenDir,'src','external','hmmer'); sentinel = fullfile(destDir,'hmmsearch'); execs = {'hmmsearch'}; otherwise @@ -78,11 +80,11 @@ function downloadRavenBinaries(tools) continue; % already provisioned end - zipPath = fetchRavenDataAsset(fullfile(ravenDir,'software'),tag,asset); + zipPath = fetchToolboxAsset(fullfile(ravenDir,'src','external'),tag,asset); unzip(zipPath,destDir); delete(zipPath); - % macOS uses the .mac suffix throughout RAVEN, but the raven-data macOS ZIP + % macOS uses the .mac suffix throughout RAVEN, but the toolbox-assets macOS ZIP % ships bare names, so rename the extracted executables to match. if ismac for k = 1:numel(execs) diff --git a/src/installation/fetchToolboxAsset.m b/src/installation/fetchToolboxAsset.m new file mode 100644 index 00000000..cbd3b0bf --- /dev/null +++ b/src/installation/fetchToolboxAsset.m @@ -0,0 +1,131 @@ +function localPath=fetchToolboxAsset(destDir,releaseTag,assetName) +% fetchToolboxAsset Download (if needed) a toolbox-assets release asset. +% +% Downloads assetName from the given release tag of +% https://github.com/CompMetEng/toolbox-assets into destDir +% and returns its local path. An already-downloaded copy is reused without a +% new download or checksum test. The function applies to every toolbox-assets +% release family (KEGG artefacts, HMM libraries, binaries), and the caller +% supplies the release tag and the asset name. +% +% A download is checked against the SHA-256 checksum that the +% toolbox-assets manifest (release manifest-v3) lists for it. A file whose +% checksum differs is deleted and an error is raised. An asset that the +% manifest does not list, or a manifest that cannot be read, gives a +% warning and the file is kept unchecked. +% +% Parameters +% ---------- +% destDir : char +% directory to download into (created if it does not exist yet). +% releaseTag : char +% the toolbox-assets release tag, for example 'kegg118'. +% assetName : char +% the release asset file name, for example 'kegg118_core.tar.gz'. +% +% Returns +% ------- +% localPath : char +% path to the downloaded file. + +if ~isfolder(destDir) + mkdir(destDir); +end +localPath=fullfile(destDir,assetName); +if isfile(localPath) + return; +end +url=['https://github.com/CompMetEng/toolbox-assets/releases/download/' releaseTag '/' assetName]; +partPath=[localPath '.part']; +fprintf(['Downloading ' assetName '... ']); +try + websave(partPath,url); +catch ME + if isfile(partPath) + delete(partPath); + end + if strcmp(ME.identifier,'MATLAB:webservices:HTTP404StatusCodeError') + error('Failed to download %s, the server returned a 404 error, try again later. If the problem persists please report it on the RAVEN GitHub Issues page: https://github.com/CompMetEng/RAVEN/issues',assetName) + else + rethrow(ME); + end +end +expected=manifestChecksum(url); +if isempty(expected) + warning('RAVEN:fetchToolboxAsset:unverified',['The toolbox-assets manifest gives no ' ... + 'checksum for %s; the download is not verified.'],assetName); +else + actual=sha256File(partPath); + if ~strcmpi(actual,expected) + delete(partPath); + error('RAVEN:fetchToolboxAsset:checksum',['The download of %s is not the published ' ... + 'file (SHA-256 %s, expected %s) and was deleted. Try again; if it persists, ' ... + 'report it on the RAVEN GitHub Issues page: https://github.com/CompMetEng/RAVEN/issues'], ... + assetName,actual,expected) + end +end +movefile(partPath,localPath); +fprintf('COMPLETE\n'); +end + +function sha=manifestChecksum(url) +% The sha256 the toolbox-assets manifest lists for an asset URL, or '' when +% it lists none or cannot be read. The manifest is read once per session. +persistent manifest +sha=''; +if isempty(manifest) + try + manifest=webread('https://github.com/CompMetEng/toolbox-assets/releases/download/manifest-v3/manifest.json', ... + weboptions('ContentType','json','Timeout',30)); + catch ME + warning('RAVEN:fetchToolboxAsset:manifest','Cannot read the toolbox-assets manifest: %s',ME.message); + return + end +end +sha=findUrl(manifest,url); +end + +function sha=findUrl(node,url) +% Depth-first search for a struct with this url and a sha256 field. The +% manifest's file names are not valid MATLAB field names, so the search +% goes by value. +sha=''; +if isstruct(node) + for k=1:numel(node) + if isfield(node(k),'url') && isfield(node(k),'sha256') && strcmp(node(k).url,url) + sha=node(k).sha256; + return + end + f=fieldnames(node(k)); + for i=1:numel(f) + sha=findUrl(node(k).(f{i}),url); + if ~isempty(sha) + return + end + end + end +elseif iscell(node) + for i=1:numel(node) + sha=findUrl(node{i},url); + if ~isempty(sha) + return + end + end +end +end + +function sha=sha256File(path) +% SHA-256 of a file as lower-case hex, read in chunks. +md=java.security.MessageDigest.getInstance('SHA-256'); +fid=fopen(path,'r'); +cleanup=onCleanup(@() fclose(fid)); +while true + chunk=fread(fid,2^24,'*uint8'); + if isempty(chunk) + break + end + md.update(typecast(chunk,'int8')); +end +digest=typecast(md.digest(),'uint8'); +sha=lower(reshape(dec2hex(digest,2)',1,[])); +end diff --git a/installation/findRAVENroot.m b/src/installation/findRAVENroot.m similarity index 59% rename from installation/findRAVENroot.m rename to src/installation/findRAVENroot.m index e828bf79..567364d6 100755 --- a/installation/findRAVENroot.m +++ b/src/installation/findRAVENroot.m @@ -1,17 +1,18 @@ function [ravenPath, prevDir] = findRAVENroot() % findRAVENroot % Finds the root of the RAVEN directory by searching for the path to -% RAVEN.png. Can also record the current directory, in case a function will -% use the ravenPath to navigate to a precise folder, and it should return to -% the previous directory afterwards. See e.g. optimizeProb calling glpk. +% version.txt. Can also record the current directory, for functions that use +% the ravenPath to navigate to a precise folder and return to the previous +% directory afterwards. See, for example, optimizeProb calling glpk. % -% The resolved path is cached for the rest of the MATLAB session: this is -% called on every solver invocation, and re-reading the RAVEN.ravenPath -% preference from disk every time is both wasteful and, under the sustained -% call volume of a full test run, an intermittent source of failures from -% MATLAB's own preference-file I/O. Run `clear findRAVENroot` after -% changing that preference (checkRaven does this already) to pick -% up the change without restarting MATLAB. +% The RAVEN.ravenPath preference is used if it points to a directory with a +% version.txt file. Otherwise the search starts at the location of this +% function file and moves up the directory tree. +% +% The resolved path is cached for the rest of the MATLAB session, so the +% preference is read once although the function is called on every solver +% invocation. `clear findRAVENroot` drops the cached path after a change of +% the preference (checkRaven does this) without a restart of MATLAB. persistent cachedPath prevDir = pwd(); @@ -28,7 +29,7 @@ ravenPath = ''; if ispref('RAVEN','ravenPath') prefPath = getpref('RAVEN','ravenPath'); - if isfile(fullfile(prefPath,'RAVEN.png')) + if isfile(fullfile(prefPath,'version.txt')) ravenPath = prefPath; end end @@ -37,14 +38,14 @@ ravenPath = ST(strcmp({ST.name},'findRAVENroot')).file; rootFound = 0; while rootFound == 0 - isRoot = isfile(fullfile(ravenPath,'RAVEN.png')); + isRoot = isfile(fullfile(ravenPath,'version.txt')); if isRoot rootFound = 1; else ravenPathOld = ravenPath; ravenPath = fileparts(ravenPath); if strcmp(ravenPathOld,ravenPath) - error('Cannot find the RAVEN root directory. Make sure you have not removed the RAVEN.png file from your RAVEN installation.') + error('Cannot find the RAVEN root directory. Make sure you have not removed the version.txt file from your RAVEN installation.') end end end diff --git a/installation/removeRavenFromPath.m b/src/installation/removeRavenFromPath.m similarity index 86% rename from installation/removeRavenFromPath.m rename to src/installation/removeRavenFromPath.m index e2738339..c867e62b 100755 --- a/installation/removeRavenFromPath.m +++ b/src/installation/removeRavenFromPath.m @@ -1,10 +1,9 @@ function removeRavenFromPath() % removeRavenFromPath % This function removes all RAVEN directories and subdirectories from the -% MATLAB path. This function only removes RAVEN from the MATLAB path, it -% does not delete the RAVEN folder itself. If RAVEN was installed as -% MATLAB Add-On, the user is prompted to instead uninstall RAVEN via the -% Add-On Manager. +% MATLAB path and saves the path. The RAVEN folder itself is kept. If RAVEN +% is installed as MATLAB Add-On, the function raises an error that +% advises uninstalling RAVEN via the Add-On Manager. % % Usage: removeRavenFromPath() diff --git a/installation/updateDocumentation.m b/src/installation/updateDocumentation.m similarity index 57% rename from installation/updateDocumentation.m rename to src/installation/updateDocumentation.m index 1975dde5..e899f706 100755 --- a/installation/updateDocumentation.m +++ b/src/installation/updateDocumentation.m @@ -1,47 +1,47 @@ function updateDocumentation() % updateDocumentation -% Regenerates the HTML documentation in RAVEN's doc directory from the -% function help texts. The doc directory is deleted and rebuilt, so -% anything hand-edited there is lost. +% Regenerates the HTML documentation in the docs/html directory of RAVEN from +% the function help texts. The docs/html directory is deleted and rebuilt, so +% manual edits there are lost. % -% The Documentation workflow runs this on every push to a release branch -% and commits the result, so doc stays in step with the sources without -% anyone running it by hand. Run it locally only to preview. +% The Documentation workflow runs this function on every push to the develop +% or main branch that changes a .m file or the workflow file, and commits the +% result. Running the function locally previews the documentation. % % Usage: updateDocumentation() %Get the RAVEN path ravenDir=findRAVENroot(); +srcDir=fullfile(ravenDir,'src'); %Make sure that RAVEN-provided m2html is used -path(fullfile(ravenDir,'software','m2html'),path); +path(fullfile(ravenDir,'src','external','m2html'),path); %Get a non-redundant list of RAVEN subdirectories containing MATLAB %functions. Absolute paths are not compatible with M2HTML, so convert them %to the relative paths instead. -ravenDirs=dir(fullfile(ravenDir,'**/*.m')); +ravenDirs=dir(fullfile(srcDir,'**/*.m')); ravenDirs=unique({ravenDirs.folder})'; %Get rid of MATLAB functions from external software -ravenDirs(startsWith(ravenDirs,strcat(ravenDir,filesep,'software')))=[]; -ravenDirs(startsWith(ravenDirs,strcat(ravenDir,filesep,'legacy',filesep,'software')))=[]; +ravenDirs(startsWith(ravenDirs,strcat(srcDir,filesep,'external')))=[]; %Skip deprecated wrappers: they are on the path so existing scripts keep %working, but documenting them would advertise functions that are on their %way out. Each one names its replacement when called. -ravenDirs(startsWith(ravenDirs,strcat(ravenDir,filesep,'deprecated')))=[]; +ravenDirs(startsWith(ravenDirs,strcat(srcDir,filesep,'deprecated')))=[]; %Remove keggModel.mat if it exists -if exist(fullfile(ravenDir,'reconstruction','kegg','keggModel.mat'), 'file') == 2 - delete(fullfile(ravenDir,'reconstruction','kegg','keggModel.mat')); +if exist(fullfile(ravenDir,'src','reconstruction','kegg','keggModel.mat'), 'file') == 2 + delete(fullfile(ravenDir,'src','reconstruction','kegg','keggModel.mat')); end -%Remove the existing "doc" directory from RAVEN -docDir=fullfile(ravenDir,'doc'); +%Remove the existing "docs/html" directory from RAVEN +docDir=fullfile(ravenDir,'docs','html'); if isfolder(docDir) rmdir(docDir,'s'); end %Make relative path -relStart = numel(ravenDir)+2; +relStart = numel(srcDir)+2; for i=1:numel(ravenDirs) ravenDirs{i,1} = ravenDirs{i,1}(relStart:end); end @@ -58,11 +58,11 @@ function updateDocumentation() end end -%Save the current working directory and go to RAVEN root directory +%Save the current working directory and go to RAVEN source directory originalDir=pwd; -cd(ravenDir); +cd(srcDir); %Generate HTML documentation files for RAVEN MATLAB functions -m2html('mFiles',ravenDirs,'htmldir','doc'); +m2html('mFiles',ravenDirs,'htmldir',docDir); %Go back to the original working directory cd(originalDir); diff --git a/io/checkFileExistence.m b/src/io/checkFileExistence.m similarity index 78% rename from io/checkFileExistence.m rename to src/io/checkFileExistence.m index 05d0019d..5244719f 100755 --- a/io/checkFileExistence.m +++ b/src/io/checkFileExistence.m @@ -1,14 +1,14 @@ function files=checkFileExistence(files,varargin) % checkFileExistence Check whether files exist. % -% If no full path is given a file should be located in the current folder, -% which by default is appended to the filename. +% A file name without a full path refers to a file in the current folder, +% which is prepended to the file name. % % Parameters % ---------- % files : char or cell -% string or cell array of strings with path to file(s) or path or -% filename(s). +% string or cell array of strings with the path or file name of one or +% more files. % % Name-Value Arguments % -------------------- @@ -20,17 +20,17 @@ % - 2 : copy file(s) to system default temporary folder and return % full path % allowSpace : logical -% whether the 'space' character is allowed in the path (default true). +% true if the space character is allowed in the path (default true). % checkExist : logical -% whether file existence should really be checked, as this function can -% also be used to return the full path to a new file (default true). -% Can only be set to false if fullOrTemp is set to 1. +% true if file existence is checked. Setting it to false returns the full +% path to a file that does not exist yet (default true). Can only be set +% to false if fullOrTemp is set to 1. % % Returns % ------- % files : char or cell -% string or cell array of strings with updated paths if fullOrTemp was -% set as 1 or 2, otherwise original paths are returned. +% string or cell array of strings with updated paths if fullOrTemp is 1 +% or 2, otherwise the original paths. % % Examples % -------- diff --git a/io/cleanSheet.m b/src/io/cleanSheet.m similarity index 91% rename from io/cleanSheet.m rename to src/io/cleanSheet.m index 3065abba..6b71cd7a 100755 --- a/io/cleanSheet.m +++ b/src/io/cleanSheet.m @@ -1,6 +1,6 @@ % cleanSheet Clean up an Excel sheet. % -% Removes empty rows/columns (and performs some other checks). +% Removes empty rows and columns, and performs further checks. % % Parameters % ---------- @@ -13,16 +13,17 @@ % true if commented lines (non-empty first cell in each row) should be % removed (default true). % removeOnlyCap : logical -% remove columns with captions but no other values (default false). +% true to remove columns with captions but no other values (default +% false). % removeNoCap : logical -% remove columns without captions (default true). +% true to remove columns without captions (default true). % removeEmptyRows : logical -% remove rows with no non-empty cells (default true). +% true to remove rows with no non-empty cells (default true). % % Returns % ------- % raw : cell -% cleaned version. +% the cleaned cell array. % keptRows : double % indices of the kept rows in the original structure. % keptCols : double diff --git a/io/exportForGit.m b/src/io/exportForGit.m similarity index 70% rename from io/exportForGit.m rename to src/io/exportForGit.m index 2c9838ed..4e183734 100755 --- a/io/exportForGit.m +++ b/src/io/exportForGit.m @@ -2,9 +2,13 @@ % exportForGit Export a model for a Git-maintained model repository. % % Generates a directory structure and populates it with model files, ready -% to be committed to a Git(Hub) maintained model repository. Writes the -% model as SBML L3V1 FBCv2 (both XML and YAML), COBRA text, Matlab MAT-file -% and Microsoft Excel formats. +% to be committed to a Git or GitHub repository of a model. Writes the +% model as SBML L3V1 FBCv2 (XML) and YAML, COBRA text, MATLAB MAT-file and +% Microsoft Excel formats. +% +% The flags mainBranchFlag, subDirs, COBRAtext and neverPrefixIDs accept a +% scalar logical, the value 0 or 1, or an empty value for the default. Text +% and other types raise RAVEN:badInput. % % Parameters % ---------- @@ -17,27 +21,27 @@ % prefix for all filenames (default 'model'). % path : char % path where the directory structure should be generated and populated -% with all files (default current working directory). +% with all files (default current working directory). The folder, with +% any missing parent folders, is created if it does not exist. % formats : cell % cell array of strings specifying in what file formats the model % should be exported (default all formats as {'mat', 'txt', 'xlsx', % 'xml', 'yml'}). % mainBranchFlag : logical -% if true, function will error if RAVEN (and GECKO for enzyme- -% constrained models) is/are not on the main branch (default false). +% true to raise an error if RAVEN (and GECKO for enzyme-constrained +% models) is not on the main or master branch (default false). % subDirs : logical -% whether model files for each file format should be written in their -% own subdirectory, with 'model' as parent directory, in accordance to -% the standard-GEM repository format. If false, all files are stored in -% the same folder (default true). +% true to write the model files of each file format in their own +% subdirectory, with 'model' as parent directory, following the +% standard-GEM repository format. If false, all files are stored in the +% same folder (default true). % COBRAtext : logical -% whether the txt file should be in COBRA Toolbox format using -% metabolite IDs, instead of metabolite names and compartments -% (default false). +% true to write the txt file in COBRA Toolbox format, which uses +% metabolite IDs. If false, the file uses metabolite names and +% compartments (default false). % neverPrefixIDs : logical % true if prefixes are never added to identifiers, even if they start -% with e.g. digits. This might result in invalid SBML files (default -% false). +% with digits. This can result in invalid SBML files (default false). % % Examples % -------- @@ -45,15 +49,10 @@ % COBRAtext, neverPrefixIDs); p=parseRAVENargs(varargin, {'prefix',[]; 'path',[]; 'formats',[]; 'mainBranchFlag',[]; 'subDirs',[]; 'COBRAtext',[]; 'neverPrefixIDs',false}); prefix=p.prefix; path=p.path; formats=p.formats; mainBranchFlag=p.mainBranchFlag; subDirs=p.subDirs; COBRAtext=p.COBRAtext; neverPrefixIDs=p.neverPrefixIDs; -if isempty(COBRAtext) - COBRAtext=false; -end -if isempty(subDirs) - subDirs=true; -end -if isempty(mainBranchFlag) - mainBranchFlag=false; -end +COBRAtext=checkFlag(COBRAtext,'COBRAtext',false); +subDirs=checkFlag(subDirs,'subDirs',true); +mainBranchFlag=checkFlag(mainBranchFlag,'mainBranchFlag',false); +neverPrefixIDs=checkFlag(neverPrefixIDs,'neverPrefixIDs',false); if isempty(formats) formats={'mat', 'txt', 'xlsx', 'xml', 'yml'}; else @@ -98,7 +97,7 @@ delete('tempModelForLibSBMLversion.xml'); end -% Make models folder, no warnings if folder already exists +% Make the folders, no warnings if they already exist if subDirs path=fullfile(path,'model'); filePath=strcat(path,filesep,{'txt','yml','mat','xlsx','xml'}); @@ -107,6 +106,7 @@ [~,~,~]=mkdir(fullfile(path,formats{i})); end else + [~,~,~]=mkdir(path); filePath=cell(1,5); filePath(:)={path}; end @@ -125,12 +125,12 @@ eqns=constructEquations(model,model.rxns); grRules=model.grRules; end - fprintf(fid, 'Rxn name\tFormula\tGene-reaction association\tLB\tUB\tObjective\n'); + fprintf(fid, 'Rxn id\tFormula\tGene-reaction association\tLB\tUB\tObjective\n'); for i = 1:numel(model.rxns) fprintf(fid, '%s\t', model.rxns{i}); - fprintf(fid, '%s \t', eqns{i}); + fprintf(fid, '%s\t', strtrim(eqns{i})); fprintf(fid, '%s\t', grRules{i}); - fprintf(fid, '%6.2f\t%6.2f\t%6.2f\n', model.lb(i), model.ub(i), model.c(i)); + fprintf(fid, '%s\t%s\t%s\n', shortestNumber(model.lb(i)), shortestNumber(model.ub(i)), shortestNumber(model.c(i))); end fclose(fid); end @@ -173,6 +173,32 @@ fclose(fid); end +function s = shortestNumber(x) +% shortestNumber The shortest text that reads back as exactly x, so a bound +% is never rounded on the way out (1/3 is 0.3333333333333333, 1000 is 1000). +if isinf(x) + if x > 0 + s = 'Inf'; + else + s = '-Inf'; + end + return +elseif isnan(x) + s = 'NaN'; + return +end +if x == floor(x) && abs(x) < 1e15 + s = sprintf('%d', x); + return +end +for digits = 1:17 + s = sprintf(['%.' num2str(digits) 'g'], x); + if str2double(s) == x + break + end +end +end + function version = getToolboxVersion(toolbox,fileID,mainBranchFlag) % getToolboxVersion Return the version of RAVEN or GECKO. % @@ -209,7 +235,7 @@ currentBranch = strtrim(currentBranch); if ~any([strcmp(currentBranch, "main"), strcmp(currentBranch, "master")]) cd(currentPath); - error(['ERROR: ' toolbox ' not in main (or master) branch. Check-out this branch of ' toolbox ' before submitting model for Git.']) + error('RAVEN:notMainBranch',['ERROR: ' toolbox ' not in main (or master) branch. Check-out this branch of ' toolbox ' before submitting model for Git.']) end end %Read the version from the toolbox version.txt; report 'unknown' otherwise @@ -232,3 +258,14 @@ slashPos = strfind(path,'/'); %MAC/Linux end end + +function flag = checkFlag(flag, name, default) +%An empty value gives the default; otherwise a scalar logical or 0/1 is required +if isempty(flag) + flag = default; +elseif (islogical(flag) || isnumeric(flag)) && isscalar(flag) && isreal(flag) && (flag == 0 || flag == 1) + flag = logical(flag); +else + error('RAVEN:badInput', '%s must be a scalar logical or 0/1.', name); +end +end diff --git a/io/exportModel.m b/src/io/exportModel.m similarity index 98% rename from io/exportModel.m rename to src/io/exportModel.m index 2bbda7f2..af668d07 100755 --- a/io/exportModel.m +++ b/src/io/exportModel.m @@ -9,18 +9,18 @@ function exportModel(model,varargin) % Name-Value Arguments % -------------------- % fileName : char -% filename to export the model to. A dialog window will open if no file -% name is specified. +% file name to export the model to. A dialog window opens if no file name +% is specified. % neverPrefix : logical % true if prefixes are never added to identifiers, even if they start -% with e.g. digits. This might result in invalid SBML files (default -% false). +% with digits. This can result in invalid SBML files (default false). % supressWarnings : logical -% true if warnings should be suppressed. This might result in invalid -% SBML files, as no checks are performed (default false). +% true if warnings should be suppressed. The check of the model +% structure (checkModelStruct) is skipped, which can result in invalid +% SBML files (default false). % sortIds : logical -% whether metabolites, reactions and genes should be sorted -% alphabetically by their identifiers (default false). +% true to sort metabolites, reactions and genes alphabetically by their +% identifiers (default false). % % Examples % -------- diff --git a/io/exportToExcelFormat.m b/src/io/exportToExcelFormat.m similarity index 98% rename from io/exportToExcelFormat.m rename to src/io/exportToExcelFormat.m index 94db137c..fe719d55 100755 --- a/io/exportToExcelFormat.m +++ b/src/io/exportToExcelFormat.m @@ -9,11 +9,11 @@ function exportToExcelFormat(model,varargin) % Name-Value Arguments % -------------------- % fileName : char -% file name of the Excel file. Only xlsx format is supported. A dialog -% window will open if fileName is empty. +% file name of the Excel file. Only the xlsx format is supported. A +% dialog window opens if fileName is empty. % sortIds : logical -% whether metabolites, reactions and genes should be sorted -% alphabetically by their identifiers (default false). +% true to sort metabolites, reactions and genes alphabetically by their +% identifiers (default false). % % Examples % -------- diff --git a/io/importModel.m b/src/io/importModel.m similarity index 96% rename from io/importModel.m rename to src/io/importModel.m index 00ec9e95..347e31bb 100755 --- a/io/importModel.m +++ b/src/io/importModel.m @@ -4,19 +4,18 @@ % Name-Value Arguments % -------------------- % fileName : char -% a SBML file to import. A dialog window will open if no file name is +% a SBML file to import. A dialog window opens if no file name is % specified. % removeExcMets : logical -% if true, exchange metabolites are removed from the imported model -% (default true). +% true to remove exchange metabolites from the imported model (default +% true). % removePrefix : logical -% true if identifier prefixes should be removed when loading the model: -% G_ for genes, R_ for reactions, M_ for metabolites, and C_ for -% compartments. These are only removed if all identifiers of a certain -% type contain the prefix (default true). +% true to remove identifier prefixes when loading the model: G_ for +% genes, R_ for reactions, M_ for metabolites, and C_ for compartments. +% The prefix of a type is removed only if all identifiers of that type +% contain it (default true). % supressWarnings : logical -% true if warnings regarding the model structure should be supressed -% (default false). +% true to suppress warnings about the model structure (default false). % % Returns % ------- @@ -50,13 +49,13 @@ % - rxnNotes : reaction notes % - rxnReferences : reaction references % - rxnConfidenceScores : reaction confidence scores -% - rxnDeltaG : reaction Gibbs free energy, from a `deltaG` notes key -% (same convention exportModel writes and raven-toolbox reads/writes; -% omitted entirely if no reaction has one) +% - rxnDeltaG : reaction Gibbs free energy, from the `deltaG` notes +% field (the convention that exportModel writes and raven-toolbox +% reads and writes; omitted if no reaction has one) % - genes : list of all genes % - geneComps : compartments for genes % - geneMiriams : structure with MIRIAM information about the genes -% - geneShortNames : gene alternative names (e.g. ERG10) +% - geneShortNames : gene alternative names (for example ERG10) % - proteins : protein associated to each gene % - metNames : metabolite description % - metComps : compartments for metabolites @@ -64,9 +63,9 @@ % - metFormulas : metabolite chemical formula % - metMiriams : structure with MIRIAM information about the metabolites % - metCharges : metabolite charge -% - metDeltaG : metabolite Gibbs free energy, from a `deltaG` notes key -% (same convention exportModel writes and raven-toolbox reads/writes; -% omitted entirely if no metabolite has one) +% - metDeltaG : metabolite Gibbs free energy, from the `deltaG` notes +% field (the convention that exportModel writes and raven-toolbox +% reads and writes; omitted if no metabolite has one) % - unconstrained : true if the metabolite is an exchange metabolite % % Examples @@ -75,9 +74,9 @@ % % Notes % ----- -% A number of consistency checks are performed to ensure that the model is -% valid. Take these warnings seriously and modify the model structure to -% solve them. +% checkModelStruct checks the consistency of the imported model and raises +% warnings about problems in the model structure. supressWarnings skips this +% check. p=parseRAVENargs(varargin, {'fileName',[]; 'removeExcMets',[]; 'removePrefix',[]; 'supressWarnings',false}); fileName=p.fileName; removeExcMets=p.removeExcMets; removePrefix=p.removePrefix; supressWarnings=p.supressWarnings; diff --git a/io/parseYAML.m b/src/io/parseYAML.m similarity index 93% rename from io/parseYAML.m rename to src/io/parseYAML.m index 602f4441..c33b61a7 100644 --- a/io/parseYAML.m +++ b/src/io/parseYAML.m @@ -1,21 +1,20 @@ function out = parseYAML(filename) -% parseYAML Read a YAML file into a MATLAB struct/cell tree. +% parseYAML Read a YAML file into a tree of MATLAB structs and cells. % % Supports block-style mappings and sequences (indentation-based), % flow-style mappings and sequences (`{ key: value }`, `[a, b]`), quoted % and unquoted scalars, and comments. Does not support multi-document -% files, anchors/aliases, tags, or block scalars (`|`, `>`). +% files, anchors, aliases, tags, or block scalars (`|`, `>`). % -% Use this for parsing YAML configuration / data files with a nested -% mapping/sequence/scalar shape (e.g. condition files consumed by -% applyCondition). For loading a cobra-format model YAML, use -% readYAMLmodel instead — that function knows the model schema and -% returns a populated RAVEN model struct. +% The function parses YAML configuration and data files with nested +% mappings, sequences and scalars (for example condition files consumed by +% applyCondition). readYAMLmodel loads a cobra-format model YAML: it applies +% the model schema and returns a populated RAVEN model struct. % % Parameters % ---------- % filename : char -% Path to the YAML file. +% path to the YAML file. % % Returns % ------- @@ -26,7 +25,7 @@ % - sequence -> cell column vector % - quoted or unquoted string -> char % - integer or float -> double -% - true/false -> logical +% - true, false -> logical % - null / ~ / empty value -> [] % % Examples diff --git a/io/readFasta.m b/src/io/readFasta.m similarity index 100% rename from io/readFasta.m rename to src/io/readFasta.m diff --git a/io/readYAMLmodel.m b/src/io/readYAMLmodel.m similarity index 91% rename from io/readYAMLmodel.m rename to src/io/readYAMLmodel.m index 9d1dcb43..29a6ff9b 100755 --- a/io/readYAMLmodel.m +++ b/src/io/readYAMLmodel.m @@ -1,16 +1,16 @@ function model=readYAMLmodel(varargin) % readYAMLmodel Read a model structure from a YAML file. % -% Reads a yaml file matching (roughly) the standard cobra-format model -% yaml structure. +% Reads a yaml file in the cobra-format model yaml structure that +% writeYAMLmodel writes. % % Name-Value Arguments % -------------------- % fileName : char -% a model file in yaml file format. A dialog window will open if no -% file name is specified. +% a model file in yaml file format. A dialog window opens if no file +% name is specified. % verbose : logical -% set as true to monitor progress (default false). +% true to print the progress (default false). % % Returns % ------- @@ -58,9 +58,11 @@ % A continuation line is recognised by NOT starting with '- ': every real % entry in this format, at every nesting depth (including a nested list % under a key, e.g. an annotation sub-entry indented 6+ spaces), is itself -% introduced with a literal leading dash, while a folded scalar's -% continuation is raw text with no list-item marker of its own. -newLine=regexp(line_raw,'^ {6,}([^-\s].*)','tokenExtents'); +% introduced with a list-item marker (a dash followed by a space, or a dash +% ending the line), while a folded scalar's continuation is raw text with no +% list-item marker of its own. Such text can itself start with a dash, as in +% a KEGG reaction name wrapped before "-epoxidizing)". +newLine=regexp(line_raw,'^ {6,}((?!-(\s|$))\S.*)','tokenExtents'); brokenLine=find(~cellfun('isempty',newLine)); for i=flip(1:numel(brokenLine)) extraLine = char(line_raw(brokenLine(i))); @@ -193,6 +195,7 @@ end section = 0; +readList = ''; metMiriams=cell(100000,3); metMirNo=1; rxnMiriams=cell(100000,3); rxnMirNo=1; geneMiriams=cell(100000,3); genMirNo=1; @@ -344,6 +347,12 @@ % import metabolites: if section == 2 + % The lines of a notes map are indented below the entry's own + % fields; their keys are not entry fields, whatever their name. + if strcmp(readList, 'notes') && ~isempty(regexp(tline_raw, '^ {6,}', 'once')) + model = appendNote(model, 'metNotes', tline_raw, tline_key, tline_value, pos); + continue + end switch tline_key case 'id' pos = pos + 1; @@ -362,8 +371,14 @@ model = readFieldValue(model, 'metCharges', tline_value, pos); readList=''; miriamKey=''; case 'notes' - model = readFieldValue(model, 'metNotes', tline_value, pos); - readList=''; miriamKey=''; + % A notes map (other tools' keys, as cobrapy and + % raven-toolbox write them) is read into the note text. + if isempty(tline_value) || strcmp(tline_value, '!!omap') + readList = 'notes'; + else + model = readFieldValue(model, 'metNotes', tline_value, pos); + readList=''; miriamKey=''; + end case 'inchis' model = readFieldValue(model, 'inchis', tline_value, pos); readList=''; miriamKey=''; @@ -419,6 +434,12 @@ % import reactions: if section == 3 + % The lines of a notes map are indented below the entry's own + % fields; their keys are not entry fields, whatever their name. + if strcmp(readList, 'notes') && ~isempty(regexp(tline_raw, '^ {6,}', 'once')) + model = appendNote(model, 'rxnNotes', tline_raw, tline_key, tline_value, pos); + continue + end switch tline_key case 'id' pos = pos + 1; @@ -443,8 +464,12 @@ % `notes` is the canonical reaction-side key; `rxnNotes` % is the legacy RAVEN MATLAB key, kept here for % backward-compatible reads. - model = readFieldValue(model, 'rxnNotes', tline_value, pos); - readList=''; miriamKey=''; + if isempty(tline_value) || strcmp(tline_value, '!!omap') + readList = 'notes'; + else + model = readFieldValue(model, 'rxnNotes', tline_value, pos); + readList=''; miriamKey=''; + end case 'rxnFrom' model = readFieldValue(model, 'rxnFrom', tline_value, pos); readList=''; miriamKey=''; @@ -788,9 +813,12 @@ % end if isfield(model,'subSystems') +% A reaction without subsystem holds [] or '' here: give it an empty entry, +% so every reaction keeps its place when the entries are flattened + model.subSystems(cellfun(@isempty, model.subSystems)) = {{''}}; % If all entries are 1x1, then flatten if all(cellfun(@(x) numel(x) <= 1, model.subSystems)) - model.subSystems = transpose([model.subSystems{:}]); + model.subSystems = cellfun(@(x) x{1}, model.subSystems, 'UniformOutput', false); end end @@ -876,6 +904,47 @@ model.(fieldName)(pos,1) = {value}; end +function model = appendNote(model, fieldName, raw, key, value, pos) +% One line of a notes map: a 'note' entry adds its value (RAVEN's own note +% text), another entry adds 'key: value', a list item adds its value to the +% entry above it. Entries are separated by '; ', list items by ', '. A +% quoted list item is read whole, as the key detection would split it at +% a ': ' inside the quotes. +item = regexp(raw, '^ +- ([''"])(.*)\1$', 'tokens', 'once'); +if ~isempty(item) + key = ''; + value = item{2}; +end +if strcmp(value, '!!omap') + value = ''; +end +text = ''; +if numel(model.(fieldName)) >= pos + text = model.(fieldName){pos}; +end +if isempty(key) + if isempty(text) || endsWith(text, ':') + text = strtrim([text ' ' value]); + else + text = [text ', ' value]; + end +else + if isempty(value) + entry = [key ':']; + elseif strcmp(key, 'note') + entry = value; + else + entry = [key ': ' value]; + end + if isempty(text) + text = entry; + else + text = [text '; ' entry]; + end +end +model = readFieldValue(model, fieldName, text, pos); +end + function [miriams,miriamKey,entryNumber] = gatherAnnotation(pos,miriams,key,value,miriamKey,entryNumber) if isempty(key) key=miriamKey; diff --git a/io/writeExcel.m b/src/io/writeExcel.m similarity index 100% rename from io/writeExcel.m rename to src/io/writeExcel.m diff --git a/io/writeFasta.m b/src/io/writeFasta.m similarity index 100% rename from io/writeFasta.m rename to src/io/writeFasta.m diff --git a/io/writeYAMLmodel.m b/src/io/writeYAMLmodel.m similarity index 95% rename from io/writeYAMLmodel.m rename to src/io/writeYAMLmodel.m index 5b9b892b..22df44db 100755 --- a/io/writeYAMLmodel.m +++ b/src/io/writeYAMLmodel.m @@ -1,10 +1,10 @@ function writeYAMLmodel(model,varargin) -% writeYAMLmodel Write a model to a yaml file in the standard cobra-format -% !!omap structure. +% writeYAMLmodel Write a model to a YAML file in the cobra-format !!omap structure. % % The format uses the standard !!omap layout, extended with RAVEN-only % top-level per-entry keys (inchis, deltaG, metFrom, rxnFrom, references, -% confidence_score, protein) and the GECKO ec-rxns / ec-enzymes sections. +% confidence_score, protein, notes) and the GECKO ec-rxns and ec-enzymes +% sections. % Reaction EC numbers are written inside the `annotation` block as % `ec-code`, not as a top-level reaction key. % @@ -16,12 +16,11 @@ function writeYAMLmodel(model,varargin) % Name-Value Arguments % -------------------- % fileName : char -% name that the file will have. A dialog window will open if no file -% name is specified. +% name of the file. A dialog window opens if no file name is specified. % sortIds : logical -% if metabolites, reactions, genes and compartments should be sorted -% alphabetically by their identifier, otherwise they are kept in their -% original order (default false). +% true to sort metabolites, reactions, genes and compartments +% alphabetically by their identifier. If false, they keep their original +% order (default false). % % Examples % -------- @@ -225,16 +224,17 @@ function writeField(model,fid,fieldName,type,pos,name,keyIndent) end elseif strcmp(fieldName,'rxnEnzMat') - %S: create header & write each enzyme in a new line - fprintf(fid,'%s: !!omap\n',name); + %enzymes: create header & write each enzyme in a new line. A + %reaction without enzymes has no entry at all. if sum(field(pos,:) ~= 0) > 0 + fprintf(fid,'%s: !!omap\n',name); model.enzymes = model.enzymes(field(pos,:) ~= 0); model.coeffs = field(pos,field(pos,:) ~= 0); %Sort metabolites: [model.enzymes,order] = sort(model.enzymes); model.coeffs = model.coeffs(order); for i = 1:length(model.enzymes) - writeField(model, fid, 'coeffs', 'flt', i, [' - ' model.enzymes{i}], 4) + writeField(model, fid, 'coeffs', 'flt', i, [' - ' model.enzymes{i}], 6) end end @@ -382,7 +382,15 @@ function emitMetaField(fid, key, value) if x == floor(x) && abs(x) < 1e16 s = sprintf('%.1f', x); % e.g. 1000 -> "1000.0" else - s = sprintf('%.15g', x); + % Shortest text that reads back as exactly x (15 to 17 significant + % digits), as Python's repr does, so 1/3 is written 0.3333333333333333 + % and a value is never rounded on the way out. + for digits = 15:17 + s = sprintf(['%.' num2str(digits) 'g'], x); + if str2double(s) == x + break + end + end end end @@ -549,7 +557,9 @@ function emitScalarCore(fid, ~, value, ~, bareNumeric) % because the text itself is not valid as a plain block scalar (leading % indicator character, a mid-string ": "/" #", leading/trailing % whitespace, or embedded control characters). Implements the YAML 1.2 -% rules for resolving implicit scalar types and analyzing scalar style. +% rules for resolving implicit scalar types and analyzing scalar style, +% and also quotes the YAML 1.1 booleans y, n, yes, no, on and off in any +% case, which YAML 1.1 loaders such as PyYAML read as booleans. if isempty(s) tf = true; return @@ -557,6 +567,7 @@ function emitScalarCore(fid, ~, value, ~, bareNumeric) resolverPattern = [ ... '^(true|True|TRUE|false|False|FALSE)$|' ... + '^([Yy]|[Nn]|[Yy][Ee][Ss]|[Nn][Oo]|[Oo][Nn]|[Oo][Ff][Ff])$|' ... '^[-+]?[0-9][0-9_]*\.[0-9_]*([eE][-+]?[0-9]+)?$|' ... '^[-+]?[0-9][0-9_]*[eE][-+]?[0-9]+$|' ... '^[-+]?\.[0-9_]+([eE][-+][0-9]+)?$|' ... diff --git a/localization/assignCompartments.m b/src/localization/assignCompartments.m similarity index 100% rename from localization/assignCompartments.m rename to src/localization/assignCompartments.m diff --git a/src/localization/defaultCompartmentMap.m b/src/localization/defaultCompartmentMap.m new file mode 100644 index 00000000..3bad38cf --- /dev/null +++ b/src/localization/defaultCompartmentMap.m @@ -0,0 +1,48 @@ +function map = defaultCompartmentMap() +% defaultCompartmentMap Default map from compartment labels to model compartment ids. +% +% Returns a containers.Map from lower-case compartment labels of predictors and databases (as +% used by DeepLoc, MULocDeep, COMPARTMENTS and UniProt) to compartment ids, for yeast and fungal +% models (for example yeast-GEM codes). The map is passed to parseScores or getUniProtScores as +% 'compartmentMap'. +% +% The map holds three decisions: +% +% - Compartments present. A label absent from the map is dropped along with its score column, so +% the map lists only the compartment ids of the model. 'plastid' is absent because fungi have +% no plastid; a plant model adds it. +% - Labels that name the same compartment. Predictors differ in wording, so 'cytoplasm' and +% 'cytosol' both map to 'c', and 'mitochondrion', 'mitochondria' and 'mitochondrial' all map +% to 'm'. Merged columns are combined by their maximum. +% - Distinct compartments that are collapsed. 'lysosome' maps to 'v' because a yeast model has +% no lysosome and the vacuole is its functional equivalent. A model that separates the two +% needs a different mapping. +% +% Changes to the mapping go into a copy of the map, which leaves this function unedited, for +% example: +% +% map = defaultCompartmentMap; +% map('cell wall') = 'ce'; +% GSS = parseScores(file, 'compartmentMap', map); +% +% Returns +% ------- +% map : containers.Map +% label (char, lower case) -> compartment id (char). +% +% See also +% -------- +% parseScores, getUniProtScores, predictLocalization + +labels = {'cytoplasm','cytosol','nucleus','nucleoplasm','mitochondrion','mitochondria', ... + 'mitochondrial','peroxisome','endoplasmic reticulum','golgi apparatus','golgi', ... + 'vacuole','lysosome/vacuole','lysosome','extracellular','extracellular space', ... + 'extracellular region','secreted','cell membrane','plasma membrane','cell envelope', ... + 'lipid particle','lipid droplet'}; +ids = {'c','c','n','n','m','m', ... + 'm','p','er','g','g', ... + 'v','v','v','e','e', ... + 'e','e','ce','ce','ce', ... + 'lp','lp'}; +map = containers.Map(labels, ids); +end diff --git a/localization/getUniProtScores.m b/src/localization/getUniProtScores.m similarity index 61% rename from localization/getUniProtScores.m rename to src/localization/getUniProtScores.m index 913e8964..3799b031 100644 --- a/localization/getUniProtScores.m +++ b/src/localization/getUniProtScores.m @@ -1,36 +1,36 @@ function GSS = getUniProtScores(organism, varargin) -% getUniProtScores Fetch curated subcellular locations from UniProt -> gene scoring structure. +% getUniProtScores Fetch curated subcellular locations from UniProt as a gene scoring structure. % -% Queries the UniProtKB REST API for an organism's "Subcellular location" annotations and parses -% them into the GSS consumed by predictLocalization (no manual export needed). UniProt's curated -% location is qualitative, so each annotated compartment gets score 1.0 (a multi-located protein -% lands in several). Requires an internet connection. +% Queries the UniProtKB REST API for the "Subcellular location" annotations of an organism and +% parses them into the GSS consumed by predictLocalization. No manual export is needed. UniProt +% locations are qualitative, so each annotated compartment gets score 1.0 and a protein with +% several annotated locations scores 1.0 in each of them. The function requires an internet +% connection. % % Parameters % ---------- % organism : double or char -% a UniProt organism / taxonomy id (e.g. 559292 for S. cerevisiae S288C). +% a UniProt organism (taxonomy) id (for example 559292 for S. cerevisiae S288C). % % Name-Value Arguments % -------------------- % idField : char -% which identifier becomes the gene id: 'gene_oln' (ordered locus / ORF, the default, which -% matches yeast-GEM), 'accession', or 'gene_primary'. +% which identifier becomes the gene id: 'gene_oln' (ordered locus or ORF name, the default, +% which matches yeast-GEM), 'accession', or 'gene_primary'. % reviewed : logical -% restrict to curated Swiss-Prot entries (default true). +% true to restrict the query to curated Swiss-Prot entries (default true). % compartmentMap : containers.Map -% UniProt location term -> your model's compartment id (default -% defaultCompartmentMap). Here the map is not only a renaming: its keys are -% also the vocabulary searched for in UniProt's "Subcellular location [CC]" -% free text, matched whole-word and case insensitively, so they must be -% UniProt's own noun forms ('endoplasmic reticulum', not 'ER') and a term -% that is not a key is never looked for. Several keys may share one -% compartment id, which merges them; since every hit scores 1.0, merging -% here means "annotated to any of these counts as this compartment". One -% key cannot be split across two compartment ids - see parseScores for why, -% and for the recipe to duplicate a column on the returned GSS instead. +% maps UniProt location terms to the compartment ids of the model (default +% defaultCompartmentMap). The keys are also the vocabulary searched for in the +% "Subcellular location [CC]" free text of UniProt, matched as whole words and +% case insensitively. The keys must be the noun forms used by UniProt +% ('endoplasmic reticulum', not 'ER'), and a term that is not a key is not searched. +% Several keys may share one compartment id, which merges them; since every hit +% scores 1.0, a protein annotated to any of the merged terms scores 1.0 in that +% compartment. One key cannot map to two compartment ids; parseScores describes +% how to duplicate a column of the returned GSS instead. % extraQuery : char -% an additional UniProt query clause, ANDed into the query (e.g. 'gene:CIT1'). +% an additional UniProt query clause, combined with the query by AND (for example 'gene:CIT1'). % % Returns % ------- diff --git a/localization/parseScores.m b/src/localization/parseScores.m similarity index 77% rename from localization/parseScores.m rename to src/localization/parseScores.m index 15f3c7fa..6306da62 100755 --- a/localization/parseScores.m +++ b/src/localization/parseScores.m @@ -1,14 +1,14 @@ function GSS = parseScores(inputFile, varargin) -% parseScores Parse predictor / database output into a gene scoring structure (GSS). +% parseScores Parse predictor or database output into a gene scoring structure (GSS). % -% Reads the output of a subcellular-localization predictor or evidence database and +% Reads the output of a subcellular localisation predictor or evidence database and % builds the gene scoring structure consumed by predictLocalization. Scores are -% normalized so the best score for each gene is 1.0. +% normalised so that the highest score of each gene is 1.0. % % Parameters % ---------- % inputFile : char -% the file with the predictor / database output. +% the file with the predictor or database output. % % Name-Value Arguments % -------------------- @@ -18,15 +18,15 @@ % compartment), 'compartments' (a COMPARTMENTS jensenlab.org channel TSV), or % 'uniprot' (a UniProtKB TSV export with a "Subcellular location [CC]" column). % compartmentMap : containers.Map -% renames the predictor's / database's compartment labels to your model's -% compartment ids. Keys are the labels as the predictor writes them, lower -% case; values are the compartment ids you want in the GSS. See -% Compartment mapping below. Optional for every predictor except 'uniprot', -% where it also supplies the location vocabulary and defaults to -% defaultCompartmentMap. +% maps the compartment labels of the predictor or database to the compartment ids of +% the model. Keys are the labels as the predictor writes them, in lower case; values +% are the compartment ids in the GSS. Optional for every predictor except 'uniprot', +% where the map also supplies the location vocabulary and defaults to +% defaultCompartmentMap. See Compartment mapping below. % idColumn : char or double -% column (name or 1-based index) holding the gene id for 'mulocdeep' / 'uniprot'. -% Default: the first column. For yeast-GEM, use the UniProt ordered-locus column. +% column (name or 1-based index) holding the gene id for 'mulocdeep' and 'uniprot' +% (default: the first column). For yeast-GEM, the UniProt ordered-locus column holds +% the matching ids. % minConfidence : double % for 'compartments', drop annotations with confidence below this (default 0). % @@ -37,34 +37,31 @@ % % - genes : cell, one entry per gene. % - compartments : cell, one entry per compartment column. -% - scores : double, numel(genes)-by-numel(compartments), normalized so -% the best score per gene is 1.0. +% - scores : double, numel(genes)-by-numel(compartments), normalised so +% that the highest score per gene is 1.0. % % Compartment mapping % ------------------- -% Predictors emit their own compartment vocabulary ("Endoplasmic reticulum", -% "Golgi apparatus", ...), which rarely matches a model's compartment ids. -% compartmentMap is a containers.Map that translates them, and it is the only -% place that translation happens - the returned GSS already uses your ids. -% defaultCompartmentMap returns a worked example, tuned for yeast/fungal -% models; use it as-is, or as the template for your own. Three rules: +% Predictors use their own compartment vocabulary ("Endoplasmic reticulum", +% "Golgi apparatus", ...), which need not match the compartment ids of a model. +% compartmentMap is a containers.Map that translates the labels; the translation +% happens only there, so the returned GSS contains the ids of the map values. +% defaultCompartmentMap returns an example map for yeast and fungal models, which can +% be used as it is or as a template. Three rules apply: % -% - **A label not in the map is dropped**, together with its whole score -% column. This is how you exclude compartments your model does not have -% (defaultCompartmentMap has no 'plastid' key, so plastid scores never reach -% a fungal model). Passing no map at all keeps every label untouched. -% - **Several labels may share one compartment id**, which merges their -% columns. The merged score for a gene is the *maximum* over the merged -% labels, not their sum, so a gene predicted 0.7 cytoplasm and 0.4 cytosol -% scores 0.7 for 'c'. Use this for synonyms and for deliberately collapsing -% distinct compartments your model does not separate (defaultCompartmentMap -% sends both 'vacuole' and 'lysosome' to 'v'). Column order follows the first -% appearance of each id. -% - **One label cannot be split across two compartment ids**: a containers.Map -% holds one value per key, and the GSS is what predictLocalization scores -% against, so a split has to be an explicit statement about the returned -% scores rather than a mapping rule. Duplicate the column afterwards, e.g. to -% let a gene predicted "golgi" compete for both 'g' and 'v': +% - A label that is not in the map is dropped, together with its whole score +% column. This excludes compartments that the model does not have +% (defaultCompartmentMap has no 'plastid' key, so plastid scores are dropped for a +% fungal model). Without a map, every label is kept unchanged. +% - Several labels may share one compartment id, which merges their +% columns. The merged score of a gene is the maximum over the merged labels, not +% their sum: a gene predicted 0.7 cytoplasm and 0.4 cytosol scores 0.7 for 'c'. +% Sharing an id merges synonyms and collapses compartments that the model does not +% separate (defaultCompartmentMap sends both 'vacuole' and 'lysosome' to 'v'). +% Column order follows the first appearance of each id. +% - One label cannot map to two compartment ids: a containers.Map holds one +% value per key. A split is made on the returned scores. Duplicating the +% column afterwards lets a gene predicted "golgi" compete for both 'g' and 'v': % % GSS = parseScores(file, 'compartmentMap', map); % g = strcmp(GSS.compartments, 'g'); @@ -72,14 +69,14 @@ % GSS.scores(:,end+1) = GSS.scores(:,g); % % The two columns are then independent: predictLocalization assigns each gene -% to exactly one compartment, so it picks whichever of 'g'/'v' keeps the -% network better connected. Halve the copied column instead of duplicating it -% if the evidence should count for less in the second compartment. +% to one compartment, so it picks whichever of 'g' and 'v' keeps the network +% better connected. Halving the copied column gives the evidence half the +% weight in the second compartment. % -% For 'uniprot' the keys do double duty: they are also the terms searched for -% in the "Subcellular location [CC]" free text, matched whole-word and case -% insensitively, so they must be UniProt's own noun forms. A term that is not a -% key is never looked for. +% For 'uniprot', the keys are also the terms searched for in the +% "Subcellular location [CC]" free text, matched as whole words and case +% insensitively. The keys must be the noun forms used by UniProt. A term that is +% not a key is not searched. % % Examples % -------- diff --git a/localization/predictLocalization.m b/src/localization/predictLocalization.m similarity index 94% rename from localization/predictLocalization.m rename to src/localization/predictLocalization.m index 9920f27e..015360e2 100755 --- a/localization/predictLocalization.m +++ b/src/localization/predictLocalization.m @@ -3,77 +3,69 @@ defaultCompartment, varargin) % predictLocalization Assign reactions to compartments using localization predictors. % -% Tries to assign reactions to compartments in a manner that is in -% agreement with localization predictors while at the same time maintaining -% connectivity. +% Assigns reactions to compartments so that the assignment agrees with the localization +% predictors and the network stays connected. % % Parameters % ---------- % model : struct -% a model structure. If the model contains several compartments they -% will be merged. +% a model structure. If the model contains several compartments, they +% are merged. % GSS : struct % a gene scoring structure as from parseScores. % defaultCompartment : char % transport reactions are expressed as diffusion between the -% defaultCompartment and the others. This is usually the cytosol. The -% default compartment must have a match in GSS. +% defaultCompartment and the others. The compartment is usually the cytosol. It must +% be one of GSS.compartments. % % Name-Value Arguments % -------------------- % transportCost : double -% the cost for including a transport reaction. If this is a scalar then -% the same cost is used for all metabolites. It can also be a vector of -% costs with the same dimension as model.mets. Negative costs encourage -% transport of the metabolite (default 0.5). +% the cost for including a transport reaction. A scalar applies the same cost to all +% metabolites; a vector must have the same dimension as model.mets. A negative cost +% favours transport of the metabolite (default 0.5). % maxTime : double -% maximum optimization time in minutes (default 15). +% maximum optimisation time in minutes (default 15). % plotResults : logical -% true if the results should be plotted during the optimization -% (default false). +% true to plot the results during the optimisation (default false). % % Returns % ------- % outModel : struct % the resulting model structure. % geneLocalization : struct -% structure with the genes and their resulting localization. +% structure with the genes and their resulting localisation. % transportStruct : struct -% structure with the transport reactions that had to be inferred and -% between which compartments. +% structure with the inferred transport reactions and the compartments between which +% they transport. % scores : struct -% structure that contains the total score history together with the -% score based on gene localization and the score based on included -% transport reactions. +% structure with the total score history, the score based on gene localisation and +% the score based on the included transport reactions. % removedRxns : cell -% cell array with the reaction ids that had to be removed to have a -% connected input model. +% cell array with the ids of the reactions removed to obtain a connected input model. % % Notes % ----- -% This function requires that the starting network is connected when it is -% in one compartment. Reactions that are unconnected are removed and saved -% in removedRxns. Try running fillGaps to have a more connected input model -% if there are many such reactions. The input model should also not include -% any exchange, demand or sink reactions, otherwise this function would not -% provide any results. +% The starting network must be connected when it is in one compartment. Unconnected +% reactions are removed and returned in removedRxns. If many reactions are removed, +% fillGaps can produce a more connected input model. The input model must not include +% exchange, demand or sink reactions; otherwise the function provides no results. % -% In the final model all metabolites are produced in at least one reaction. -% This does not guarantee a fully functional model since there can be -% internal loops. Transport reactions are only included as passive diffusion -% (A <=> B). +% In the final model, every metabolite is produced in at least one reaction. Internal +% loops can still make the model non-functional. Transport reactions are included only +% as passive diffusion (A <=> B). % -% The score of a model is the sum of scores for all genes in their assigned -% compartment minus the cost of all transport reactions that had to be -% included. A gene can only be assigned to one compartment. This is a -% simplification to keep the problem size down. The problem is solved using -% simulated annealing. +% The score of a model is the sum of the scores of all genes in their assigned +% compartment minus the cost of all included transport reactions. A gene is assigned +% to one compartment, which limits the problem size. The problem is solved by simulated +% annealing. % % Examples % -------- % [outModel, geneLocalization, transportStruct, scores, removedRxns] = ... % predictLocalization(model, GSS, defaultCompartment, ... -% transportCost, maxTime, plotResults); +% 'transportCost', transportCost, 'maxTime', maxTime, ... +% 'plotResults', plotResults); p=parseRAVENargs(varargin, {'transportCost',[]; 'maxTime',[]; 'plotResults',[]}); transportCost=p.transportCost; diff --git a/manipulation/addExchangeRxns.m b/src/manipulation/addExchangeRxns.m similarity index 83% rename from manipulation/addExchangeRxns.m rename to src/manipulation/addExchangeRxns.m index ddb6af8f..d23bfdfb 100755 --- a/manipulation/addExchangeRxns.m +++ b/src/manipulation/addExchangeRxns.m @@ -1,9 +1,11 @@ function [model, addedRxns]=addExchangeRxns(model,reactionType,varargin) % addExchangeRxns Add exchange reactions for some metabolites. % -% This is a faster version than addRxns when adding exchange reactions. -% New reactions are named "metName exchange (OUT/IN/BOTH)" while reaction -% ids are formatted as "EXC_OUT/IN/BOTH_METID". +% The function builds the stoichiometric columns directly from the metabolite list, +% without the equation parsing of addRxns, which makes it faster for adding exchange +% reactions. New reactions are +% named "metName exchange (TYPE)" and have the ids "EXC_TYPE_METID", where TYPE is IN, +% OUT or BOTH according to reactionType. % % Parameters % ---------- @@ -14,15 +16,15 @@ % % - 'in' : input reactions % - 'out' : output reactions -% - 'both' : reversible input/output reactions. Positive direction +% - 'both' : reversible input and output reactions. The positive direction % corresponds to output % % Name-Value Arguments % -------------------- % mets : cell or logical or double -% either a cell array of metabolite IDs, a logical vector with the same -% number of elements as metabolites in the model, or a vector of -% indexes to add for (default model.mets). +% a cell array of metabolite ids, a logical vector with one element per metabolite of +% the model, or a vector of metabolite indexes to add reactions for +% (default model.mets). % % Returns % ------- diff --git a/manipulation/addGenesRaven.m b/src/manipulation/addGenesRaven.m similarity index 91% rename from manipulation/addGenesRaven.m rename to src/manipulation/addGenesRaven.m index cb00a033..a3e7ef37 100755 --- a/manipulation/addGenesRaven.m +++ b/src/manipulation/addGenesRaven.m @@ -1,8 +1,7 @@ function newModel=addGenesRaven(model,genesToAdd) % addGenesRaven Add genes to a model. % -% This function does not make extensive checks about MIRIAM formats, -% forbidden characters or such. +% The function checks neither MIRIAM formats nor forbidden characters in gene ids. % % Parameters % ---------- @@ -11,15 +10,15 @@ % genesToAdd : struct % the genes to add, which can have the following fields: % -% - genes : cell array with unique strings that identifies each gene. -% Only characters which are allowed in SBML ids are allowed (mainly -% a-z, 0-9 and "_"). However, there is no check for this performed, -% as it only matters if the model should be exported to SBML +% - genes : cell array with unique strings that identify each gene. +% The characters allowed in SBML ids (mainly a-z, 0-9 and "_") are the +% valid ones, but the function does not check them; they matter only for +% export to SBML % - geneShortNames : cell array of gene abbreviations (optional, % default "") % - geneMiriams : cell array with MIRIAM structures (optional, % default []) -% - proteins : cell array of protein names associated to each gene +% - proteins : cell array of the protein names associated with each gene % (optional, default "") % % Returns diff --git a/manipulation/addMets.m b/src/manipulation/addMets.m similarity index 91% rename from manipulation/addMets.m rename to src/manipulation/addMets.m index c8858de4..d9c01ec9 100755 --- a/manipulation/addMets.m +++ b/src/manipulation/addMets.m @@ -1,8 +1,7 @@ function newModel=addMets(model,metsToAdd,varargin) % addMets Add metabolites to a model. % -% This function does not make extensive checks about MIRIAM formats, -% forbidden characters or such. +% The function checks neither MIRIAM formats nor forbidden characters in metabolite ids. % % Parameters % ---------- @@ -11,13 +10,14 @@ % metsToAdd : struct % the metabolite structure, which can have the following fields: % -% - mets : cell array with unique strings that identifies each -% metabolite (optional, default IDs of new metabolites are numbered -% with the prefix defined below) -% - metNames : cell array with the names of each metabolite +% - mets : cell array with unique strings that identify each +% metabolite (optional, default: ids numbered with the prefix defined +% below) +% - metNames : cell array with the names of each metabolite (optional if +% mets is given, default: the ids) % - compartments : cell array with the compartment of each metabolite. -% Should match model.comps. If this is a string rather than a cell -% array it is assumed that all mets are in that compartment +% Each entry must match model.comps. A single string, or a cell array +% with one entry, assigns all metabolites to that compartment % - b : Nx1 or Nx2 matrix with equality constraints for each % metabolite (optional, default 0) % - unconstrained : vector describing if each metabolite is an exchange @@ -29,21 +29,19 @@ % default []) % - metCharges : metabolite charge (optional, default NaN) % - metDeltaG : Gibbs free energy of formation at biochemical standard -% condition in kJ/mole (optional, default NaN) +% condition in kJ/mol (optional, default NaN) % - metNotes : cell array with metabolite notes as strings (optional, % default "") % % Name-Value Arguments % -------------------- % copyInfo : logical -% when adding metabolites to a compartment where it previously did not -% exist, the function will copy any available annotation from the -% metabolite in another compartment (default true). +% true to copy the available annotation from the metabolite with the same name in +% another compartment of the model (default true). % prefix : char -% when metsToAdd.mets is not specified, new metabolite IDs are -% generated with the prefix specified here. If IDs with the prefix are -% already used in the model then the numbering will start from the -% highest existing integer+1 (default "m_"). +% prefix of the generated metabolite ids when metsToAdd.mets is not specified. If ids +% with the prefix exist in the model, the numbering starts at the highest +% existing number plus 1 (default "m_"). % % Returns % ------- @@ -56,8 +54,8 @@ % % Notes % ----- -% If multiple metabolites are added at once, the metMiriams cell array -% should be defined as (example with ChEBI and KEGG): +% When multiple metabolites are added at once, metMiriams is a cell array with one MIRIAM +% structure per metabolite (example with ChEBI and KEGG): % % metsToAdd.metMiriams{1} = struct("name",{{"chebi";"kegg.compound"}},... % "value",{{"CHEBI:18072";"C11821"}}); diff --git a/manipulation/addRxns.m b/src/manipulation/addRxns.m similarity index 89% rename from manipulation/addRxns.m rename to src/manipulation/addRxns.m index 574c6985..91dbe531 100755 --- a/manipulation/addRxns.m +++ b/src/manipulation/addRxns.m @@ -1,12 +1,12 @@ function newModel=addRxns(model,rxnsToAdd,varargin) % addRxns Add reactions to a model. % -% This function does not make extensive checks about formatting of -% gene-reaction rules. +% The function checks that grRules has one entry per reaction and that every gene of +% a rule is in model.genes (an error otherwise, unless allowNewGenes is true). +% standardizeGrRules then writes the rules in the standard format. % -% When adding metabolites to a compartment where they previously do not -% exist, the function will copy any available information from the -% metabolite in another compartment. +% New metabolites receive the available annotation of the metabolite with the same name +% in another compartment, when one exists. % % Parameters % ---------- @@ -15,26 +15,26 @@ % rxnsToAdd : struct % the reaction structure, which can have the following fields: % -% - rxns : cell array with unique strings that identifies each reaction +% - rxns : cell array with unique strings that identify each reaction % - equations : cell array with equation strings. Decimal coefficients % are expressed as "1.2". Reversibility is indicated by "<=>" or "=>" % - mets : (alternative to equations) cell array with the metabolites -% involved in each reaction as nested arrays. E.g.: -% {{"met1","met2"},{"met1","met3","met4"}}. In the case of one single -% reaction added, it can be a string array: {"met1","met2"} +% involved in each reaction as nested arrays, for example +% {{"met1","met2"},{"met1","met3","met4"}}. For a single added +% reaction, it can be a string array: {"met1","met2"} % - stoichCoeffs : (alternative to equations) cell array with the -% corresponding stoichiometries as nested vectors. E.g.: -% {[-1,+2],[-1,-1,+1]}. In the case of one single reaction added, it -% can be a vector: [-1,+2] +% corresponding stoichiometries as nested vectors, for example +% {[-1,+2],[-1,-1,+1]}. For a single added reaction, it can be a +% vector: [-1,+2] % - rxnNames : cell array with the names of each reaction (optional, % default "") % - lb : vector with the lower bounds (optional, default -% model.annotations.defaultLB or -inf for reversible reactions and 0 -% for irreversible when "equations" is used. When "mets" and -% "stoichCoeffs" are used it defaults for all to -% model.annotations.defaultLB or -inf) +% model.annotation.defaultLB or -inf for reversible reactions and 0 +% for irreversible reactions when "equations" is used. When "mets" and +% "stoichCoeffs" are used, the default for all reactions is +% model.annotation.defaultLB or -inf) % - ub : vector with the upper bounds (optional, default -% model.annotations.defaultUB or inf) +% model.annotation.defaultUB or inf) % - c : vector with the objective function coefficients (optional, % default 0) % - eccodes : cell array with the EC-numbers for each reaction. Delimit @@ -42,18 +42,17 @@ % - subSystems : cell array with the subsystems for each reaction % (optional, default "") % - grRules : cell array with the gene-reaction relationship for each -% reaction. E.g. "(A and B) or (C)" means that the reaction could be -% catalyzed by a complex between A & B or by C on its own. All the -% genes have to be present in model.genes. Add genes with -% addGenesRaven before calling this function if needed (optional, -% default "") +% reaction. "(A and B) or (C)" means that the reaction is +% catalysed by a complex of A and B or by C on its own. All genes must +% be present in model.genes, unless allowNewGenes is true; addGenesRaven +% adds genes (optional, default "") % - rxnMiriams : cell array with Miriam structures (optional, % default []) % - rxnComps : cell array with compartments (as in model.comps) % (optional, default {}) % - rxnNotes : cell array with reaction notes (optional, default "") % - rxnDeltaG : Gibbs free energy at biochemical standard condition in -% kJ/mole (optional, default NaN) +% kJ/mol (optional, default NaN) % - rxnReferences : cell array with reaction references (optional, % default "") % - rxnConfidenceScores : vector with reaction confidence scores @@ -62,36 +61,33 @@ % Name-Value Arguments % -------------------- % eqnType : double or char -% describes how the equation string should be interpreted (default 1). -% Accepts integer values 1/2/3 or the string aliases "id", "name", -% "name[comp]": +% the interpretation of the equation strings (default 1). The values are the +% integers 1, 2 and 3, or the string aliases "id", "name" and "name[comp]": % -% - 1 / "id" : the metabolites are matched to model.mets. New +% - 1 or "id" : the metabolites are matched to model.mets. New % metabolites (if allowed) are added to "compartment" -% - 2 / "name" : the metabolites are matched to model.metNames and all -% metabolites are assigned to "compartment". Any new metabolites that -% are added will be assigned IDs "m1", "m2"... If IDs on the same -% form are already used in the model then the numbering will start -% from the highest used integer+1 -% - 3 / "name[comp]" : the metabolites are written as "metNames[comps]". -% Only compartments in model.comps are allowed. Any new metabolites -% that are added will be assigned IDs "m1", "m2"... If IDs on the -% same form are already used in the model then the numbering will -% start from the highest used integer+1 +% - 2 or "name" : the metabolites are matched to model.metNames and all +% metabolites are assigned to "compartment". New metabolites get +% ids generated by generateNewIds with the prefix "m_" (or the +% prefix given in allowNewMets), numbered from the highest existing +% number with that prefix plus 1 +% - 3 or "name[comp]" : the metabolites are written as "metNames[comps]". +% Only compartments in model.comps are allowed. New metabolites get +% ids generated in the same way as for eqnType 2 % compartment : char -% the compartment the metabolites should be placed in when using -% eqnType=2. Must match model.comps (optional when eqnType=1 or -% eqnType=3). +% the compartment of the metabolites for eqnType=2 and of new metabolites for +% eqnType=1. Must match model.comps. Not used for eqnType=3. % allowNewMets : logical or char -% true if the function is allowed to add new metabolites. Can also be a -% string, which will be used as prefix for the new metabolite IDs. It -% is highly recommended to first add any new metabolites with addMets -% rather than automatically through this function. addMets supports -% more annotation of metabolites, allows for the use of exchange -% metabolites, and using it reduces the risk of parsing errors -% (default false). +% true to allow the function to add new metabolites. A string also allows it and +% is used as the prefix of the new metabolite ids. The function calls addMets +% with copyInfo true, so a new metabolite takes the charge and deltaG from a +% metabolite with the same name in another compartment, and InChI, SMILES, +% formula and MIRIAM annotation from it when its own are empty. When the +% annotation of new metabolites is to be set explicitly, or when they are to be +% exchange (unconstrained) metabolites, adding them with addMets first gives +% that control (default false). % allowNewGenes : logical -% true if the function is allowed to add new genes (default false). +% true to allow the function to add new genes (default false). % % Returns % ------- diff --git a/manipulation/addRxnsGenesMets.m b/src/manipulation/addRxnsGenesMets.m similarity index 83% rename from manipulation/addRxnsGenesMets.m rename to src/manipulation/addRxnsGenesMets.m index 5cb31994..79205543 100755 --- a/manipulation/addRxnsGenesMets.m +++ b/src/manipulation/addRxnsGenesMets.m @@ -1,24 +1,22 @@ function model=addRxnsGenesMets(model,sourceModel,rxns,varargin) % addRxnsGenesMets Copy reactions from a source model into another model. % -% Copies reactions from a source model to a new model, including (new) -% metabolites and genes. +% Copies reactions from a source model to a draft model, together with their new +% metabolites and genes. Reactions that are already in the draft model are skipped. % -% This function only works if the draft model and source model follow the -% same metabolite and compartment naming convention. Metabolites are only -% matched by metaboliteName[compartment]. Useful if one wants to copy -% additional reactions from source to draft after getModelFromHomology was -% used involving the same models. +% The draft model and the source model must follow the same metabolite and compartment +% naming convention, because metabolites are matched only by metaboliteName[compartment]. +% The function can copy additional reactions from a source model to a draft model that +% getModelFromHomology created from the same models. % % Parameters % ---------- % model : struct -% draft model where reactions should be copied to. +% the draft model that receives the reactions. % sourceModel : struct -% model where reactions and metabolites are sourced from. +% the model that the reactions and metabolites are copied from. % rxns : cell or char -% reaction IDs (from source model). Can also be a string if only one -% reaction is added. +% reaction ids from the source model. A string adds one reaction. % % Name-Value Arguments % -------------------- @@ -28,22 +26,21 @@ % - false : no genes are annotated to the new reactions % - true : grRules are copied from the sourceModel and new genes are % added when required -% - string or cell array : new grRules are specified as string or cell -% array, and any new genes are added when required +% - string or cell array : the grRules of the new reactions, as a string or a +% cell array, and new genes are added when required % rxnNote : cell or char -% strings explaining why reactions were copied to the model, to be -% included as newModel.rxnNotes. Can also be a string if the same -% rxnNotes should be added for each new reaction, or only one reaction -% is to be added (default "Added via addRxnsAndMets()"). +% strings that explain why the reactions were copied, stored in rxnNotes of the new +% reactions. A string applies the same note to every new reaction +% (default "Added via addRxnsGenesMets()"). % confidence : double -% integer specifying confidence score for all reactions, following +% integer confidence score for all new reactions, following % doi:10.1038/nprot.2009.203 (default 0): % % - 4 : biochemical data: direct evidence from enzyme assays % - 3 : genetic data: knockout/-in or overexpression analysis -% - 2 : physiological data: indirect evidence, e.g. secretion products +% - 2 : physiological data: indirect evidence, for example secretion products % or defined medium requirement; sequence data: genome annotation -% - 1 : modeling data: required for functional model, hypothetical +% - 1 : modelling data: required for a functional model, hypothetical % reaction % - 0 : no evidence % diff --git a/manipulation/addTransport.m b/src/manipulation/addTransport.m similarity index 88% rename from manipulation/addTransport.m rename to src/manipulation/addTransport.m index d488ce27..2fbf2538 100755 --- a/manipulation/addTransport.m +++ b/src/manipulation/addTransport.m @@ -1,20 +1,22 @@ function [model, addedRxns]=addTransport(model,fromComp,toComps,varargin) % addTransport Add transport reactions between compartments. % -% This is a faster version than addRxns when adding transport reactions. -% New reaction names are formatted as "metaboliteName, fromComp-toComp", -% while new reaction IDs are sequentially counted with a tr_ prefix: -% e.g. tr_0001, tr_0002, etc. +% The function builds the stoichiometric columns directly from the metabolite names, +% without the equation parsing of addRxns, which makes it faster for adding transport +% reactions. New reaction names +% have the format "metaboliteName transport, fromCompName-toCompName", where the +% compartment names are the entries of model.compNames. New reaction ids are numbered +% sequentially with the prefix tr_ (tr_0001, tr_0002, ...). % % Parameters % ---------- % model : struct % a model structure. % fromComp : char -% the id of the compartment to transport from (should match -% model.comps). +% the id of the compartment to transport from. Must match one entry of model.comps. % toComps : cell -% compartment names to transport to (should match model.comps). +% the ids of the compartments to transport to. Each must match an entry of +% model.comps. % % Name-Value Arguments % -------------------- @@ -24,11 +26,11 @@ % isRev : logical % true if the transport reactions should be reversible (default true). % onlyToExisting : logical -% true if transport of a metabolite should only be added if it already -% exists in toComp. If false, then new metabolites are added with -% addMets first (default true). +% true to add transport of a metabolite only if the metabolite exists in the target +% compartment. If false, the missing metabolites are first added with addMets +% (default true). % prefix : char -% prefix to reaction IDs (default "tr_"). +% prefix of the reaction ids (default "tr_"). % % Returns % ------- diff --git a/manipulation/changeGrRules.m b/src/manipulation/changeGrRules.m similarity index 79% rename from manipulation/changeGrRules.m rename to src/manipulation/changeGrRules.m index 09db0fe2..55f792ef 100755 --- a/manipulation/changeGrRules.m +++ b/src/manipulation/changeGrRules.m @@ -1,5 +1,7 @@ function model = changeGrRules(model,rxns,grRules,varargin) -% changeGrRules Change multiple grRules at the same time. +% changeGrRules Change the grRules of multiple reactions. +% +% Genes in grRules that are not in the model are added to the model. % % Parameters % ---------- @@ -8,16 +10,15 @@ % rxns : char or cell % reaction IDs. % grRules : char or cell -% additional or replacement gene association. Should be written with -% ' and ' to indicate subunits, ' or ' to indicate isoenzymes, and -% brackets '()' to separate different instances. +% the replacement or additional gene association. ' and ' indicates subunits, ' or ' +% indicates isoenzymes, and brackets '()' separate different instances. A single +% rule applies to all reactions in rxns. % % Name-Value Arguments % -------------------- % replace : logical -% true if old gene association should be replaced with new association. -% False if new gene association should be concatenated to the old -% association (default true). +% true to replace the old gene association with the new association. False to +% combine them as "(old) or (new)" (default true). % % Returns % ------- diff --git a/manipulation/changeRxns.m b/src/manipulation/changeRxns.m similarity index 64% rename from manipulation/changeRxns.m rename to src/manipulation/changeRxns.m index feca030e..dc1d4aa5 100755 --- a/manipulation/changeRxns.m +++ b/src/manipulation/changeRxns.m @@ -8,9 +8,8 @@ % rxns : cell % cell array with reaction ids. % equations : cell or struct -% cell array with equations. Alternatively, it can be a structure with -% the fields "mets" and "stoichCoeffs", in the same fashion as addRxns. -% E.g.: +% cell array with equations, or a structure with the fields "mets" and +% "stoichCoeffs" as in addRxns, for example: % % - equations.mets = {{"met1","met2"},{"met1","met3"}} % - equations.stoichCoeffs = {[-1,+2],[-1,+1]} @@ -18,30 +17,28 @@ % Name-Value Arguments % -------------------- % eqnType : double -% describes how the equation string should be interpreted (default 1): +% the interpretation of the equation strings (default 1): % % - 1 : the metabolites are matched to model.mets. New metabolites (if % allowed) are added to "compartment". % - 2 : the metabolites are matched to model.metNames and all -% metabolites are assigned to "compartment". Any new metabolites that -% are added will be assigned IDs "m1", "m2"... If IDs on the same form -% are already used in the model then the numbering will start from the -% highest used integer+1. -% - 3 : the metabolites are written as "metNames[compNames]". Only -% compartments in model.compNames are allowed. Any new metabolites -% that are added will be assigned IDs "m1", "m2"... If IDs on the same -% form are already used in the model then the numbering will start -% from the highest used integer+1. +% metabolites are assigned to "compartment". New metabolites get +% ids generated by generateNewIds with the prefix "m_", numbered from the +% highest existing number with that prefix plus 1. +% - 3 : the metabolites are written as "metNames[comps]". Only +% compartments in model.comps are allowed. New metabolites get ids +% generated in the same way as for eqnType 2. % compartment : char -% a string with the compartment the metabolites should be placed in when -% using eqnType=2. Must match model.compNames (optional when eqnType=1 or -% eqnType=3). +% the compartment of the metabolites for eqnType=2 and of new metabolites for +% eqnType=1. Must match model.comps. Not used for eqnType=3. % allowNewMets : logical -% true if the function is allowed to add new metabolites. It is highly -% recommended to first add any new metabolites with addMets rather than -% automatically through this function. addMets supports more annotation -% of metabolites, allows for the use of exchange metabolites, and using -% it reduces the risk of parsing errors (default false). +% true to allow the function to add new metabolites. The function calls addMets +% with copyInfo true, so a new metabolite takes the charge and deltaG from a +% metabolite with the same name in another compartment, and InChI, SMILES, +% formula and MIRIAM annotation from it when its own are empty. When the +% annotation of new metabolites is to be set explicitly, or when they are to be +% exchange (unconstrained) metabolites, adding them with addMets first gives +% that control (default false). % % Returns % ------- @@ -54,16 +51,13 @@ % % Notes % ----- -% This function should be used with some care, since it does not care about -% bounds on the reactions. Changing an irreversible reaction to a reversible -% one (or the other way around) will only change the model.rev field and not -% the model.lb/model.ub fields. The reaction will therefore still be having -% the same reversibility because of the bounds. Use setParams to change the -% bounds. +% The function keeps the bounds of the reactions. Changing an irreversible reaction to a +% reversible one, or the reverse, changes the model.rev field but not model.lb and +% model.ub, so the bounds still define the reversibility of the reaction. setParam +% changes the bounds. % -% When adding metabolites to a compartment where it previously does not -% exist, the function will copy any available information from the metabolite -% in another compartment. +% New metabolites receive the annotation of the metabolite with the same name in +% another compartment, when one exists (see allowNewMets). p=parseRAVENargs(varargin, {'eqnType',1; 'compartment',[]; 'allowNewMets',false}); eqnType=p.eqnType; diff --git a/manipulation/closeModel.m b/src/manipulation/closeModel.m similarity index 83% rename from manipulation/closeModel.m rename to src/manipulation/closeModel.m index a83ecdff..d3bea092 100755 --- a/manipulation/closeModel.m +++ b/src/manipulation/closeModel.m @@ -1,7 +1,13 @@ function closedModel=closeModel(model) -% closeModel Add boundary metabolites and their exchange reactions. +% closeModel Add boundary metabolites to exchange reactions. % -% Adds boundary metabolites and their participation in exchange reactions. +% Adds a compartment "b" (boundary) and a boundary metabolite in it for every reaction +% that has metabolites on one side only (exchange, sink and demand reactions). The +% boundary metabolite takes part in the reaction with the coefficient 1 or -1 and is +% marked as unconstrained in model.unconstrained; all other metabolites are marked as +% constrained. A reaction with one metabolite gets the boundary metabolite "_b" with +% the annotation of that metabolite; a reaction with several metabolites gets +% "_b" without annotation. % % Parameters % ---------- @@ -30,15 +36,9 @@ col=closedModel.S(:,i); %A boundary/exchange/sink/demand reaction has metabolites on only one %side, same definition as getExchangeRxns: no substrates or no - %products. The previous coefficient-magnitude rule (all nonzero - %coefficients summing to 1 in absolute value) missed a scaled - %single-metabolite sink such as "2 A =>" (sum 2) outright, and only - %matched a multi-metabolite one-sided reaction such as - %"0.5 A + 0.5 B =>" by coincidence, when its magnitudes summed to - %exactly 1 --- and even then mishandled it below, since find() on such - %a reaction returns more than one index and only the first metabolite - %was ever used to build the boundary metabolite, silently dropping the - %rest. + %products. The coefficient magnitudes do not matter, so a scaled sink + %such as "2 A =>" and a multi-metabolite one-sided reaction such as + %"0.5 A + 0.5 B =>" are both boundary reactions. if any(col>0) && any(col<0) continue end @@ -47,7 +47,7 @@ continue end %A single metabolite names and inherits properties from that - %metabolite, as before. Several metabolites on the one populated side + %metabolite. Several metabolites on the one populated side %have no single metabolite to name the boundary after or copy %properties from, so it is named after the reaction instead and left %without those properties. diff --git a/manipulation/contractModel.m b/src/manipulation/contractModel.m similarity index 82% rename from manipulation/contractModel.m rename to src/manipulation/contractModel.m index cc0996f9..a0e58b68 100755 --- a/manipulation/contractModel.m +++ b/src/manipulation/contractModel.m @@ -1,14 +1,14 @@ function [reducedModel, removedRxns, indexedDuplicateRxns]=contractModel(model,varargin) -% contractModel Contract a model by grouping all identical reactions. +% contractModel Contract a model by merging identical reactions. % -% This is what simplifyModel's deleteDuplicates option runs; it is not a -% separate implementation. If the duplicated reactions have "_EXP_*" suffixes -% (where * is a digit), then the model is assumed to have been passed through -% expandModel, and these suffixes are removed here. +% simplifyModel runs contractModel for its deleteDuplicates option. If the duplicated +% reactions have "_EXP_*" suffixes (where * is a digit), the model has passed through +% expandModel, and the function removes these suffixes. % -% Reactions are merged, so their gene associations are combined rather than -% discarded. Use findDuplicateRxns instead to see which reactions are -% duplicates without changing the model. +% The gene associations and EC numbers of merged reactions are combined. The merged +% reaction keeps the widest bounds and the largest objective coefficient, and a warning +% is given when the duplicates differ in these. findDuplicateRxns lists duplicate +% reactions without changing the model. % % Parameters % ---------- @@ -18,22 +18,22 @@ % Name-Value Arguments % -------------------- % distReverse : logical -% distinguish reactions with same metabolites but different reversibility -% as different reactions (default true). +% true to treat reactions with the same metabolites but different reversibility as +% different reactions (default true). % mets : char or cell -% string or cell array of strings with metabolite identifiers, whose -% involved reactions should be checked for duplication (by default all -% reactions are considered). This option is used by replaceMets. +% metabolite ids as a string or a cell array of strings; only the reactions that +% involve these metabolites are checked for duplication (default: all reactions). +% replaceMets uses this option. % % Returns % ------- % reducedModel : struct % a model structure without duplicate reactions. % removedRxns : cell -% cell array for the removed duplicate reactions. +% cell array with the ids of the removed duplicate reactions. % indexedDuplicateRxns : cell -% indexed cell array for the removed duplicate reactions (multiple values -% separated by semicolon). +% cell array with one entry per reaction of reducedModel, holding the ids of the +% duplicates removed into that reaction (multiple ids separated by semicolon). % % Examples % -------- @@ -41,8 +41,7 @@ % % Notes % ----- -% This code might not work for advanced grRules strings that involve nested -% expressions of "and" and "or". +% The function may not work for grRules strings with nested expressions of "and" and "or". p=parseRAVENargs(varargin, {'distReverse',true; 'mets',[]}); distReverse=p.distReverse; @@ -50,7 +49,7 @@ %First sort the model so that reversible reactions are in the same %direction -modelS=sortModel(model); +modelS=sortModel(model,true); %Get a list of duplicate reactions if distReverse diff --git a/manipulation/convertToIrrev.m b/src/manipulation/convertToIrrev.m similarity index 96% rename from manipulation/convertToIrrev.m rename to src/manipulation/convertToIrrev.m index faef2815..fe29a9ae 100755 --- a/manipulation/convertToIrrev.m +++ b/src/manipulation/convertToIrrev.m @@ -2,8 +2,8 @@ % convertToIrrev Convert a model to irreversible form. % % Reversible reactions are split into one forward and one reverse -% reaction. The reverse reactions are saved as 'rxnID_REV'. A warning is -% shown if some reaction identifiers already end with '_REV'. +% reaction. The reverse reactions are named 'rxnID_REV'. A warning is +% shown if some reaction ids already end with '_REV'. % % Parameters % ---------- @@ -13,7 +13,7 @@ % Name-Value Arguments % -------------------- % rxns : cell -% cell array with the reactions to split, if reversible (default +% cell array with the ids of the reactions to split, if reversible (default % model.rxns). % % Returns diff --git a/manipulation/copyToComps.m b/src/manipulation/copyToComps.m similarity index 80% rename from manipulation/copyToComps.m rename to src/manipulation/copyToComps.m index 72a423f2..fdc81131 100755 --- a/manipulation/copyToComps.m +++ b/src/manipulation/copyToComps.m @@ -6,26 +6,22 @@ % model : struct % a model structure. % toComps : cell -% cell array of compartment ids. If there is no match to model.comps -% then it is added as a new compartment (see compNames and -% compOutside). +% cell array of compartment ids. An id that is not in model.comps is added as a +% new compartment (see compNames and compOutside). % % Name-Value Arguments % -------------------- % rxns : cell or logical or double -% either a cell array of reaction IDs, a logical vector with the same -% number of elements as reactions in the model, or a vector of indexes -% to copy (default model.rxns). +% a cell array of reaction ids, a logical vector with one element per reaction of +% the model, or a vector of reaction indexes to copy (default model.rxns). % deleteOriginal : logical -% true if the original reactions should be removed, making it move the -% reactions instead (default false). +% true to remove the original reactions, which moves the reactions (default false). % compNames : cell -% cell array of compartment names. Used if new compartments should be -% added (default toComps). +% cell array of compartment names. Used when new compartments are added +% (default toComps). % compOutside : cell -% cell array of the id (as in comps) for the compartment surrounding -% each of the compartments. Used if new compartments should be added -% (default all {''}). +% cell array with the id (as in comps) of the compartment that surrounds each new +% compartment. Used when new compartments are added (default all {''}). % % Returns % ------- @@ -38,7 +34,7 @@ % % Notes % ----- -% New reactions and metabolites will be named as "id_toComps(i)". +% New reactions and metabolites have the ids "id_toComps(i)". p=parseRAVENargs(varargin, {'rxns',[]; 'deleteOriginal',false; 'compNames',[]; 'compOutside',[]}); rxns=p.rxns; diff --git a/manipulation/deleteUnusedGenes.m b/src/manipulation/deleteUnusedGenes.m similarity index 100% rename from manipulation/deleteUnusedGenes.m rename to src/manipulation/deleteUnusedGenes.m diff --git a/manipulation/expandModel.m b/src/manipulation/expandModel.m similarity index 87% rename from manipulation/expandModel.m rename to src/manipulation/expandModel.m index f3fb2498..441b273f 100755 --- a/manipulation/expandModel.m +++ b/src/manipulation/expandModel.m @@ -1,8 +1,8 @@ function [newModel, rxnToCheck]=expandModel(model) % expandModel Expand reactions that use several gene associations. % -% Each reaction that uses several gene associations is split into several -% reactions, each under the control of only one gene. +% Each reaction with several gene associations (isozymes) is split into one reaction per +% association. % % Parameters % ---------- @@ -12,12 +12,12 @@ % Returns % ------- % newModel : struct -% model structure with separate reactions for iso-enzymes, where the -% reaction ids are renamed as id_EXP_1, id_EXP_2, etc. +% model structure with separate reactions for isozymes. The ids of split reactions +% are id_EXP_1, id_EXP_2 and so on, numbered per isozyme. % rxnToCheck : cell -% cell array with original reaction identifiers for those whose grRule -% was not in disjunctive normal form, and so had to be expanded by -% distributivity, plus any whose grRule could not be parsed. +% cell array with the original ids of the reactions whose grRule is not in +% disjunctive normal form and was expanded by distributivity, and of the reactions +% whose grRule cannot be parsed and is left unchanged. % % Examples % -------- @@ -26,11 +26,11 @@ % Notes % ----- % The isozymes are the AND-clauses of the grRule in disjunctive normal form, -% so nested expressions are expanded correctly: "g1 and (g2 or g3)" yields -% "g1 and g2" and "g1 and g3". Reactions listed in rxnToCheck are expanded -% correctly too; they are reported only because a rule needing -% distributivity is often a sign that the GPR was not what its author -% intended. Use standardizeGrRules/findPotentialErrors to inspect them. +% so nested expressions are expanded: "g1 and (g2 or g3)" yields "g1 and g2" and +% "g1 and g3". Reactions listed in rxnToCheck because of distributivity are expanded in +% the same way; they are listed because a rule that needs distributivity can indicate +% that the GPR differs from what its author intended. standardizeGrRules and +% findPotentialErrors inspect them. %Work out the isozymes of every reaction up front. The number of copies is %the number of DNF clauses, which is not the number of ' or ' substrings: diff --git a/manipulation/findDuplicateRxns.m b/src/manipulation/findDuplicateRxns.m similarity index 79% rename from manipulation/findDuplicateRxns.m rename to src/manipulation/findDuplicateRxns.m index be0a18ac..d0a3c928 100644 --- a/manipulation/findDuplicateRxns.m +++ b/src/manipulation/findDuplicateRxns.m @@ -1,21 +1,19 @@ function pairs = findDuplicateRxns(model, varargin) % findDuplicateRxns Find reactions that share identical stoichiometry. % -% The upstream version of yeast-GEM's findDuplicatedRxns. -% -% Only stoichiometry is compared — bounds, GPRs, and annotations are -% ignored. The default treats A→B and B→A as duplicates (typical curation -% use case: "find reactions that could be merged"). +% Only the stoichiometry is compared; bounds, GPRs and annotations are ignored. With +% the default ignoreDirection, A→B and B→A are duplicates. The pairs identify +% reactions that could be merged (contractModel merges duplicate reactions). % % Parameters % ---------- % model : struct -% RAVEN model struct. +% a model structure. % % Name-Value Arguments % -------------------- % ignoreDirection : logical -% treat A→B and B→A as duplicates (default true). +% true to treat A→B and B→A as duplicates (default true). % % Returns % ------- diff --git a/manipulation/findPotentialErrors.m b/src/manipulation/findPotentialErrors.m similarity index 64% rename from manipulation/findPotentialErrors.m rename to src/manipulation/findPotentialErrors.m index 48960d9a..b4d8780e 100644 --- a/manipulation/findPotentialErrors.m +++ b/src/manipulation/findPotentialErrors.m @@ -1,11 +1,10 @@ function issues=findPotentialErrors(model) % findPotentialErrors Identify reactions with non-DNF GPR rules. % -% Detects grRules that are not in disjunctive normal form (DNF) — -% i.e. rules where an OR operator is nested inside an AND context. -% Standard RAVEN format requires DNF: groups of genes that must all be -% present together (enzyme complexes) are written as conjunctions, and -% the reaction is catalysed by at least one complex (disjunction of +% Detects grRules that are not in disjunctive normal form (DNF), that is, rules in +% which an OR operator is nested inside an AND context. The RAVEN format requires DNF: +% groups of genes that must all be present together (enzyme complexes) are written as +% conjunctions, and the reaction is catalysed by at least one complex (a disjunction of % conjunctions). % % Parameters @@ -16,23 +15,22 @@ % Returns % ------- % issues : struct -% struct array — one element per problematic reaction — with fields: +% struct array with one element per problematic reaction and the fields: % -% - index : double — 1-based index into model.rxns. -% - rxn : char — reaction ID. -% - grRule : char — the problematic grRule string. -% - reason : char — short explanation. +% - index : double, 1-based index into model.rxns. +% - rxn : char, reaction id. +% - grRule : char, the problematic grRule string. +% - reason : char, short explanation. % % Notes % ----- -% Detection walks the parse tree from parseGrRule rather than searching the -% raw string, so gene IDs that contain substrings such as "and" or "or" do -% not cause false positives or negatives. +% Detection walks the parse tree from parseGrRule, so gene ids that contain substrings +% such as "and" or "or" are not misread as operators. % % A rule is non-DNF when an AND operator has an OR anywhere beneath it. -% Bracketing alone never makes a rule non-DNF: "((G1 and G2) or G3)" and -% "(G1 or G2) or (G3 and G4)" are both fine. -% Use standardizeGrRules to attempt automatic repair. +% Bracketing alone does not make a rule non-DNF: "((G1 and G2) or G3)" and +% "(G1 or G2) or (G3 and G4)" are both in DNF. standardizeGrRules attempts an +% automatic repair. % % Rules that cannot be parsed at all are reported as issues too, with the % parser's message as the reason. diff --git a/manipulation/generateNewIds.m b/src/manipulation/generateNewIds.m similarity index 57% rename from manipulation/generateNewIds.m rename to src/manipulation/generateNewIds.m index cf16c6c3..ca6e157a 100755 --- a/manipulation/generateNewIds.m +++ b/src/manipulation/generateNewIds.m @@ -1,8 +1,11 @@ function newIds=generateNewIds(model,type,prefix,varargin) % generateNewIds Generate a list of new metabolite or reaction ids. % -% The ids are sequentially numbered with a defined prefix. The model is -% queried for the highest existing number of that type of id. +% The ids are numbered sequentially with a defined prefix. The highest existing number +% among the ids of that type with the prefix is read from the model. Suffixes are +% read with str2double. A suffix counts as a number when the result is a +% finite integer value below 2^52; text, Inf, NaN, fractions (r_2.5) and +% numbers of 2^52 or more (r_1e300, r_1e400) do not count. % % Parameters % ---------- @@ -11,17 +14,16 @@ % type : char % type of id, 'rxns' or 'mets'. % prefix : char -% prefix to be used in all ids, e.g. 's_' or 'r_'. +% prefix to be used in all ids, for example 's_' or 'r_'. % % Name-Value Arguments % -------------------- % quantity : double -% number of new ids that should be generated (default 1). +% number of new ids to generate (default 1). % numLength : double -% length of the numerical part of the id. E.g. 4 gives ids like r_0001 -% and 6 gives ids like r_000001. If the prefix is already used in the -% model, then the model-defined length will be used instead -% (default 4). +% length of the numerical part of the id; 4 gives ids like r_0001 and 6 gives ids like +% r_000001. If the prefix is used in the model, the length of the existing ids +% applies instead (default 4). % % Returns % ------- @@ -52,10 +54,19 @@ if ~isempty(existingIds) existingIds=regexprep(existingIds,['^' escapedPrefix],''); - existingIds=sort(existingIds); - lastId=existingIds{end}; - numLength=length(lastId); - lastId=str2double(lastId); + numbers=str2double(existingIds); + notInteger=~isfinite(numbers) | imag(numbers)~=0 | real(numbers)~=floor(real(numbers)) | abs(real(numbers))>=flintmax/2; + numbers=real(numbers); + numbers(notInteger)=NaN; + if all(isnan(numbers)) + [~,I]=sort(existingIds); + lastId=existingIds{I(end)}; + numLength=length(lastId); + lastId=NaN; + else + [lastId,I]=max(numbers); + numLength=length(existingIds{I}); + end else lastId=0; fprintf(['No ' type ' ids with prefix "' prefix ... diff --git a/manipulation/mergeCompartments.m b/src/manipulation/mergeCompartments.m similarity index 62% rename from manipulation/mergeCompartments.m rename to src/manipulation/mergeCompartments.m index 80c7982e..42de7294 100755 --- a/manipulation/mergeCompartments.m +++ b/src/manipulation/mergeCompartments.m @@ -9,28 +9,29 @@ % Name-Value Arguments % -------------------- % keepUnconstrained : logical -% keep metabolites that are unconstrained in a "unconstrained" -% compartment. If these are merged the exchange reactions will most often -% be deleted (default false). +% true to keep the unconstrained metabolites in a separate compartment "b" +% (name "Unconstrained"). If they are merged, an exchange reaction between an +% unconstrained metabolite and a metabolite of the same name has no net +% stoichiometry after merging and is deleted (default false). % deleteRxnsWithOneMet : logical -% delete reactions with only one metabolite. These reactions come from -% reactions such as A[c] + B[c] => A[m]. In some models hydrogen is -% balanced around each membrane with reactions like this (default -% false). +% true to delete reactions that have one metabolite after merging and had more +% before. Such reactions arise from reactions such as A[c] + B[c] => A[m]. Some +% models balance hydrogen around each membrane with reactions like this +% (default false). % distReverse : logical -% distinguish reactions with same metabolites but different reversibility -% as different reactions (default true). +% true to treat reactions with the same metabolites but different reversibility as +% different reactions (default true). % % Returns % ------- % model : struct -% a model with all reactions located to one compartment. +% a model with all reactions in one compartment. % deletedRxns : cell -% reactions that were deleted because of only having one metabolite -% after merging. +% the ids of the reactions deleted because they have one metabolite after merging. % duplicateRxns : cell -% identical reactions that occurred in different compartments and were -% deleted because they turned to be duplicated after merging. +% one entry per reaction of the returned model: '' when no duplicate was merged +% into it, otherwise the ids of the identical reactions from different +% compartments that were removed into it, joined by ";". % % Examples % -------- @@ -38,12 +39,16 @@ % % Notes % ----- -% Merges all compartments into one "s" compartment (for "System"). This can -% be useful for example to ensure that there are metabolic capabilities to -% synthesize all metabolites. +% Merges all compartments into one compartment "s" (name "System"). The merged model +% can be used to test whether the network can synthesise all metabolites. % -% If the metabolite IDs reflect the compartment that they are in the IDs may -% no longer be representative. +% Metabolites with the same name are merged into one. The merged metabolite +% keeps the id and the other fields of one of the copies: the copy that is +% already in compartment "s" if there is one, otherwise the copy whose id +% comes first in character code order (for example ACCOA_c before ACCOA_m). +% With keepUnconstrained only the copies that are not unconstrained are +% candidates. If the metabolite ids contain the compartment, the ids of merged +% metabolites can name a compartment that no longer exists. p=parseRAVENargs(varargin, {'keepUnconstrained',false; 'deleteRxnsWithOneMet',false; 'distReverse',true}); keepUnconstrained=p.keepUnconstrained; @@ -66,9 +71,8 @@ end end -%Loop through each metabolite, and if it is not unconstrained then change -%the S matrix to use the metabolite with the lowest index in model.comps -%instead +%Loop through each metabolite name, and if a copy is not unconstrained then +%change the S matrix to use the copy chosen as described in the Notes %Group metabolite indices by name once, rather than re-scanning all of %model.metNames (via ismember) for every unique name, which is quadratic %in a genome-scale model's (unique names x mets) count. @@ -79,19 +83,26 @@ I=false(numel(model.metNames),1); I(sameNameIdx{i})=true; - %Find the first of those that is not unconstrained. This is the one - %that the other "un-unconstrained" should be changed to. + %Choose the copy that the other "un-unconstrained" copies are merged + %into: one already in compartment "s" if there is one, otherwise the + %one whose id sorts first. if keepUnconstrained==true - mergeTo=find(I & model.unconstrained==false,1); + candidates=find(I & model.unconstrained==false); %This could happen if there is only one metabolite and it is %unconstrained - if isempty(mergeTo) + if isempty(candidates) continue; end else - mergeTo=find(I,1); + candidates=find(I); end + inMerged=candidates(strcmp(model.comps(model.metComps(candidates)),'s')); + if ~isempty(inMerged) + candidates=inMerged; + end + [~,first]=sort(model.mets(candidates)); + mergeTo=candidates(first(1)); I(mergeTo)=false; %Do not do anything for itself I=find(I); diff --git a/manipulation/mergeModels.m b/src/manipulation/mergeModels.m similarity index 96% rename from manipulation/mergeModels.m rename to src/manipulation/mergeModels.m index 5ece7c76..411a161f 100755 --- a/manipulation/mergeModels.m +++ b/src/manipulation/mergeModels.m @@ -1,10 +1,11 @@ function model=mergeModels(models,varargin) % mergeModels Merge models into one model structure. % -% Merges models into one model structure. Reactions are added without any -% checks, so duplicate reactions might appear. Metabolites are matched by -% their name and compartment (metaboliteName[comp]), while genes are matched -% by their name. +% Merges models into one model structure. Reactions are added without a check for +% duplicate reactions, so duplicates can appear. A reaction id that already exists in +% the merged model is renamed by appending "_" and the id of its source model. +% Metabolites are matched by name and compartment (metaboliteName[comp]) and genes by +% name. % % Parameters % ---------- @@ -14,23 +15,22 @@ % Name-Value Arguments % -------------------- % metParam : char -% string, metabolite name ("metNames") or ID ("mets") are used for -% matching (default "metNames"). +% the metabolite field used for matching: "metNames" (name and compartment) or +% "mets" (id) (default "metNames"). % supressWarnings : logical -% whether warnings should be supressed (default false). +% true to suppress warnings (default false). % copyToComps : logical -% internal flag used by copyToComps.m when it calls mergeModels to -% duplicate a model into a new compartment; leave at default when -% calling mergeModels directly (default false). +% internal flag set by copyToComps when it calls mergeModels to duplicate a model into +% a new compartment. Left at the default when calling mergeModels directly +% (default false). % % Returns % ------- % model : struct -% a model structure with the merged model. Follows the structure of -% normal models but also has "rxnFrom/metFrom/geneFrom" fields to -% indicate from which model each reaction/metabolite/gene was taken. If -% the model already has "rxnFrom/metFrom/geneFrom" fields, then these -% fields are not modified. +% the merged model structure. In addition to the usual fields, it has the fields +% rxnFrom, metFrom and geneFrom, which hold the id of the model that each reaction, +% metabolite and gene was taken from. If a model already has these fields, its +% entries are not modified. % % Examples % -------- diff --git a/manipulation/permuteModel.m b/src/manipulation/permuteModel.m similarity index 100% rename from manipulation/permuteModel.m rename to src/manipulation/permuteModel.m diff --git a/manipulation/removeBadRxns.m b/src/manipulation/removeBadRxns.m similarity index 72% rename from manipulation/removeBadRxns.m rename to src/manipulation/removeBadRxns.m index 17a891a8..382ebf70 100755 --- a/manipulation/removeBadRxns.m +++ b/src/manipulation/removeBadRxns.m @@ -1,87 +1,77 @@ function [newModel, removedRxns]=removeBadRxns(model,varargin) -% removeBadRxns Remove reactions that enable production/consumption from nothing. +% removeBadRxns Remove reactions that enable production or consumption from nothing. % -% Iteratively removes reactions which enable production/consumption of some -% metabolite without any uptake/excretion. +% Iteratively removes reactions that enable production or consumption of a metabolite +% without any uptake or excretion. % % Parameters % ---------- % model : struct -% a model structure. For the intended function, the model should not -% allow for any uptake/excretion. The easiest way to achieve this is to -% import the model using importModel("filename", false). +% a model structure. The model must not allow any uptake or excretion; the function +% warns about open exchange reactions when the model has no unconstrained field. +% importModel("filename", false) imports a model without uptake and excretion. % % Name-Value Arguments % -------------------- % rxnRules : double % which reactions may be removed (default 1): % -% - 1 : only remove reactions which are unbalanced -% - 2 : also remove reactions which could not be checked for mass -% balancing -% - 3 : all reactions can be removed +% - 1 : remove only unbalanced reactions +% - 2 : also remove reactions that could not be checked for mass +% balance +% - 3 : any reaction can be removed % ignoreMets : cell or logical or double -% either a cell array of metabolite IDs, a logical vector with the same -% number of elements as metabolites in the model, or a vector of indexes -% for metabolites to exclude from this analysis (default []). +% a cell array of metabolite ids, a logical vector with one element per metabolite +% of the model, or a vector of metabolite indexes to exclude from the analysis +% (default []). % isNames : logical -% true if the supplied mets represent metabolite names (as opposed to -% IDs). This is a way to delete metabolites in several compartments at -% once without knowing the exact IDs. This only works if ignoreMets is a +% true if ignoreMets holds metabolite names (false: ids), which excludes the +% metabolites of that name in all compartments. Applies only if ignoreMets is a % cell array (default false). % balanceElements : cell -% a cell array with the elements for which to balance the reactions. May -% contain any combination of the elements defined in parseFormulas -% (default {"C";"P";"S";"N";"O"}). +% a cell array with the elements for which to balance the reactions, chosen from +% the elements defined in parseFormulas (default {"C";"P";"S";"N";"O"}). % refModel : struct -% a reference model which can be used to ensure that the resulting model -% is still functional. The intended use is that the reference model is a -% copy of model, but with uptake/excretion allowed and some objectives -% (such as production of biomass) constrained to a non-zero flux. Before -% a reaction is removed from "model" the function first checks that the -% same deletion in "refModel" does not render the problem unfeasible. +% a reference model, intended as a copy of model with uptake and excretion allowed +% and some objectives (such as production of biomass) constrained to a non-zero +% flux. A supplied reference model triggers a warning that the feature is not +% supported, followed by an error. % ignoreIntBounds : logical -% true if internal bounds (including reversibility) should be ignored. -% Exchange reactions are not affected. This can be used to find -% unbalanced solutions which are not possible using the default -% constraints (default false). +% true to ignore internal bounds (including reversibility). Exchange reactions are +% not affected. Ignoring the bounds finds unbalanced solutions that the default +% constraints do not allow (default false). % printReport : logical -% true if a report should be printed (default false). +% true to print a report (default false). % seed : double -% seed for the random number generator, so that which reaction is -% removed among several equally-valid candidates is reproducible -% across runs (default: current rng state is left untouched). +% seed for the random number generator. A seed makes the choice among several +% candidate reactions reproducible (default: the current rng state is not +% changed). % % Returns % ------- % newModel : struct -% a model structure after the problematic reactions have been deleted. +% the model structure without the problematic reactions. % removedRxns : cell -% a cell array with the reactions that were removed. +% a cell array with the ids of the removed reactions. % % Notes % ----- -% The purpose of this function is to remove reactions which enable -% production/consumption of metabolites even when exchange reactions are not -% used. Many models, especially if they are automatically inferred from -% databases, will have unbalanced reactions which allow for -% net-production/consumption of metabolites without any consumption or -% excretion. A common reason for this is when general compounds have -% different meaning in different reactions (as DNA has in these two -% reactions): +% The function removes reactions that enable production or consumption of metabolites +% when no exchange reaction is used. Models inferred automatically from databases can +% contain unbalanced reactions that allow net production or consumption of metabolites +% without any consumption or excretion. One cause is a general compound that has a +% different meaning in different reactions (as DNA has in these two reactions): % % dATP + dGTP + dCTP + dTTP <=> DNA + 4 PPi % 0.25 dATP + 0.25 dGTP + 0.25 dCTP + 0.25 dTTP <=> DNA + PPi % -% Reactions that are problematic like this are always elementally -% unbalanced, but it is not always the case that you would like to exclude -% all unbalanced reactions from your model. This function tries to remove as -% few problematic reactions as possible so that the model cannot -% produce/consume anything from nothing. This is done by repeatedly calling -% findLeakMetabolite, checking if any of the involved reactions are -% elementally unbalanced, removing one of them, and then iterating until no -% metabolites can be produced/consumed. The 'produce' direction is run -% before the 'consume' one. +% Such reactions are elementally unbalanced, but the model can contain further +% unbalanced reactions that are not problematic. The function removes as few reactions +% as possible so that the model cannot produce or consume anything from nothing. It +% calls findLeakMetabolite repeatedly, selects at random one of the elementally +% unbalanced reactions that carry flux in the solution, removes it, and iterates until +% no metabolite can be produced or consumed. The 'produce' direction runs before the +% 'consume' direction. % % Examples % -------- diff --git a/manipulation/removeGenes.m b/src/manipulation/removeGenes.m similarity index 84% rename from manipulation/removeGenes.m rename to src/manipulation/removeGenes.m index e2a50f32..5c215b9b 100755 --- a/manipulation/removeGenes.m +++ b/src/manipulation/removeGenes.m @@ -12,18 +12,28 @@ % Name-Value Arguments % -------------------- % removeUnusedMets : logical -% remove metabolites that are no longer in use (default false). +% remove metabolites that no remaining reaction uses (default false). % removeBlockedRxns : logical -% remove reactions that get blocked after deleting the genes (default -% false). +% remove reactions that cannot carry flux after the genes are deleted. +% When false, the bounds of these reactions are set to 0 (default false). % standardizeRules : logical -% format gene rules to be compliant with the standard format (default -% true). +% write the gene rules in the standard format (default true). % % Returns % ------- % reducedModel : struct -% an updated model structure. +% an updated model structure. Genes in genesToRemove that are not in the +% model are ignored. When none of them is in the model, no gene, reaction +% or bound changes (with standardizeRules true the gene rules are still +% written in the standard format). Otherwise, besides genesToRemove, every +% gene that no remaining reaction used before the removal is deleted as +% well, also when it was not requested (for example a gene that no +% reaction uses). A gene that only occurs in a rule part removed with +% another gene stays, because the reaction used it before the removal +% (for example G3 in (G1 and G3) or G2 after removing G1). A gene also +% stays when its reaction remains, even if that reaction is blocked +% (removeBlockedRxns false), such as the other subunit of a complex that +% lost one. % % Examples % -------- diff --git a/manipulation/removeLowScoreGenes.m b/src/manipulation/removeLowScoreGenes.m similarity index 84% rename from manipulation/removeLowScoreGenes.m rename to src/manipulation/removeLowScoreGenes.m index df2378d8..160b81ef 100644 --- a/manipulation/removeLowScoreGenes.m +++ b/src/manipulation/removeLowScoreGenes.m @@ -1,10 +1,10 @@ function [newModel,remGenes] = removeLowScoreGenes(model,geneScores,varargin) -% removeLowScoreGenes Remove low-scoring genes from model. +% removeLowScoreGenes Remove low-score genes from a model. % -% This function removes genes from a model based on their scores, a step used -% by the tINIT package. The function recognizes and differentiates between -% isozymes and subunits of an enzyme complex. Genes are removed from each -% grRule, subject to the following conditions: +% Removes genes from a model based on their scores, a step used by the tINIT +% package. The function distinguishes isozymes from subunits of an enzyme +% complex. Genes are removed from each grRule, subject to the following +% conditions: % % 1) At least one gene must remain associated with the reaction. % 2) Genes involved in a complex (joined by ANDs) are not removed. @@ -14,17 +14,18 @@ % model : struct % model structure from which genes are to be removed. % geneScores : double -% a vector of scores associated with the model genes. Genes with a -% positive score will remain in the model, whereas genes with a negative -% score will try to be removed. +% a vector of scores associated with the model genes, with the same +% dimensions as model.genes. Genes with a score of zero or above and genes +% without a score (NaN) remain in the rules; a gene that no rule uses +% afterwards is removed from model.genes. Genes with a negative score +% are removed from the rules when the conditions above allow it. % -% If all genes associated with a reaction have a negative score, then the -% least-negative gene will remain; if there is a tie, one will be selected -% at random. +% If all genes associated with a reaction have a negative score, the +% least-negative gene remains; in a tie, the gene listed first in the rule +% is kept. % -% If a negative-scoring gene is a subunit in a complex, it will not be -% removed; however, the entire complex may be removed. See the following -% example cases: +% A negative-scoring gene that is a subunit in a complex is not removed, +% but the entire complex may be removed. Example cases: % % - Original: G1 or (G2 and G3 and G4); Negative: G1, G2; New: G2 and G3 % and G4. @@ -36,21 +37,23 @@ % Name-Value Arguments % -------------------- % isozymeScoring : char -% method used to combine the scores of multiple isozymes; "min", "max", -% "median", or "average" (default "max"). +% method used to combine the scores of multiple isozymes: "min", "max", +% "median" or "average" (default "max"). % complexScoring : char % method used to combine the scores of enzyme complex subunits: "min", -% "max", "median", or "average" (default "min"). +% "max", "median" or "average" (default "min"). % % Returns % ------- % newModel : struct -% model with updated gene, grRules, and rxnGeneMat fields after attempting -% to remove negative-score genes. +% model with updated genes, grRules and rxnGeneMat fields, and with the +% gene-associated fields (geneShortNames, proteins, geneMiriams, geneFrom, +% geneComps) trimmed to the remaining genes. % remGenes : cell -% a list of negative-score genes that were fully removed from the model. -% Negative-score genes that were removed from some grRules but not all -% will not be included in this list. +% the genes removed from model.genes: negative-score genes removed from every +% grRule and genes that no rule uses, whatever their score. Negative-score +% genes that are removed from some grRules but not all are not included in +% this list. p=parseRAVENargs(varargin, {'isozymeScoring',[]; 'complexScoring','min'}); @@ -149,7 +152,7 @@ % get IDs and indices of genes involved in rule -ruleGenes = unique(regexp(rule,'[^&|\(\) ]+','match')); +ruleGenes = unique(regexp(rule,'[^&|\(\) ]+','match'),'stable'); [~,geneInd] = ismember(ruleGenes,genes); % rules with one or no genes remain unchanged @@ -164,8 +167,8 @@ scoreMethod = isozymeScoring; negInd = gScores(geneInd) < 0; % NaNs will return false here if all(negInd) - % deterministic least-negative: sort descending; stable sort breaks - % ties by original gene order (left-to-right in the rule string) + % deterministic least-negative: sort descending; the stable sort + % breaks ties by the order of the genes in the rule string [~,sortOrder] = sort(gScores(geneInd),'descend'); maxInd = sortOrder(1); updatedRule = ruleGenes{maxInd}; @@ -179,7 +182,7 @@ end % update ruleGenes and their indices - ruleGenes = unique(regexp(updatedRule,'[^&|\(\) ]+','match')); + ruleGenes = unique(regexp(updatedRule,'[^&|\(\) ]+','match'),'stable'); [~,geneInd] = ismember(ruleGenes,genes); elseif ~contains(rule,'|') % rule contains enzyme complex diff --git a/manipulation/removeMets.m b/src/manipulation/removeMets.m similarity index 87% rename from manipulation/removeMets.m rename to src/manipulation/removeMets.m index e494d3ac..e9fb6c3c 100755 --- a/manipulation/removeMets.m +++ b/src/manipulation/removeMets.m @@ -13,16 +13,17 @@ % Name-Value Arguments % -------------------- % isNames : logical -% true if the supplied mets represent metabolite names (as opposed to -% IDs). This is a way to delete metabolites in several compartments at -% once without knowing the exact IDs. This only works if metsToRemove -% is a cell array (default false). +% true if the supplied mets are metabolite names, false if they are IDs. +% All metabolites with a listed name are deleted, which removes +% metabolites in several compartments at once. Requires metsToRemove to +% be a cell array (default false). % removeUnusedRxns : logical -% remove reactions that are no longer in use (default false). +% remove reactions that involve no metabolite (default false). % removeUnusedGenes : logical -% remove genes that are no longer in use (default false). +% remove genes that no remaining reaction uses. Takes effect only when +% removeUnusedRxns is true (default false). % removeUnusedComps : logical -% remove compartments that are no longer in use (default false). +% remove compartments that contain no metabolite (default false). % % Returns % ------- diff --git a/manipulation/removeReactions.m b/src/manipulation/removeReactions.m similarity index 91% rename from manipulation/removeReactions.m rename to src/manipulation/removeReactions.m index a19fbf9f..ae124b72 100755 --- a/manipulation/removeReactions.m +++ b/src/manipulation/removeReactions.m @@ -13,11 +13,12 @@ % Name-Value Arguments % -------------------- % removeUnusedMets : logical -% remove metabolites that are no longer in use (default false). +% remove metabolites that no remaining reaction uses (default false). % removeUnusedGenes : logical -% remove genes that are no longer in use (default false). +% remove genes that no remaining reaction uses (default false). % removeUnusedComps : logical -% remove compartments that are no longer in use (default false). +% remove compartments that contain no metabolite. Takes effect only when +% removeUnusedMets is true (default false). % % Returns % ------- diff --git a/manipulation/replaceMets.m b/src/manipulation/replaceMets.m similarity index 75% rename from manipulation/replaceMets.m rename to src/manipulation/replaceMets.m index bfc31a9f..bb1198d2 100755 --- a/manipulation/replaceMets.m +++ b/src/manipulation/replaceMets.m @@ -1,20 +1,24 @@ function [model, removedRxns, idxDuplRxns]=replaceMets(model,metabolite,replacement,varargin) % replaceMets Replace a metabolite with another already in the model. % -% Replaces metabolite names and annotation with a replacement metabolite -% that is already in the model. If this results in duplicate metabolites, -% the replacement metabolite will be kept, while the S matrix is updated to -% use the replacement metabolite instead. At the end, contractModel is run -% to remove any duplicate reactions that might have occurred. +% Replaces the name and annotation (formula, miriams, charge, deltaG, InChI +% and SMILES) of a metabolite with those of a replacement metabolite that is +% already in the model. If this results in duplicate metabolites, the S matrix +% is updated to use one of them. With identifiers true the replacement +% metabolite is kept; by name, the metabolite with the lowest index among those +% of the same name and compartment is kept. At the end, contractModel is run to remove +% duplicate reactions that this creates. % % Parameters % ---------- % model : struct % a model structure. % metabolite : char -% string with name of metabolite to be replaced. +% name of the metabolite to be replaced (its identifier if identifiers is +% true). % replacement : char -% string with name of replacement metabolite. +% name of the replacement metabolite (its identifier if identifiers is +% true). % % Name-Value Arguments % -------------------- @@ -22,17 +26,17 @@ % whether to print the ids of reactions that involve the replaced % metabolite (default false). % identifiers : logical -% true if 'metabolite' and 'replacement' refer to metabolite -% identifiers instead of metabolite names (default false). +% true if 'metabolite' and 'replacement' are metabolite identifiers, +% false if they are metabolite names (default false). % % Returns % ------- % model : struct -% model structure with selected metabolites replaced. +% model structure with the selected metabolites replaced. % removedRxns : cell -% identifiers of duplicate reactions that were removed. +% not assigned by the function; requesting this output raises an error. % idxDuplRxns : double -% index of removedRxns in original model. +% not assigned by the function; requesting this output raises an error. % % Examples % -------- @@ -41,9 +45,9 @@ % % Notes % ----- -% This function is useful when the model contains both 'oxygen' and 'o2' as -% metabolite names. If 'oxygen' and 'o2' are identifiers instead, then the -% 'identifiers' flag should be set to true. +% The function applies to a model that contains both 'oxygen' and 'o2' as +% metabolite names. If 'oxygen' and 'o2' are identifiers, 'identifiers' is +% set to true. metabolite=char(metabolite); replacement=char(replacement); @@ -79,6 +83,12 @@ error('The to-be-replaced metabolite cannot be found in the model.'); end +% Either of the two rows can be the one that survives the merge: with identifiers the +% replacement stays, by name the replaced metabolite is renamed and stays while the +% replacement's row is deleted below. Both ids are kept so that contractModel is +% restricted to the metabolite that is left. +repMetIds = [repMetIds; model.mets(metIdx)]; + rxnsWithMet = find(model.S(metIdx,:)); if verbose==true fprintf('\n\nThe following reactions contain the to-be-replaced metabolite as reactant:\n') @@ -173,10 +183,13 @@ % This could now have created duplicate reactions. Contract model. % repIdx may point past the end, or at the wrong row, once idxDelete has -% shifted things above; re-resolve the replacement metabolite by id. +% shifted things above; re-resolve the metabolite that is left by id. An empty +% list must not reach contractModel, which would then check every reaction. % distReverse is passed by name rather than positionally as [], which % parseRAVENargs would take literally instead of falling back to its % documented default of true. repIdx = find(ismember(model.mets, repMetIds)); -model=contractModel(model,'mets',repIdx); +if ~isempty(repIdx) + model=contractModel(model,'mets',repIdx); +end end diff --git a/src/manipulation/reverseRxns.m b/src/manipulation/reverseRxns.m new file mode 100644 index 00000000..8ab24ff3 --- /dev/null +++ b/src/manipulation/reverseRxns.m @@ -0,0 +1,37 @@ +function model=reverseRxns(model, rxns) +% reverseRxns Reverse the direction of reactions in a model. +% +% Negates the stoichiometric coefficients and the objective coefficient of +% the reactions, and sets the lower bound to the negated upper bound and the +% upper bound to the negated lower bound. A flux in the new positive +% direction equals a flux in the original negative direction. Reaction IDs +% in rxns that are not in the model are ignored. +% +% Parameters +% ---------- +% model : struct +% a model structure. +% rxns : cell +% IDs of the reactions to reverse. +% +% Returns +% ------- +% model : struct +% an updated model structure. +% +% Examples +% -------- +% model = reverseRxns(model, rxns); + +rxnInd = find(ismember(model.rxns,rxns)); + +%reverse the reactions +model.S(:,rxnInd) = model.S(:,rxnInd)*-1; +%swap the bounds +ub = model.ub(rxnInd); +model.ub(rxnInd) = -model.lb(rxnInd); +model.lb(rxnInd) = -ub; +%flip the objective +model.c(rxnInd) = -model.c(rxnInd); + +end diff --git a/manipulation/setExchangeBounds.m b/src/manipulation/setExchangeBounds.m similarity index 79% rename from manipulation/setExchangeBounds.m rename to src/manipulation/setExchangeBounds.m index a3a32b5a..a118a175 100755 --- a/manipulation/setExchangeBounds.m +++ b/src/manipulation/setExchangeBounds.m @@ -10,31 +10,34 @@ % -------------------- % mets : cell or double % a cell array of metabolite names (case insensitive) or metabolite -% IDs, or a vector of metabolite indices (default all exchanged +% IDs, or a vector of indices in model.mets (default all exchanged % metabolites). % lb : double -% lower bound of exchange flux. Can be either a vector of bounds -% corresponding to each of the provided metabolites, or a single value -% that will be applied to all (default model.annotation.defaultLB if it -% exists, otherwise -1000). +% lower bound of exchange flux. Either a vector of bounds corresponding +% to each of the provided metabolites, or a single value that is applied +% to all (default model.annotation.defaultLB if it exists, otherwise +% -1000). Without mets, only a single value is accepted. % ub : double -% upper bound of exchange flux. Can be either a vector of bounds -% corresponding to each of the provided metabolites, or a single value -% that will be applied to all (default model.annotation.defaultUB if it -% exists, otherwise 1000). +% upper bound of exchange flux. Either a vector of bounds corresponding +% to each of the provided metabolites, or a single value that is applied +% to all (default model.annotation.defaultUB if it exists, otherwise +% 1000). Without mets, only a single value is accepted. % closeOthers : logical -% close exchange reactions for all other exchanged metabolites not -% present in the provided list. This will prevent IMPORT of the -% metabolites, but their EXPORT will not be modified (default true). +% close the exchange reactions of all exchanged metabolites that are not +% in the provided list. This prevents import of these metabolites and +% leaves their export unchanged. Set to false, with a warning, when the +% exchange reactions are not all written in the same direction +% (default true). % mediaOnly : logical % only consider exchange reactions involving exchange to or from the % extracellular (media) compartment. Reactions such as "sink" reactions % that exchange metabolites directly with an intracellular compartment -% will therefore be ignored even though "getExchangeRxns" identifies -% such reactions as exchange reactions. The function will attempt to -% identify the extracellular compartment by the "compNames" field, and -% also requires the "metComps" field to be present, otherwise the -% mediaOnly flag will be ignored (default false). +% are therefore ignored, although "getExchangeRxns" identifies such +% reactions as exchange reactions. The extracellular compartment is the +% one named "extracellular" in the "compNames" field (case insensitive). +% The "compNames" and "metComps" fields are required; an error is thrown +% if either is missing or if no compartment has this name +% (default false). % % Returns % ------- @@ -42,8 +45,8 @@ % a model structure with updated exchange flux bounds for the provided % set of metabolites. % unusedMets : cell -% metabolites provided by the user that were not used because they are -% not involved in any exchange reactions in the model. +% metabolites in mets that were not used because they are not involved +% in any of the considered exchange reactions in the model. % % Examples % -------- @@ -52,7 +55,7 @@ % % Notes % ----- -% Exchange reactions involving more than one metabolite will be ignored. +% Exchange reactions involving more than one metabolite are ignored. % handle input arguments @@ -197,10 +200,8 @@ % check for any metabolites that are exchanged in more than one reaction. % metInd holds one entry per (metabolite,reaction) pair, so a metabolite -% involved in more than one exchange reaction is a repeated *value* in -% metInd, not a position past numel(unique(metInd)) -- comparing those two -% index spaces directly (as a previous version of this check did) compares -% positions in the unique list against indices into metInd itself. +% involved in more than one exchange reaction is a value that occurs more +% than once in metInd. The occurrences are counted per unique value. [uniqueMetInd,~,ic] = unique(metInd); counts = accumarray(ic,1); multiMetInd = exchMetInd(uniqueMetInd(counts>1)); diff --git a/manipulation/setParam.m b/src/manipulation/setParam.m similarity index 79% rename from manipulation/setParam.m rename to src/manipulation/setParam.m index 07304c7e..df82afb4 100755 --- a/manipulation/setParam.m +++ b/src/manipulation/setParam.m @@ -6,23 +6,31 @@ % model : struct % a model structure. % paramType : char -% the type of parameter to set: +% the type of parameter to set. The value is matched case-insensitively, +% except for the reset of the other objective coefficients under "obj" +% (below): % % - "lb" : lower bound. % - "ub" : upper bound. % - "eq" : both upper and lower bound (equality constraint). -% - "obj" : objective coefficient. -% - "rev" : reversibility (only changes the model.rev fields, does not -% affect model.lb and model.ub). +% - "obj" : objective coefficient. The objective coefficients of all +% other reactions are set to 0 only when every element of paramType +% contains "obj" in lower case; with another capitalisation, or when +% paramType also holds other types, the other coefficients keep their +% values. +% - "rev" : reversibility (only changes the model.rev field, does not +% affect model.lb and model.ub). A non-zero value sets the reaction +% reversible. % - "var" : variance around measured bound. -% - "unc" : unconstrained, set lower and upper bound to the default -% values (-1000 and 1000, or any other values that are defined in -% model.annotation.defaultLB and .defaultUB). +% - "unc" : unconstrained, sets the lower and upper bound to +% model.annotation.defaultLB and model.annotation.defaultUB if these +% are defined, otherwise to -1000 and 1000. % rxnList : cell or double % a cell array of reaction IDs or a vector with their corresponding -% indexes. +% indexes. IDs that are not in the model are ignored with a warning. % params : double -% a vector of the corresponding values. +% a vector of the corresponding values, or a single value that is +% applied to all reactions in rxnList. % var : double, optional % percentage of variance around measured value, if "var" is set as % paramType. Defining "var" as 5 results in lb and ub at 97.5% and diff --git a/manipulation/simplifyModel.m b/src/manipulation/simplifyModel.m similarity index 80% rename from manipulation/simplifyModel.m rename to src/manipulation/simplifyModel.m index e5dd214f..2031936b 100755 --- a/manipulation/simplifyModel.m +++ b/src/manipulation/simplifyModel.m @@ -1,9 +1,8 @@ function [reducedModel, deletedReactions, deletedMetabolites]=simplifyModel(model,varargin) % simplifyModel Simplify a model by deleting reactions and metabolites. % -% This function is for reducing the model size by removing reactions and -% associated metabolites that cannot carry flux. It can also be used for -% identifying different types of gaps. +% Deletes reactions and metabolites that cannot carry flux. The deleted +% reactions and metabolites identify different types of gaps. % % Parameters % ---------- @@ -13,27 +12,37 @@ % Name-Value Arguments % -------------------- % deleteUnconstrained : logical -% delete metabolites marked as unconstrained (default true). +% delete metabolites marked as unconstrained in model.unconstrained, and +% the compartments that are then left without metabolites (default true). % deleteDuplicates : logical -% delete all but one of duplicate reactions (default false). +% delete all but the first occurrence of duplicate reactions, as found by +% contractModel. Duplicate reactions have the same stoichiometry and +% reversibility. The kept reaction takes the minimum lower bound, the +% maximum upper bound and the maximum objective coefficient of the +% duplicates, with a warning when these differ (default false). % deleteZeroInterval : logical % delete reactions that are constrained to zero flux (default false). % deleteInaccessible : logical % delete dead end reactions (default false). % deleteMinMax : logical -% delete reactions that cannot carry a flux by trying to -% minimize/maximize the flux through that reaction. May be time -% consuming (default false). +% delete reactions that cannot carry a flux, as determined by haveFlux +% under the bounds of the model. The run time grows with the number of +% reactions, since haveFlux solves linear programs (default false). % groupLinear : logical -% group linearly dependent pathways (default false). +% group linearly dependent pathways. The grouped model is irreversible, +% and all gene information (genes, rxnGeneMat, grRules and the +% gene-associated fields) is deleted (default false). % constrainReversible : logical % check if there are reversible reactions which can only carry flux in -% one direction, and if so constrain them to be irreversible. This -% tends to allow for more reactions grouped when using groupLinear -% (default false). +% one direction, and if so constrain them to be irreversible. A reversible +% reaction that is constrained to one direction is not split into two +% reactions when groupLinear converts the model to irreversible, so the +% metabolites that it connects can meet the condition for grouping (one +% producing and one consuming reaction) (default false). % reservedRxns : cell -% cell array with reaction IDs that are not allowed to be removed -% (default none). +% cell array with reaction IDs that are not deleted by +% deleteZeroInterval, deleteInaccessible and deleteMinMax and not merged +% by groupLinear (default none). % suppressWarnings : logical % true if warnings should be suppressed (default false). % @@ -81,9 +90,10 @@ end if deleteDuplicates==true - %Delete all but the last occurrence of duplicate reactions. The - %reactions must have the same bounds, reversibility, and objective - %coefficient to be regarded as duplicate + %Merge duplicate reactions into the first occurrence and delete the + %others. Reactions with the same stoichiometry and reversibility are + %duplicates; the merged reaction keeps the widest bounds and the largest + %objective coefficient (see contractModel) [reducedModel, rxnsToDelete, ~]=contractModel(reducedModel); deletedReactions=[deletedReactions; rxnsToDelete]; end @@ -185,30 +195,33 @@ if constrainReversible==true revs=find(reducedModel.rev); - [I,J]=getAllowedBounds(reducedModel,revs); + %getAllowedBounds treats an empty reaction list as all reactions + if ~isempty(revs) + [I,J]=getAllowedBounds(reducedModel,revs); - I=abs(I); - J=abs(J); + I=abs(I); + J=abs(J); - %Get the "small" values - K=I<10^-10; - L=J<10^-10; + %Get the "small" values + K=I<10^-10; + L=J<10^-10; - %Keep the values where only one direction is zero (small) - I(K==L)=[]; - J(K==L)=[]; - revs(K==L)=[]; + %Keep the values where only one direction is zero (small) + I(K==L)=[]; + J(K==L)=[]; + revs(K==L)=[]; - %Change the reversibility of the remaining reactions - reducedModel.rev(revs(J>10^-10))=0; %Ignore reverse direction - reducedModel.lb(revs(J>10^-10))=0; + %Change the reversibility of the remaining reactions + reducedModel.rev(revs(J>10^-10))=0; %Ignore reverse direction + reducedModel.lb(revs(J>10^-10))=0; - toSwitch=revs(I>10^-10); - reducedModel.rev(toSwitch)=0; %Change directionality - reducedModel.ub(toSwitch)=reducedModel.lb(toSwitch)*-1; - reducedModel.lb(toSwitch)=0; - reducedModel.S(:,toSwitch)=reducedModel.S(:,toSwitch).*-1; - reducedModel.c(toSwitch)=reducedModel.c(toSwitch)*-1; + toSwitch=revs(I>10^-10); + reducedModel.rev(toSwitch)=0; %Change directionality + reducedModel.ub(toSwitch)=reducedModel.lb(toSwitch)*-1; + reducedModel.lb(toSwitch)=0; + reducedModel.S(:,toSwitch)=reducedModel.S(:,toSwitch).*-1; + reducedModel.c(toSwitch)=reducedModel.c(toSwitch)*-1; + end end if groupLinear==true if suppressWarnings==false diff --git a/manipulation/sortIdentifiers.m b/src/manipulation/sortIdentifiers.m similarity index 84% rename from manipulation/sortIdentifiers.m rename to src/manipulation/sortIdentifiers.m index 5a062a1e..acc0e833 100755 --- a/manipulation/sortIdentifiers.m +++ b/src/manipulation/sortIdentifiers.m @@ -1,8 +1,8 @@ function newModel = sortIdentifiers(model) % sortIdentifiers Sort model identifiers alphabetically. % -% Sort reactions, metabolites, genes and compartments alphabetically by -% their identifier. +% Sorts reactions, metabolites, genes (if the genes field exists) and +% compartments alphabetically by their identifier. % % Parameters % ---------- diff --git a/manipulation/sortModel.m b/src/manipulation/sortModel.m similarity index 83% rename from manipulation/sortModel.m rename to src/manipulation/sortModel.m index 40ed0efc..010d669c 100755 --- a/manipulation/sortModel.m +++ b/src/manipulation/sortModel.m @@ -1,8 +1,9 @@ function model=sortModel(model,varargin) -% sortModel Standardize a model's internal ordering: by default orients -% reversible reactions consistently; can also reorder metabolites within -% reaction equations and/or reactions within each subsystem, depending on -% which options are enabled. +% sortModel Sort the metabolites and reactions of a model. +% +% Depending on the options, the function sorts the metabolites by name and +% compartment, orients reversible reactions consistently, and reorders the +% reactions within each subsystem. % % Parameters % ---------- @@ -12,21 +13,25 @@ % Name-Value Arguments % -------------------- % sortReversible : logical -% sorts the reversible reactions so that the metabolite that is first -% in lexicographical order is a reactant (default true). +% orient each reversible reaction by changing the sign of its +% stoichiometric coefficients. The metabolite that is first in +% lexicographical order (metabolite name followed by compartment ID) +% becomes a reactant. Not applied when sortReactionOrder is true +% (default false). % sortMetName : logical -% sort the metabolite names in the equation, also uses compartment -% abbreviation (default false). +% sort the metabolites of the model by metabolite name followed by +% compartment ID (default false). % sortReactionOrder : logical -% sorts the reaction order within each subsystem so that reactions -% consuming some metabolite come after reactions producing it. This -% overrides the sortReversible option and reactions are sorted so that -% the production direction matches the consumption direction (default -% false). +% reorder the reactions within each subsystem, such that reactions +% consuming a metabolite come after reactions producing it. The order is +% found by a random local search that also changes the direction of +% reversible reactions to match production and consumption direction. +% Requires the subSystems field. Subsystems with fewer than 2 or more +% than 250 reactions are not reordered (default false). % seed : double % seed for the random number generator used by the sortReactionOrder -% local search, so its result is reproducible across runs (default: -% current rng state is left untouched). +% local search, which makes its result reproducible across runs +% (default: the current rng state is not changed). % % Returns % ------- @@ -37,7 +42,7 @@ % -------- % model = sortModel(model, sortReversible, sortMetName, sortReactionOrder); -p=parseRAVENargs(varargin, {'sortReversible',true; 'sortMetName',false; 'sortReactionOrder',false; 'seed',[]}); +p=parseRAVENargs(varargin, {'sortReversible',false; 'sortMetName',false; 'sortReactionOrder',false; 'seed',[]}); if ~isempty(p.seed) rng(p.seed); end diff --git a/manipulation/standardizeGrRules.m b/src/manipulation/standardizeGrRules.m similarity index 86% rename from manipulation/standardizeGrRules.m rename to src/manipulation/standardizeGrRules.m index b95babdb..e59ddff4 100755 --- a/manipulation/standardizeGrRules.m +++ b/src/manipulation/standardizeGrRules.m @@ -1,13 +1,17 @@ function [grRules,rxnGeneMat,indexes2check] = standardizeGrRules(model,varargin) -% standardizeGrRules Standardize gene-reaction rules in a model. +% standardizeGrRules Standardise gene-reaction rules in a model. % -% The grRules are standardized according to the following: +% The grRules are standardised according to the following: % % - No overall containing brackets -% - Just enzyme complexes are enclosed into brackets -% - " and " and " or " strings are strictly set to lowercase +% - Only enzyme complexes are enclosed in brackets +% - " and " and " or " strings are set to lowercase % -% A rxnGeneMat matrix consistent with the standardized grRules is created. +% A rxnGeneMat matrix consistent with the standardised grRules is created. +% grRules that are not in disjunctive normal form (an "or" nested inside an +% "and") or that cannot be parsed are not standardised, apart from the +% removal of double spaces, spaces inside brackets and uppercase operators. +% The function throws an error if the model has no grRules field. % % Parameters % ---------- @@ -17,21 +21,21 @@ % Name-Value Arguments % -------------------- % embedded : logical -% if true, suppresses the console warning about potentially -% problematic grRules relationships, for use when this function is -% called inside of another RAVEN function that handles reporting -% itself (default false). +% if true, suppresses the console warning about grRules that are not in +% disjunctive normal form or cannot be parsed, for use when this +% function is called inside another RAVEN function that handles +% reporting itself (default false). % % Returns % ------- % grRules : cell -% [nRxns x 1] cell array with the standardized grRules. +% [nRxns x 1] cell array with the standardised grRules. % rxnGeneMat : double -% [nRxns x nGenes] sparse matrix consistent with the standardized +% [nRxns x nGenes] sparse matrix consistent with the standardised % grRules. % indexes2check : double -% indices of the grRules with potentially problematic relationships -% that should be checked manually. +% indices of the grRules that are not in disjunctive normal form or +% cannot be parsed. These grRules need a manual check. % % Examples % -------- diff --git a/queries/buildEquation.m b/src/queries/buildEquation.m similarity index 84% rename from queries/buildEquation.m rename to src/queries/buildEquation.m index 6c93679e..9bb5951c 100755 --- a/queries/buildEquation.m +++ b/src/queries/buildEquation.m @@ -6,14 +6,17 @@ % mets : cell % metabolites involved in the reaction. % stoichCoeffs : double -% vector with corresponding stoichiometric coeffs. +% vector with the corresponding stoichiometric coefficients, of the same +% length as mets. Negative coefficients are reactants and positive +% coefficients are products. % isrev : logical -% indicates if the reaction is reversible or not. +% true if the reaction is reversible (written with "<=>"), false if it +% is irreversible (written with "=>"). % % Returns % ------- % equationString : char -% equation as a string. +% the equation as a string. % % Examples % -------- diff --git a/queries/checkModelStruct.m b/src/queries/checkModelStruct.m similarity index 94% rename from queries/checkModelStruct.m rename to src/queries/checkModelStruct.m index 59b11149..38e2f4b1 100755 --- a/queries/checkModelStruct.m +++ b/src/queries/checkModelStruct.m @@ -1,5 +1,5 @@ function issues=checkModelStruct(model,varargin) -% checkModelStruct Perform a number of checks to ensure a model structure is ok. +% checkModelStruct Check a model structure for errors and inconsistencies. % % Parameters % ---------- @@ -10,32 +10,33 @@ % -------------------- % throwErrors : logical % true if structural problems (wrong types, missing required fields, -% duplicate IDs, etc.) should throw an error. Advisory notices such as +% duplicate IDs and other problems of structure) should throw an error. Advisory notices such as % unused elements and bound inconsistencies are always reported as % warnings regardless of this flag (default true). % trimWarnings : logical -% true if only a maximum of 10 items should be displayed in a given -% warning or error (default true). +% true if a given warning or error lists at most 10 items, the last of +% which reports the number of remaining items (default true). % % Returns % ------- % issues : struct -% When called with an output argument, returns a struct array with one -% element per finding (does not throw or print) instead of the default -% print/throw behaviour. Fields: +% When called with an output argument, the function returns a struct +% array with one element per finding and neither throws nor prints. The +% default behaviour without an output argument is to print warnings and +% throw errors. Fields: % -% - category : char — type of issue: "missing_field", "wrong_type", -% "empty_id", "duplicate", "invalid_id", "invalid_bounds", -% "unused", "objective", "gpr", "invalid_formula", -% "cross_reference", or "other". -% - target : char — the specific item involved (field name, reaction -% ID, metabolite ID, etc.), or "" when not applicable. -% - message : char — the full diagnostic text. +% - category (char): type of issue, one of "missing_field", +% "wrong_type", "empty_id", "duplicate", "invalid_id", +% "invalid_bounds", "unused", "objective", "gpr", "invalid_formula", +% "cross_reference" or "other". +% - target (char): the item involved (field name, reaction ID, +% metabolite ID or another item), or "" when not applicable. +% - message (char): the full diagnostic text. % % Notes % ----- -% This is typically performed after importing or constructing a model, or -% before attempting to export a model to SBML format. +% The check is intended for use after importing or constructing a model and +% before exporting a model to SBML format. % % Examples % -------- diff --git a/queries/constructEquations.m b/src/queries/constructEquations.m similarity index 79% rename from queries/constructEquations.m rename to src/queries/constructEquations.m index a439bccf..23f5d3ee 100755 --- a/queries/constructEquations.m +++ b/src/queries/constructEquations.m @@ -18,21 +18,23 @@ % sort reversible reactions so that the metabolite that is first in the % lexicographic order is a reactant (default false). % sortMetNames : logical -% sort the metabolite names in the equation. Uses compartment even if -% useComps is false (default false). +% sort the metabolite names in the equation. The compartment is included +% in the sorting even if useComps is false (default false). % useMetID : logical -% use metabolite ID in generated equations (default false). +% use metabolite ID in generated equations. Cannot be true together with +% useFormula (default false). % useFormula : logical -% use metabolite formula in generated equations (default false). +% use metabolite formula in generated equations. Cannot be true together +% with useMetID (default false). % useRevField : logical -% use the model.rev field to indicate reaction reversibility, -% alternatively this is determined from the model.ub and model.lb fields +% use the model.rev field to indicate reaction reversibility. When +% false, a reaction is reversible if model.lb < 0 and model.ub > 0 % (default true). % % Returns % ------- % equationStrings : cell -% a cell array with equations. +% a cell array with the equation of each reaction. % % Examples % -------- @@ -41,9 +43,9 @@ % % Notes % ----- -% If useRevField is false, then reactions should be organized in their -% forward direction (e.g. ub = 1000 and lb = -1000/0) for the reversibility -% to be correctly determined. +% If useRevField is false, the reactions have to be written in their forward +% direction (for example ub = 1000 and lb = -1000 or 0) for the reversibility +% to be determined correctly. p=parseRAVENargs(varargin, {'rxns',[]; 'useComps',true; 'sortRevRxns',false; 'sortMetNames',false; 'useMetID',false; 'useFormula',false; 'useRevField',true}); rxns=p.rxns; @@ -61,7 +63,7 @@ %Sort reversible equations if sortRevRxns==true - model=sortModel(model); + model=sortModel(model,true); end %Sort metabolite names, including compartment diff --git a/queries/constructS.m b/src/queries/constructS.m similarity index 90% rename from queries/constructS.m rename to src/queries/constructS.m index 03b03162..f837a4ed 100755 --- a/queries/constructS.m +++ b/src/queries/constructS.m @@ -4,32 +4,34 @@ % Parameters % ---------- % equations : cell -% cell array of equations on the form "A + 2 B <=> 3 C", where <=> +% cell array of equations in the form "A + 2 B <=> 3 C", where <=> % indicates reversible and => irreversible reactions. % % Name-Value Arguments % -------------------- % mets : cell % cell array of metabolites. All metabolites in the equations must be -% present in the list (default generated from the equations). +% present in the list, otherwise an error is thrown (default generated +% from the equations). % rxns : cell -% cell array of reaction ids. This is only used for printing reaction ids -% instead of equations in warnings/errors (default []). +% cell array of reaction ids. Used to print reaction ids in error +% messages in place of equations (default []). % % Returns % ------- % S : double % the resulting stoichiometric matrix. % mets : cell -% cell array with metabolites that corresponds to the order in the S +% cell array with the metabolites in the order of the rows of the S % matrix. % badRxns : logical -% boolean vector with the reactions that have one or more metabolites as -% both substrate and product. An example would be the phosphotransferase -% ATP + ADP <=> ADP + ATP. In the stoichiometric matrix this equals to an -% empty reaction which can be problematic. +% logical vector with true for the reactions in which one or more +% metabolites occur more than once, for example as both substrate and +% product. An example is the phosphotransferase reaction +% ATP + ADP <=> ADP + ATP, which gives an empty column in the +% stoichiometric matrix. % reversible : logical -% boolean vector with true if the equation is reversible. +% logical vector with true if the equation is reversible. % % Examples % -------- diff --git a/queries/getAllRxnsFromGenes.m b/src/queries/getAllRxnsFromGenes.m similarity index 81% rename from queries/getAllRxnsFromGenes.m rename to src/queries/getAllRxnsFromGenes.m index a1710c92..046e7b31 100755 --- a/queries/getAllRxnsFromGenes.m +++ b/src/queries/getAllRxnsFromGenes.m @@ -2,7 +2,8 @@ % getAllRxnsFromGenes Find all reactions annotated by the genes of a set. % % Given a list of reactions, this function finds the associated genes in -% the model and returns all reactions that are annotated by these genes. +% the model and returns all reactions that are annotated by these genes, +% including the given reactions. % % Parameters % ---------- @@ -15,9 +16,9 @@ % Returns % ------- % allRxns : cell or logical or double -% either a cell array of IDs, a logical vector with the same number of -% elements as reactions in the model, or a vector of indexes, -% dependent on the format of rxns. +% the reactions in the same format as rxns: a cell array of IDs (unique +% and sorted), a logical vector with the same number of elements as +% reactions in the model, or a vector of indexes (unique and sorted). % % Examples % -------- diff --git a/queries/getElementalBalance.m b/src/queries/getElementalBalance.m similarity index 88% rename from queries/getElementalBalance.m rename to src/queries/getElementalBalance.m index f015984c..d13d0593 100755 --- a/queries/getElementalBalance.m +++ b/src/queries/getElementalBalance.m @@ -1,7 +1,10 @@ function balanceStructure=getElementalBalance(model,varargin) % getElementalBalance Check whether the reactions of a model are balanced. % -% Checks a model to see if the reactions are elementally balanced. +% Checks whether the reactions are elementally balanced, using the +% metFormulas field or, when that is absent, the inchis field. An error is +% thrown if the model has neither field. The charge balance is reported +% separately. % % Parameters % ---------- @@ -13,13 +16,13 @@ % rxns : cell or logical or double % either a cell array of reaction IDs, a logical vector with the same % number of elements as reactions in the model, or a vector of -% indexes. Only these reactions will be checked (default model.rxns). +% indexes. Only these reactions are checked (default model.rxns). % printUnbalanced : logical -% print warnings about the reactions that were unbalanced +% print warnings about the reactions that are unbalanced % (default false). % printUnparsable : logical -% print warnings about the reactions that cannot be parsed -% (default false). +% print warnings about the reactions that cannot be balanced because of +% missing information or parsing errors (default false). % % Returns % ------- @@ -28,12 +31,13 @@ % % - balanceStatus : 1 if the reaction is balanced, 0 if it is % unbalanced, -1 if it could not be balanced due to missing -% information, -2 if it could not be balanced due to an error. -% Elemental only; charge is reported separately below +% information (including a reaction without metabolites), -2 if it +% could not be balanced because a formula could not be parsed. +% Elemental only; charge is reported in chargeStatus % - chargeStatus : 1 if the reaction is charge balanced, 0 if it is -% not, -1 if any participating metabolite has no charge (or the -% model has no metCharges field), in which case the charge balance -% is unknown rather than zero +% not, -1 if any participating metabolite has no charge, the reaction +% has no metabolites, or the model has no metCharges field; the +% charge balance is then unknown % - chargeResidual : the sum of charges over the reaction, NaN where % chargeStatus is -1 % - elements : struct with fields abbrevs (cell array with diff --git a/queries/getExchangeRxns.m b/src/queries/getExchangeRxns.m similarity index 92% rename from queries/getExchangeRxns.m rename to src/queries/getExchangeRxns.m index aa86b753..b6c35575 100755 --- a/queries/getExchangeRxns.m +++ b/src/queries/getExchangeRxns.m @@ -2,7 +2,9 @@ % getExchangeRxns Retrieve the exchange reactions from a model. % % Exchange reactions are identified by having either no substrates or no -% products. +% products. In a model with metabolites marked in the unconstrained field, +% the exchange reactions are the reactions that involve such a metabolite +% (as substrate for 'in', as product for 'out'). % % Parameters % ---------- @@ -39,8 +41,9 @@ % exchangeRxnsIndexes : double % vector with the indexes of the exchange reactions. % exchangedMets : double -% vector with the metabolite index for each exchange reaction (one -% metabolite per exchange reaction). Only computed when requested. +% vector with the index of the first metabolite with a non-zero +% coefficient in each exchange reaction (0 if there is none). Only +% computed when requested. % % Notes % ----- diff --git a/queries/getGenesFromGrRules.m b/src/queries/getGenesFromGrRules.m similarity index 79% rename from queries/getGenesFromGrRules.m rename to src/queries/getGenesFromGrRules.m index 5b561ec5..945addb0 100755 --- a/queries/getGenesFromGrRules.m +++ b/src/queries/getGenesFromGrRules.m @@ -5,25 +5,25 @@ % ---------- % grRules : cell % a cell array of model grRules, from which a list of genes is to be -% extracted. NOTE: Boolean operators can be text ("and", "or") or -% symbolic ("&", "|"), but there must be a space between operators and -% gene names/IDs. +% extracted. Boolean operators can be text ("and", "or") or symbolic +% ("&", "|"), with a space between operators and gene names or IDs. % % Name-Value Arguments % -------------------- % originalGenes : cell % the original gene list from the model. If supplied, the genes parsed -% from grRules must match it exactly (as a set), an error is thrown -% otherwise, and the returned genes list follows originalGenes' order. +% from grRules must match it as a set, otherwise an error is thrown, and +% the returned genes list follows the order of originalGenes. % % Returns % ------- % genes : cell -% a unique list of all gene IDs that appear in grRules. +% a unique, sorted list of all gene IDs that appear in grRules (in the +% order of originalGenes when supplied). % rxnGeneMat : double -% (optional) a binary matrix indicating which genes participate in each -% reaction, where rows correspond to reactions (entries in grRules) and -% columns correspond to genes. +% (optional) a sparse binary matrix indicating which genes participate in +% each reaction, where rows correspond to reactions (entries in grRules) +% and columns correspond to genes. Only computed when requested. % % Examples % -------- diff --git a/queries/getIndexes.m b/src/queries/getIndexes.m similarity index 73% rename from queries/getIndexes.m rename to src/queries/getIndexes.m index 87770bbc..71fb1c59 100755 --- a/queries/getIndexes.m +++ b/src/queries/getIndexes.m @@ -7,25 +7,32 @@ % a model structure. % objects : cell or logical or double % either a cell array of IDs, a logical vector with the same number of -% elements as metabolites in the model, or a vector of indexes. +% elements as objects of the given type in the model, or a vector of +% indexes. An error is raised if an ID is not found in the model, except +% for type 'metnames', where a name that is not found gives an empty +% index vector. % type : char -% 'rxns', 'mets', or 'genes' depending on what to retrieve. 'metnames' -% queries metabolite names, while 'metcomps' allows providing specific +% 'rxns', 'mets' or 'genes', depending on what to retrieve. 'metnames' +% queries metabolite names and returns, for each name, the indexes of all +% metabolites with that name. 'metcomps' queries specific % metabolites and their compartments in the format metaboliteName[comp]. -% If a model.ec structure exists (GECKO 3), then also 'ecenzymes', -% 'ecrxns' and 'ecgenes' are allowed. +% An error is raised if such a name matches no metabolite, or more than +% one. +% If a model.ec structure exists (GECKO 3), 'ecenzymes', 'ecrxns' and +% 'ecgenes' are also allowed. % % Name-Value Arguments % -------------------- % returnLogical : logical % sets whether to return a logical array or an array with the indexes -% (default false). +% (default false). It cannot be true for type 'metnames', which raises +% the error RAVEN:badInput. % % Returns % ------- % indexes : logical or double -% can be a logical array or a double array depending on the value of -% returnLogical. +% a logical array if returnLogical is true, otherwise a double array. For +% 'metnames' with several names, a cell array of double vectors. % % Notes % ----- @@ -55,6 +62,12 @@ case 'genes' searchIn=model.genes; case 'metnames' + if returnLogical==true + error('RAVEN:badInput', ['The combination of type ''metnames'' and ' ... + 'returnLogical true is not supported, because each name can ' ... + 'match several metabolites. Use type ''mets'' or ''metcomps'' ' ... + 'for a logical vector.']); + end searchIn=model.metNames; case 'metcomps' % If provided as metaboliteName[comp], then index search is quite @@ -65,10 +78,14 @@ for i=1:numel(objects) index=find(strcmp(mets(i),model.metNames)); index=index(strcmp(comps(i),model.comps(model.metComps(index)))); - if ~isempty(index) + if isscalar(index) indexes(i)=index; + elseif isempty(index) + error(['Could not find object ''' objects{i} ''' in the model']); else - error(['Could not find object ' objects{i} ' in the model']); + error(['''' objects{i} ''' is ambiguous: it matches the ' ... + 'metabolites ' strjoin(model.mets(index),', ') ... + '. Use metabolite ids (type ''mets'') instead.']); end end indexes=indexes(:); diff --git a/queries/getRxnsInComp.m b/src/queries/getRxnsInComp.m similarity index 79% rename from queries/getRxnsInComp.m rename to src/queries/getRxnsInComp.m index ce980a90..a4ac4f63 100755 --- a/queries/getRxnsInComp.m +++ b/src/queries/getRxnsInComp.m @@ -6,20 +6,21 @@ % model : struct % a model structure. % comp : char -% string with the compartment id. +% the compartment id (case insensitive). An error is thrown if the id +% does not match exactly one compartment. % % Name-Value Arguments % -------------------- % includePartial : logical -% true if reactions with metabolites in several compartments (normally -% transport reactions) should be included (default false). +% true if reactions with metabolites in several compartments (for +% example transport reactions) are included (default false). % % Returns % ------- % I : double -% boolean vector of the reactions. +% indexes of the reactions in the compartment. % rxnNames : cell -% the names of the reactions. +% the names of these reactions. % % Examples % -------- diff --git a/queries/getTransportRxns.m b/src/queries/getTransportRxns.m similarity index 89% rename from queries/getTransportRxns.m rename to src/queries/getTransportRxns.m index a1d989f7..e7678af0 100755 --- a/queries/getTransportRxns.m +++ b/src/queries/getTransportRxns.m @@ -19,7 +19,8 @@ % Notes % ----- % Transport reactions are defined as reactions involving (at least) one -% metabolite name in more than one compartment. +% metabolite name in more than one compartment. Exchange reactions, as +% identified by getExchangeRxns, are not transport reactions. transportRxns=false(numel(model.rxns),1); diff --git a/queries/modelSummary.m b/src/queries/modelSummary.m similarity index 79% rename from queries/modelSummary.m rename to src/queries/modelSummary.m index a8fa03c9..0a26f6ed 100644 --- a/queries/modelSummary.m +++ b/src/queries/modelSummary.m @@ -1,9 +1,9 @@ function modelSummary(model, varargin) -% modelSummary Print a concise overview of a RAVEN model and its flux state. +% modelSummary Print an overview of a RAVEN model and its flux state. % -% Prints model dimensions, the objective function, and — when a flux vector -% is supplied — the objective value plus all net exchange fluxes partitioned -% into uptake and secretion. +% Prints the model dimensions and the objective function. When a flux vector +% is supplied, it also prints the objective value and the net exchange +% fluxes, partitioned into uptake and secretion. % % Parameters % ---------- @@ -13,12 +13,14 @@ function modelSummary(model, varargin) % Name-Value Arguments % -------------------- % fluxes : double -% flux vector (length = numel(model.rxns)). When provided, shows the -% objective value and the exchange-flux summary (default []). +% flux vector with numel(model.rxns) elements. When provided, the +% objective value and the exchange-flux summary are printed +% (default []). % cutoff : double % minimum absolute net exchange flux to include (default 1e-8). % nMax : double -% maximum exchange reactions displayed per direction (default 20). +% maximum number of exchange reactions displayed per direction, in +% descending order of absolute flux (default 20). % % Examples % -------- @@ -28,12 +30,12 @@ function modelSummary(model, varargin) % % See also % -------- -% printModelStats : the structural view instead. It breaks the counts down per -% compartment, adds unique EC codes, biochemically unique reactions and -% unique metabolite names, and optionally reports dead ends and elemental -% imbalances. It knows nothing about fluxes or the objective. -% printFluxes : the exchange fluxes on their own, with more control over -% formatting and filtering than the summary here offers. +% printModelStats : model statistics. It breaks the counts down per +% compartment, adds the number of unique EC codes, biochemically unique +% reactions and unique metabolite names, and optionally reports dead ends +% and elemental imbalances. It prints no fluxes or objective. +% printFluxes : prints the fluxes of reactions with options for filtering +% and for the format of the output. p = parseRAVENargs(varargin, {'fluxes',[]; 'cutoff',1e-8; 'nMax',20}); fluxes = p.fluxes; diff --git a/queries/parseFormulas.m b/src/queries/parseFormulas.m similarity index 92% rename from queries/parseFormulas.m rename to src/queries/parseFormulas.m index c827e387..d394496a 100755 --- a/queries/parseFormulas.m +++ b/src/queries/parseFormulas.m @@ -9,15 +9,16 @@ % Name-Value Arguments % -------------------- % noPolymers : logical -% assume that all polymers consist of one element. Corresponds to -% counting everything between (...)n as n being equal to one. Only one -% set of parentheses is allowed. If this is false then polymers are -% returned as "Could not parse formula" (default false). +% treat polymers as a single unit: everything between (...)n is counted +% with n equal to one. Only one set of parentheses is allowed. If this is +% false, polymers get exitFlag -1, "Could not parse formula" +% (default false). % isInchi : logical % true if the formulas are in the InChI format (default false). % ignoreRX : logical -% ignore R-groups and bound protein. This can be useful since they are -% often used only as intermediates (default false). +% set the atomic weight of R-groups and bound protein to 0, which allows +% MW to be calculated for formulas that contain them. The atoms are still +% counted in useMat (default false). % % Returns % ------- @@ -26,6 +27,8 @@ % % - abbrevs : cell array with abbreviations for all used elements % - names : cell array with the names for all used elements +% +% Elements that occur in none of the formulas are not included. % useMat : double % MxN matrix with the number of atoms for each formula (M) and each % element (N). @@ -36,10 +39,11 @@ % - 0 : no formula found % - -1 : could not parse formula % MW : double -% predicted molecular weight (g/mol). This is only returned for -% formulas which can be successfully parsed, and its calculation -% doesn't affect the exitFlag variable. NaN is returned if the weight -% couldn't be calculated. +% predicted molecular weight (g/mol). This is only calculated when +% requested, and only for formulas which can be successfully parsed; +% its calculation does not affect the exitFlag variable. NaN is returned +% if the weight cannot be calculated, for example for formulas that +% contain R-groups or bound protein while ignoreRX is false. % % Examples % -------- diff --git a/queries/parseRxnEqu.m b/src/queries/parseRxnEqu.m similarity index 86% rename from queries/parseRxnEqu.m rename to src/queries/parseRxnEqu.m index a1ecb934..219ce0ff 100755 --- a/queries/parseRxnEqu.m +++ b/src/queries/parseRxnEqu.m @@ -9,7 +9,7 @@ % Returns % ------- % metabolites : cell -% a cell array with the involved metabolites. +% a unique, sorted cell array with the involved metabolites. % % Examples % -------- @@ -17,12 +17,13 @@ % % Notes % ----- -% The equations should be written like: +% The equations are written like: % -% 1 A + 3 B (=> or <=>) 5C + 2 D +% 1 A + 3 B (=> or <=>) 5 C + 2 D % -% If the equation is expressed as for example '... + (n-1) starch' then -% '(n-1) starch' will be interpreted as one metabolite. +% with a space around each arrow and plus sign and between a coefficient and +% its metabolite name. If the equation is expressed as for example +% '... + (n-1) starch', '(n-1) starch' is interpreted as one metabolite. if ~iscell(equations) equations={equations}; diff --git a/queries/printFluxes.m b/src/queries/printFluxes.m similarity index 91% rename from queries/printFluxes.m rename to src/queries/printFluxes.m index c592d499..41654d17 100755 --- a/queries/printFluxes.m +++ b/src/queries/printFluxes.m @@ -6,7 +6,8 @@ function printFluxes(model, fluxes, varargin) % model : struct % a model structure. % fluxes : double -% a vector with fluxes. +% a vector with fluxes, or a matrix with one row per reaction and one +% column per flux distribution. % % Name-Value Arguments % -------------------- @@ -22,8 +23,8 @@ function printFluxes(model, fluxes, varargin) % a string that specifies the output of each reaction (default % "%rxnID\t(%rxnName):\t%flux\n"). % metaboliteList : cell -% cell array of metabolite names. Only reactions involving any of these -% metabolites will be printed. +% cell array of metabolite names (case insensitive). Only reactions +% involving any of these metabolites are printed. % % Notes % ----- @@ -36,17 +37,17 @@ function printFluxes(model, fluxes, varargin) % - %obj : objective coefficient % - %eqn : equation % - %flux : flux -% - %element : equation using the metabolite formulas rather than metabolite +% - %element : equation using the metabolite formulas in place of metabolite % names % - %unbalanced : "(*)" if the reaction is unbalanced and "(-)" if it could % not be parsed -% - %lumped : equation where the elemental compositions for the left/right -% hand sides are lumped +% - %lumped : equation where the elemental compositions of the left-hand and +% right-hand sides are lumped % % Examples % -------- -% printFluxes(model, fluxes, onlyExchange, cutOffFlux, outputFile, ... -% outputString, metaboliteList); +% printFluxes(model, fluxes, 'onlyExchange', false, 'cutOffFlux', 10^-6, ... +% 'outputFile', 'fluxes.txt', 'metaboliteList', {'glucose'}); p=parseRAVENargs(varargin, {'onlyExchange',true; 'cutOffFlux',10^-8; 'outputFile',[]; 'outputString',[]; 'metaboliteList',{}}); onlyExchange=p.onlyExchange; diff --git a/queries/printModelStats.m b/src/queries/printModelStats.m similarity index 88% rename from queries/printModelStats.m rename to src/queries/printModelStats.m index c0c8f4ed..595edf9c 100755 --- a/queries/printModelStats.m +++ b/src/queries/printModelStats.m @@ -1,5 +1,10 @@ function printModelStats(model, varargin) -% printModelStats Print some statistics about a model to the screen. +% printModelStats Print statistics about a model to the screen. +% +% Prints the numbers of genes, reactions and metabolites in total and per +% compartment, the number of unique EC codes (if the eccodes field exists), +% the number of biochemically unique reactions and the number of unique +% metabolite names. % % Parameters % ---------- @@ -9,11 +14,12 @@ function printModelStats(model, varargin) % Name-Value Arguments % -------------------- % printModelIssues : logical -% true if information about unconnected reactions/metabolites and -% elemental balancing should be printed (default false). +% true if information about unused metabolites, empty reactions, +% dead-end reactions and metabolites, and elemental balancing is printed +% (default false). % printDetails : logical -% true if detailed information should be printed about model issues. -% Only used if printModelIssues is true (default true). +% true if the individual metabolites and reactions of each issue are +% listed. Used only if printModelIssues is true (default true). % % Examples % -------- @@ -21,9 +27,9 @@ function printModelStats(model, varargin) % % See also % -------- -% modelSummary : the shorter view, without the per-compartment breakdown, but -% adding the objective function and - given a flux vector - the objective -% value and the net uptake and secretion. +% modelSummary : prints the model dimensions without the per-compartment +% breakdown, the objective function and, given a flux vector, the +% objective value and the net uptake and secretion. p=parseRAVENargs(varargin, {'printModelIssues',false; 'printDetails',true}); printModelIssues=p.printModelIssues; diff --git a/reconstruction/guessComposition.m b/src/reconstruction/guessComposition.m similarity index 85% rename from reconstruction/guessComposition.m rename to src/reconstruction/guessComposition.m index a2ccb2c6..2eeaf741 100755 --- a/reconstruction/guessComposition.m +++ b/src/reconstruction/guessComposition.m @@ -1,8 +1,8 @@ function [model, guessedFor, couldNotGuess]=guessComposition(model, varargin) % guessComposition Guess the composition of metabolites without one. % -% Attempts to guess the composition of metabolites without information about -% elemental composition. +% Derives the elemental composition of metabolites without a formula from the +% reactions they participate in, and writes it to the metFormulas field. % % Parameters % ---------- @@ -12,17 +12,18 @@ % Name-Value Arguments % -------------------- % printResults : logical -% true if the output should be printed (default true). +% true if the predicted compositions are printed (default true). % % Returns % ------- % model : struct -% a model structure with information about elemental composition added. -% guessedFor : double -% indexes for the metabolites for which a composition could be guessed. -% couldNotGuess : double -% indexes for the metabolites for which no composition could be -% assigned. +% a model structure with the predicted formulas added to metFormulas, for +% all metabolites that share the metabolite name. +% guessedFor : cell +% metabolite names for which a composition could be guessed. +% couldNotGuess : cell +% metabolite names, among those without a formula in the input model, +% for which no composition could be assigned. % % Examples % -------- @@ -33,12 +34,13 @@ % A metabolite's composition can only be resolved from a reaction in which % every other participating metabolite already has a known composition; % the function repeats this until no further metabolites can be resolved. -% It requires that the existing compositions and reactions are correct, -% and throws an error if a metabolite would need inconsistent -% compositions in different reactions -- so it can also be used to check -% a model for such errors. Note that successful completion does not -% guarantee correctness if the pre-existing compositions were themselves -% wrong. +% It requires that the existing compositions and reactions are correct. If a +% metabolite would need different compositions in different reactions, or a +% reaction gives both positive and negative atom counts, the function issues +% a warning and assigns no composition to that metabolite, so the warnings +% can also be used to check a model for such inconsistencies. Completion +% without warnings does not show that the pre-existing compositions are +% correct. p=parseRAVENargs(varargin, {'printResults',true}); printResults=p.printResults; diff --git a/reconstruction/homology/getBlast.m b/src/reconstruction/homology/getBlast.m similarity index 80% rename from reconstruction/homology/getBlast.m rename to src/reconstruction/homology/getBlast.m index a85e00e4..f87daa2d 100755 --- a/reconstruction/homology/getBlast.m +++ b/src/reconstruction/homology/getBlast.m @@ -19,25 +19,28 @@ % Name-Value Arguments % -------------------- % developMode : logical -% true if blastReport should be generated that is used in the unit -% testing function for BLAST+ (default false). +% true if blastReport is filled, as used in the unit test for BLAST+ +% (default false). % hideVerbose : logical % true if no status messages should be printed (default false). % % Returns % ------- % blastStructure : struct -% structure containing the bidirectional homology measurements that -% can be used by getModelFromHomology. +% structure array with one element per search direction and template +% organism (2 times the number of modelIDs), containing the bidirectional +% homology measurements (fromId, toId, fromGenes, toGenes, evalue, +% identity, aligLen, bitscore and ppos) for use by getModelFromHomology. % blastReport : struct % structure containing MD5 hashes for FASTA database files and -% non-parsed BLAST output data. Will be blank if developMode is false. +% unparsed BLAST output data. The fields are empty if developMode is +% false. % % Notes % ----- % This function calls BLAST+ to perform a bidirectional homology test % between the organism of interest and a set of other organisms using -% standard settings. The only filtering this function does is the removal +% standard settings. The only filtering this function applies is the removal % of hits with an E-value higher than 10e-5. % % Examples @@ -47,7 +50,7 @@ % % See also % -------- -% getModelFromHomology, getDiamond +% getModelFromHomology, getDiamond, runSuppOrth p=parseRAVENargs(varargin, {'developMode',false; 'hideVerbose',false}); developMode=p.developMode; @@ -109,7 +112,7 @@ end %Fetch the BLAST+ binaries on demand if they are not already present -if ~exist(fullfile(ravenPath,'software','blast+',['blastp' binEnd]),'file') +if ~exist(fullfile(ravenPath,'src','external','blast+',['blastp' binEnd]),'file') downloadRavenBinaries({'blast+'}); end @@ -121,7 +124,7 @@ %Create a database for the new organism and blast each of the refFastaFiles %against it -[status, message]=system(['"' fullfile(ravenPath,'software','blast+',['makeblastdb' binEnd]) '" -in "' fastaFile{1} '" -out "' fullfile(tmpDB, 'tmpDB') '" -dbtype prot']); +[status, message]=system(['"' fullfile(ravenPath,'src','external','blast+',['makeblastdb' binEnd]) '" -in "' fastaFile{1} '" -out "' fullfile(tmpDB, 'tmpDB') '" -dbtype prot']); if developMode blastReport.dbHashes.phr{numel(blastReport.dbHashes.phr)+1}=filemd5(fullfile(tmpDB, 'tmpDB.phr')); blastReport.dbHashes.pot{numel(blastReport.dbHashes.pot)+1}=filemd5(fullfile(tmpDB, 'tmpDB.pot')); @@ -136,7 +139,7 @@ if ~hideVerbose fprintf(['BLASTing "' modelIDs{i} '" against "' organismID{1} '"..\n']); end - [status, message]=system(['"' fullfile(ravenPath,'software','blast+',['blastp' binEnd]) '" -query "' refFastaFiles{i} '" -out "' outFile '_' num2str(i) '" -db "' fullfile(tmpDB, 'tmpDB') '" -evalue 10e-5 -outfmt "10 qseqid sseqid evalue pident length bitscore ppos" -num_threads "' cores '"']); + [status, message]=system(['"' fullfile(ravenPath,'src','external','blast+',['blastp' binEnd]) '" -query "' refFastaFiles{i} '" -out "' outFile '_' num2str(i) '" -db "' fullfile(tmpDB, 'tmpDB') '" -evalue 10e-5 -outfmt "10 qseqid sseqid evalue pident length bitscore ppos" -num_threads "' cores '"']); if developMode blastReport.blastTxtOutput{numel(blastReport.blastTxtOutput)+1}=importdata([outFile '_' num2str(i)]); end @@ -152,11 +155,11 @@ if ~hideVerbose fprintf(['BLASTing "' organismID{1} '" against "' modelIDs{i} '"..\n']); end - [status, message]=system(['"' fullfile(ravenPath,'software','blast+',['makeblastdb' binEnd]) '" -in "' refFastaFiles{i} '" -out "' fullfile(tmpDB, 'tmpDB') '" -dbtype prot']); + [status, message]=system(['"' fullfile(ravenPath,'src','external','blast+',['makeblastdb' binEnd]) '" -in "' refFastaFiles{i} '" -out "' fullfile(tmpDB, 'tmpDB') '" -dbtype prot']); if status~=0 error('makeblastdb did not run successfully, error:\n%s',strip(message)) end - [status, message]=system(['"' fullfile(ravenPath,'software','blast+',['blastp' binEnd]) '" -query "' fastaFile{1} '" -out "' outFile '_r' num2str(i) '" -db "' fullfile(tmpDB, 'tmpDB') '" -evalue 10e-5 -outfmt "10 qseqid sseqid evalue pident length bitscore ppos" -num_threads "' cores '"']); + [status, message]=system(['"' fullfile(ravenPath,'src','external','blast+',['blastp' binEnd]) '" -query "' fastaFile{1} '" -out "' outFile '_r' num2str(i) '" -db "' fullfile(tmpDB, 'tmpDB') '" -evalue 10e-5 -outfmt "10 qseqid sseqid evalue pident length bitscore ppos" -num_threads "' cores '"']); if developMode blastReport.dbHashes.phr{numel(blastReport.dbHashes.phr)+1}=filemd5(fullfile(tmpDB, 'tmpDB.phr')); blastReport.dbHashes.pot{numel(blastReport.dbHashes.pot)+1}=filemd5(fullfile(tmpDB, 'tmpDB.pot')); diff --git a/reconstruction/homology/getDiamond.m b/src/reconstruction/homology/getDiamond.m similarity index 75% rename from reconstruction/homology/getDiamond.m rename to src/reconstruction/homology/getDiamond.m index bea379d5..09fca52b 100755 --- a/reconstruction/homology/getDiamond.m +++ b/src/reconstruction/homology/getDiamond.m @@ -19,28 +19,30 @@ % Name-Value Arguments % -------------------- % developMode : logical -% true if blastReport should be generated that is used in the unit -% testing function for DIAMOND (default false). +% true if diamondReport is filled, as used in the unit test for DIAMOND +% (default false). % hideVerbose : logical % true if no status messages should be printed (default false). % % Returns % ------- % blastStructure : struct -% structure containing the bidirectional homology measurements which -% are used by getModelFromHomology. +% structure array with one element per search direction and template +% organism (2 times the number of modelIDs), containing the bidirectional +% homology measurements (fromId, toId, fromGenes, toGenes, evalue, +% identity, aligLen, bitscore and ppos) for use by getModelFromHomology. % diamondReport : struct % structure containing MD5 hashes for FASTA database files and -% non-parsed BLAST output data. Will be blank if developMode is false. +% unparsed DIAMOND output data. The fields are empty if developMode is +% false. % % Notes % ----- % This function calls DIAMOND to perform a bidirectional homology search % between the organism of interest and a set of other organisms using the -% "--more-sensitive" setting from DIAMOND. For the most sensitive results, -% the use of getBlast() is adviced, however, getDiamond() is a fast -% alternative (>15x faster). The blastStructure generated is in the same -% format as those obtained from getBlast(). +% "--more-sensitive" setting from DIAMOND. getBlast() gives the most +% sensitive results; getDiamond() is >15x faster. The blastStructure is in +% the same format as that obtained from getBlast(). % % Examples % -------- @@ -49,7 +51,7 @@ % % See also % -------- -% getModelFromHomology, getBlast +% getModelFromHomology, getBlast, runSuppOrth p=parseRAVENargs(varargin, {'developMode',false; 'hideVerbose',false}); developMode=p.developMode; @@ -98,7 +100,7 @@ end %Fetch the DIAMOND binary on demand if it is not already present -if ~exist(fullfile(ravenPath,'software','diamond',['diamond' binEnd]),'file') +if ~exist(fullfile(ravenPath,'src','external','diamond',['diamond' binEnd]),'file') downloadRavenBinaries({'diamond'}); end @@ -110,7 +112,7 @@ %Create a database for the new organism and blast each of the refFastaFiles %against it -[status, message]=system(['"' fullfile(ravenPath,'software','diamond',['diamond' binEnd]) '" makedb --in "' fastaFile{1} '" --db "' fullfile(tmpDB) '"']); +[status, message]=system(['"' fullfile(ravenPath,'src','external','diamond',['diamond' binEnd]) '" makedb --in "' fastaFile{1} '" --db "' fullfile(tmpDB) '"']); if developMode diamondReport.dbHashes{numel(diamondReport.dbHashes)+1} = char(regexp(message,'[a-f0-9]{32}','match')); end @@ -122,7 +124,7 @@ if ~hideVerbose fprintf(['Running DIAMOND blastp with "' modelIDs{i} '" against "' organismID{1} '"..\n']); end - [status, message]=system(['"' fullfile(ravenPath,'software','diamond',['diamond' binEnd]) '" blastp --query "' refFastaFiles{i} '" --out "' outFile '_' num2str(i) '" --db "' fullfile(tmpDB) '" --more-sensitive --outfmt 6 qseqid sseqid evalue pident length bitscore ppos --threads ' cores ]); + [status, message]=system(['"' fullfile(ravenPath,'src','external','diamond',['diamond' binEnd]) '" blastp --query "' refFastaFiles{i} '" --out "' outFile '_' num2str(i) '" --db "' fullfile(tmpDB) '" --more-sensitive --outfmt 6 qseqid sseqid evalue pident length bitscore ppos --threads ' cores ]); if developMode diamondReport.diamondTxtOutput{numel(diamondReport.diamondTxtOutput)+1}=importdata([outFile '_' num2str(i)]); end @@ -138,11 +140,11 @@ if ~hideVerbose fprintf(['Running DIAMOND blastp with "' organismID{1} '" against "' modelIDs{i} '"..\n']); end - [status, message1]=system(['"' fullfile(ravenPath,'software','diamond',['diamond' binEnd]) '" makedb --in "' refFastaFiles{i} '" --db "' fullfile(tmpDB) '"']); + [status, message1]=system(['"' fullfile(ravenPath,'src','external','diamond',['diamond' binEnd]) '" makedb --in "' refFastaFiles{i} '" --db "' fullfile(tmpDB) '"']); if status~=0 error('DIAMOND makedb did not run successfully, error:\n%s',strip(message1)) end - [status, message]=system(['"' fullfile(ravenPath,'software','diamond',['diamond' binEnd]) '" blastp --query "' fastaFile{1} '" --out "' outFile '_r' num2str(i) '" --db "' fullfile(tmpDB) '" --more-sensitive --outfmt 6 qseqid sseqid evalue pident length bitscore ppos --threads ' cores]); + [status, message]=system(['"' fullfile(ravenPath,'src','external','diamond',['diamond' binEnd]) '" blastp --query "' fastaFile{1} '" --out "' outFile '_r' num2str(i) '" --db "' fullfile(tmpDB) '" --more-sensitive --outfmt 6 qseqid sseqid evalue pident length bitscore ppos --threads ' cores]); if developMode diamondReport.dbHashes{numel(diamondReport.dbHashes)+1} = char(regexp(message1,'[a-f0-9]{32}','match')); diamondReport.diamondTxtOutput{numel(diamondReport.diamondTxtOutput)+1}=importdata([outFile '_r' num2str(i)]); diff --git a/reconstruction/homology/getModelFromHomology.m b/src/reconstruction/homology/getModelFromHomology.m similarity index 69% rename from reconstruction/homology/getModelFromHomology.m rename to src/reconstruction/homology/getModelFromHomology.m index e6cbca09..41101910 100755 --- a/reconstruction/homology/getModelFromHomology.m +++ b/src/reconstruction/homology/getModelFromHomology.m @@ -11,7 +11,8 @@ % a cell array of model structures to build the model from. These % models must be sorted by importance in decreasing order. % blastStructure : struct -% a blastStructure as produced by getBlast or makeFakeBlastStructure. +% a blastStructure as produced by getBlast, getDiamond, +% hitsFromSuppOrth, runSuppOrth or makeFakeBlastStructure. % getModelFor : char % a three-four letter abbreviation of the organism to build a model % for. Must have BLASTP hits in both directions to the organisms in @@ -20,28 +21,30 @@ % Name-Value Arguments % -------------------- % preferredOrder : cell -% the order in which reactions should be added from the models. If not -% supplied, reactions will be included from all models, otherwise one -% gene will only result in reactions from one model (default {}). +% the order in which reactions are added from the models, as a cell array +% of model IDs. If not supplied, reactions are included from all models, +% otherwise one gene results in reactions from one model only +% (default {}). % strictness : double -% integer that specifies which reactions should be included (default 1): +% integer that specifies which reactions are included (default 1): % -% - 1 : map new genes to old for all pairs which have acceptable BLASTP -% results in both directions. -% - 2 : map new genes to old for all pairs which have acceptable BLASTP -% results in the correspondent direction (mapping can be done in the -% opposite direction, see mapNewGenesToOld below). -% - 3 : check all BLASTP results and retain only the best hit (by -% bitscore) for each gene pair in each direction separately, then map -% new genes to old for all pairs which have acceptable BLASTP results -% in both directions. +% - 1 : map new genes to old for all pairs which have BLASTP results +% that pass the filters (maxE, minLen, minIde) in both directions. +% - 2 : map new genes to old for all pairs which have BLASTP results +% that pass the filters in one direction, selected by mapNewGenesToOld. +% - 3 : apply the filters (maxE, minLen, minIde), retain only the best hit +% (highest bitscore, ties included) among the hits that passed them for +% each query gene in each direction separately, then map new genes to +% old for all pairs which have BLASTP results in both directions. % onlyGenesInModels : logical % consider BLASTP results only for genes that exist in the models. This % tends to import a larger fraction from the existing models but may -% give less reliable results. Has effect only if strictness=3 +% give less reliable results. Changes the result only if strictness=3 % (default false). % maxE : double -% only look at genes with E-values <= this value (default 10^-30). +% only look at genes with E-values < this value (default 10^-30). In a +% benchmark against KEGG and OMA orthology, with the minIde filter in +% place, values between 10^-4 and 10^-50 gave the same result. % minLen : double % only look at genes with alignment length >= this value (default 100). % Measured against KEGG and OMA orthology across four organisms: any @@ -49,20 +52,37 @@ % real orthologs without reducing the number of wrong ones. % minIde : double % only look at genes with identity >= this value (default 40 (%)). -% The filter that decides the outcome, and 40 is the measured optimum -% once a wrongly transferred reaction is treated as worse than a missing -% one. maxE, by contrast, changes nothing between 10^-4 and 10^-50. +% This filter determines the result. In the calibration, with a wrongly +% transferred reaction counted as worse than a missing one, 40 lies in the +% 35 to 45 range that the KEGG and OMA references favour. % mapNewGenesToOld : logical -% determines how to match genes if not looking at only 1-1 orthologs. -% Either map the new genes to the old or old genes to new. The default -% is to map the new genes (default true). +% determines how to match genes when strictness is 2: true maps the new +% genes to the old, false maps the old genes to the new (default true). +% complexPolicy : char +% what happens to an "and"-linked subunit without an ortholog (case +% insensitive): "flag" keeps the reaction with the subunit renamed +% OLD_MODELID_geneName, for review; "keep" drops that subunit; +% "drop" drops the whole reaction (default "flag"). +% keepGeneFree : logical +% also transfer the template reactions that have no grRule (spontaneous, +% transport, exchange, artificial). By default they are dropped, because +% no gene supports them in the new organism. When the template is a +% curated reference whose gene-free reactions belong in every derived +% model, true transfers them. With several templates, a gene-free +% reaction comes in once per template (default false). +% preserveNotes : logical +% keep rxnNotes and rxnConfidenceScores of each transferred reaction from +% the template. By default the notes say the reaction was included by +% getModelFromHomology and the confidence score is 2. The rxnReferences of +% the template reactions are always kept (default false). % % Returns % ------- % draftModel : struct % a model structure for the new organism. % hitGenes : struct -% collect the old and new genes. +% structure with the fields oldGenes (the template genes that were +% mapped) and newGenes (the corresponding genes of the new organism). % % Examples % -------- @@ -70,18 +90,37 @@ % % Notes % ----- -% The models in the "models" structure should have named the metabolites in -% the same manner, have their reversible reactions in the same direction -% (run sortModel), and use the same compartment names. To avoid keeping -% unneccesary old genes, the models should not have "or"-relations in their +% The models in the "models" structure should name the metabolites in the +% same manner, have their reversible reactions in the same direction (run +% sortModel), and use the same compartment names. To avoid keeping +% unnecessary old genes, the models should not have "or"-relations in their % grRules (use expandModel). % +% The gene ids of the new organism in blastStructure become its gene ids in +% the draft model, so they cannot contain whitespace, parentheses, "|" or +% "&". FASTA headers from UniProt, such as "sp|P33284|HXK_KLULA", should be +% reduced to the accession first. +% % The resulting draft model contains only reactions associated with -% orthologous genes. The old (original) genes involved in "and" relations -% in grRules without any orthologs are still included in the draft model as -% OLD_MODELID_geneName. +% orthologous genes, plus the gene-free ones with keepGeneFree. With +% complexPolicy "flag", the old (original) genes involved in "and" relations +% in grRules without any orthologs are included in the draft model as +% OLD_MODELID_geneName. A gene that several template genes map to appears +% once in a grRule. The draft does not keep the version, date or +% descriptive annotation of the templates. +% +% "to" and "from" are relative to the new organism: "to" is a BLAST search from +% a template organism to the new organism, "from" is a search from the new +% organism to a template organism. +% +% The hits can also come from SuppOrth consensus orthologs (hitsFromSuppOrth, +% runSuppOrth). Their e-value, identity and alignment length are +% placeholders that pass the filters, so minSupport and the other filters of +% hitsFromSuppOrth decide which pairs are used. % -% "to" and "from" means relative to the new organism. +% See also +% -------- +% getBlast, getDiamond, hitsFromSuppOrth, runSuppOrth, makeFakeBlastStructure hitGenes.oldGenes = []; % collect the old genes from the template model (organism) hitGenes.newGenes = []; % collect the new genes of the draft model (target organism) @@ -90,7 +129,14 @@ p=parseRAVENargs(varargin, {'preferredOrder',[]; 'strictness',1; ... 'onlyGenesInModels',false; 'maxE',10^-30; 'minLen',100; 'minIde',40; ... - 'mapNewGenesToOld',true}); + 'mapNewGenesToOld',true; 'complexPolicy','flag'; 'keepGeneFree',false; ... + 'preserveNotes',false}); +complexPolicy=lower(char(p.complexPolicy)); +if ~ismember(complexPolicy,{'flag','keep','drop'}) + error('complexPolicy must be "flag", "keep" or "drop", not "%s"',complexPolicy); +end +keepGeneFree=p.keepGeneFree; +preserveNotes=p.preserveNotes; preferredOrder=p.preferredOrder; if isempty(preferredOrder) preferredOrder=[]; @@ -194,14 +240,20 @@ blastStructure(i).ppos(~indexes)=[]; end -%Remove all reactions from the models that have no genes encoding for them. -%Also remove all genes that encode for no reactions. There should not be any -%but there might be mistakes +%Remove all reactions from the models that have no genes encoding for them, +%unless keepGeneFree. Also remove all genes that encode for no reactions. +%There should not be any but there might be mistakes +geneFreeRxns=cell(numel(models),1); +geneFreeRxns(:)={{}}; for i=1:numel(models) [hasGenes, ~]=find(models{i}.rxnGeneMat); hasNoGenes=1:numel(models{i}.rxns); hasNoGenes(hasGenes)=[]; - models{i}=removeReactions(models{i},hasNoGenes,true,true); + if keepGeneFree + geneFreeRxns{i}=models{i}.rxns(hasNoGenes); + else + models{i}=removeReactions(models{i},hasNoGenes,true,true); + end end %Create a structure that contains all genes used in the blasts in any @@ -390,6 +442,17 @@ finalMappings{i}=finalMappings{i}(usedNewGenes,:); end +%Gene ids end up in grRules, which are split at whitespace and parentheses, +%and which COBRA also splits at "|" and "&" +unusable=allGenes{1}(~cellfun(@isempty,regexp(allGenes{1},'[\s()|&]','once'))); +if ~isempty(unusable) + error('RAVEN:badInput',['%d gene ids of "%s" cannot be written into a grRule, '... + 'because they contain whitespace, parentheses, "|" or "&": %s. Map them '... + 'to gene ids first, or use the accession alone for UniProt headers such '... + 'as "sp|P33284|HXK_KLULA".'],numel(unusable),getModelFor,... + strjoin(unusable(1:min(3,end))',', ')); +end + %Now is it time to choose which reactions should be included from which %models. If there is a preferred order specified then each gene can only %result in reactions from one model, otherwise they should all be included @@ -409,6 +472,7 @@ [rxnsToKeep,~] = find(models{useOrderIndexes(i)}.rxnGeneMat(:,a)); rxnsToRemove = repmat(1,numel(models{useOrderIndexes(i)}.rxns),1); rxnsToRemove(rxnsToKeep) = 0; + rxnsToRemove(ismember(models{useOrderIndexes(i)}.rxns,geneFreeRxns{useOrderIndexes(i)})) = 0; rxnsToRemove = find(rxnsToRemove); models{useOrderIndexes(i)}=removeReactions(models{useOrderIndexes(i)},rxnsToRemove,true,true,true); end @@ -433,7 +497,9 @@ %Remove all the genes that were already found and add the other %ones to allUsedGenes - models{useOrderIndexes(i)}=removeGenes(models{useOrderIndexes(i)},allGenes{i+1}(genesToDelete),true,true,false); + if ~isempty(genesToDelete) + models{useOrderIndexes(i)}=removeGenes(models{useOrderIndexes(i)},allGenes{i+1}(genesToDelete),true,true,false); + end allUsedGenes(usedGenes)=true; %Remove the deleted genes from finalMappings and allGenes. @@ -571,6 +637,27 @@ draftModel.grRules{oldI}=gprBuildRule(tkns); end +%Apply the complex policy to the OLD_ genes left in "and" relations, and +%write each grRule without repeated genes or redundant brackets +toRemove=false(numel(draftModel.rxns),1); +for j=1:numel(draftModel.grRules) + rule=draftModel.grRules{j}; + if isempty(rule) + continue + end + if contains(rule,'OLD_') && strcmp(complexPolicy,'drop') + toRemove(j)=true; + elseif contains(rule,'OLD_') && strcmp(complexPolicy,'keep') + draftModel.grRules{j}=gprSimplify(rule,'OLD_'); + toRemove(j)=isempty(draftModel.grRules{j}); + else + draftModel.grRules{j}=gprSimplify(rule,''); + end +end +if any(toRemove) + draftModel=removeReactions(draftModel,draftModel.rxns(toRemove),true,true,true); +end + %Change name of the resulting model draftModel.id=getModelFor; name='Generated by getModelFromHomology using '; @@ -582,10 +669,21 @@ end end draftModel.name=name; -draftModel.rxnNotes=cell(length(draftModel.rxns),1); -draftModel.rxnNotes(:)={'Included by getModelFromHomology'}; -draftModel.rxnConfidenceScores=NaN(length(draftModel.rxns),1); -draftModel.rxnConfidenceScores(:)=2; +%The draft is a new model: the templates' version, date and description are +%not its own +draftModel=rmfield(draftModel,intersect({'version','date','description'},fieldnames(draftModel))); +if isfield(draftModel,'annotation') + descriptive={'taxonomy','sourceUrl','note','givenName','familyName','authors','email','organization'}; + draftModel.annotation=rmfield(draftModel.annotation,intersect(descriptive,fieldnames(draftModel.annotation))); +end +if ~preserveNotes || ~isfield(draftModel,'rxnNotes') + draftModel.rxnNotes=cell(length(draftModel.rxns),1); + draftModel.rxnNotes(:)={'Included by getModelFromHomology'}; +end +if ~preserveNotes || ~isfield(draftModel,'rxnConfidenceScores') + draftModel.rxnConfidenceScores=NaN(length(draftModel.rxns),1); + draftModel.rxnConfidenceScores(:)=2; +end draftModel=deleteUnusedGenes(draftModel,0); %Standardize grRules and notify if problematic grRules are found [draftModel.grRules,draftModel.rxnGeneMat]=standardizeGrRules(draftModel,false); @@ -646,3 +744,116 @@ end end end + + +function rule=gprSimplify(rule,removePrefix) +% Rewrite a grRule as a tree: genes starting with removePrefix (if any) are +% dropped from their group, groups of the same operator are flattened, +% repeated genes or groups are removed, and brackets appear only around a +% group of the other operator. A rule with no gene left becomes ''. +tokens=gprTokenize(rule); +if isempty(tokens) + rule=''; + return +end +[node,~]=gprParseOr(tokens,1); +node=gprPrune(node,removePrefix); +if isempty(node) + rule=''; +else + rule=gprRender(node,''); +end +end + + +function [node,pos]=gprParseOr(tokens,pos) +[node,pos]=gprParseAnd(tokens,pos); +kids={node}; +while pos<=numel(tokens) && strcmpi(tokens{pos},'or') + [node,pos]=gprParseAnd(tokens,pos+1); + kids{end+1}=node; %#ok +end +if numel(kids)>1 + node=struct('op','or','gene','','kids',{kids}); +end +end + + +function [node,pos]=gprParseAnd(tokens,pos) +[node,pos]=gprParseAtom(tokens,pos); +kids={node}; +while pos<=numel(tokens) && strcmpi(tokens{pos},'and') + [node,pos]=gprParseAtom(tokens,pos+1); + kids{end+1}=node; %#ok +end +if numel(kids)>1 + node=struct('op','and','gene','','kids',{kids}); +end +end + + +function [node,pos]=gprParseAtom(tokens,pos) +if pos>numel(tokens) + error('Malformed grRule: it ends with an operator'); +end +if strcmp(tokens{pos},'(') + [node,pos]=gprParseOr(tokens,pos+1); + if pos>numel(tokens) || ~strcmp(tokens{pos},')') + error('Malformed grRule: unbalanced brackets'); + end + pos=pos+1; +else + node=struct('op','gene','gene',tokens{pos},'kids',{{}}); + pos=pos+1; +end +end + + +function node=gprPrune(node,removePrefix) +if strcmp(node.op,'gene') + if ~isempty(removePrefix) && startsWith(node.gene,removePrefix) + node=[]; + end + return +end +kids={}; +seen={}; +for k=1:numel(node.kids) + kid=gprPrune(node.kids{k},removePrefix); + if isempty(kid) + continue + end + if strcmp(kid.op,node.op) + parts=kid.kids; + else + parts={kid}; + end + for m=1:numel(parts) + key=gprRender(parts{m},node.op); + if ~ismember(key,seen) + seen{end+1}=key; %#ok + kids{end+1}=parts{m}; %#ok + end + end +end +if isempty(kids) + node=[]; +elseif numel(kids)==1 + node=kids{1}; +else + node.kids=kids; +end +end + + +function rule=gprRender(node,parentOp) +if strcmp(node.op,'gene') + rule=node.gene; + return +end +parts=cellfun(@(k) gprRender(k,node.op),node.kids,'UniformOutput',false); +rule=strjoin(parts,[' ' node.op ' ']); +if ~isempty(parentOp) && ~strcmp(parentOp,node.op) + rule=['(' rule ')']; +end +end diff --git a/src/reconstruction/homology/hitsFromSuppOrth.m b/src/reconstruction/homology/hitsFromSuppOrth.m new file mode 100644 index 00000000..37fb1e2f --- /dev/null +++ b/src/reconstruction/homology/hitsFromSuppOrth.m @@ -0,0 +1,340 @@ +function [blastStructure,suppOrthInfo]=hitsFromSuppOrth(source,organismID,varargin) +% hitsFromSuppOrth Read SuppOrth consensus orthologs into a blastStructure. +% +% SuppOrth (https://github.com/fma91/SuppOrth) runs several ortholog +% predictors (OrthoFinder, SonicParanoid2, Broccoli, and optionally an +% imported OMA run) on a set of proteomes and writes suppOrth.tsv: one row +% per cross-species protein pair, with the number of predictors that +% support it. This function turns that table into a blastStructure for +% getModelFromHomology. +% +% Parameters +% ---------- +% source : char or table +% path to suppOrth.tsv, or a MATLAB table with its columns. Protein +% ids are read as text, so ids such as "NA" or "0123" are kept. +% organismID : char +% SuppOrth species label of the organism to build a model for: the +% FASTA file name without extension, or the --labels value. It becomes +% fromId and toId unless speciesToModel maps it to another id. +% +% Name-Value Arguments +% -------------------- +% speciesToModel : cell or containers.Map +% SuppOrth species label to model id, as an N-by-2 cell array or a +% containers.Map. A label mapped to '' is left out. When given, every +% label in the table must be included. When empty, labels are used as +% model ids (default []). +% minSupport : double +% minimum number of predictors that found the pair (Lv_support). +% SuppOrth has already filtered the table with its own --supportLv and +% --identity_filter; this filter is applied in addition (default 1). +% requireTools : cell +% predictors that must all have found the pair, by column name +% ('orthofinder', 'sonicparanoid', 'broccoli', 'oma') (default {}). +% minIdentity : double +% minimum SuppOrth identity. A pair without an alignment hit fails this +% filter (default [], no filter). +% maxEvalue : double +% maximum SuppOrth e-value. A pair without an alignment hit fails this +% filter (default [], no filter). +% proteinToGene : cell, containers.Map or char +% protein id to model gene id, as an N-by-2 cell array, a +% containers.Map, a JSON file with one object of strings, or a +% two-column tab-separated file (lines starting with # are skipped). +% Proteins missing from the map keep their id, and their number is +% reported in a warning. A gene pair takes the highest support over its +% protein pairs, the union of their predictors, the highest identity +% and bitscore, and the lowest e-value; these values can come from +% different protein pairs (default [], no mapping). +% +% Returns +% ------- +% blastStructure : struct +% one element per search direction between organismID and each other +% species, as from getBlast or getDiamond. Every pair is written in +% both directions, so strictness 1 and 2 of getModelFromHomology give +% the same result. evalue, identity, aligLen and ppos are placeholders +% (0, 100, 1000, 100) that pass the filters of getModelFromHomology; +% bitscore is SuppOrth's value (0 when it has none), which strictness 3 +% ranks by. +% suppOrthInfo : table +% one row per gene pair, with modelId, newGene, templateGene, support, +% supportedBy, identity, evalue and bitscore (SuppOrth's values). +% +% Notes +% ----- +% Only pairs between organismID and another species are kept. Species that +% are not templates in getModelFromHomology have to be left out with +% speciesToModel, because getModelFromHomology requires every id in the +% blastStructure to be the new organism or a template. +% +% Examples +% -------- +% blastStructure = hitsFromSuppOrth('out/suppOrth.tsv','klac',... +% 'minSupport',2,'proteinToGene','klac_genes.tsv'); +% +% See also +% -------- +% runSuppOrth, getModelFromHomology, getDiamond + +p=parseRAVENargs(varargin, {'speciesToModel',[]; 'minSupport',1; ... + 'requireTools',{}; 'minIdentity',[]; 'maxEvalue',[]; 'proteinToGene',[]}); +organismID=char(organismID); +requireTools=cellstr(p.requireTools); +if isempty(p.requireTools) + requireTools={}; +end + +T=readTable(source); +columns=T.Properties.VariableNames; + +labels=unique([T.Species1;T.Species2]); +if ~ismember(organismID,labels) + error('RAVEN:badInput','organismID "%s" is not a species in the table: %s',... + organismID,strjoin(labels,', ')); +end +if isempty(p.speciesToModel) + idOf=containers.Map(labels,labels); +else + idOf=toMap(p.speciesToModel); + unmapped=labels(~isKey(idOf,labels)); + if ~isempty(unmapped) + error('RAVEN:badInput',['speciesToModel has no entry for %s; map each '... + 'label to a model id, or to '''' to leave it out.'],strjoin(unmapped,', ')); + end + if isempty(idOf(organismID)) + error('RAVEN:badInput','speciesToModel maps organismID "%s" to ''''.',organismID); + end +end + +%The per-predictor 0/1 columns are those between SupportedBy and Identity +alignment={'Identity','e_val','Bitscore'}; +start=find(strcmp(columns,'SupportedBy'))+1; +stop=find(strcmp(columns,'Identity'))-1; +if isempty(stop) + stop=numel(columns); +end +toolColumns=setdiff(columns(start:stop),alignment,'stable'); +unknown=setdiff(requireTools,toolColumns,'stable'); +if ~isempty(unknown) + error('RAVEN:badInput','requireTools %s not in the table; its predictors are %s',... + strjoin(unknown,', '),strjoin(toolColumns,', ')); +end + +keep=T.Lv_support>=p.minSupport; +for i=1:numel(requireTools) + keep=keep & toNumber(T.(requireTools{i}))==1; +end +if ~isempty(p.minIdentity) + keep=keep & T.Identity>=p.minIdentity; %NaN fails +end +if ~isempty(p.maxEvalue) + keep=keep & T.e_val<=p.maxEvalue; %NaN fails +end +T=T(keep,:); + +%Orient every pair as (organism protein, template species, template protein) +first=strcmp(T.Species1,organismID) & ~strcmp(T.Species2,organismID); +second=strcmp(T.Species2,organismID) & ~strcmp(T.Species1,organismID); +newProt=[T.Protein1(first);T.Protein2(second)]; +label=[T.Species2(first);T.Species1(second)]; +template=[T.Protein2(first);T.Protein1(second)]; +support=[T.Lv_support(first);T.Lv_support(second)]; +supportedBy=[T.SupportedBy(first);T.SupportedBy(second)]; +identity=[T.Identity(first);T.Identity(second)]; +evalue=[T.e_val(first);T.e_val(second)]; +bitscore=[T.Bitscore(first);T.Bitscore(second)]; +modelId=values(idOf,label); +modelId=modelId(:); +I=~cellfun(@isempty,modelId); +[newProt,template,modelId,support,supportedBy,identity,evalue,bitscore]=... + deal(newProt(I),template(I),modelId(I),support(I),supportedBy(I),... + identity(I),evalue(I),bitscore(I)); + +if ~isempty(p.proteinToGene) + geneOf=toMap(p.proteinToGene); + proteins=unique([newProt;template]); + nMissing=sum(~isKey(geneOf,proteins)); + if nMissing>0 + warning('RAVEN:hitsFromSuppOrth:unmappedProteins',['%d of %d proteins are not '... + 'in proteinToGene and keep their protein id.'],nMissing,numel(proteins)); + end + newProt=mapIds(geneOf,newProt); + template=mapIds(geneOf,template); +end + +if isempty(newProt) + error('RAVEN:badInput',['No SuppOrth pairs between "%s" and a template species '... + 'remain after filtering.'],organismID); +end + +%One row per gene pair, sorted by model, new gene and template gene +key=strcat(modelId,char(31),newProt,char(31),template); +[key,firstRow,group]=unique(key); +info=table(); +info.modelId=modelId(firstRow); +info.newGene=newProt(firstRow); +info.templateGene=template(firstRow); +info.support=accumarray(group,support,[],@max); +rows=accumarray(group,(1:numel(group))',[],@(r) {r}); +info.supportedBy=cellfun(@(r) joinLabels(supportedBy(r)),rows,'UniformOutput',false); +info.identity=accumarray(group,identity,[],@max); +info.evalue=accumarray(group,evalue,[],@min); +info.bitscore=accumarray(group,bitscore,[],@max); +suppOrthInfo=info; + +newId=idOf(organismID); +score=info.bitscore; +score(isnan(score))=0; +models=unique(info.modelId); +blastStructure=[]; +for direction=1:2 + for i=1:numel(models) + r=strcmp(info.modelId,models{i}); + element=[]; + if direction==1 + element.fromId=models{i}; + element.toId=newId; + element.fromGenes=info.templateGene(r); + element.toGenes=info.newGene(r); + else + element.fromId=newId; + element.toId=models{i}; + element.fromGenes=info.newGene(r); + element.toGenes=info.templateGene(r); + end + n=sum(r); + element.evalue=zeros(n,1); + element.identity=100*ones(n,1); + element.aligLen=1000*ones(n,1); + element.bitscore=score(r); + element.ppos=100*ones(n,1); + blastStructure=[blastStructure element]; %#ok + end +end +end + +function T=readTable(source) +required={'Species1','Species2','Protein1','Protein2','Lv_support','SupportedBy'}; +text=[required(1:4),{'SupportedBy'}]; +if istable(source) + T=source; +else + source=char(source); + if ~isfile(source) + error('RAVEN:badInput','SuppOrth table "%s" does not exist.',source); + end + lines=splitlines(fileread(source)); + lines=lines(~cellfun(@isempty,lines)); + header=strsplit(lines{1},'\t','CollapseDelimiters',false); + fields=regexp(lines(2:end),'\t','split'); + width=cellfun(@numel,fields); + bad=find(width~=numel(header),1); + if ~isempty(bad) + error('RAVEN:badInput','%s: line %d has %d fields, the header has %d.',... + source,bad+1,width(bad),numel(header)); + end + fields=vertcat(fields{:}); + if isempty(fields) + fields=cell(0,numel(header)); + end + T=cell2table(fields,'VariableNames',header); +end +missing=setdiff(required,T.Properties.VariableNames,'stable'); +if ~isempty(missing) + error('RAVEN:badInput',['Not a suppOrth.tsv table (SuppOrth 0.4): missing columns %s. '... + 'suppOrth.genes.tsv is not supported; use proteinToGene instead.'],strjoin(missing,', ')); +end +%Protein ids such as "NA" or "0123" stay text +for i=1:numel(text) + T.(text{i})=cellstr(string(T.(text{i}))); +end +for c={'Identity','e_val','Bitscore'} + if ismember(c{1},T.Properties.VariableNames) + T.(c{1})=toNumber(T.(c{1})); + else + T.(c{1})=nan(height(T),1); + end +end +T.Lv_support=toNumber(T.Lv_support); +end + +function x=toNumber(x) +%Read numbers exactly; empty and non-numeric fields become NaN +if ~isnumeric(x) + x=str2double(x); +end +x=double(x(:)); +end + +function m=toMap(x) +if isa(x,'containers.Map') + m=x; +elseif iscell(x) + if size(x,2)~=2 + error('RAVEN:badInput','A cell array map needs two columns.'); + end + m=containers.Map(cellstr(string(x(:,1))),cellstr(string(x(:,2)))); +elseif ischar(x) || isstring(x) + m=readMapFile(char(x)); +else + error('RAVEN:badInput','A map must be a two-column cell array, a containers.Map or a file.'); +end +end + +function m=readMapFile(file) +[~,~,ext]=fileparts(file); +if any(strcmpi(ext,{'.pkl','.pickle'})) + error('RAVEN:badInput',['%s: pickle maps are not read; use a cell array, '... + 'a JSON file or a two-column tab-separated file.'],file); +end +if ~isfile(file) + error('RAVEN:badInput','Map file "%s" does not exist.',file); +end +text=fileread(file); +if strcmpi(ext,'.json') + %Keys are protein ids, which jsondecode would turn into field names + pairs=regexp(text,'"((?:[^"\\]|\\.)*)"\s*:\s*"((?:[^"\\]|\\.)*)"','tokens'); + pairs=vertcat(pairs{:}); + if isempty(pairs) + m=containers.Map('KeyType','char','ValueType','char'); + return + end + pairs=regexprep(pairs,'\\(["\\/])','$1'); + m=containers.Map(pairs(:,1),pairs(:,2)); + return +end +lines=splitlines(text); +lines=lines(~cellfun(@isempty,strtrim(lines)) & ~startsWith(lines,'#')); +fields=regexp(lines,'\t','split'); +short=find(cellfun(@numel,fields)<2,1); +if ~isempty(short) + error('RAVEN:badInput','%s: expected two tab-separated columns, got "%s"',file,lines{short}); +end +keys=strtrim(cellfun(@(f) f{1},fields,'UniformOutput',false)); +vals=strtrim(cellfun(@(f) f{2},fields,'UniformOutput',false)); +if isempty(keys) + m=containers.Map('KeyType','char','ValueType','char'); +else + m=containers.Map(keys,vals); +end +end + +function ids=mapIds(m,ids) +known=isKey(m,ids); +if any(known) + ids(known)=values(m,ids(known)); +end +end + +function joined=joinLabels(labels) +%Union of comma-separated predictor labels, in SuppOrth's F, S, B, O order +found=strtrim(split(strjoin(labels,','),',')); +found=unique(found(~cellfun(@isempty,found))); +order={'F','S','B','O'}; +[isKnown,rank]=ismember(found,order); +known=found(isKnown); +[~,o]=sort(rank(isKnown)); +joined=strjoin([known(o);sort(found(~isKnown))],','); +end diff --git a/reconstruction/homology/makeFakeBlastStructure.m b/src/reconstruction/homology/makeFakeBlastStructure.m similarity index 77% rename from reconstruction/homology/makeFakeBlastStructure.m rename to src/reconstruction/homology/makeFakeBlastStructure.m index e7724cd9..78bdd77b 100755 --- a/reconstruction/homology/makeFakeBlastStructure.m +++ b/src/reconstruction/homology/makeFakeBlastStructure.m @@ -1,12 +1,11 @@ function blastStructure=makeFakeBlastStructure(orthologList,sourceModelID,getModelFor) % makeFakeBlastStructure Make a fake blastStructure from an ortholog list. % -% This is a structure that would normally be generated by getBlast. It -% allows to feed a predefined list of orthologs to getModelFromHomology -% while retaining the further use of that function. For this function to -% work, it is crucial that the orthologList is a cell array where the first -% column contains the genes from the source organism, and the second column -% contains the genes from the target organism. +% The structure has the format that getBlast generates. It feeds a +% predefined list of orthologs to getModelFromHomology. The orthologList +% is a cell array with at least 10 rows and two columns: the first column +% contains the genes from the source organism and the second column the +% genes from the target organism. % % Parameters % ---------- @@ -24,9 +23,10 @@ % Returns % ------- % blastStructure : struct -% a fake blastStructure, where the evalue, identity and aligLen are -% set at extreme values, such that all orthologous pairs will pass the -% filter when running getModelFromHomology. +% a fake blastStructure with one element per direction. For every pair, +% evalue is 0, identity is 100, aligLen is 1000, bitscore is 100 and +% ppos is 100. All orthologous pairs pass the filters of +% getModelFromHomology. % % Examples % -------- @@ -34,7 +34,7 @@ % % See also % -------- -% getModelFromHomology, getBlast +% getModelFromHomology, getBlast, hitsFromSuppOrth if nargin<3 error('All three parameters should be set'); diff --git a/src/reconstruction/homology/runSuppOrth.m b/src/reconstruction/homology/runSuppOrth.m new file mode 100644 index 00000000..b4039a3e --- /dev/null +++ b/src/reconstruction/homology/runSuppOrth.m @@ -0,0 +1,279 @@ +function [blastStructure,suppOrthInfo]=runSuppOrth(organismID,fastaFile,modelIDs,refFastaFiles,outDir,varargin) +% runSuppOrth Run SuppOrth on an organism and template proteomes. +% +% Runs the suppOrth command (https://github.com/fma91/SuppOrth) and reads +% its consensus orthologs with hitsFromSuppOrth. +% +% Parameters +% ---------- +% organismID : char +% the id of the organism of interest, as in getModelFromHomology. +% fastaFile : char +% a FASTA file (optionally gzipped) with the proteins of the organism. +% modelIDs : cell +% the template model ids, as "model.id" in getModelFromHomology. +% refFastaFiles : cell +% the proteome of each template, in the order of modelIDs, with FASTA +% ids equal to the gene ids of that model (or mapped to them with +% proteinToGene). +% outDir : char +% folder for SuppOrth's output. +% +% Name-Value Arguments +% -------------------- +% tools : char +% comma-separated predictors, or 'all' for orthofinder, sonicparanoid +% and broccoli (default 'all'). +% supportLv : double +% SuppOrth's --supportLv: a pair is kept if at least this many +% predictors found it, or if its identity is above identityFilter +% (default 3, or the number of tools when fewer are selected). +% identityFilter : double +% SuppOrth's --identity_filter (default 80). +% alignment : char +% SuppOrth's --alignment (default 'diamond'). +% threads : double +% number of threads (default: the cores assigned to MATLAB). +% extraArgs : cell +% further arguments for "suppOrth run", for example {'--resume'} or +% {'--oma','path/to/Output'} (default {}). +% suppOrth : char +% the suppOrth command. If empty, the RAVEN_SUPPORTH environment +% variable is used, and then suppOrth on the PATH (default ''). +% +% The name-value arguments of hitsFromSuppOrth (minSupport, requireTools, +% minIdentity, maxEvalue, proteinToGene, speciesToModel) are passed on. +% +% Returns +% ------- +% blastStructure : struct +% as from hitsFromSuppOrth, for getModelFromHomology. +% suppOrthInfo : table +% SuppOrth's values per gene pair, as from hitsFromSuppOrth. +% +% Notes +% ----- +% SuppOrth requires protein ids to be unique over all proteomes. When two +% FASTA files share an id (for example two templates of the same species), +% every id is given a "