Korali Basics
This document describes the basic concepts that make a Korali application.
Creating a Korali Experiment
Korali provides a range of optimization and uncertainty quantification tools. To use these tools, a user needs to create a Korali Experiment. The following python code snippet shows how to load Korali’s library and instantiate a new experiment:
import korali
e = korali.Experiment()
This creates a new experiment object which can be then configured by specifying a Korali problem and solver module combination, as shown below.
The solver represents a specific algorithm or method find the solution of the specified problem. Most problem types require the evaluation of a computational model (function) to evaluate samples, whose results are returned to the Korali engine.
The cycle of sample creation and evaluation is called a generation. Korali will run as many generations required to reach a certain termination criterion
Experiments contain general settings which affect the way in which Korali runs the experiment and outputs its results.
Korali Problems
A Korali Experiment is configured to run a specific problem type by specifying its problem type setting, for example:
# Example: Setting the experiment to run an optimization problem
e["Problem"]["Type"] = "Optimization"
Sets the experiment to solve an optimization problem. The complete list of problem types can be found here.
Problem Configuration
Each problem type has its own set of properties necessary to be fully described. These settings can be specified by using the following syntax:
# Example: Configuring settings 1 and 2, of string and real types, respectively.
e["Problem"]["Setting 1"] = "String"
e["Problem"]["Setting 2"] = 1.0
Depending on the property, their values could be numeric, text strings, functions, or arrays thereof. They can also contain sub-properties which require their own set of properties, as shown below:
# Example: Configuring setting 3, which contains sub-properties to be defined.
e["Problem"]["Setting 3"]["Sub-Type"] = "myType"
e["Problem"]["Setting 3"]["Parameter 1"] = 0.0
e["Problem"]["Setting 3"]["Parameter 2"] = 1.0
To find the full list of properties for each problem type, look for “Configuration Settings” in the problem’s configuration page. Here is, for example, the configuration settings for Optimization/Stochastic.
Choosing a Solver Method
The next step is to choose which solver algorithm should be used to obtain the results required by the problem. This can be done by specifying the solver type setting.
Solver-Problem Compatibility
Although the complete list of solver types can be found here, each solver can only solve a specific set of problem types. To find which solver methods can be used for a specific problem type, look for “Compatible Solvers” in the problem’s configuration page. Here is, for example, the compatible solvers list for Optimization/Stochastic.
To continue our example above, we will choose to use the DEA, which is a compatible solver for the Optimization/Stochastic problem type.
e["Solver"]["Type"] = "DEA"
It is possible, however, to choose another solver to solve a given problem, simply by changing the solver method choice. For example, if now we wanted to solve the problem using CMAES instead, we simply change the field:
e["Solver"]["Type"] = "Optimizer/CMAES"
Solver Configuration
Korali solvers, just like problems, also contain their own set of settings to configure. For example, CMAES requires defining a population size, the number of samples to run per iteration.:
e["Solver"]["Population Size"] = 32
Termination Criteria
A Korali solver will run until at least one of its termination criteria is met. Termination criteria are entirely user-defined, and can be modified just like any other parameter, for example:
e["Solver"]["Termination Criteria"]["Min Value Difference Threshold"] = 0.0001
e["Solver"]["Termination Criteria"]["Max Generations"] = 1000
Will run iterations of the CMAES algorithm until the difference in objective value (optimization) is less than 0.0001, meaning it has reached convergence within an accepted tolerance OR until it has reached a total of 1000 generations (iterations). The list of termination criteria for each solver can be found in the “Termination Criteria” section of their documentation. Here is, for example, the termination criteria list for CMAES.
Configuration Defaults
Not all the properties or termination criteria of a solver method need to be explicitly defined. Instead, every solver provides a set of defaults values which should work fine in the majority of cases. To see which defaults have been defined for a given method, look for the “Default Configuration” section in their configuration page. Here is, for example, the default configuration for CMAES.
Variables
Most problem types require the description of the parameter-space that represents physical or mathematical phenomenon to analyze. To describe the parameter-space a user needs to define one or more Korali Variable. Variables are created by simply adding their name into the experiment:
# Example: Defining two variables for my problem.
e["Variables"][0]["Name"] = "Thermal Conductivity"
e["Variables"][1]["Name"] = "Heat Source Position"
Variable Configuration
Variable definitions require additional parameters depending on which problem and solver types have been selected. These parameters are explained in detail in each solver/problem documentation page.
For example, the following variable settings are mandatory for the CMAES solver, and these variable settings are mandatory for the optimization problem.
In the code snippet below, we show how the configuration for each variable is specified:
# Example: Defining two variables for my problem and their DEA lower/upper exploration bounds.
e["Variables"][0]["Name"] = "Thermal Conductivity"
e["Variables"][0]["Lower Bound"] = 0.0
e["Variables"][0]["Upper Bound"] = 1.0
e["Variables"][1]["Name"] = "Heat Source Position"
e["Variables"][1]["Lower Bound"] = -10.0
e["Variables"][1]["Upper Bound"] = +10.0
Variable Defaults
Korali problem or solver can specify defaults for their variable settings. To see which variable defaults (if any) have been defined for a given method, look for the “Default Configuration” section in their configuration page. Here is, for example, the variable defaults for CMAES.
Korali Samples and Models
Most problems require defining a model to be optimized/sampled from (among other purposes). A model in Korali is specified as a python function which contains a mathematical formula or an entire computational simulation of a given phenomenon. Model functions accept a single parameter representing a Korali sample. A sample is an input/output object that contains a determinate value for each of the variables defined in the experiment. The syntax to access these values is shown below:
# Defining a model function for my experiment
def myModel(k):
thermalConductivity = k["Parameters"][0]
heatSourcePosition = k["Parameters"][1]
The sample (k) contains an array of values (Parameters) that hold the value of each variable, in the order as they were defined in the experiment.
Model Output
Different problem types require the output of different results from the model. For example, Optimization/Stochastic requires as output the value of the function at the given point (F(x)), as shown below:
# Defining a model function for my experiment that returns F(x)
def myModel(sample):
thermalConductivity = sample["Parameters"][0]
heatSourcePosition = sample["Parameters"][1]
distanceFromSource = 1.0 - heatSourcePosition
sample["F(x)"] = thermalConductivity * distanceFromSource * distanceFromSource
Users can also save custom quantities of interest for each samples. These quantities are not used by Korali, but they can be later retrieved from the result files to provide additional data for post-processing.
# Defining a model function for my experiment that returns F(x) and quantities of interest
def myModel(sample):
thermalConductivity = sample["Parameters"][0]
heatSourcePosition = sample["Parameters"][1]
distanceFromSource = 1.0 - heatSourcePosition
sample["Distance From Source"] = distanceFromSource
sample["F(x)"] = thermalConductivity * distanceFromSource * distanceFromSource
Model functions can also be represented as lambda functions:
# Defining a lambda model function for my experiment that returns F(x)
myModel = lambda sample : sample["F(x)"] = sample["Parameters"][0] * sample["Parameters"][1]
Using the Model
To assign the model to the experiment, the user passes it as parameter to the corresponding setting. For example, for the Optimization/Stochastic problem, we need to define its Objective Function, as follows:
# Setting model to optimize
e["Problem"]["Type"] = "Optimization/Stochastic"
e["Problem"]["Objective Function"] = myModel
Distributions
Some problem type or solvers require the specification of probability distributions. To create distribution, use the following syntax to specify them by name, type, and properties:
# Example: Defining two variables for my problem.
e["Distributions"][0]["Name"] = "My Distribution 1"
e["Distributions"][0]["Type"] = "Univariate/Uniform"
e["Distributions"][0]["Minimum"] = -10.0
e["Distributions"][0]["Maximum"] = +10.0
e["Distributions"][1]["Name"] = "My Distribution 2"
e["Distributions"][1]["Type"] = "Univariate/Normal"
e["Distributions"][1]["Mean"] = 0.0
e["Distributions"][1]["Sigma"] = 5.0
A complete list of distribution types and their configuration can be found here.
Linking Distribution to Variable
Some problems type (e.g., Bayesian) require that variables define a prior distribution. This requires linking a variable to a specific distribution, which can be done by name referencing, for example:
# Example: Linking a variable with its prior distribution
e["Variables"][0]["Name"] = "Thermal Conductivity"
e["Variables"][0]["Prior Distribution"] = "My Distribution 1"
It is possible also to assign the same distribution to different variables:
# Example: Using the same distribution for multiple variables
e["Variables"][0]["Name"] = "Thermal Conductivity"
e["Variables"][0]["Prior Distribution"] = "My Distribution 1"
e["Variables"][1]["Name"] = "Heat Source Position"
e["Variables"][1]["Prior Distribution"] = "My Distribution 1"
Conditional Properties
Some problem types (e.g., Hierarchical Bayesian) require the definition of conditional priors, distributions for which properties are given by the value of a variable, for example:
# Defining conditional prior distributions for a hierarchical Bayesian problem
e["Variables"][0]["Name"] = "Psi 1"
e["Variables"][1]["Name"] = "Psi 2"
e["Distributions"][0]["Name"] = "Conditional 0"
e["Distributions"][0]["Type"] = "Univariate/Normal"
e["Distributions"][0]["Mean"] = "Psi 1"
e["Distributions"][0]["Standard Deviation"] = "Psi 2"
e["Problem"]["Conditional Priors"] = [ "Conditional 0" ]
Running Korali
After the experiment has been fully configured, the user needs to instantiate a Korali Engine object:
k = korali.Engine()
The engine contains all necessary execution logic to run the experiment and produce the results.
Running Experiments
To run a given experiment, simply use the engine’s run() function, passing the experiment as argument.
k.run(e)
It is not necessary to instantiate multiple Korali engines if the application needs to run multiple experiment; it suffices to call the run function as many times as necessary:
k.run(e0)
k.run(e1)
k.run(e2)
Similarly, it is possible to launch multiple experiments simultaneously:
k.run( [e0, e1, e2] )
In this case, Korali will not return until all three experiments have finished.
Running your Korali Application
To run an python application containing a Korali experiment, simply run:
python3 ./myKoraliApp arguments
Accessing Results
When called, the run will not return until one of the experiment’s termination criteria has been met. After return, the experiment will contain a Results section, from which the user can retrieve the desired results.
Each solver type prescribes a different set of results that it produces. To see which results are produced (if any) by a given method, look for the “Results” section in their configuration page. Here is, for example, the results.
To access the results, use the following syntax:
bestSample = e["Results"]["Best Sample"]
print('Found best sample at:')
print('Thermal Conductivity = ' + str(bestSample["Parameters"][0])
print('Heat Source Position = ' + str(bestSample["Parameters"][1])
print('Evaluation: ' + bestSample["F(x)"])
Result Files
After every generation, Korali stores the entire state of the framework (including results) to a results directory. The default path is given in experiment defaults.
To set a different results folder for a given experiment (recommended when you run multiple experiments), use the following syntax:
# Setting a different results folder for my experiment
e["File Output"]["Path"] = "./myResultsFolder"
If you would like to reduce the frequency of state files output or outright disable it, use the follwing syntax:
# Saving results to a file every 5 generations, instead of 1
e0["File Output"]["Enabled"] = True
e0["File Output"]["Frequency"] = 5
e0["File Output"]["Path"] = "/home/user/my.custom.path"
# Disable the output for this other experiment
e1["File Output"]["Enabled"] = False
To preserve the all input/output parameters for every sample generated in Korali, you need to enable it by:
# Saving the details of all samples generated during execution and their results (this file can become really large)
e["Store Sample Information"] = True
This option is by default disabled, since storing all samples may require large file sizes.
Console Verbosity
If you’d like to reduce or increase the amount of information that Korali outputs to console when running, you can use the following syntax:
To set a different results folder for a given experiment (recommended when you run multiple experiments), use the following syntax:
# Do not print anything to console.
e["Console Output"]["Verbosity"] = "Silent"
# Only print important progress notifications to console
e["Console Output"]["Verbosity"] = "Minimal"
# Print all possible information available.
e["Console Output"]["Verbosity"] = "Detailed"
To reduce the output frequency, use the following:
# Print partial results only every 5 generations
e["Console Output"]["Frequency"] = 5
Plotting Results
To generate a plot with the results of your experiment, check the documentation for the Korali Plotter and Korali Reinforcement Learning View tools.