How to contribute

How to contribute to the COBRA Toolbox

This guide explains, step by step, how to contribute code to the COBRA Toolbox: how to fork the repository, work on your changes locally, place your code in the correct folder inside src, add tests, and open a pull request (PR).

1. Overview of the contribution workflow

The typical workflow for contributing is:

  1. Fork the COBRA Toolbox repository on GitHub.

  2. Clone your fork to your local machine.

  3. Add your code to the appropriate subfolder of src.

  4. Add tests for your new code (see Guide for writing a test).

  5. Commit your changes locally.

  6. Push your changes to your fork on GitHub.

  7. Open a pull request to the develop branch of the COBRA Toolbox repository.

  8. Address any review comments from the maintainers.

The following sections describe each step in more detail.

2. Fork the COBRA Toolbox repository

  1. Go to the main COBRA Toolbox repository on GitHub:

  2. In the top right corner, click the Fork button.

  3. Choose your GitHub account as the destination.

  4. GitHub will create a copy of the COBRA Toolbox under your account, for example:

    • https://github.com/<your-username>/cobratoolbox

All your changes will be pushed to this fork.

3. Clone your fork locally

To work on the code, clone your fork to your local machine.

  1. Open a terminal.

  2. Run:

    git clone https://github.com/<your-username>/cobratoolbox.git
    cd cobratoolbox
    
  3. Add the original repository as an upstream remote so that you can pull in future updates:

    git remote add upstream https://github.com/opencobra/cobratoolbox.git
    git fetch upstream
    

4. Add your code to the correct folder in src

All new COBRA Toolbox code should be added under the src folder. The main subfolders under src are:

  • analysis

  • base

  • dataIntegration

  • design

  • reconstruction

  • visualization

It is recommended to create a new subfolder inside the most appropriate parent folder for your project. This keeps the toolbox organised and makes it easier for others to navigate the code.

Below is guidance on when to use each folder.

4.1. src/analysis

Use analysis for methods that analyse existing metabolic models. Examples:

  • Flux balance analysis, flux variability analysis, phenotype simulations.

  • Comparison pipelines, performance profiling.

  • Statistical or diagnostic analyses.

Suggested structure: src/analysis/myNewAnalysisTool/

4.2. src/base

Use base for shared utilities and core methods needed across the toolbox. Examples include:

  • General-purpose helper functions.

  • IO utilities used by multiple modules.

  • Shared mathematical or solver-related helper functions.

Suggested structure: src/base/myUtilityFunctions/

4.3. src/dataIntegration

Use dataIntegration for code that integrates omics or experimental data with a model. Examples:

  • Transcriptomics-, proteomics-, or metabolomics-based integration.

  • Condition-specific model building.

  • Data-driven constraint generation.

Suggested structure: src/dataIntegration/myIntegrationPipeline/

4.4. src/design

Use design for algorithms that propose modifications or interventions. Examples:

  • Strain design algorithms.

  • Knockout or overexpression strategies.

  • Intervention ranking, phenotype optimisation.

Suggested structure: src/design/myDesignAlgorithm/

4.5. src/reconstruction

Use reconstruction for tools that construct, curate or update metabolic models. Examples:

  • Model reconstruction pipelines.

  • Gap filling algorithms.

  • Model merging and quality control tools.

Suggested structure: src/reconstruction/myReconstructionPipeline/

4.6. src/visualization

Use visualization for tools that generate plots, diagrams or graphical summaries. Examples:

  • Flux distribution plots.

  • Network diagrams.

  • Pathway visualisation tools.

Suggested structure: src/visualization/myVisualisationTools/

5. Add a test for every new code module

Every contribution must include a corresponding test. This ensures that new code is validated and remains stable.

Your test should:

  • Cover the main functions of your addition.

  • Run without errors in a clean installation.

  • Include checks for both expected behaviour and edge-case failures when possible.

For more information on writing tests, refer to:

6. Commit and push your changes

Once your code and tests are ready, commit your work.

  1. Check which files have changed:

    git status
    
  2. Stage your new code and tests:

    git add src/ path/to/your/files
    git add test/ path/to/your/tests
    
  3. Commit your changes:

    git commit -m "Add new analysis tool for XYZ with tests"
    
  4. Push your changes to your fork:

    git push origin develop
    

7. Open a pull request

Once your changes are pushed, open a pull request on GitHub.

  1. Visit your fork:

    • https://github.com/<your-username>/cobratoolbox

  2. Click Compare & pull request.

  3. Ensure the PR target is:

    • Base repository: opencobra/cobratoolbox

    • Base branch: develop

    • Head repository: your fork

    • Head branch: develop (or whichever branch you pushed to)

  4. Describe:

    • What your contribution does.

    • Where the code lives in src.

    • What tests were added.

    • Any notes for reviewers.

  5. Submit the pull request.

8. Address review comments

Maintainers may request revisions. This is normal.

  1. Make the changes locally.

  2. Commit and push them again:

    git commit -am "Address review comments"
    git push origin develop
    

Your pull request will update automatically.

Once approved, it will be merged into the develop branch of the COBRA Toolbox.

Contribute using MATLAB.devTools

In order to lower the technological barrier to the use of the popular software development tool git, we have developed MATLAB.devTools, a new user-friendly software extension that enables the submission of new COBRA toolbox functions and tutorials.

devTools

You can install the MATLAB.devTools from within MATLAB by typing:

>> installDevTools()

💡 Check out MATLAB.devTools – and contribute the smart way! The official documentation is here.

👉 Contribute to the opencobra/cobratoolbox repository by following these instructions:

>> contribute('opencobra/cobratoolbox');

👉 Contribute to the opencobra/COBRA.tutorials repository by following these instructions:

>> contribute('opencobra/COBRA.tutorials');
  • Please follow the Style Guide.

  • More information on writing a test is here and a template is here.

  • More information on formatting the documentation is here

  • A guide for reporting an issue is here.

If you want to use git via the command line interface and need help, this guide or the official GitHub guide come in handy.

Unified COBRA model field specification

To support consistent handling of genome-scale metabolic models across the COBRA Toolbox ecosystem, a unified model field structure has been established. This shared specification ensures that models originating from different repositories, formats or reconstruction pipelines can be interpreted reliably by toolbox functions. By adhering to a common set of field names, dimensions and semantics, users and developers gain full interoperability, improved compatibility with input and output routines, and predictable behaviour when applying analysis methods. The definitions below describe the agreed standard for COBRA models and serve as the reference for constructing, validating and extending model structures.

Fields in the model structure

The following fields are defined in the COBRA Toolbox. If a field is included in a model, it should match the definition and dimensions listed below. The dimensions refer to m (metabolites), n (reactions), g (genes) and c (compartments). Singular fields are explicitly marked as scalar, string or struct.

Field Name

Dimension

Field Type

Field Description

COBRA Core Field

model.S

m x n

Sparse or Full Matrix of Double

Stoichiometric matrix

model.mets

m x 1

Column Cell Array of Strings

Metabolite identifiers

model.b

m x 1

Column Vector of Doubles

Right hand side values for metabolite constraints

model.csense

m x 1

Column Vector of Chars

Constraint senses (E, L, G)

model.rxns

n x 1

Column Cell Array of Strings

Reaction identifiers

model.lb

n x 1

Column Vector of Doubles

Lower bounds

model.ub

n x 1

Column Vector of Doubles

Upper bounds

model.c

n x 1

Column Vector of Doubles

Objective coefficients

model.osenseStr

string

String

Objective sense: max or min

model.genes

g x 1

Column Cell Array of Strings

Gene identifiers

model.metNames

m x 1

Column Cell Array of Strings

Metabolite names

model.grRules

n x 1

Column Cell Array of Strings

Readable gene protein reaction rules

model.rxnNames

n x 1

Column Cell Array of Strings

Reaction names

model.rules

n x 1

Column Cell Array of Strings

Evaluatable GPR rules

model.geneNames

g x 1

Column Cell Array of Strings

Gene names

model.compNames

c x 1

Column Cell Array of Strings

Compartment names

model.comps

c x 1

Column Cell Array of Strings

Compartment symbols

model.proteinNames

g x 1

Column Cell Array of Strings

Protein names

model.proteins

g x 1

Column Cell Array of Strings

Proteins associated with genes

model.metCharges

m x 1

Column Vector of Double

Metabolite charges

model.metFormulas

m x 1

Column Cell Array of Strings

Elemental formulas

model.metSmiles

m x 1

Column Cell Array of Strings

SMILES strings

model.metNotes

m x 1

Column Cell Array of Strings

Notes for metabolites

model.metHMDBID

m x 1

Column Cell Array of Strings

HMDB identifiers

model.metInChIString

m x 1

Column Cell Array of Strings

InChI strings

model.metKEGGID

m x 1

Column Cell Array of Strings

KEGG metabolite identifiers

model.metChEBIID

m x 1

Column Cell Array of Strings

ChEBI identifiers

model.metPubChemID

m x 1

Column Cell Array of Strings

PubChem identifiers

model.metMetaNetXID

m x 1

Column Cell Array of Strings

MetaNetX metabolite identifiers

model.metSEEDID

m x 1

Column Cell Array of Strings

SEED metabolite identifiers

model.metBiGGID

m x 1

Column Cell Array of Strings

BiGG metabolite identifiers

model.metBioCycID

m x 1

Column Cell Array of Strings

BioCyc metabolite identifiers

model.metEnviPathID

m x 1

Column Cell Array of Strings

enviPath identifiers

model.metLIPIDMAPSID

m x 1

Column Cell Array of Strings

LipidMaps identifiers

model.metReactomeID

m x 1

Column Cell Array of Strings

Reactome metabolite identifiers

model.metSABIORKID

m x 1

Column Cell Array of Strings

SABIO RK metabolite identifiers

model.metSLMID

m x 1

Column Cell Array of Strings

SwissLipids identifiers

model.metSBOTerms

m x 1

Column Cell Array of Strings

SBO terms for metabolites

model.geneEntrezID

g x 1

Column Cell Array of Strings

Entrez gene identifiers

model.geneRefSeqID

g x 1

Column Cell Array of Strings

RefSeq gene identifiers

model.geneUniprotID

g x 1

Column Cell Array of Strings

UniProt identifiers

model.geneEcoGeneID

g x 1

Column Cell Array of Strings

EcoGene identifiers

model.geneKEGGID

g x 1

Column Cell Array of Strings

KEGG gene identifiers

model.geneHPRDID

g x 1

Column Cell Array of Strings

HPRD identifiers

model.geneASAPID

g x 1

Column Cell Array of Strings

ASAP identifiers

model.geneCCDSID

g x 1

Column Cell Array of Strings

CCDS identifiers

model.geneNCBIProteinID

g x 1

Column Cell Array of Strings

NCBI protein identifiers

model.rxnGeneMat

n x g

Sparse or Full Matrix

Reaction gene incidence matrix

model.rxnConfidenceScores

n x 1

Column Vector of Double

Reaction confidence scores

model.rxnNotes

n x 1

Column Cell Array of Strings

Notes on reactions

model.rxnECNumbers

n x 1

Column Cell Array of Strings

EC numbers

model.rxnReferences

n x 1

Column Cell Array of Strings

Literature references

model.rxnKEGGID

n x 1

Column Cell Array of Strings

KEGG reaction identifiers

model.rxnKEGGPathways

n x 1

Column Cell Array of Strings

KEGG pathway mappings

model.rxnMetaNetXID

n x 1

Column Cell Array of Strings

MetaNetX reaction identifiers

model.rxnBRENDAID

n x 1

Column Cell Array of Strings

BRENDA reaction identifiers

model.rxnBioCycID

n x 1

Column Cell Array of Strings

BioCyc reaction identifiers

model.rxnReactomeID

n x 1

Column Cell Array of Strings

Reactome reaction identifiers

model.rxnSABIORKID

n x 1

Column Cell Array of Strings

SABIO RK reaction identifiers

model.rxnSEEDID

n x 1

Column Cell Array of Strings

SEED reaction identifiers

model.rxnRheaID

n x 1

Column Cell Array of Strings

Rhea identifiers

model.rxnBiGGID

n x 1

Column Cell Array of Strings

BiGG identifiers

model.rxnSBOTerms

n x 1

Column Cell Array of Strings

SBO terms for reactions

model.subSystems

n x 1

Column Cell Array of Cell Arrays

Subsystem annotations

model.description

string

String

General description of the model

model.modelVersion

string

Struct

Version metadata

model.modelName

string

String

Descriptive name

model.modelID

string

String

Short identifier

model.E

m x evars

Sparse or Full Matrix

Extra variable constraint matrix

model.evarlb

evars x 1

Column Vector of Double

Lower bounds of extra variables

model.evarub

evars x 1

Column Vector of Double

Upper bounds of extra variables

model.evarc

evars x 1

Column Vector of Double

Objective coefficients for extra variables

model.evars

evars x 1

Column Cell Array of Strings

Extra variable identifiers

model.evarNames

evars x 1

Column Cell Array of Strings

Extra variable names

model.C

ctrs x n

Sparse or Full Matrix

Additional constraints matrix

model.ctrs

ctrs x 1

Column Cell Array of Strings

Constraint identifiers

model.ctrNames

ctrs x 1

Column Cell Array of Strings

Constraint names

model.d

ctrs x 1

Column Vector of Double

Right-hand side values for constraints

model.dsense

ctrs x 1

Column Vector of Chars

Constraint senses

model.D

ctrs x evars

Sparse or Full Matrix

Coupling terms between extra variables and constraints

Model Specific Fields

Some models may contain additional fields that are not part of the COBRA Toolbox standard. Such fields are not used by core functions, and using toolbox operations may cause inconsistencies if these fields depend on reaction or metabolite counts.

Field Support

All fields listed above are supported by COBRA Toolbox methods. Toolbox functions will preserve consistency, but manual editing of the model structure may not. Use verifyModel(model) to check whether a model meets the COBRA Toolbox structural requirements.

Additional fields

Fields beginning with met, rxn, comp, protein or gene that are not defined above are treated as annotation fields. Input and output functions will attempt to map these to identifiers in registered databases where possible.

Guide for writing a test

Before starting to write a test on your own, it might be instructive to follow common test practices in /test/verifiedTests. A style guide on how to write tests is given here.

Prepare the test (define requirements)

There are functions that need a specific solver or that can only be run if a certain toolbox is installed on a system. To address these, you should specify the respective requirements by using

solvers = prepareTest(requirements)

If successfull and all requirements are fulfilled, prepareTest will return a struct with one field for each problem type (solvers.LP, solvers.MILP etc.). Each field will be a cell array of solver names (if any are available). If the test does not ask for multiple solvers (via the requiredSolvers or the useSolversIfAvailable arguments), the returned cell array will only contain at most one solver.

Here are a few examples:

Example A: Require Windows for the test

The same works with needsMac, needsLinux and needsUnix instead of needsWindows.

solvers = prepareTest('needsWindows', true);

Example B: Require an LP solver (needsLP)

The same works with needsNLP, needsMILP, needsQP or needsMIQP. solvers.LP, solvers.MILP etc. will be cell arrays of string with the respective available solver for the problem type. If the 'useSolversIfAvailable' parameter is non empty, all installed solvers requested will be in the cell array. Otherwise, there will be at most one solver (if no solver for the problem is installed, the cell array is empty).

solvers = prepareTest('needsLP', true);

Example C: Use multiple solvers if present

If multiple solvers are requested. solvers.LP, solvers.MILP etc will contain all those requested solvers that can solve the respective problem type and that are installed.

solvers = prepareTest('needsLP', true, 'useSolversIfAvailable', {'ibm_cplex', 'gurobi'});

Example D: Require one of a set of solvers

Some functionalities do only work properly with a limited set of solvers. with the keyword requireOneSolverOf you can specify a set of solvers of which the test requires at least one to be present. This option, will make the test never be run with any but the solvers specified in the supplied list. E.g. if your test only works with gurobi or mosek you would call prepareTest as

solvers = prepareTest('requireOneSolverOf', {'ibm_cplex', 'gurobi'})

Example E: Exclude a solver

If for some reason, a function is known not to work with a few specific solvers (e.g. precision is insufficient) but works with all others, it might be advantageous to explicitly exclude that one solver instead of defining the list of possible solvers. This can be done with the excludeSolvers parameter. Eg. to exclude matlab and lp_solve you would use the following command:

solvers = prepareTest('excludeSolvers', {'matlab', 'lp_solve'})

Example F: Require multiple solvers

Some tests require more than one solver to be run, and otherwise fail. To require multiple solvers for a test use the requiredSolvers parameters. E.g. if your function requires ibm_cplex and gurobi use the following call:

solvers = prepareTest('requiredSolvers', {'ibm_cplex', 'gurobi'})

Example G: Require a specific MATLAB toolbox

The toolbox IDs are specified as those used in license('test', 'toolboxName'). The following example requires the statistics toolbox to be present.

solvers = prepareTest('requiredToolboxes', {'statistics_toolbox'})

Example H: Multiple requirements

If the test requires multiple different properties to be met, you should test them all in the same call. To keep the code readable, first define the requirements and then pass them in.

% define required toolboxes
requiredToolboxes = {'bioinformatics_toolbox', 'optimization_toolbox'};

% define the required solvers (in this case matlab and dqqMinos)
requiredSolvers = {'dqqMinos', 'matlab'};

% check if the specified requirements are fullfilled (toolboxes, solvers in thhis example, a unix OS).
solversPkgs = prepareTest('requiredSolvers', requiredSolvers, 'requiredToolboxes', requiredToolboxes, 'needsUnix', true);

Test if an output is correct

If you want to test if the output of a function [output1, output2] = function1(input1, input2) is correct, you should call this function at least 4 times in your test. The argument ìnput2 might be an optional input argument.

% Case 1: test with 1 input and 1 output argument
output1 = function1(input1)

% Case 2: test with 1 input and 2 output arguments
[output1, output2] = function1(input1)

% Case 3: test with 1 output and 2 input arguments
output1 = function1(input1, input2)

% Case 4: test with 2 input and 2 output arguments
[output1, output2] = function1(input1, input2)

Each of the 4 test scenarios should be followed by a test on output1 and output2. For instance, for Case 4:

% Case 4: test with 2 input and 2 output arguments
[output1, output2] = function1(input1, input2)

% test on output1
assert(output1 < tol); % tol must be defined previously, e.g. tol = 1e-6;

% test on output2
assert(abs(output2 - refData_output2) < tol); % refData_output2 can be loaded from a file

The test succeeds if the argument of assert() yields a true logical condition.

Test if a function throws an error or warning message

If you want to test whether your function1 correctly throws an error message, you can test as follows:

% Case 5: test with 2 input and 1 output arguments (2nd input argument is of wrong dimension)
% There are two options. If a particular error message is to be tested (here, 'Input2 has the wrong dimension'):
assert(verifyCobraFunctionError('function1', 'inputs', {input1, input2'}, 'testMessage', 'Input2 has the wrong dimension'));

% If the aim is to test, that the function throws an error at all
assert(verifyCobraFunctionError('function1', 'inputs', {input1, input2'}));

If you want to test whether your function1 correctly throws a warning message, you can test as follows:

warning('off', 'all')
    output1 = function1(input1, input2');
    assert(length(lastwarn()) > 0)
warning('on', 'all')

Note that this allows the error message to be thrown without failing the test.

Test template

A test template is readily available here. The following sections shall be included in a test file:

1. Header

% The COBRAToolbox: <testNameOfSrcFile>.m
%
% Purpose:
%     - <provide a short description of the purpose of the test
%
% Authors:
%     - <major change>: <your name> <date>
%

2. Test initialization

global CBTDIR

% save the current path and switch to the test path
currentDir = cd(fileparts(which('fileName')));

% get the path of the test folder
testPath = pwd;

3. Define the solver packages to be tested and the tolerance

% set the tolerance
tol = 1e-8;

% define the solver packages to be used to run this test
solvers = prepareTest('needsLP',true);

4. Load a model and/or reference data

% load a model distributed by the toolbox
getDistributedModel('testModel.mat');
% load a particular model for this test:
readCbModel([testPath filesep 'SpecificModel.mat'])
% load reference data
load([testPath filesep 'testData_functionToBeTested.mat']);

Please only load small models, i.e. less than 100 reactions. If you want to use a non-standard test model that is already available online, please make a pull request with the URL entry to the COBRA.models repository.

warning:

In order to guarantee compatibility across platforms, please use the full path to the model. For instance:

5. Create a parallel pool

This is only necessary for tests that test a function that runs in parallel.

% create a parallel pool
poolobj = gcp('nocreate'); % if no pool, do not create new one.
if isempty(poolobj)
    parpool(2); % launch 2 workers
end
warning:

Please only launch a pool of 2 workers - more workers

should not be needed to test a parallel function efficiently.

6. Body of test

The test itself. If the solvers are essential for the functionality tested in this test use:

for k = 1:length(solvers.LP)
    fprintf(' -- Running <testFile> using the solver interface: %s ... ', solvers.LP{k});

    solverLPOK = changeCobraSolver(solvers.LP{k}, 'LP', 0);
    % <your test goes here>

    % output a success message
    fprintf('Done.\n');
end

This is important, as the continuous integration system will run other solvers on the test in its nightly build. That way, we can determine solvers that work with a specific method, and those that do not (potentially due to precision problems or other issues). If the solvers are only used to test the outputs of a function for correctness, use:

solverLPOK = changeCobraSolver(solvers.LP{1}, 'LP', 0);
% <your test goes here>

% output a success message
fprintf('Done.\n');

7. Return to the original directory

% change the directory
cd(currentDir)

Run the test locally on your machine

Please make sure that your test runs individually by typing after a fresh start:

>> initCobraToolbox
>> <testName>

Please then verify that the test runs in the test suite by running:

>> testAll

Alternatively, you can run the test suite in the background by typing:

$ matlab -nodesktop -nosplash < test/testAll.m

Verify that your test passed

Once your pull request (PR) has been submitted, you will notice an orange mark next to your latest commit. Once the continuous integration (CI) server succeeded, you will see a green check mark. If the CI failed, you will see a red cross.

What should I do in case my PR failed?

You can check why your PR failed by clicking on the mark and following the respective links. Alternatively, you can see the output of the CI for your PR here. You can then click on the build number. Under Console Output, you can see the output of test/testAll.m with your integrated PR.

Once you understood why the build for your proposed PR failed, you can add more commits that aim at fixing the error, and the CI will be re-triggered.

Common errors include:

  • Double percentage sign %% in your test file to separate code blocks. Replace %% with %.

  • Compatibility issues (ILOG Cplex is not compatible with R2015b+). Add an additional test on the version of matlab using verLessThan('matlab', '<version>').

Can I find out how many tests have failed?

The logical conditions, when tested using assert(), will throw an error when not satisfied. It is bad practice to test the sum of tests passed and failed. Please only test using assert(logicalCondition). Even though a test may fail using assert(), a summary table with comprehensive information is provided at the end of the test run.

For instance, the following test script do not do this - bad practice!:

% do not do this: bad practice!
testPassed = 0;
testFailed = 0;

% test on logical condition 1 - do not do this: bad practice!
if logicalCondition1
    testPassed = testPassed + 1;
else
    testFailed = testFailed + 1;
end

% test on logical condition 2 - do not do this: bad practice!
if logicalCondition2
    testPassed = testPassed + 1;
else
    testFailed = testFailed + 1;
end

assert(testPassed == 2 && testFailed == 0); % do not do this: bad practice!

shall be rewritten as follows:

% good practice
assert(logicalCondition1);
assert(logicalCondition2);

Documentation guide

In order to enable the automatic documentation generation, the header of a MATLAB function has to be formatted properly. The automatic documentation extracts the commented header of the function (commented lines between the function’s signature and the first line of code) based on keywords and blocks.

Rule 1: There should be 1 free space between the percentage sign % and the text. For instance, a correctly formatted line in the header reads:

% this is correct

A line in the header that is formatted as follows is ignored:

%this is not correct (note the space)

Rule 2: After the last line of the header, leave one empty line before the body of the function.

% This is the end of the header of the function

x = 5;  % the body of the function begins after one empty line

Importantly, do not put a comment above the first line of code, but include the comment inline:

% This is the end of the header of the function

% the body of the function begins after one empty line
x = 5;

Rule 3: A line in the header that includes .. after the percentage sign will be ignored in the documentation:

% .. this line will be ignored

Function signature

The function signature must be correctly formatted. Leave a space after every comma and before and after the equal sign =. A correctly formatted function signature reads:

function [output1, output2, output3] = someFunction(input1, input2, input3) % good practice

A function signature that is not formatted properly throws an error during the automatic documentation generation:

function [ output1,output2,output3 ]=someFunction( input1,input2,input3 ) % bad practice

Function description

The description of the function is a brief explanation of the purpose of the function. The description of the function may extend over several lines. However, try to keep the explanations as brief as possible.

function [output1, output2, output3] = someFunction(input1, input2, input3)
% This is a description of the function that helps to understand how the function works
% Here the description continues, then we leave an empty comment line
%

Keywords

The automatic documentation software relies on keywords to properly format the documented function. A keyword also defines the start of a block with the header of a function. Main keywords include:

  • USAGE:: block for defining how to use the function

  • INPUT: or INPUTS:: block with input argument(s)

  • OUTPUT: or OUTPUTS:: block with output argument(s)

  • EXAMPLE:: block with example code (formatted MATLAB syntax)

  • NOTE:: highlighted box with text

  • Author:: list with author(s)

Each of them must be followed by non-empty lines and should be separated from the next block by an empty line.

All keywords are optional. For instance, if a function does not have any input arguments, the keyword INPUTS: can be omitted.

Any line of the block must be indented by 4 spaces after the comment sign %:

% INPUTS:
%    input1:     Description of input1
%    input2:     Description of input2
% input3:    Description <-- this is bad practice
%
% OUTPUTS:
%    output1:    Description of output1

If the indentation differs, there will be an error.

Keyword USAGE:

In the block starting with the keyword USAGE:, the function’s signature must be given in order to show how the function should be used. It is important to leave one empty line before the keyword USAGE:, after the keyword, and after the function’s signature.

% the end of the description
%
% USAGE:
%
%    [output1, output2, output3] = someFunction(input1, input2, input3)
%
% here the other section can begin

Keyword INPUT: and OUTPUT:

The arguments declared in the blocks: INPUT:, INPUTS:, OUTPUT: and OUTPUTS: must be followed by a colon : before the argument description is provided.

The indentation between the argument (with colon) and the description should be at least 4 spaces, so that all argument descriptions are aligned.

% INPUTS:
%    input1:     Description of input1 <-- good practice
%    input2      No colon <-- bad practice
%    input3: Not enough distance (4+ spaces) <-- bad practice
%
% OUTPUTS:
%    longerNameOutput:    Description of longerNameOutput after 4 spaces
%    output1:             Description begins at the same place as the longest argument <-- good practice
%    output2:    Description begins too soon <-- bad practice

For a structure argument, it is possible to list its fields. An empty line is added after the structure argument. Then, in the next line, the field is written aligned with the description of the structure plus 2 extra spaces.

The field is listed beginning as * .field - description (note the space between * and .). It is not necessary to leave an empty line after listing fields and writing the next argument. The following illustrates how to list a structure with its fields:

% OUTPUT:
%    output:    output argument with fields:
%
%                 * .field1 - first field of the structure.
%               * .field2 - no indent <-- bad practice
%                 * .field3 - multi-line comment must begin always
%                   where the text of the first line begins <-- good practice
%                 * .field4 - multi-line comment where
%                 the text in line 2 begins too soon <-- bad practice
%    next:      next argument can be added without empty line

It is also possible to replace * with a numbered list. You can use numbers followed by a dot (e.g., 1.) instead of * ..

% OPTIONAL INPUT:
%    input:    optional input argument with fields:
%
%                1. first element of a numbered list
%                2. second element of a numbered list

Keyword EXAMPLE:

A common usage example can be included in the EXAMPLE: block. Code included in this block will be formatted as MATLAB formatted code. Leave one empty line before the keyword EXAMPLE:, after the keyword, and after the properly indented (4 spaces) code snippet.

% the previous block ends here
%
% EXAMPLE:
%
%    result = someFunction(input1, input2)
%    %additional comment if necessary
%
% another block begins here

Keyword NOTE:

Important information, such as common errors, can be included in the block that starts with the keyword NOTE:. A NOTE: block is formatted in a dedicated and highlighted box in the documentation.

Leave one empty line before the keyword NOTE:, after the keyword, and after the properly indented text (indent of 4 spaces).

Normally formatted text can be left at the with one space after the comment sign. An example of a NOTE: block reads:

%
% NOTE:
%
%    This is a note that contains a important information.
%    It will be clearly visible in the documentation online.
%
% This is an additional final comment that can be added and that is
% only relevant to the code itself

Keyword Author: or Author(s):

In the Author(s) block, the author(s) that have written or contributed to the function are listed. Authors are not shown in the documentation itself, so the keyword is preceded by ... List 1 author as follows:

%
% .. Author: - Name, date, additional information if needed

x = 5;  % here the body of the function begins

If there are 2 or more authors, format as follows:

%
% .. Authors:
%       - Name1, date, additional information if needed
%       - Name2, date, additional information if needed

x = 5;  % here the body of the function begins

Example

A complete example of a function is provided here. Please remember that colons, indentations, and keywords are important to guarantee pretty formatting.

function [output1, output2] = someFunction(input1, input2, input3, input4)
% This is a description of the function that helps understand how the function works
% Here the description continues, then we leave an empty comment line
%
% USAGE:
%
%    [output1, output2] = someFunction(input1, input2, input3, input4)
%
% INPUTS:
%    input1:     Description of input1
%    input2:     Description of input2
%
% OPTIONAL INPUTS:
%    input3:     Structure with fields:
%
%                       * First field - description
%                       * Second field - description
%    input4:     Description of input4
%
% OUTPUT:
%    output1:    Description of output1
%
% OPTIONAL OUTPUT:
%    output2:    Description of output2
%
% EXAMPLE:
%
%    % this could be an example that can be copied from the documentation to MATLAB
%    [output1, output2] = someFunction(11, '22', structure, [1;2])
%    % without optional values
%    output1 = someFunction(11, '22')
%
% NOTE:
%    This is a very important information to be highlighted
%
% This is a final comment that cannot be in the description but can be useful
%
% .. Author: - Name, date, some information

x = 5;  % here the body of the function begins

Style guide

A comprehensive MATLAB style guide written by Richard Jonson can be found here.

Code

  1. Spacing

  • Write if singleCondition, and not if (singleCondition). Use brackets only for multiple conditions.

  • Use spaces around operators, e.g., i + 1 instead of i+1

  • Use spaces after commas (unless separated by newlines)

  • Avoid spaces inside the curly-braces of cells: {a, b} instead of { a, b }

  • Use spaces after commas in lists, after operands, after names, etc. This also improves readability. e.g. a = [1, 2, 3; 4, 5, 6]; instead of a=[1,2,3;4,5,6];

  • Include a single line of whitespace between blocks of code

  • Include a whitespace after a comment sign %

  1. Variable names

  • When using mixed words, separate with capital letters (with no intervening spaces or punctuation), e.g. calculateKineticFlux

  • Avoid ambiguity when naming variables: be as specific as possible

  • All variable names must be written in English

  • Use verb-noun structure for functions: allows to explain the operations performed

  • Append meaningful prefixes when possible, e.g. Av, Sum, Min, Max, etc

  • Boolean type variables, i.e. with only true/false values, with Is or is to stress this fact, e.g. if dataIsLoaded

  • Reuse names for short-life and variables with local scope, such as indexes of loops

  • Only use i, j, etc., as indexes for very short loops

  1. Miscellaneous

  • Add sanity checks to the code, e.g., if something does not work as expected, there should be code to check for this and either issue a warning or an error if there is a problem.

  • Do not encode the absolute position of any files within any function: use relative paths, where possible

  • Indent the code: really improves readability.

  • Fix a maximum line length: break large ones if needed. Ensure that it is clear that the sentence is separated through different lines, e.g.:

function [parameter1, parameter2, parameter3, parameter4] = functionManyParameters...
          (InputParameter1, InputParameter2, InputParameter3, InputParameter3, ...
           InputParameter4, InputParameter5)
  • Divide the code in separate functional files whenever it is possible (and logical)

  1. Platform independent code

  • Use pwd to get the current directory

  • Use filesep for paths (e.g., ['myPath' filesep 'myFile.m'])

Documentation and comments

  • Make sure the code is fully documented and commented, especially parts of the code that might be difficult to understand for beginner users.

  • Header for each file with the following elements:

    • Brief description (easy and short functions) or more detailed explanations (more complicated functions).

    • Description of INPUT and OUTPUT variables

    • Authors, co-authors, contributors (and the contribution of each of them)

    • Date of first fully operative version, and dates of consequent modifications with the corresponding number of version, e.g. v1 - 11/06/2014 / v2 - 12/08/2014

    • Description of modifications in later versions, e.g. v2: the efficiency has been improved by substituting the loops with matrices operations

  • Throughout the file:

    • Comment smartly. Not every line, but enough to allow tracking the execution

    • Try to use brief comments.

    • In case you believe a more complicated part requires a more comprehensive explanation, describe What are you doing and How it is done through a more detailed paragraph.

    • If the code is divided in blocks, you can also introduce briefly what is the function of each block beforehand.

    • Format the comments with a whitespace after the % sign. Try to use lowercase letters for comments.

Tests

  • Annotate the individual tests extensively for review

  • Use assert(computedResult == expectedResult) to logically test the computedResult and the expectedResult (you may also use < or >)

  • For testing all entries of a vector, use assert(all(vector1 == vector2))

  • Only use equality assert tests for integer values

  • Make sure that equality assert tests within a given tolerance, e.g., tol = 1e-9; assert(condition < tol);

  • Write loops for testing multiple models and/or solvers

  • Try to make your tests compatible with as many solvers as possible

  • Test, if possible, your contribution on Linux

  • Make sure to limit the output of the function to a minimum - only print the necessary information

  • Use verbose or printLevel to switch the verbose mode

  • Ensure that the solution of optimization problems is actually a solution (test that the solution vector satisfies the imposed constraints)

Git commit messages

  • Use the present tense (“Add feature” not “Added feature”)

  • Limit the first line to 72 characters or less

  • Reference issues and pull requests liberally

  • When only changing documentation, include [documentation] in the commit description

  • Consider starting the commit message (not the title of the PR) with an applicable emoji:

    • 🐛 :bug: when fixing a bug

    • 🎨 :art: when improving the format or structure of the code

    • 🐎 :racehorse: when improving performance

    • 📝 :memo: when writing docs

    • 🔥 :fire: when removing code or files

    • :white_check_mark: when adding tests

    • 🐧 :penguin: when fixing something on Linux

    • 🍎 :apple: when fixing something on macOS

    • 💻 :computer: when fixing something on Windows

    • 💚 :green_heart: when fixing the CI build

Issues/enhancements guide

What should I do before opening an issue?

Following these guidelines helps maintainers and the community understand your report pencil, reproduce the behavior computer, and fix it.

  • Get the latest version Check if you can reproduce the problem with the latest version, if the problem happens when you run your script/function with a different version of MATLAB or when using a different computer computer or different operating system (such as Linux, Windows, or macOS).

  • Check The COBRA Toolbox Forum on Google here Has your question already been asked, or has your problem already been reported? If it has, add a comment to the existing issue instead of opening a new one.

  • Check the list of currently reported issues here. If you happen to find a related issue, please use that thread or refer in your new issue to an already existing one.

  • Check the list of open pull requests here.

If you provide snippets in the issue/pull request, use Markdown code blocks.

How can I report an issue (or enhancement)?

Explain the problem and include additional details to help maintainers reproduce the problem:

  • Use a clear and descriptive title.

  • Provide information when you last updated The COBRA Toolbox.

  • Describe the exact steps that reproduce the problem and that describe the proposed enhancement in as many details as possible. Start by explaining how you started and initialized The COBRA Toolbox.

  • Provide specific examples to demonstrate the steps.

  • Describe the current behavior you observed after following the steps and point out what exactly is the problem with that behavior.

  • Explain what behavior you expected to see instead and why.

Provide details on the nature of the issue:

  • If you’re reporting that MATLAB crashed, include a crash report. Indicate the line that you think that made MATLAB crash. As this is an operating system related issue, please make sure to test the same code on a different computer and/or operating system computer before reporting the issue.

  • If the problem is related to performance, include more details, such as CPU information, RAM, computer brand/model. Eventually, mention where you feel potential improvements could be made.

  • If the problem wasn’t triggered by a specific action, describe what you were doing before the problem happened.

Include details about your configuration and environment:

  • Provide details on your startup.m and your pathdef.m file.

  • Which version of MATLAB are you using?

  • What operating system are you using?

  • Which solvers do you have installed? You can get that table by running initCobraToolbox.

Provide more context by answering these questions:

  • Might the problem be related to an external program?

  • Did the problem start happening recently?

  • If the problem started happening recently, can you reproduce the problem in an older version of The COBRA Toolbox?

  • Can you reliably reproduce the issue?

  • Does the problem happen for all COBRA models, or only some?