Specifying a new blackbox simulator¶
One of the key features of the SELFI algorithm is its likelihood-free nature, which implies its ability to deal with arbitrary simulators as data models, i.e. blackboxes.
In pySELFI, new blackbox simulators can be defined by the user in a new python module, as a class named blackbox
.
The constructor for blackbox
objects shall initialize \(P\) (the dimension of summaries space, only mandatory attribute), as well as any other attribute. Its prototype shall therefore be:
def __init__(self, P, **kwargs):
self.P=P
# Other initializations
The only requirement for a blackbox class to work with the pySELFI core infrastructure is a method named compute_pool()
with the following prototype and returning a pool
object:
def compute_pool(self, theta, d, pool_fname, N):
from pyselfi.pool import pool
p=pool(pool_fname,N)
# Compute the simulations going into the pool
return p
The arguments (handed by pySELFI) are theta (a \(S\)-dimensional vector of input parameters), d (the expansion index: \(0\) for the expansion point and \(1\) to \(S\) for all possible directions in parameter space), the pool filename pool_fname, and the required number of blackbox realizations N.
Instances of the new blackbox
class can then be used to define and manipulate selfi
objects, as described in this page.
Working examples of blackbox
classes can be found in src/pyselfi_examples/grf/model/blackbox_GRF.py
, src/pyselfi_examples/simbelmyne/model/blackbox_SBMY.py
, or src/pyselfi_examples/lotkavolterra/model/blackbox_LV.py
.
Blackbox class template¶
A more extensive template for a new blackbox
class is provided below and downloadable here: new_blackbox.py
.
#!/usr/bin/env python
#-------------------------------------------------------------------------------------
# pySELFI v2.0 -- src/pyselfi_examples/templates/new_blackbox.py
# Copyright (C) 2019-2023 Florent Leclercq.
#
# This file is part of the pySELFI distribution
# (https://github.com/florent-leclercq/pyselfi/)
#
# 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, version 3.
#
# 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.
#
# The text of the license is located in the root directory of the source package.
#-------------------------------------------------------------------------------------
"""Template for a new blackbox for pySELFI
"""
__author__ = "Florent Leclercq"
__version__ = "2.0"
__date__ = "2018-2023"
__license__ = "GPLv3"
class blackbox(object):
"""This class represents a SELFI blackbox.
Attributes
----------
P : int
number of output summary statistics. This is the only mandatory argument
**kwargs : dictionary, optional
any other argument
"""
# Initialization
def __init__(self, P, **kwargs):
"""Initializes the blackbox object.
"""
self.P=P
for key, value in kwargs.items():
setattr(self,key,value)
def _private_method(self, **args):
"""A private method of the blackbox class.
"""
# DO SOMETHING
return result
def make_auxiliary_needed_product(self, **args):
"""Produces any auxiliary product needed, such as a survey geometry file
"""
#DO SOMETHING...
def make_data(self, **args):
"""Evaluates the blackbox to make mock data.
Returns
-------
Phi : array, double, dimension=P
vector of summary statistics
"""
# DO SOMETHING
Phi = self._private_method(args)
# DO SOMETHING
return Phi
def evaluate(self, theta, **args):
"""Evaluates the blackbox from an input vector of parameters.
Parameters
----------
theta : array, double, dimension=S
vector of input parameters
Returns
-------
Phi : array, double, dimension=P
vector of summary statistics
"""
# DO SOMETHING
Phi = self._private_method(args)
# DO SOMETHING
return Phi
def compute_pool(self, theta, d, pool_fname, N, **args):
"""Computes a pool of realizations of the blackbox. A method compute_pool with this prototype is the only mandatory method.
Parameters
----------
theta : array, double, dimension=S
vector of input parameters
d : int
direction in parameter space, from 0 to S
pool_fname : :obj:`str`
pool file name
N : int
number of realizations required
Returns
-------
p : :obj:`pool`
simulation pool
"""
from pyselfi.pool import pool
p=pool(pool_fname,N)
# Run N evaluations of the blackbox at the desired point
while not p.finished:
i = p.N_sims+1
Phi = self.evaluate(theta, args) # args will contain d,i,N, etc.
p.add_sim(Phi)
# It can be convenient to define a variable save_frequency, otherwise one can just call p.save() after every blackbox evaluation
if i%self.save_frequency==0:
p.save()
p.save()
return p
#end class(blackbox)