Welcome to this short NNERO tutorial.

Installation guide

NNERO can be installed using pip with the following command

pip install nnero

For a manual installation or development you can clone this repository and install it with

git clone https://github.com/gaetanfacchinetti/NNERO.git 
pip install -e .

Simple case

NNERO combines two neural networks, a classifier that identifies if a model leads to a reionization that is early enough, and a regressor that predict the evolution of the free-electron fraction and the associated optical depth to reionization.

from nnero import predict_Xe
from nnero import predict_tau

# load classifier and regressor at <path_*>, if no <path> given, the defaults are loaded
classifier=nnero.Classifier.load(<path_c>)
regressor=nnero.Regressor.load(<path_r>)

# print general information
# - structure of the network
# - input parameters name and training range
regressor.info()

# get Xe from loaded classifier and regressor
# **kwargs can be any parameter that is printed calling the info() function above
Xe=predict_Xe(classifier, regressor, **kwargs)
z=regressor.z

# get tau similarly
tau=predict_tau(classifier, regressor, **kwargs)

Run simple MCMC on astrophysical parameters

With NNERO it is possible to run simple MCMC using emcee on the astrophysical packages, combining the UV-Luminosity function likelihood, the likelihood on the reionzation history and a constraint on the optical depth to reionization. A simple example is given below.

import nnero
import emcee

classifier = nnero.Classifier.load()
regressor  = nnero.Regressor.load()

filename = "output.h5"

# varying parameters
p_theta = ['F_STAR10', 'ALPHA_STAR', 'F_ESC10', 'ALPHA_ESC', 'M_TURN', 't_STAR', 'L_X', 'NU_X_THRESH']

# fixed parameters and their value
p_xi = ['Ombh2', 'Omdmh2', 'hlittle', 'Ln_1010_As', 'POWER_INDEX']
xi = np.array([0.0224, 0.12, 0.677, 3.05, 0.965])

# save the parameters used
nnero.analysis.save_sampling_parameters(filename, p_theta, p_xi, xi)

# define the Likelihoods
uv_lkl   = nnero.UVLFLikelihood(parameters=p_theta+p_xi, 
    parameters_xi=p_xi, xi = xi, precompute=True, k = k, pk = pk)
tau_lkl  = nnero.mcmc.OpticalDepthLikelihood(parameters=p_theta+p_xi, 
    classifier=classifier, regressor=regressor, 
    median_tau=0.0557, sigma_tau=np.array([0.0075, 0.0067]))
reio_lkl = nnero.mcmc.ReionizationLikelihood(parameters=p_theta+p_xi, 
    classifier=classifier, regressor=regressor)

# get the name and the range of the parameters on which the NN have been trained
p_range = regressor.parameters_range
p_names = list(regressor.parameters_name)

# definen the prior range for the varying parameters
theta_min  = np.array([p_range[p_names.index(param), 0] for param in p_theta])
theta_max  = np.array([p_range[p_names.index(param), 1] for param in p_theta])

# initialise the walkers ar random values
pos = nnero.initialise_walkers(theta_min, theta_max, xi, 
    likelihoods = [uv_lkl, tau_lkl, reio_lkl],  n_walkers = 32)
nwalkers, ndim = pos.shape

# define the emcee backend
backend = emcee.backends.HDFBackend(filename)

# sample over the distribution
sampler = emcee.EnsembleSampler(nwalkers, ndim, nnero.log_probability, args = (xi, theta_min, theta_max, [tau_lkl, reio_lkl, uv_lkl]), backend=backend, vectorize=True)
sampler.run_mcmc(pos, 200000, progress=True);

Use analysis / plotting tools in NNERO

NNERO has a built-in set of function to perform MCMCs and In the example below we show how to plot the result of a MCMC performed with emcee as described above or with MontePython

import nnero

classifier = nnero.Classifier.load()
regressor  = nnero.Regressor.load()

# import the samples from generated files
# assume that both samples are for the p_theta parameters above
samples_MP = nnero.MPSamples('<path>/chains/test/2025-03-12_500000_')
samples_EM = nnero.analysis.EMCEESamples('output.h5', add_tau=True)
parameters = p_theta

# prepare the data for plotting                 
data_MP = nnero.prepare_data_plot(samples_MP, data_to_plot=parameters)
data_EM = nnero.prepare_data_plot(samples_EM, data_to_plot=parameters,
    thin=20, classifier = classifier, regressor = regressor)

# process the data to generate the contours and all statistics
c_data_MP = nnero.generate_contours(data_MP, bins=25, 
    smooth_1D=True, smooth_2D=True, sigma_smooth=1.5)
c_data_EM = nnero.generate_contours(data_EM, bins=25, 
    smooth_1D=True, smooth_2D=True, sigma_smooth=1.5)

# get the labels associated to the parameters
labels = nnero.latex_labels(parameters)

# prepare the grid for the triangle plot
grid = nnero.AxesGrid(c_data.size, labels = labels, names = data, scale=1.4)

# plot the contours on the grid
nnero.plot_data(grid, c_data_MP, show_contour=True, show_points = False)
nnero.plot_data(grid, c_data_EM, show_contour=True, show_points = False)