From a5d6d9045d7197a308a4dea415819ccef9085d20 Mon Sep 17 00:00:00 2001 From: Pietro Date: Sun, 25 Apr 2021 15:29:22 +0200 Subject: [PATCH] Documentation Comments --- .../structure_graph/network_generator.py | 58 +++++++++++++++++-- .../structure_graph/trajectory_generator.py | 38 +++++++++++- PyCTBN/PyCTBN/utility/json_exporter.py | 26 +++++++++ 3 files changed, 114 insertions(+), 8 deletions(-) diff --git a/PyCTBN/PyCTBN/structure_graph/network_generator.py b/PyCTBN/PyCTBN/structure_graph/network_generator.py index 9c36ecf..5f429b8 100644 --- a/PyCTBN/PyCTBN/structure_graph/network_generator.py +++ b/PyCTBN/PyCTBN/structure_graph/network_generator.py @@ -5,6 +5,21 @@ from .set_of_cims import SetOfCims import numpy as np class NetworkGenerator(object): + """Provides the methods to generate a network graph and the CIMs related to it + Items in _labels, _vals and _indxs are related, and therefore respect the same order + + :param _labels: List of variables labels that will be part of the network + :type _labels: List + :param _vals: List of cardinalities of the variables in network (defined in the same order as _labels) + :type _vals: List + :param _indxs: List of the nodes indexes + :type _indxs: List + :param _cims: It contains, for each variable label (the key), the SetOfCims object related to it + :type _cims: Dict + :param _graph: The NetworkGraph object representing the generated structure + :type _graph: NetworkGraph + """ + def __init__(self, labels, vals): self._labels = labels self._vals = vals @@ -13,14 +28,30 @@ class NetworkGenerator(object): self._cims = None def generate_graph(self, density): + """Generate the edges according to specified density, and then instantiate the NetworkGraph object + to represent the network + + :param density: Probability of an edge between two nodes to exist + :type density: float + """ + edges = [(i, j) for i in self._labels for j in self._labels if np.random.binomial(1, density) == 1 and i != j] - indxs = np.array([i for i, l in enumerate(self._labels)]) s = Structure(self._labels, self._indxs, self._vals, edges, len(self._labels)) self._graph = NetworkGraph(s) self._graph.add_nodes(s.nodes_labels) self._graph.add_edges(s.edges) def generate_cims(self, min_val, max_val): + """For each node, generate the corresponding SetOfCims. The objective is to group the CIMs + (actually generated by private method __generate_cim) according to parents possibles states of every node. + This method must obviously be executed after the graph has been generated. + + :param min_val: Minimum value allowed for the coefficients in the CIMs + :type min_val: float + :param max_val: Maximum value allowed for the coefficients in the CIMs + :type max_val: float + """ + if self._graph is None: return @@ -44,6 +75,16 @@ class NetworkGenerator(object): cims = np.array(node_cims)) def __generate_cim(self, min_val, max_val, shape): + """Generate a valid CIM matrix, with coefficients in the range [min_val, max_val] and with dimension [shape, shape] + + :param min_val: Minimum value allowed for the coefficients in the CIMs + :type min_val: float + :param max_val: Maximum value allowed for the coefficients in the CIMs + :type max_val: float + :param shape: Number of elements in each dimension of the matrix (this actually is the cardinality of the node) + :type shape: int + """ + cim = np.empty(shape=(shape, shape)) cim[:] = np.nan @@ -58,6 +99,13 @@ class NetworkGenerator(object): return cim def out_json(self, filename): + """Create a file in current directory and write on it the generated network and the CIMs, + after having restructured the objects in order to respect the standard JSON file structure. + + :param filename: Name of the output file (it must include json extension) + :type filename: string + """ + dyn_str = [{"From": edge[0], "To": edge[1]} for edge in self._graph.edges] variables = [{"Name": l, "Value": self._vals[i]} for i, l in enumerate(self._labels)] dyn_cims = {} @@ -89,11 +137,9 @@ class NetworkGenerator(object): "samples": [] } - print(data) - - """ path = os.getcwd() - with open(path + "/" + filename, "w") as json_file: - json.dump(data, json_file) """ + path = os.getcwd() + with open(path + "/" + filename, "w") as json_file: + json.dump(data, json_file) @property def graph(self) -> NetworkGraph: diff --git a/PyCTBN/PyCTBN/structure_graph/trajectory_generator.py b/PyCTBN/PyCTBN/structure_graph/trajectory_generator.py index 8b00186..8a2d308 100644 --- a/PyCTBN/PyCTBN/structure_graph/trajectory_generator.py +++ b/PyCTBN/PyCTBN/structure_graph/trajectory_generator.py @@ -9,7 +9,27 @@ import json from numpy import random class TrajectoryGenerator(object): + """Provides the methods to generate a trajectory basing on the network defined + in the importer. + + :param _importer: the Importer object which contains the imported and processed data + :type _importer: AbstractImporter + :param _vnames: List of the variables labels that belong to the network + :type _vnames: List + :param _parents: It contains, for each variable label (the key), the list of related parents labels + :type _parents: Dict + :param _cims: It contains, for each variable label (the key), the SetOfCims object related to it + :type _cims: Dict + :param _generated_trajectory: Result of the execution of CTBN_Sample, contains the output trajectory + :type _generated_trajectory: pandas.DataFrame + """ + def __init__(self, importer: AbstractImporter): + """Constructor Method + It parses and elaborates the data fetched from importer in order to make the objects structure + more suitable for the forthcoming trajectory generation + """ + self._importer = importer self._vnames = self._importer._df_variables.iloc[:, 0].to_list() @@ -32,14 +52,24 @@ class TrajectoryGenerator(object): node_states_number = self._importer._df_variables.where(self._importer._df_variables["Name"] == v)["Value"], p_combs = p_combs, cims = v_cims) self._cims[v] = sof - self._generated_trajectory = None - def CTBN_Sample(self, t_end = -1, max_tr = -1): + """This method implements the generation of a trajectory, basing on the network structure and + on the coefficients defined in the CIMs. + The variables are initialized with value 0, and the method takes care of adding the + conventional last row made up of -1. + + :param t_end: If defined, the sampling ends when end time is reached + :type t_end: float + :param max_tr: Parameter taken in consideration in case that t_end isn't defined. It specifies the number of transitions to execute + :type max_tr: int + """ + t = 0 sigma = pd.DataFrame(columns = (["Time"] + self._vnames)) sigma.loc[len(sigma)] = [0] + [0 for v in self._vnames] time = np.full(len(self._vnames), np.NaN) n_tr = 0 + self._generated_trajectory = None while True: current_values = sigma.loc[len(sigma) - 1] @@ -96,4 +126,8 @@ class TrajectoryGenerator(object): time[i] = np.NaN def to_json(self): + """Convert the last generated trajectory from pandas.DataFrame object type to JSON format + (suitable to do input/output of the trajectory with file) + """ + return json.loads(self._generated_trajectory.to_json(orient="records")) \ No newline at end of file diff --git a/PyCTBN/PyCTBN/utility/json_exporter.py b/PyCTBN/PyCTBN/utility/json_exporter.py index 1fbcccb..688d6aa 100644 --- a/PyCTBN/PyCTBN/utility/json_exporter.py +++ b/PyCTBN/PyCTBN/utility/json_exporter.py @@ -3,6 +3,19 @@ import pandas as pd import os class JsonExporter(object): + """Provides the methods to save in json format a network information + along with one or more trajectories generated basing on it + + :param _variables: List of dictionaries, representing the variables in the network and their cardinality + :type _variables: List + :param _dyn_str: List of dictionaries, each of which represents an edge ({"From": "", "To": ""}) + :type _dyn_str: List + :param _dyn_cims: It contains, for every variable (label is the key), the CIM values related to it + :type _dyn_cims: Dict + :param _trajectories: List of trajectories, that can be added subsequently + :type _trajectories: List + """ + def __init__(self, variables, dyn_str, dyn_cims): self._variables = variables self._dyn_str = dyn_str @@ -10,9 +23,22 @@ class JsonExporter(object): self._trajectories = [] def add_trajectory(self, trajectory: list): + """Add a new trajectory to the current list + + :param trajectory: The trajectory to add. It must already be in json form, and not as pandas.DataFrame + :type trajectory: List + """ + self._trajectories.append(trajectory) def out_json(self, filename): + """Create a file in current directory and write on it the previously added data + (variables, dyn_str, dyn_cims and trajectories) + + :param filename: Name of the output file (it must include json extension) + :type filename: string + """ + data = [{ "dyn.str": self._dyn_str, "variables": self._variables,