Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retiefasuarus/issue55 #62

Merged
merged 5 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 12 additions & 30 deletions grainlearning/bayesian_calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,40 +67,30 @@ class BayesianCalibration:
:param curr_iter: Current iteration step
:param save_fig: Flag for skipping (-1), showing (0), or saving (1) the figures
"""
#: Dynamic system whose parameters or hidden states are being inferred
system: Type["DynamicSystem"]

#: Calibration method (e.g, Iterative Bayesian Filter)
calibration: Type["IterativeBayesianFilter"]

#: Number of iterations
num_iter: int

#: Current calibration step
curr_iter: int = 0

#: Flag to save figures
save_fig: int = -1

def __init__(
self,
system: Type["DynamicSystem"],
calibration: Type["IterativeBayesianFilter"],
num_iter: int,
curr_iter: int,
save_fig: int
num_iter: int = 10,
curr_iter: int = 0,
save_fig: int = -1
):
"""Initialize the Bayesian calibration class"""
self.system = system

self.calibration = calibration

self.num_iter = num_iter

self.set_curr_iter(curr_iter)

self.save_fig = save_fig

self.system = system

self.system.curr_iter = curr_iter

self.curr_iter = curr_iter

self.calibration = calibration


def run(self):
""" This is the main calibration loop which does the following steps
1. First iteration of Bayesian calibration starts with a Halton sequence
Expand Down Expand Up @@ -272,14 +262,6 @@ def get_most_prob_params(self):
most_prob = argmax(self.calibration.posterior)
return self.system.param_data[most_prob]

def set_curr_iter(self, curr_iter: int):
"""Set the current iteration step

param curr_iter: Current iteration step
"""
self.system.curr_iter = curr_iter
self.curr_iter = self.system.curr_iter

def increase_curr_iter(self):
"""Increase the current iteration step by one
"""
Expand Down
147 changes: 37 additions & 110 deletions grainlearning/dynamic_systems.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,12 @@ class DynamicSystem:

:param obs_data: observation or reference data
:param num_samples: Sample size of the ensemble of model evaluations
:param num_steps: Number of steps or sequence size in the dataset
:param num_obs: Number of observations in the dataset
:param num_ctrl: Number of control data (identical between simulation and observation)
:param param_min: List of parameter lower bounds
:param param_max: List of parameter Upper bounds
:param callback: Callback function, defaults to None
:param callback: Callback function to run simulations, defaults to None
:param curr_iter: current iteration ID, defaults to 0
:param ctrl_data: control data (e.g, time), defaults to None, optional
:param obs_names: Column names of the observation data, defaults to None, optional
Expand All @@ -101,91 +104,11 @@ class DynamicSystem:
:param sigma_max: Maximum uncertainty, defaults to 1.0e6, optional
:param sigma_tol: Tolerance of the estimated uncertainty, defaults to 1.0e-3, optional
:param sim_name: Name of the simulation, defaults to 'sim', optional
:param sigma_min: Minimum uncertainty, defaults to 1.0e-6, optional
:param _inv_normalized_sigma: Calculated normalized sigma to weigh the covariance matrix
:param estimated_params: Estimated parameter as the first moment of the distribution (:math:`x_\mu = \sum_i w_i * x_i`), defaults to None, optional
:param estimated_params_cv: Estimated parameter coefficient of variation as the second moment of the distribution (:math:`x_\sigma = \sqrt{\sum_i w_i * (x_i - x_\mu)^2} / x_\mu`), defaults to None, optional
"""

##### Parameters #####

#: Parameter data of shape (num_samples, num_params)
param_data: np.ndarray

#: Parameter data of previous iteration
param_data_prev: np.ndarray

#: Number of unknown parameters
num_params: int

#: Lower bound of the parameters
param_min: List

#: Upper bound of the parameters
param_max: List

#: Names of the parameters
param_names: List[str]

##### Observations #####

#: Observation (or reference) data of shape (num_obs, num_steps)
obs_data: np.ndarray

#: Observation keys
obs_names: List[str]

#: Inverse of the observation weight
inv_obs_weight: List[float]

#: Number of steps or sequence size in the dataset
num_steps: int

#: Number of observations in the dataset
num_obs: int

#: Control dataset (num_ctrl, num_steps)
ctrl_data: np.ndarray

#: Observation control (e.g., time)
ctrl_name: str

#: Number of control data (identical between simulation and observation)
num_ctrl: int

##### Simulations #####

#: Name of the simulation (e.g., sim)
sim_name: str = 'sim'

#: Simulation data of shape (num_samples, num_obs, num_steps)
sim_data: np.ndarray

#: Number of samples (usually specified by user)
num_samples: int

#: Callback function to run the forward predictions
callback: Callable

#: Current iteration ID
curr_iter: int

##### Uncertainty #####

#: Minimum value of the uncertainty
sigma_min: float = 1.0e-6

#: Maximum value of the uncertainty
sigma_max: float = 1.0e6

#: Sigma tolerance
sigma_tol: float = 1.0e-3

#: Calculated normalized sigma to weigh the covariance matrix
_inv_normalized_sigma: np.array

#: Estimated parameter as the first moment of the distribution (:math:`x_\mu = \sum_i w_i * x_i`)
estimated_params: np.ndarray = None

#: Parameter uncertainty as the second moment of the distribution (:math:`x_\sigma = \sum_i w_i \cdot (x_i - x_\mu)^2 / x_\mu`)
estimated_params_cv: np.ndarray = None

def __init__(
self,
obs_data: np.ndarray,
Expand All @@ -196,7 +119,7 @@ def __init__(
obs_names: List[str] = None,
ctrl_name: str = None,
inv_obs_weight: List[float] = None,
sim_name: str = None,
sim_name: str = 'sim',
sim_data: np.ndarray = None,
callback: Callable = None,
curr_iter: int = 0,
Expand All @@ -211,8 +134,7 @@ def __init__(
obs_data, ndmin=2
) # ensure data is of shape (num_obs,num_step).

if ctrl_data is not None:
self.ctrl_data = ctrl_data
self.ctrl_data = ctrl_data

self.obs_names = obs_names

Expand Down Expand Up @@ -247,19 +169,29 @@ def __init__(

if param_min:
self.num_params = len(param_min)
else:
self.num_params = 0

self.param_data = param_data

self.param_names = param_names

#### Uncertainty ####

self.sigma_min = 1.0e-6

self.sigma_max = sigma_max

self.sigma_tol = sigma_tol

self._inv_normalized_sigma = None

self.compute_inv_normalized_sigma()

self.estimated_params = None

self.estimated_params_cv = None

@classmethod
def from_dict(cls: Type["DynamicSystem"], obj: dict):
""" Initialize the class using a dictionary style
Expand All @@ -283,7 +215,7 @@ def from_dict(cls: Type["DynamicSystem"], obj: dict):
obs_names=obj.get("obs_names", None),
ctrl_name=obj.get("ctrl_name", None),
inv_obs_weight=obj.get("inv_obs_weight", None),
sim_name=obj.get("sim_name", None),
sim_name=obj.get("sim_name", 'sim'),
sim_data=obj.get("sim_data", None),
callback=obj.get("callback", None),
param_data=obj.get("param_data", None),
Expand Down Expand Up @@ -317,6 +249,18 @@ def set_param_data(self, data: list):
"""
self.param_data = np.array(data)

def set_obs_data(self, data: list):
"""Set the observation data
:param data: observation data of shape (num_obs, num_steps)
"""
self.obs_data = np.array(data,ndmin=2)

def set_ctrl_data(self, data: list):
"""Set the control data
:param data: control data of shape (num_ctrl, num_steps)
"""
self.ctrl_data = np.array(data)

def compute_inv_normalized_sigma(self):
"""Compute the inverse of the matrix that apply different weights on the observables"""
inv_obs_weight = np.diagflat(self.inv_obs_weight)
Expand Down Expand Up @@ -439,29 +383,9 @@ class IODynamicSystem(DynamicSystem):
:param inv_obs_weight: Inverse of the observation weight, defaults to None, optional
:param param_data: Parameter data, defaults to None, optional
:param sim_data: Simulation data, defaults to None, optional
:param sim_data_files: List of simulation data files (num_samples), defaults to None, optional
"""

##### Parameters #####

#: Name of the parameter data file
param_data_file: str

##### Observations #####

#: Name of the observation data file
obs_data_file: str

##### Simulations #####

#: Simulation data files (num_samples)
sim_data_files: List[str]

#: Name of the directory where simulation data is stored
sim_data_dir: str = './sim_data'

#: Extension of simulation data files
sim_data_file_ext: str = '.npy'

def __init__(
self,
sim_name: str,
Expand Down Expand Up @@ -503,6 +427,7 @@ def __init__(
param_data,
param_names
)
# TODO: reuse initialization from base class

##### Parameters #####

Expand Down Expand Up @@ -535,6 +460,8 @@ def __init__(

self.compute_inv_normalized_sigma()

self.sim_data_files = []

@classmethod
def from_dict(cls: Type["IODynamicSystem"], obj: dict):
""" Initialize the class using a dictionary style
Expand Down
28 changes: 9 additions & 19 deletions grainlearning/inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,26 +43,10 @@ class SMC:
with the maxima of the observations, defaults to True
:param cov_matrices: Covariance matrices of shape (num_steps, num_obs, num_obs),
defaults to None, Optional
:param likelihoods: Likelihood distributions of shape (num_steps, num_samples)
:param posteriors: Posterior distributions of shape (num_steps, num_samples)
:param ess: Time evolution of the effective sample size
"""

#: Target effective sample size
ess_target: float

#: True if the covariance matrix is scaled with the maximum of the observations, defaults to True
scale_cov_with_max: bool = False

#: Covariance matrices of shape (num_steps, num_obs, num_obs)
cov_matrices: np.array

#: Likelihood distributions of shape (num_steps, num_samples)
likelihoods: np.array

#: Posterior distributions of shape (num_steps, num_samples)
posteriors: np.array

#: Time evolution of the effective sample size
ess: np.array

def __init__(
self,
ess_target: float,
Expand All @@ -76,6 +60,12 @@ def __init__(

self.cov_matrices = cov_matrices

self.likelihoods = None

self.posteriors = None

self.ess = None

@classmethod
def from_dict(cls: Type["SMC"], obj: dict):
"""Initialize the class using a dictionary style
Expand Down
Loading
Loading