Config File And Using SciUnit In A Shell

Create Config File And Execute Tests In A Shell

We can build a scientific computing project with a SciUnit config file. Then, we will be able to run sciunit In A Shell

Here is an example of well written SciUnit config file. This file was generated by executing sciunit create in the shell. A SciUnit config file is always named sciunit.ini.

[misc]
config-version = 1.0
nb-name = scidash

[root]
path = .

[models]
module = models

[tests]
module = tests

[suites]
module = suites

config-version is the version of the config file.

nb-name is the name of the IPython Notebook file that can be create with sciunit make-nb.

root is the root of the project. The path is the path to the project from the directory that contains this config file.

module in the models section is the path from the root of the project to the file that contains models, which is a list of Model instances.

module in the tests section is the path the root of the project to the file that contains tests, which is a list of Test instances.

module in the suites section is the path the root of the project to the file that contains suites, which is a list of TestSuite instances.

Let’s use the config file above and create corresponding files that contain definitions models, tests, and suites.

In the root directory of the project, let’s create three files.

tests.py

import sciunit
from sciunit.scores import BooleanScore
from sciunit.capabilities import ProducesNumber



class EqualsTest(sciunit.Test):
    """Tests if the model predicts
    the same number as the observation."""

    required_capabilities = (ProducesNumber,)
    score_type = BooleanScore

    def generate_prediction(self, model):
        return model.produce_number() # The model has this method if it inherits from the 'ProducesNumber' capability.

    def compute_score(self, observation, prediction):
        score = self.score_type(observation['value'] == prediction)
        score.description = 'Passing score if the prediction equals the observation'
        return score

tests = []

suites.py

import sciunit
from tests import EqualsTest


equals_1_test = EqualsTest({'value':1}, name='=1')
equals_2_test = EqualsTest({'value':2}, name='=2')
equals_37_test = EqualsTest({'value':37}, name='=37')

equals_suite = sciunit.TestSuite([equals_1_test, equals_2_test, equals_37_test], name="Equals test suite")

suites = [equals_suite]

models.py

import sciunit
from sciunit.capabilities import ProducesNumber


class ConstModel(sciunit.Model,
                ProducesNumber):
    """A model that always produces a constant number as output."""

    def __init__(self, constant, name=None):
        self.constant = constant
        super(ConstModel, self).__init__(name=name, constant=constant)

    def produce_number(self):
        return self.constant

const_model_1 = ConstModel(1, name='Constant Model 1')
const_model_2 = ConstModel(2, name='Constant Model 2')
const_model_37 = ConstModel(37, name="Constant Model 37")

models = [const_model_1, const_model_2, const_model_37]

We have tests at the end of tests.py, models at the end of models.py, and suites at the end of suites.py. Since we are using test suites instead of tests, tests is an empty list in this example. They will be taken by sciunit when command sciunit run is being executing

Execute sciunit run in the root directory, and then sciunit will run each test in the suites against each model and give us the result.

$ sciunit run


Executing test =1 on model Constant Model 1... Score is Pass
Executing test =2 on model Constant Model 1... Score is Fail
Executing test =37 on model Constant Model 1... Score is Fail
Executing test =1 on model Constant Model 2... Score is Fail
Executing test =2 on model Constant Model 2... Score is Pass
Executing test =37 on model Constant Model 2... Score is Fail
Executing test =1 on model Constant Model 37... Score is Fail
Executing test =2 on model Constant Model 37... Score is Fail
Executing test =37 on model Constant Model 37... Score is Pass

Suite Equals test suite:
                    =1    =2   =37
Constant Model 1   Pass  Fail  Fail
Constant Model 2   Fail  Pass  Fail
Constant Model 37  Fail  Fail  Pass

Create and Run IPython Notebook File

Next, let’s move to creating and executing IPython Notebook file with sciunit make-nb and sciunit run-nb commands.

Let’s add a file, __init__.py, to our project directory and import everything including suites, tests, and models in the file. This is necessary because the made notebook file will try to import everything in __init__.py and run each suite (a collection of tests instances) against each model.

__init__.py

from . import models
from . import tests
from . import suites

Now, let’s execute sciunit make-nb SciUnit will automatically generate a notebook file.

$ sciunit make-nb
Created Jupyter notebook at:
/the_path_to_the_project.../test_sciunit.ipynb

The notebook file will contains two blocks of code:

Note:

  1. the name of generated notebook file will be the value of nb-name attribute in the config file, sciunit.ini
  2. The path to the project’s root can be different on different machine. So, The notebook file generated usually only be valid on the machine where it is generated. If you want to execute it on different machine, try to re-generate it or change the path.

Let’s execute sciunit run-nb command.

$ sciunit run-nb
Entering run function
/the_path_to_the_project_config_file..././test_sciunit.ipynb
/the_path_to_the_project_config_file.../.

The result of running the notebook will be in the notebook file. You can open it by many tools like VS Code and Jupyter Lab