diff --git a/.gitignore b/.gitignore index 49fc910..e7be5ce 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ test2.json test3.json result0.png example.json +test_time.py diff --git a/PyCTBN/PyCTBN/structure_graph/network_generator.py b/PyCTBN/PyCTBN/structure_graph/network_generator.py index 3930607..d10ba9e 100644 --- a/PyCTBN/PyCTBN/structure_graph/network_generator.py +++ b/PyCTBN/PyCTBN/structure_graph/network_generator.py @@ -4,8 +4,7 @@ from .conditional_intensity_matrix import ConditionalIntensityMatrix from .set_of_cims import SetOfCims import numpy as np import pandas as pd -import os -import json +import random class NetworkGenerator(object): """Provides the methods to generate a network graph and the CIMs related to it @@ -30,15 +29,28 @@ class NetworkGenerator(object): self._graph = None self._cims = None - def generate_graph(self, density): + def generate_graph(self, density, fixed: bool = False): """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 + :param fixed: Specifies whether the required density is mandatory or it's just a probability + :type fixed: bool """ + + if fixed: + n_edges = density * len(self._labels) * (len(self._labels) - 1) + if not float.is_integer(n_edges): + raise RuntimeError("Invalid Density") + else: + n_edges = int(n_edges) + edges = [(i, j) for i in self._labels for j in self._labels if i != j] + random.shuffle(edges) + edges = edges[:n_edges] + else: + edges = [(i, j) for i in self._labels for j in self._labels if np.random.binomial(1, density) == 1 and i != j] - edges = [(i, j) for i in self._labels for j in self._labels if np.random.binomial(1, density) == 1 and i != j] s = Structure(self._labels, self._indxs, self._vals, edges, len(self._labels)) self._graph = NetworkGraph(s) self._graph.add_nodes(s.nodes_labels) diff --git a/PyCTBN/PyCTBN/structure_graph/trajectory_generator.py b/PyCTBN/PyCTBN/structure_graph/trajectory_generator.py index 50262a2..56e170f 100644 --- a/PyCTBN/PyCTBN/structure_graph/trajectory_generator.py +++ b/PyCTBN/PyCTBN/structure_graph/trajectory_generator.py @@ -25,7 +25,7 @@ class TrajectoryGenerator(object): :type _generated_trajectory: pandas.DataFrame """ - def __init__(self, importer: AbstractImporter = None, variables: list = None, dyn_str: list = None, dyn_cims: dict = None): + def __init__(self, importer: AbstractImporter = None, variables: pd.DataFrame = None, dyn_str: pd.DataFrame = None, dyn_cims: dict = None): """Constructor Method It parses and elaborates the data fetched from importer (if defined, otherwise variables, dyn_str and dyn_cims are used) in order to make the objects structure more suitable for the forthcoming trajectory generation @@ -34,31 +34,35 @@ class TrajectoryGenerator(object): self._importer = importer self._vnames = self._importer._df_variables.iloc[:, 0].to_list() if importer is not None else variables.iloc[:, 0].to_list() - + self._parents = {} for v in self._vnames: if importer is not None: self._parents[v] = self._importer._df_structure.where(self._importer._df_structure["To"] == v).dropna()["From"].tolist() else: self._parents[v] = dyn_str.where(dyn_str["To"] == v).dropna()["From"].tolist() - + self._cims = {} - sampled_cims = self._importer._raw_data[0]["dyn.cims"] if importer is not None else dyn_cims - for v in sampled_cims.keys(): - p_combs = [] - v_cims = [] - for comb in sampled_cims[v].keys(): - p_combs.append(np.array(re.findall(r"=(\d)", comb)).astype("int")) - cim = pd.DataFrame(sampled_cims[v][comb]).to_numpy() - v_cims.append(ConditionalIntensityMatrix(cim = cim)) - - if importer is not None: - sof = SetOfCims(node_id = v, parents_states_number = [self._importer._df_variables.where(self._importer._df_variables["Name"] == p)["Value"] for p in self._parents[v]], - node_states_number = self._importer._df_variables.where(self._importer._df_variables["Name"] == v)["Value"], p_combs = np.array(p_combs), cims = v_cims) - else: - sof = SetOfCims(node_id = v, parents_states_number = [variables.where(variables["Name"] == p)["Value"] for p in self._parents[v]], - node_states_number = variables.where(variables["Name"] == v)["Value"], p_combs = np.array(p_combs), cims = v_cims) - self._cims[v] = sof + if importer is not None: + sampled_cims = self._importer._cims + + for v in sampled_cims.keys(): + p_combs = [] + v_cims = [] + for comb in sampled_cims[v].keys(): + p_combs.append(np.array(re.findall(r"=(\d)", comb)).astype("int")) + cim = pd.DataFrame(sampled_cims[v][comb]).to_numpy() + v_cims.append(ConditionalIntensityMatrix(cim = cim)) + + if importer is not None: + sof = SetOfCims(node_id = v, parents_states_number = [self._importer._df_variables.where(self._importer._df_variables["Name"] == p)["Value"] for p in self._parents[v]], + node_states_number = self._importer._df_variables.where(self._importer._df_variables["Name"] == v)["Value"], p_combs = np.array(p_combs), cims = v_cims) + else: + sof = SetOfCims(node_id = v, parents_states_number = [variables.where(variables["Name"] == p)["Value"] for p in self._parents[v]], + node_states_number = variables.where(variables["Name"] == v)["Value"], p_combs = np.array(p_combs), cims = v_cims) + self._cims[v] = sof + else: + self._cims = dyn_cims def CTBN_Sample(self, t_end = -1, max_tr = -1): """This method implements the generation of a trajectory, basing on the network structure and @@ -97,7 +101,6 @@ class TrajectoryGenerator(object): time[i] = t + random.exponential(scale = param) - # next = index of the variable that will transition first next = time.argmin() t = time[next] @@ -173,11 +176,4 @@ class TrajectoryGenerator(object): for p in processes: p.join() - return trajectories - - 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 + return trajectories \ No newline at end of file diff --git a/PyCTBN/PyCTBN/utility/abstract_exporter.py b/PyCTBN/PyCTBN/utility/abstract_exporter.py index 1c7c237..a3e1a28 100644 --- a/PyCTBN/PyCTBN/utility/abstract_exporter.py +++ b/PyCTBN/PyCTBN/utility/abstract_exporter.py @@ -4,7 +4,7 @@ import os from abc import ABC, abstractmethod class AbstractExporter(ABC): - """Provides the methods to save in json format a network information + """Abstract class that exposes the methods to save in json format a network information along with one or more trajectories generated basing on it :param _variables: Dataframe containing the nodes labels and cardinalities diff --git a/PyCTBN/PyCTBN/utility/json_importer.py b/PyCTBN/PyCTBN/utility/json_importer.py index edff212..6b6c3ed 100644 --- a/PyCTBN/PyCTBN/utility/json_importer.py +++ b/PyCTBN/PyCTBN/utility/json_importer.py @@ -30,8 +30,8 @@ class JsonImporter(AbstractImporter): :type _raw_data: List """ - def __init__(self, file_path: str, samples_label: str, structure_label: str, variables_label: str, time_key: str, - variables_key: str): + def __init__(self, file_path: str, samples_label: str, structure_label: str, variables_label: str, time_key: str, + variables_key: str, cims_label: str = None): """Constructor method .. note:: @@ -42,6 +42,7 @@ class JsonImporter(AbstractImporter): self._samples_label = samples_label self._structure_label = structure_label self._variables_label = variables_label + self._cims_label = cims_label self._time_key = time_key self._variables_key = variables_key self._df_samples_list = None @@ -63,6 +64,9 @@ class JsonImporter(AbstractImporter): self._df_structure = self.import_structure(self._raw_data) self._df_variables = self.import_variables(self._raw_data) + if self._cims_label != None: + self._cims = self._raw_data[indx][self._cims_label] + def import_trajectories(self, raw_data: typing.List) -> typing.List: """Imports the trajectories from the list of dicts ``raw_data``. diff --git a/example.py b/example.py index aa0ceeb..8b00fee 100644 --- a/example.py +++ b/example.py @@ -6,40 +6,36 @@ from PyCTBN.PyCTBN.structure_graph.structure import Structure from PyCTBN.PyCTBN.structure_graph.sample_path import SamplePath from PyCTBN.PyCTBN.estimators.structure_constraint_based_estimator import StructureConstraintBasedEstimator -""" - if __name__ == "__main__": - trajectories = tg.multi_trajectory(t_ends = [100, 100, 100]) -""" +def main(): + # Network Generation + labels = ["X", "Y", "Z"] + card = 3 + vals = [card for l in labels] + cim_min = 1 + cim_max = 3 + ng = NetworkGenerator(labels, vals) + ng.generate_graph(0.3) + ng.generate_cims(cim_min, cim_max) -# Network Generation -labels = ["X", "Y", "Z"] -card = 3 -vals = [card for l in labels] -cim_min = 1 -cim_max = 3 -ng = NetworkGenerator(labels, vals) -ng.generate_graph(0.3) -ng.generate_cims(cim_min, cim_max) + # Trajectory Generation + e1 = JsonExporter(ng.variables, ng.dyn_str, ng.cims) + tg = TrajectoryGenerator(variables = ng.variables, dyn_str = ng.dyn_str, dyn_cims = ng.cims) + sigma = tg.CTBN_Sample(max_tr = 30000) + e1.add_trajectory(sigma) + e1.out_file("example.json") -# Trajectory Generation -print(ng.dyn_str) -e1 = JsonExporter(ng.variables, ng.dyn_str, ng.cims) -tg = TrajectoryGenerator(variables = ng.variables, dyn_str = ng.dyn_str, dyn_cims = e1.cims_to_json()) -sigma = tg.CTBN_Sample(max_tr = 30000) -e1.add_trajectory(sigma) -e1.out_file("example.json") + # Network Estimation (Constraint Based) + importer = JsonImporter(file_path = "example.json", samples_label = "samples", + structure_label = "dyn.str", variables_label = "variables", + cims_label = "dyn.cims", time_key = "Time", + variables_key = "Name") + importer.import_data(0) + s1 = SamplePath(importer=importer) + s1.build_trajectories() + s1.build_structure() + se1 = StructureConstraintBasedEstimator(sample_path=s1, exp_test_alfa=0.1, chi_test_alfa=0.1, + known_edges=[], thumb_threshold=25) + edges = se1.estimate_structure(True) -# Network Estimation (Constraint Based) -importer = JsonImporter(file_path="example.json", samples_label='samples', - structure_label='dyn.str', variables_label='variables', - time_key='Time', variables_key='Name') -importer.import_data(0) -s1 = SamplePath(importer=importer) -s1.build_trajectories() -s1.build_structure() -se1 = StructureConstraintBasedEstimator(sample_path=s1, exp_test_alfa=0.1, chi_test_alfa=0.1, - known_edges=[], thumb_threshold=25) -edges = se1.estimate_structure(True) -# se1.save_plot_estimated_structure_graph('./result1.png') -print(se1.adjacency_matrix()) -print(edges) \ No newline at end of file +if __name__ == "__main__": + main() \ No newline at end of file